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

Documentation generated on Sat, 07 Oct 2006 16:30:57 +0300 by phpDocumentor 1.3.0RC6