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

Documentation generated on Wed, 22 May 2013 04:22:12 +0200 by phpDocumentor 1.4.3