Source for file Deliver_SMTP.class.php

Documentation is available at Deliver_SMTP.class.php

  1. <?php
  2.  
  3. /**
  4.  * Deliver_SMTP.class.php
  5.  *
  6.  * SMTP delivery backend for the Deliver class.
  7.  *
  8.  * @copyright 1999-2014 The SquirrelMail Project Team
  9.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10.  * @version $Id: Deliver_SMTP.class.php 14439 2014-01-23 00:37:12Z pdontthink $
  11.  * @package squirrelmail
  12.  */
  13.  
  14. /** This of course depends upon Deliver */
  15. require_once(SM_PATH 'class/deliver/Deliver.class.php');
  16.  
  17. /**
  18.  * Deliver messages using SMTP
  19.  * @package squirrelmail
  20.  */
  21. class Deliver_SMTP extends Deliver {
  22.  
  23.     function preWriteToStream(&$s{
  24.         if ($s{
  25.             if ($s{0== '.')   $s '.' $s;
  26.             $s str_replace("\n.","\n..",$s);
  27.         }
  28.     }
  29.  
  30.     function initStream($message$domain$length=0$host=''$port=''$user=''$pass=''$authpop=false$pop_host=''$stream_options=array()) {
  31.         global $use_smtp_tls$smtp_auth_mech;
  32.  
  33.         if ($authpop{
  34.             $this->authPop($pop_host''$user$pass);
  35.         }
  36.  
  37.         $rfc822_header $message->rfc822_header;
  38.  
  39.         $from $rfc822_header->from[0];
  40.         $to =   $rfc822_header->to;
  41.         $cc =   $rfc822_header->cc;
  42.         $bcc =  $rfc822_header->bcc;
  43.         $content_type  $rfc822_header->content_type;
  44.  
  45.         // MAIL FROM: <from address> MUST be empty in cae of MDN (RFC2298)
  46.         if ($content_type->type0 == 'multipart' &&
  47.             $content_type->type1 == 'report' &&
  48.             isset($content_type->properties['report-type']&&
  49.             $content_type->properties['report-type']=='disposition-notification'{
  50.             // reinitialize the from object because otherwise the from header somehow
  51.             // is affected. This $from var is used for smtp command MAIL FROM which
  52.             // is not the same as what we put in the rfc822 header.
  53.             $from new AddressStructure();
  54.             $from->host '';
  55.             $from->mailbox '';
  56.         }
  57.  
  58.         // NB: Using "ssl://" ensures the highest possible TLS version
  59.         // will be negotiated with the server (whereas "tls://" only
  60.         // uses TLS version 1.0)
  61.         //
  62.         if (($use_smtp_tls == trueand (check_php_version(4,3)) and (extension_loaded('openssl'))) {
  63.             if (function_exists('stream_socket_client')) {
  64.                 $server_address 'ssl://' $host ':' $port;
  65.                 $ssl_context @stream_context_create($stream_options);
  66.                 $connect_timeout ini_get('default_socket_timeout');
  67.                 // null timeout is broken
  68.                 if ($connect_timeout == 0)
  69.                     $connect_timeout 30;
  70.                 $stream @stream_socket_client($server_address$errorNumber$errorString$connect_timeoutSTREAM_CLIENT_CONNECT$ssl_context);
  71.             else {
  72.                 $stream @fsockopen('ssl://' $host$port$errorNumber$errorString);
  73.             }
  74.         else {
  75.             $stream @fsockopen($host$port$errorNumber$errorString);
  76.         }
  77.  
  78.         if (!$stream{
  79.             $this->dlv_msg $errorString;
  80.             $this->dlv_ret_nr $errorNumber;
  81.             $this->dlv_server_msg _("Can't open SMTP stream.");
  82.             return(0);
  83.         }
  84.         $tmp fgets($stream1024);
  85.         if ($this->errorCheck($tmp$stream)) {
  86.             return(0);
  87.         }
  88.  
  89.         /*
  90.          * If $_SERVER['HTTP_HOST'] is set, use that in our HELO to the SMTP
  91.          * server.  This should fix the DNS issues some people have had
  92.          */
  93.         if (sqgetGlobalVar('HTTP_HOST'$HTTP_HOSTSQ_SERVER)) // HTTP_HOST is set
  94.             // optionally trim off port number
  95.             if($p strrpos($HTTP_HOST':')) {
  96.                 $HTTP_HOST substr($HTTP_HOST0$p);
  97.             }
  98.             $helohost $HTTP_HOST;
  99.         else // For some reason, HTTP_HOST is not set - revert to old behavior
  100.             $helohost $domain;
  101.         }
  102.  
  103.         // if the host is an IPv4 address, enclose it in brackets
  104.         //
  105.         if (preg_match('/^\d+\.\d+\.\d+\.\d+$/'$helohost))
  106.             $helohost '[' $helohost ']';
  107.  
  108.         $hook_result do_hook_function('smtp_helo_override'$helohost);
  109.         if (!empty($hook_result)) $helohost $hook_result;
  110.  
  111.         /* Lets introduce ourselves */
  112.         fputs($stream"EHLO $helohost\r\n");
  113.         $tmp fgets($stream,1024);
  114.         if ($this->errorCheck($tmp,$stream)) {
  115.             // fall back to HELO if EHLO is not supported (error 5xx)
  116.             if ($this->dlv_ret_nr{0== '5'{
  117.                 fputs($stream"HELO $helohost\r\n");
  118.                 $tmp fgets($stream,1024);
  119.                 if ($this->errorCheck($tmp,$stream)) {
  120.                     return(0);
  121.                 }
  122.             else {
  123.                 return(0);
  124.             }
  125.         }
  126.  
  127.         // Try authentication by a plugin
  128.         //
  129.         // NOTE: there is another hook in functions/auth.php called "smtp_auth"
  130.         // that allows a plugin to specify a different set of login credentials
  131.         // (so is slightly mis-named, but is too old to change), so be careful
  132.         // that you do not confuse your hook names.
  133.         //
  134.         $smtp_auth_args array(
  135.             'auth_mech' => $smtp_auth_mech,
  136.             'user' => $user,
  137.             'pass' => $pass,
  138.             'host' => $host,
  139.             'port' => $port,
  140.             'stream' => $stream,
  141.         );
  142.         if (boolean_hook_function('smtp_authenticate'$smtp_auth_args1)) {
  143.             // authentication succeeded
  144.         else if (( $smtp_auth_mech == 'cram-md5'or $smtp_auth_mech == 'digest-md5' )) {
  145.             // Doing some form of non-plain auth
  146.             if ($smtp_auth_mech == 'cram-md5'{
  147.                 fputs($stream"AUTH CRAM-MD5\r\n");
  148.             elseif ($smtp_auth_mech == 'digest-md5'{
  149.                 fputs($stream"AUTH DIGEST-MD5\r\n");
  150.             }
  151.  
  152.             $tmp fgets($stream,1024);
  153.  
  154.             if ($this->errorCheck($tmp,$stream)) {
  155.                 return(0);
  156.             }
  157.  
  158.             // At this point, $tmp should hold "334 <challenge string>"
  159.             $chall substr($tmp,4);
  160.             // Depending on mechanism, generate response string
  161.             if ($smtp_auth_mech == 'cram-md5'{
  162.                 $response cram_md5_response($user,$pass,$chall);
  163.             elseif ($smtp_auth_mech == 'digest-md5'{
  164.                 $response digest_md5_response($user,$pass,$chall,'smtp',$host);
  165.             }
  166.             fputs($stream$response);
  167.  
  168.             // Let's see what the server had to say about that
  169.             $tmp fgets($stream,1024);
  170.             if ($this->errorCheck($tmp,$stream)) {
  171.                 return(0);
  172.             }
  173.  
  174.             // CRAM-MD5 is done at this point.  If DIGEST-MD5, there's a bit more to go
  175.             if ($smtp_auth_mech == 'digest-md5'{
  176.                 // $tmp contains rspauth, but I don't store that yet. (No need yet)
  177.                 fputs($stream,"\r\n");
  178.                 $tmp fgets($stream,1024);
  179.  
  180.                 if ($this->errorCheck($tmp,$stream)) {
  181.                 return(0);
  182.                 }
  183.             }
  184.         // CRAM-MD5 and DIGEST-MD5 code ends here
  185.         elseif ($smtp_auth_mech == 'none'{
  186.         // No auth at all, just send helo and then send the mail
  187.         // We already said hi earlier, nothing more is needed.
  188.         elseif ($smtp_auth_mech == 'login'{
  189.             // The LOGIN method
  190.             fputs($stream"AUTH LOGIN\r\n");
  191.             $tmp fgets($stream1024);
  192.  
  193.             if ($this->errorCheck($tmp$stream)) {
  194.                 return(0);
  195.             }
  196.             fputs($streambase64_encode ($user"\r\n");
  197.             $tmp fgets($stream1024);
  198.             if ($this->errorCheck($tmp$stream)) {
  199.                 return(0);
  200.             }
  201.  
  202.             fputs($streambase64_encode($pass"\r\n");
  203.             $tmp fgets($stream1024);
  204.             if ($this->errorCheck($tmp$stream)) {
  205.                 return(0);
  206.             }
  207.         elseif ($smtp_auth_mech == "plain"{
  208.             /* SASL Plain */
  209.             $auth base64_encode("$user\0$user\0$pass");
  210.  
  211.             $query "AUTH PLAIN\r\n";
  212.             fputs($stream$query);
  213.             $read=fgets($stream1024);
  214.  
  215.             if (substr($read,0,3== '334'// OK so far..
  216.                 fputs($stream"$auth\r\n");
  217.                 $read fgets($stream1024);
  218.             }
  219.  
  220.             $results=explode(" ",$read,3);
  221.             $response=$results[1];
  222.             $message=$results[2];
  223.         else {
  224.             /* Right here, they've reached an unsupported auth mechanism.
  225.             This is the ugliest hack I've ever done, but it'll do till I can fix
  226.             things up better tomorrow.  So tired... */
  227.             if ($this->errorCheck("535 Unable to use this auth type",$stream)) {
  228.                 return(0);
  229.             }
  230.         }
  231.  
  232.         /* Ok, who is sending the message? */
  233.         $fromaddress (strlen($from->mailbox&& $from->host?
  234.             $from->mailbox.'@'.$from->host '';
  235.         fputs($stream'MAIL FROM:<'.$fromaddress.">\r\n");
  236.         $tmp fgets($stream1024);
  237.         if ($this->errorCheck($tmp$stream)) {
  238.             return(0);
  239.         }
  240.  
  241.         /* send who the recipients are */
  242.         for ($i 0$cnt count($to)$i $cnt$i++{
  243.             if (!$to[$i]->host$to[$i]->host $domain;
  244.             if (strlen($to[$i]->mailbox)) {
  245.                 fputs($stream'RCPT TO:<'.$to[$i]->mailbox.'@'.$to[$i]->host.">\r\n");
  246.                 $tmp fgets($stream1024);
  247.                 if ($this->errorCheck($tmp$stream)) {
  248.                     return(0);
  249.                 }
  250.             }
  251.         }
  252.  
  253.         for ($i 0$cnt count($cc)$i $cnt$i++{
  254.             if (!$cc[$i]->host$cc[$i]->host $domain;
  255.             if (strlen($cc[$i]->mailbox)) {
  256.                 fputs($stream'RCPT TO:<'.$cc[$i]->mailbox.'@'.$cc[$i]->host.">\r\n");
  257.                 $tmp fgets($stream1024);
  258.                 if ($this->errorCheck($tmp$stream)) {
  259.                     return(0);
  260.                 }
  261.             }
  262.         }
  263.  
  264.         for ($i 0$cnt count($bcc)$i $cnt$i++{
  265.             if (!$bcc[$i]->host$bcc[$i]->host $domain;
  266.             if (strlen($bcc[$i]->mailbox)) {
  267.                 fputs($stream'RCPT TO:<'.$bcc[$i]->mailbox.'@'.$bcc[$i]->host.">\r\n");
  268.                 $tmp fgets($stream1024);
  269.                 if ($this->errorCheck($tmp$stream)) {
  270.                     return(0);
  271.                 }
  272.             }
  273.         }
  274.         /* Lets start sending the actual message */
  275.         fputs($stream"DATA\r\n");
  276.         $tmp fgets($stream1024);
  277.         if ($this->errorCheck($tmp$stream)) {
  278.                 return(0);
  279.         }
  280.         return $stream;
  281.     }
  282.  
  283.     function finalizeStream($stream{
  284.         fputs($stream"\r\n.\r\n")/* end the DATA part */
  285.         $tmp fgets($stream1024);
  286.         $this->errorCheck($tmp$stream);
  287.         if ($this->dlv_ret_nr != 250{
  288.                 return(0);
  289.         }
  290.         fputs($stream"QUIT\r\n")/* log off */
  291.         fclose($stream);
  292.         return true;
  293.     }
  294.  
  295.     /* check if an SMTP reply is an error and set an error message) */
  296.     function errorCheck($line$smtpConnection{
  297.  
  298.         $err_num substr($line03);
  299.         $this->dlv_ret_nr $err_num;
  300.         $server_msg substr($line4);
  301.  
  302.         while(substr($line04== ($err_num.'-')) {
  303.             $line fgets($smtpConnection1024);
  304.             $server_msg .= substr($line4);
  305.         }
  306.  
  307.         if ( ((int) $err_num{0}4{
  308.             return false;
  309.         }
  310.  
  311.         switch ($err_num{
  312.         case '421'$message _("Service not available, closing channel");
  313.             break;
  314.         case '432'$message _("A password transition is needed");
  315.             break;
  316.         case '450'$message _("Requested mail action not taken: mailbox unavailable");
  317.             break;
  318.         case '451'$message _("Requested action aborted: error in processing");
  319.             break;
  320.         case '452'$message _("Requested action not taken: insufficient system storage");
  321.             break;
  322.         case '454'$message _("Temporary authentication failure");
  323.             break;
  324.         case '500'$message _("Syntax error; command not recognized");
  325.             break;
  326.         case '501'$message _("Syntax error in parameters or arguments");
  327.             break;
  328.         case '502'$message _("Command not implemented");
  329.             break;
  330.         case '503'$message _("Bad sequence of commands");
  331.             break;
  332.         case '504'$message _("Command parameter not implemented");
  333.             break;
  334.         case '530'$message _("Authentication required");
  335.             break;
  336.         case '534'$message _("Authentication mechanism is too weak");
  337.             break;
  338.         case '535'$message _("Authentication failed");
  339.             break;
  340.         case '538'$message _("Encryption required for requested authentication mechanism");
  341.             break;
  342.         case '550'$message _("Requested action not taken: mailbox unavailable");
  343.             break;
  344.         case '551'$message _("User not local; please try forwarding");
  345.             break;
  346.         case '552'$message _("Requested mail action aborted: exceeding storage allocation");
  347.             break;
  348.         case '553'$message _("Requested action not taken: mailbox name not allowed");
  349.             break;
  350.         case '554'$message _("Transaction failed");
  351.             break;
  352.         default:    $message _("Unknown response");
  353.             break;
  354.         }
  355.  
  356.         $this->dlv_msg $message;
  357.         $this->dlv_server_msg nl2br(sm_encode_html_special_chars($server_msg));
  358.  
  359.         return true;
  360.     }
  361.  
  362.     function authPop($pop_server=''$pop_port=''$user$pass{
  363.         if (!$pop_port{
  364.             $pop_port 110;
  365.         }
  366.         if (!$pop_server{
  367.             $pop_server 'localhost';
  368.         }
  369.         $popConnection @fsockopen($pop_server$pop_port$err_no$err_str);
  370.         if (!$popConnection{
  371.             error_log("Error connecting to POP Server ($pop_server:$pop_port)"
  372.                 . " $err_no : $err_str");
  373.         else {
  374.             $tmp fgets($popConnection1024)/* banner */
  375.             if (substr($tmp03!= '+OK'{
  376.                 return(0);
  377.             }
  378.             fputs($popConnection"USER $user\r\n");
  379.             $tmp fgets($popConnection1024);
  380.             if (substr($tmp03!= '+OK'{
  381.                 return(0);
  382.             }
  383.             fputs($popConnection'PASS ' $pass "\r\n");
  384.             $tmp fgets($popConnection1024);
  385.             if (substr($tmp03!= '+OK'{
  386.                 return(0);
  387.             }
  388.             fputs($popConnection"QUIT\r\n")/* log off */
  389.             fclose($popConnection);
  390.         }
  391.     }
  392. }

Documentation generated on Wed, 30 Jul 2014 04:20:45 +0200 by phpDocumentor 1.4.3