1. <?php
  2. final class Mail {
  3.     protected $to;
  4.     protected $from;
  5.     protected $sender;
  6.     protected $subject;
  7.     protected $text;
  8.     protected $html;
  9.     protected $attachments = array();
  10.     public $protocol = 'mail';
  11.     public $hostname;
  12.     public $username;
  13.     public $password;
  14.     public $port = 25;
  15.     public $timeout = 5;
  16.     public $newline = "\n";
  17.     public $crlf = "\r\n";
  18.     public $verp = FALSE;
  19.     public $parameter = '';
  20.    
  21.     public function setTo($to) {
  22.         $this->to = $to;
  23.     }
  24.    
  25.     public function setFrom($from) {
  26.         $this->from = $from;
  27.     }
  28.    
  29.     public function addheader($header, $value) {
  30.         $this->headers[$header] = $value;
  31.     }
  32.    
  33.     public function setSender($sender) {
  34.         $this->sender = $sender;
  35.     }
  36.    
  37.     public function setSubject($subject) {
  38.         $this->subject = $subject;
  39.     }
  40.    
  41.     public function setText($text) {
  42.         $this->text = $text;
  43.     }
  44.    
  45.     public function setHtml($html) {
  46.         $this->html = $html;
  47.     }
  48.    
  49.     public function addAttachment($file, $filename = '') {
  50.         if (!$filename) {
  51.             $filename = basename($file);
  52.         }
  53.      
  54.         $this->attachments[] = array(
  55.             'filename' => $filename,
  56.             'file'     => $file
  57.         );
  58.     }
  59.    
  60.     public function send() {  
  61.         if (!$this->to) {
  62.             exit('Error: E-Mail to required!');
  63.         }
  64.    
  65.         if (!$this->from) {
  66.             exit('Error: E-Mail from required!');
  67.         }
  68.    
  69.         if (!$this->sender) {
  70.             exit('Error: E-Mail sender required!');
  71.         }
  72.    
  73.         if (!$this->subject) {
  74.             exit('Error: E-Mail subject required!');
  75.         }
  76.    
  77.         if ((!$this->text) && (!$this->html)) {
  78.             exit('Error: E-Mail message required!');
  79.         }
  80.    
  81.         if (is_array($this->to)) {
  82.             $to = implode(',', $this->to);
  83.         } else {
  84.             $to = $this->to;
  85.         }
  86.    
  87.         $boundary = '----=_NextPart_' . md5(time());
  88.    
  89.         $header = '';
  90.    
  91.         if ($this->protocol != 'mail') {
  92.             $header .= 'To: ' . $to . $this->newline;
  93.             $header .= 'Subject: ' . $this->subject . $this->newline;
  94.         }
  95.    
  96.         //$header .= 'From: ' . $this->sender . '<' . $this->from . '>' . $this->newline;
  97.         $header .= 'From: "' . $this->sender . '" <' . $this->from . '>' . $this->newline;
  98.         $header .= 'Reply-To: ' . $this->sender . '<' . $this->from . '>' . $this->newline;  
  99.         $header .= 'Return-Path: ' . $this->from . $this->newline;
  100.         $header .= 'X-Mailer: PHP/' . phpversion() . $this->newline;
  101.         $header .= 'MIME-Version: 1.0' . $this->newline;
  102.         $header .= 'Content-Type: multipart/mixed; boundary="' . $boundary . '"' . $this->newline;
  103.    
  104.         if (!$this->html) {
  105.             $message  = '--' . $boundary . $this->newline;
  106.             $message .= 'Content-Type: text/plain; charset="utf-8"' . $this->newline;
  107.             $message .= 'Content-Transfer-Encoding: 8bit' . $this->newline . $this->newline;
  108.             $message .= $this->text . $this->newline;
  109.         } else {
  110.             $message  = '--' . $boundary . $this->newline;
  111.             $message .= 'Content-Type: multipart/alternative; boundary="' . $boundary . '_alt"' . $this->newline . $this->newline;
  112.             $message .= '--' . $boundary . '_alt' . $this->newline;
  113.             $message .= 'Content-Type: text/plain; charset="utf-8"' . $this->newline;
  114.             $message .= 'Content-Transfer-Encoding: 8bit' . $this->newline;
  115.        
  116.             if ($this->text) {
  117.                 $message .= $this->text . $this->newline;
  118.             } else {
  119.                 $message .= 'This is a HTML email and your email client software does not support HTML email!' . $this->newline;
  120.             }  
  121.        
  122.             $message .= '--' . $boundary . '_alt' . $this->newline;
  123.             $message .= 'Content-Type: text/html; charset="utf-8"' . $this->newline;
  124.             $message .= 'Content-Transfer-Encoding: 8bit' . $this->newline . $this->newline;
  125.             $message .= $this->html . $this->newline;
  126.             $message .= '--' . $boundary . '_alt--' . $this->newline;      
  127.         }
  128.    
  129.         foreach ($this->attachments as $attachment) {
  130.             if (file_exists($attachment['file'])) {
  131.                 $handle = fopen($attachment['file'], 'r');
  132.                 $content = fread($handle, filesize($attachment['file']));
  133.        
  134.                 fclose($handle);
  135.        
  136.                 $message .= '--' . $boundary . $this->newline;
  137.                 $message .= 'Content-Type: application/octetstream' . $this->newline;  
  138.                 $message .= 'Content-Transfer-Encoding: base64' . $this->newline;
  139.                 $message .= 'Content-Disposition: attachment; filename="' . basename($attachment['filename']) . '"' . $this->newline;
  140.                 $message .= 'Content-ID: <' . basename($attachment['filename']) . '>' . $this->newline . $this->newline;
  141.                 $message .= chunk_split(base64_encode($content));
  142.             }
  143.         }
  144.    
  145.         $message .= '--' . $boundary . '--' . $this->newline;
  146.    
  147.         if ($this->protocol == 'mail') {
  148.             ini_set('sendmail_from', $this->from);
  149.    
  150.             if ($this->parameter) {
  151.                 mail($to, $this->subject, $message, $header, $this->parameter);
  152.             } else {
  153.                 mail($to, $this->subject, $message, $header);
  154.             }
  155.            
  156.         } elseif ($this->protocol == 'smtp') {
  157.             $handle = fsockopen($this->hostname, $this->port, $errno, $errstr, $this->timeout);  
  158.    
  159.             if (!$handle) {
  160.                 error_log('Error: ' . $errstr . ' (' . $errno . ')');
  161.             } else {
  162.                 if (substr(PHP_OS, 0, 3) != 'WIN') {
  163.                     socket_set_timeout($handle, $this->timeout, 0);
  164.                 }
  165.        
  166.                 while ($line = fgets($handle, 515)) {
  167.                     if (substr($line, 3, 1) == ' ') {
  168.                         break;
  169.                     }
  170.                 }
  171.        
  172.                 if (substr($this->hostname, 0, 3) == 'tls') {
  173.                     fputs($handle, 'STARTTLS' . $this->crlf);
  174.                
  175.                     while ($line = fgets($handle, 515)) {
  176.                         $reply .= $line;
  177.                    
  178.                         if (substr($line, 3, 1) == ' ') {
  179.                             break;
  180.                         }
  181.                     }
  182.        
  183.                     if (substr($reply, 0, 3) != 220) {
  184.                         error_log('Error: STARTTLS not accepted from server!');
  185.                     }              
  186.                 }
  187.        
  188.                 if (!empty($this->username)  && !empty($this->password)) {
  189.                     fputs($handle, 'EHLO ' . getenv('SERVER_NAME') . $this->crlf);
  190.                
  191.                     $reply = '';
  192.                
  193.                     while ($line = fgets($handle, 515)) {
  194.                         $reply .= $line;
  195.                
  196.                         if (substr($line, 3, 1) == ' ') {
  197.                             break;
  198.                         }
  199.                     }
  200.        
  201.                     if (substr($reply, 0, 3) != 250) {
  202.                         error_log('Error: EHLO not accepted from server!');
  203.                     }
  204.        
  205.                     fputs($handle, 'AUTH LOGIN' . $this->crlf);
  206.        
  207.                     $reply = '';
  208.        
  209.                     while ($line = fgets($handle, 515)) {
  210.                         $reply .= $line;
  211.                    
  212.                         if (substr($line, 3, 1) == ' ') {
  213.                             break;
  214.                         }
  215.                     }
  216.        
  217.                     if (substr($reply, 0, 3) != 334) {
  218.                         error_log('Error: AUTH LOGIN not accepted from server!');
  219.                     }
  220.        
  221.                     fputs($handle, base64_encode($this->username) . $this->crlf);
  222.        
  223.                     $reply = '';
  224.        
  225.                     while ($line = fgets($handle, 515)) {
  226.                         $reply .= $line;
  227.                        
  228.                         if (substr($line, 3, 1) == ' ') {
  229.                             break;
  230.                         }
  231.                     }
  232.        
  233.                     if (substr($reply, 0, 3) != 334) {
  234.                         error_log('Error: Username not accepted from server!');
  235.                     }            
  236.        
  237.                     fputs($handle, base64_encode($this->password) . $this->crlf);
  238.        
  239.                     $reply = '';
  240.        
  241.                     while ($line = fgets($handle, 515)) {
  242.                         $reply .= $line;
  243.                    
  244.                         if (substr($line, 3, 1) == ' ') {
  245.                             break;
  246.                         }
  247.                     }
  248.        
  249.                     if (substr($reply, 0, 3) != 235) {
  250.                         error_log('Error: Password not accepted from server!');              
  251.                     }  
  252.                 } else {
  253.                     fputs($handle, 'HELO ' . getenv('SERVER_NAME') . $this->crlf);
  254.        
  255.                     $reply = '';
  256.        
  257.                     while ($line = fgets($handle, 515)) {
  258.                         $reply .= $line;
  259.                    
  260.                         if (substr($line, 3, 1) == ' ') {
  261.                             break;
  262.                         }
  263.                     }
  264.        
  265.                     if (substr($reply, 0, 3) != 250) {
  266.                         error_log('Error: HELO not accepted from server!');
  267.                     }            
  268.                 }
  269.                
  270.                 if ($this->verp) {
  271.                     fputs($handle, 'MAIL FROM: <' . $this->from . '>XVERP' . $this->crlf);
  272.                 } else {
  273.                     fputs($handle, 'MAIL FROM: <' . $this->from . '>' . $this->crlf);
  274.                 }
  275.                
  276.                 $reply = '';
  277.                
  278.                 while ($line = fgets($handle, 515)) {
  279.                     $reply .= $line;
  280.                
  281.                     if (substr($line, 3, 1) == ' ') {
  282.                         break;
  283.                     }
  284.                 }
  285.        
  286.                 if (substr($reply, 0, 3) != 250) {
  287.                     error_log('Error: MAIL FROM not accepted from server!');
  288.                 }
  289.        
  290.                 if (!is_array($this->to)) {
  291.                     fputs($handle, 'RCPT TO: <' . $this->to . '>' . $this->crlf);
  292.                
  293.                     $reply = '';
  294.                
  295.                     while ($line = fgets($handle, 515)) {
  296.                         $reply .= $line;
  297.                
  298.                         if (substr($line, 3, 1) == ' ') {
  299.                             break;
  300.                         }
  301.                     }
  302.        
  303.                     if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) {
  304.                         error_log('Error: RCPT TO not accepted from server!');
  305.                     }        
  306.                 } else {
  307.                     foreach ($this->to as $recipient) {
  308.                         fputs($handle, 'RCPT TO: <' . $recipient . '>' . $this->crlf);
  309.        
  310.                         $reply = '';
  311.        
  312.                         while ($line = fgets($handle, 515)) {
  313.                             $reply .= $line;
  314.                            
  315.                             if (substr($line, 3, 1) == ' ') {
  316.                                 break;
  317.                             }
  318.                         }
  319.        
  320.                         if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) {
  321.                             error_log('Error: RCPT TO not accepted from server!');
  322.                         }                  
  323.                     }
  324.                 }
  325.                
  326.                 fputs($handle, 'DATA' . $this->crlf);
  327.                
  328.                 $reply = '';
  329.                    
  330.                 while ($line = fgets($handle, 515)) {
  331.                     $reply .= $line;
  332.                    
  333.                     if (substr($line, 3, 1) == ' ') {
  334.                         break;
  335.                     }
  336.                 }
  337.            
  338.                 if (substr($reply, 0, 3) != 354) {
  339.                     error_log('Error: DATA not accepted from server!');
  340.                 }
  341.            
  342.                 fputs($handle, $header . $message . $this->crlf);
  343.                 fputs($handle, '.' . $this->crlf);
  344.            
  345.                 $reply = '';
  346.            
  347.                 while ($line = fgets($handle, 515)) {
  348.                     $reply .= $line;
  349.                
  350.                     if (substr($line, 3, 1) == ' ') {
  351.                         break;
  352.                     }
  353.                 }
  354.            
  355.                 if (substr($reply, 0, 3) != 250) {
  356.                     error_log('Error: DATA not accepted from server!');
  357.                 }
  358.            
  359.                 fputs($handle, 'QUIT' . $this->crlf);
  360.                
  361.                 $reply = '';
  362.                
  363.                 while ($line = fgets($handle, 515)) {
  364.                     $reply .= $line;
  365.                
  366.                     if (substr($line, 3, 1) == ' ') {
  367.                         break;
  368.                     }
  369.                 }
  370.            
  371.                 if (substr($reply, 0, 3) != 221) {
  372.                     error_log('Error: QUIT not accepted from server!');
  373.                 }        
  374.            
  375.                 fclose($handle);
  376.             }
  377.         }
  378.     }
  379. }
  380. ?>