Файл: system/libs/mail.class.php
Строк: 433
<?php
class Mail
{
    /*     определение переменных идет через VAR, для обеспечения работы в php старых версий
    массивы адресов кому отправить
    @var    array
    */
    var $sendto = array();
    /*
    @var    array
    */
    var  $acc = array();
    /*
    @var    array
    */
    var $abcc = array();
    /*
    прикрепляемые файлы
    @var array
    */
    var $aattach = array();
    /*
    массив заголовков
    @var array
    */
    var $xheaders = array();
    /*
    приоритеты
    @var array
    */
    var $priorities = array( '1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)' );
    /*
    кодировка по умолчанию
    @var string
    */
    var  $charset = "windows-1251";
    var  $ctencoding = "8bit";
    var  $receipt = 0;
    var  $text_html="text/plain"; // формат письма. по умолчанию текстовый
    var  $smtp_on=false;    // отправка через smtp. по умолчанию выключена
    var  $names_email = array(); // имена для email адресов, чтобы делать вид ("сергей" <asd@wer.re>)
    /*
    конструктор тоже по старому объявлен для совместимости со старыми версиями php
    пошел конструктор.
    входящий параметр кодировка письма
    внесено изменение webi.ru
    */
    function Mail($charset="")
    {
        $this->autoCheck( true );
        $this->boundary= "--" . md5( uniqid("myboundary") );
        if( $charset != "" ) {
            $this->charset = strtolower($charset);
            if( $this->charset == "us-ascii" )
            $this->ctencoding = "7bit";
        }
    }
    /*
    включение выключение проверки валидности email
    пример: autoCheck( true ) проверка влючена
    по умолчанию проверка включена
    */
    function autoCheck( $bool )
    {
        if( $bool )
        $this->checkAddress = true;
        else
        $this->checkAddress = false;
    }
    /*
    Тема письма
    внесено изменения кодирования не латинских символов
    */
    function Subject( $subject )
    {
        $this->xheaders['Subject'] ="=?".$this->charset."?Q?".str_replace("+","_",str_replace("%","=",urlencode(strtr( $subject, "rn" , "  " ))))."?=";
    }
    /*
    от кого
    */
    function From( $from )
    {
        if( ! is_string($from) ) {
            echo "ошибка, From должен быть строкой";
            exit;
        }
        $temp_mass=explode(';',$from); // разбиваем по разделителю для выделения имени
        if(count($temp_mass)==2) // если удалось разбить на два элемента
        {
            $this->names_email['from']=$temp_mass[0]; // имя первая часть
            $this->xheaders['From'] = $temp_mass[1]; // адрес вторая часть
        }
        else // и если имя не определено
        {
            $this->names_email['from']='';
            $this->xheaders['From'] = $from;
        }
    }
    /*
    на какой адрес отвечать
    */
    function ReplyTo( $address )
    {
        if( ! is_string($address) )
        return false;
        $temp_mass=explode(';',$address); // разбиваем по разделителю для выделения имени
        if(count($temp_mass)==2) // если удалось разбить на два элемента
        {
            $this->names_email['Reply-To']=$temp_mass[0]; // имя первая часть
            $this->xheaders['Reply-To'] = $temp_mass[1]; // адрес вторая часть
        }
        else // и если имя не определено
        {
            $this->names_email['Reply-To']='';
            $this->xheaders['Reply-To'] = $address;
        }
    }
    /*
    Добавление заголовка для получения уведомления о прочтении. обратный адрес берется из "From" (или из "ReplyTo" если указан)
    */
    function Receipt()
    {
        $this->receipt = 1;
    }
    /*
    set the mail recipient
    @param string $to email address, accept both a single address or an array of addresses
    */
    function To( $to )
    {
        // если это массив
        if( is_array( $to ) )
        {
            foreach ($to as $key => $value) // перебираем массив и добавляем в массив для отправки через smtp
            {
                $temp_mass=explode(';',$value); // разбиваем по разделителю для выделения имени
                if(count($temp_mass)==2) // если удалось разбить на два элемента
                {                    
                    $this->smtpsendto[$temp_mass[1]] = $temp_mass[1]; // ключи и значения одинаковые, чтобы исключить дубли адресов
                    $this->names_email['To'][$temp_mass[1]]=$temp_mass[0]; // имя первая часть
                    $this->sendto[]= $temp_mass[1];
                }
                else // и если имя не определено
                {
                    $this->smtpsendto[$value] = $value; // ключи и значения одинаковые, чтобы исключить дубли адресов
                    $this->names_email['To'][$value]=''; // имя первая часть
                    $this->sendto[]= $value;
                }
            }
        }
        else
        {
                $temp_mass=explode(';',$to); // разбиваем по разделителю для выделения имени
                if(count($temp_mass)==2) // если удалось разбить на два элемента
                {
                    
                    $this->sendto[] = $temp_mass[1];
                    $this->smtpsendto[$temp_mass[1]] = $temp_mass[1]; // ключи и значения одинаковые, чтобы исключить дубли адресов
                    $this->names_email['To'][$temp_mass[1]]=$temp_mass[0]; // имя первая часть
                }
                else // и если имя не определено
                {
                    
                    $this->sendto[] = $to;
                    $this->smtpsendto[$to] = $to; // ключи и значения одинаковые, чтобы исключить дубли адресов
                    $this->names_email['To'][$to]=''; // имя первая часть
                }
            
            
            
        }
        if( $this->checkAddress == true )
        $this->CheckAdresses( $this->sendto );
    }
    /*        Cc()
    *        установка заголдовка CC ( открытая копия, все получатели будут видеть куда ушла копия )
    *        $cc : email address(es), accept both array and string
    */
    function Cc( $cc )
    {
        if( is_array($cc) )
        {
            $this->acc= $cc;
            foreach ($cc as $key => $value) // перебираем массив и добавляем в массив для отправки через smtp
            {
                $this->smtpsendto[$value] = $value; // ключи и значения одинаковые, чтобы исключить дубли адресов
            }
        }
        else
        {
            $this->acc[]= $cc;
            $this->smtpsendto[$cc] = $cc; // ключи и значения одинаковые, чтобы исключить дубли адресов
        }
        if( $this->checkAddress == true )
        $this->CheckAdresses( $this->acc );
    }
    /*        Bcc()
    *        скрытая копия. не будет помещать заголовок кому ушло письмо
    *        $bcc : email address(es), accept both array and string
    */
    function Bcc( $bcc )
    {
        if( is_array($bcc) )
        {
            $this->abcc = $bcc;
            foreach ($bcc as $key => $value) // перебираем массив и добавляем в массив для отправки через smtp
            {
                $this->smtpsendto[$value] = $value; // ключи и значения одинаковые, чтобы исключить дубли адресов
            }
        }
        else
        {
            $this->abcc[]= $bcc;
            $this->smtpsendto[$bcc] = $bcc; // ключи и значения одинаковые, чтобы исключить дубли адресов
        }
        if( $this->checkAddress == true )
        $this->CheckAdresses( $this->abcc );
    }
    /*        Body( text [ text_html ] )
    *        $text_html в каком формате будет письмо, в тексте или html. по умолчанию стоит текст
    */
    function Body( $body, $text_html="" )
    {
        $this->body = $body;
        if( $text_html == "html" ) $this->text_html = "text/html";
    }
    /*        Organization( $org )
    *        set the Organization header
    */
    function Organization( $org )
    {
        if( trim( $org != "" )  )
        $this->xheaders['Organization'] = $org;
    }
    /*        Priority( $priority )
    *        set the mail priority
    *        $priority : integer taken between 1 (highest) and 5 ( lowest )
    *        ex: $mail->Priority(1) ; => Highest
    */
    function Priority( $priority )
    {
        if( ! intval( $priority ) )
        return false;
        if( ! isset( $this->priorities[$priority-1]) )
        return false;
        $this->xheaders["X-Priority"] = $this->priorities[$priority-1];
        return true;
    }
    /*
    прикрепленные файлы
    @param string $filename : путь к файлу, который надо отправить
    @param string $webi_filename : реальное имя файла. если вдруг вставляется файл временный, то его имя будет хрен пойми каким..
    @param string $filetype : MIME-тип файла. по умолчанию 'application/x-unknown-content-type'
    @param string $disposition : инструкция почтовому клиенту как отображать прикрепленный файл ("inline") как часть письма или ("attachment") как прикрепленный файл
    */
    function Attach( $filename, $webi_filename="", $filetype = "", $disposition = "inline" )
    {
        // TODO : если типа файла не указан, ставим неизвестный тип
        if( $filetype == "" )
        $filetype = "application/x-unknown-content-type";
        $this->aattach[] = $filename;
        $this->webi_filename[] = $webi_filename;
        $this->actype[] = $filetype;
        $this->adispo[] = $disposition;
    }
    /*
    Собираем письмо
    */
    function BuildMail()
    {
        $this->headers = "";
        // создание заголовка TO.
        // добавление имен к адресам
        foreach ($this->sendto as $key => $value) 
        {
            
            if( strlen($this->names_email['To'][$value])) $temp_mass[]="=?".$this->charset."?Q?".str_replace("+","_",str_replace("%","=",urlencode(strtr( $this->names_email['To'][$value], "rn" , "  " ))))."?= <".$value.">";
            else $temp_mass[]=$value;
        }
        
        $this->xheaders['To'] = implode( ", ", $temp_mass ); // этот заголовок будет не нужен при отправке через mail()
        if( count($this->acc) > 0 )
        $this->xheaders['CC'] = implode( ", ", $this->acc );
        if( count($this->abcc) > 0 )
        $this->xheaders['BCC'] = implode( ", ", $this->abcc );  // этот заголовок будет не нужен при отправке через smtp
        if( $this->receipt ) {
            if( isset($this->xheaders["Reply-To"] ) )
            $this->xheaders["Disposition-Notification-To"] = $this->xheaders["Reply-To"];
            else
            $this->xheaders["Disposition-Notification-To"] = $this->xheaders['From'];
        }
        if( $this->charset != "" ) {
            $this->xheaders["Mime-Version"] = "1.0";
            $this->xheaders["Content-Type"] = $this->text_html."; charset=$this->charset";
            $this->xheaders["Content-Transfer-Encoding"] = $this->ctencoding;
        }
        $this->xheaders["X-Mailer"] = "PerfCMS Mailer";
        // вставаляем файлы
        if( count( $this->aattach ) > 0 ) {
            $this->_build_attachement();
        } else {
            $this->fullBody = $this->body;
        }
        // создание заголовков если отправка идет через smtp
        if($this->smtp_on)
        {
            // разбиваем (FROM - от кого) на юзера и домен. домен понадобится в заголовке
            $user_domen=explode('@',$this->xheaders['From']);
            $this->headers = "Date: ".date("D, j M Y G:i:s")." +0700rn";
            $this->headers .= "Message-ID: <".rand().".".date("YmjHis")."@".$user_domen[1].">rn";
            reset($this->xheaders);
            while( list( $hdr,$value ) = each( $this->xheaders )  ) {
                if( $hdr == "From" and strlen($this->names_email['from'])) $this->headers .= $hdr.": =?".$this->charset."?Q?".str_replace("+","_",str_replace("%","=",urlencode(strtr( $this->names_email['from'], "rn" , "  " ))))."?= <".$value.">rn";
                elseif( $hdr == "Reply-To" and strlen($this->names_email['Reply-To'])) $this->headers .= $hdr.": =?".$this->charset."?Q?".str_replace("+","_",str_replace("%","=",urlencode(strtr( $this->names_email['Reply-To'], "rn" , "  " ))))."?= <".$value.">rn";
                elseif( $hdr != "BCC") $this->headers .= $hdr.": ".$value."rn"; // пропускаем заголовок для отправки скрытой копии
            }
        }
        // создание заголовоков, если отправка идет через mail()
        else
        {
            reset($this->xheaders);
            while( list( $hdr,$value ) = each( $this->xheaders )  ) {
                if( $hdr == "From" and strlen($this->names_email['from'])) $this->headers .= $hdr.": =?".$this->charset."?Q?".str_replace("+","_",str_replace("%","=",urlencode(strtr( $this->names_email['from'], "rn" , "  " ))))."?= <".$value.">rn";
                elseif( $hdr == "Reply-To" and strlen($this->names_email['Reply-To'])) $this->headers .= $hdr.": =?".$this->charset."?Q?".str_replace("+","_",str_replace("%","=",urlencode(strtr( $this->names_email['Reply-To'], "rn" , "  " ))))."?= <".$value.">rn";
                elseif( $hdr != "Subject" and $hdr != "To") $this->headers .= "$hdr: $valuen"; // пропускаем заголовки кому и тему... они вставятся сами
            }
        }
    }
    // включение отправки через smtp используя сокеты
    // после запуска этой функции отправка через smtp включена
    // для отправки через защищенное соединение сервер нужно указывать с добавлением "ssl://" например так "ssl://smtp.gmail.com"
    function smtp_on($smtp_serv, $login, $pass, $port=25,$timeout=5)
    {
        $this->smtp_on=true; // включаем отправку через smtp
        $this->smtp_serv=$smtp_serv;
        $this->smtp_login=$login;
        $this->smtp_pass=$pass;
        $this->smtp_port=$port;
        $this->smtp_timeout=$timeout;
    }
    function get_data($smtp_conn)
    {
        $data="";
        while($str = fgets($smtp_conn,515))
        {
            $data .= $str;
            if(substr($str,3,1) == " ") { break; }
        }
        return $data;
    }
    /*
    отправка письма
    */
    function Send()
    {
        $this->BuildMail();
        $this->strTo = implode( ", ", $this->sendto );
        // если отправка без использования smtp
        if(!$this->smtp_on)
        {
            $res = @mail( $this->strTo, $this->xheaders['Subject'], $this->fullBody, $this->headers );
        }
        else // если через smtp
        {
            if (!$this->smtp_serv OR !$this->smtp_login OR !$this->smtp_pass OR !$this->smtp_port) return false; // если нет хотя бы одного из основных данных для коннекта, выходим с ошибкой
            // разбиваем (FROM - от кого) на юзера и домен. юзер понадобится в приветсвии с сервом
            $user_domen=explode('@',$this->xheaders['From']);
            $this->smtp_log='';
            $smtp_conn = fsockopen($this->smtp_serv, $this->smtp_port, $errno, $errstr, $this->smtp_timeout);
            if(!$smtp_conn) {$this->smtp_log .= "соединение с сервером не прошлоnn"; fclose($smtp_conn); return; }
            $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
            fputs($smtp_conn,"EHLO ".$user_domen[0]."rn");
            $this->smtp_log .= "Я: EHLO ".$user_domen[0]."n";
            $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
            $code = substr($data,0,3); // получаем код ответа
            if($code != 250) {$this->smtp_log .= "ошибка приветсвия EHLO n"; fclose($smtp_conn); return; }
            fputs($smtp_conn,"AUTH LOGINrn");
            $this->smtp_log .= "Я: AUTH LOGINn";
            $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
            $code = substr($data,0,3);
            if($code != 334) {$this->smtp_log .= "сервер не разрешил начать авторизацию n"; fclose($smtp_conn); return;}
            fputs($smtp_conn,base64_encode($this->smtp_login)."rn");
            $this->smtp_log .= "Я: ".base64_encode($this->smtp_login)."n";
            $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
            $code = substr($data,0,3);
            if($code != 334) {$this->smtp_log .= "ошибка доступа к такому юзеруn"; fclose($smtp_conn); return ;}
            fputs($smtp_conn,base64_encode($this->smtp_pass)."rn");
            $this->smtp_log .="Я: ". base64_encode($this->smtp_pass)."n";
            $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
            $code = substr($data,0,3);
            if($code != 235) {$this->smtp_log .= "не правильный парольn"; fclose($smtp_conn); return ;}
            fputs($smtp_conn,"MAIL FROM:<".$this->xheaders['From']."> SIZE=".strlen($this->headers."rn".$this->fullBody)."rn");
            $this->smtp_log .= "Я: MAIL FROM:<".$this->xheaders['From']."> SIZE=".strlen($this->headers."rn".$this->fullBody)."n";
            $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
            $code = substr($data,0,3);
            if($code != 250) {$this->smtp_log .= "сервер отказал в команде MAIL FROMn"; fclose($smtp_conn); return ;}
            foreach ($this->smtpsendto as $keywebi => $valuewebi)
            {
                fputs($smtp_conn,"RCPT TO:<".$valuewebi.">rn");
                $this->smtp_log .= "Я: RCPT TO:<".$valuewebi.">n";
                $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
                $code = substr($data,0,3);
                if($code != 250 AND $code != 251) {$this->smtp_log .= "Сервер не принял команду RCPT TOn"; fclose($smtp_conn); return ;}
            }
            fputs($smtp_conn,"DATArn");
            $this->smtp_log .="Я: DATAn";
            $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
            $code = substr($data,0,3);
            if($code != 354) {$this->smtp_log .= "сервер не принял DATAn"; fclose($smtp_conn); return ;}
            fputs($smtp_conn,$this->headers."rn".$this->fullBody."rn.rn");
            $this->smtp_log .= "Я: ".$this->headers."rn".$this->fullBody."rn.rn";
            $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
            $code = substr($data,0,3);
            if($code != 250) {$this->smtp_log .= "ошибка отправки письмаn"; fclose($smtp_conn); return ;}
            fputs($smtp_conn,"QUITrn");
            $this->smtp_log .="QUITrn";
            $this->smtp_log .= $data = $this->get_data($smtp_conn)."n";
            fclose($smtp_conn);
        }
    }
    /*
    *        показывает что было отправлено
    *
    */
    function Get()
    {
        if(isset($this->smtp_log))
        {
            if ($this->smtp_log)
            {
                return $this->smtp_log; // если есть лог отправки smtp выведем его
            }
        }
        $this->BuildMail();
        $mail = $this->headers . "nn";
        $mail .= $this->fullBody;
        return $mail;
    }
    /*
    проверка мыла
    возвращает true или false
    */
    function ValidEmail($address)
    {
        // если существует современная функция фильтрации данных, то проверять будем этой функцией. появилась в php 5.2
        if (function_exists('filter_list'))
        {
            $valid_email = filter_var($address, FILTER_VALIDATE_EMAIL);
            if ($valid_email !== false) return true;
            else return false;
        }
        else // а если php еще старой версии, то проверка валидности пойдет старым способом
        {
            if( ereg( ".*<(.+)>", $address, $regs ) ) {
                $address = $regs[1];
            }
            if(ereg( "^[^@  ]+@([a-zA-Z0-9-]+.)+([a-zA-Z0-9-]{2}|net|com|gov|mil|org|edu|int)$",$address) )
            return true;
            else
            return false;
        }
    }
    /*
    проверка массива адресов
    */
    function CheckAdresses( $aad )
    {
        for($i=0;$i< count( $aad); $i++ ) {
            if( ! $this->ValidEmail( $aad[$i]) ) {
                echo "ошибка : не верный email ".$aad[$i];
                exit;
            }
        }
    }
    /*
    сборка файлов для отправки
    */
    function _build_attachement()
    {
        $this->xheaders["Content-Type"] = "multipart/mixed;n boundary="$this->boundary"";
        $this->fullBody = "This is a multi-part message in MIME format.n--$this->boundaryn";
        $this->fullBody .= "Content-Type: ".$this->text_html."; charset=$this->charsetnContent-Transfer-Encoding: $this->ctencodingnn" . $this->body ."n";
        $sep= chr(13) . chr(10);
        $ata= array();
        $k=0;
        // перебираем файлы
        for( $i=0; $i < count( $this->aattach); $i++ ) {
            $filename = $this->aattach[$i];
            $webi_filename =$this->webi_filename[$i]; // имя файла, которое может приходить в класс, и имеет другое имя файла
            if(strlen($webi_filename)) $basename=basename($webi_filename); // если есть другое имя файла, то оно будет таким
            else $basename = basename($filename); // а если нет другого имени файла, то имя будет выдернуто из самого загружаемого файла
            $ctype = $this->actype[$i];    // content-type
            $disposition = $this->adispo[$i];
            if( ! file_exists( $filename) ) {
                echo "ошибка прикрепления файла : файл $filename не существует"; exit;
            }
            $subhdr= "--$this->boundarynContent-type: $ctype;n name="$basename"nContent-Transfer-Encoding: base64nContent-Disposition: $disposition;n  filename="$basename"n";
            $ata[$k++] = $subhdr;
            // non encoded line length
            $linesz= filesize( $filename)+1;
            $fp= fopen( $filename, 'r' );
            $ata[$k++] = chunk_split(base64_encode(fread( $fp, $linesz)));
            fclose($fp);
        }
        $this->fullBody .= implode($sep, $ata);
    }
}