<?php

class Mail {

    var $sendto = array();
    var $acc = array();
    var $abcc = array();
    var $aattach = array();
    var $xheaders = array();
    var $priorities = array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
    var $charset = "windows-1251";
    var $ctencoding = "8bit";
    var $receipt = 0;
    var $text_html = "text/html"; // формат письма. по умолчанию текстовый
    var $smtp_on = false;    // отправка через smtp. по умолчанию выключена
    var $names_email = array(); // имена для email адресов, чтобы делать вид ("Антон" <te@gg.ru>)

    /*
      конструктор тоже по старому объявлен для совместимости со старыми версиями php
      пошел конструктор.
      входящий параметр кодировка письма
      внесено изменение
     */

    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, "\r\n", "  ")))) . "?=";
    }

    /*

      от кого
     */

    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], "\r\n", "  ")))) . "?= <" . $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;
        }

        // вставаляем файлы
        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") . " +0700\r\n";
            $this->headers .= "Message-ID: <" . rand() . "." . date("YmjHis") . "@" . $user_domen[1] . ">\r\n";


            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'], "\r\n", "  ")))) . "?= <" . $value . ">\r\n";
                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'], "\r\n", "  ")))) . "?= <" . $value . ">\r\n";
                elseif ($hdr != "BCC")
                    $this->headers .= $hdr . ": " . $value . "\r\n"; // пропускаем заголовок для отправки скрытой копии
            }
        }
        // создание заголовоков, если отправка идет через 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'], "\r\n", "  ")))) . "?= <" . $value . ">\r\n";
                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'], "\r\n", "  ")))) . "?= <" . $value . ">\r\n";
                elseif ($hdr != "Subject" and $hdr != "To")
                    $this->headers .= "$hdr: $value\n"; // пропускаем заголовки кому и тему... они вставятся сами
            }
        }
    }

    // включение отправки через 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 .= "соединение с сервером не прошло\n\n";
                fclose($smtp_conn);
                return;
            }

            $this->smtp_log .= $data = $this->get_data($smtp_conn) . "\n";

            fputs($smtp_conn, "EHLO " . $user_domen[0] . "\r\n");
            $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 LOGIN\r\n");
            $this->smtp_log .= "Я: AUTH 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_login) . "\r\n");
            $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) . "\r\n");
            $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 . "\r\n" . $this->fullBody) . "\r\n");
            $this->smtp_log .= "Я: MAIL FROM:<" . $this->xheaders['From'] . "> SIZE=" . strlen($this->headers . "\r\n" . $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 FROM\n";
                fclose($smtp_conn);
                return;
            }



            foreach ($this->smtpsendto as $keywebi => $valuewebi) {
                fputs($smtp_conn, "RCPT TO:<" . $valuewebi . ">\r\n");
                $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 TO\n";
                    fclose($smtp_conn);
                    return;
                }
            }




            fputs($smtp_conn, "DATA\r\n");
            $this->smtp_log .="Я: DATA\n";
            $this->smtp_log .= $data = $this->get_data($smtp_conn) . "\n";

            $code = substr($data, 0, 3);
            if ($code != 354) {
                $this->smtp_log .= "сервер не принял DATA\n";
                fclose($smtp_conn);
                return;
            }

            fputs($smtp_conn, $this->headers . "\r\n" . $this->fullBody . "\r\n.\r\n");
            $this->smtp_log .= "Я: " . $this->headers . "\r\n" . $this->fullBody . "\r\n.\r\n";

            $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, "QUIT\r\n");
            $this->smtp_log .="QUIT\r\n";
            $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 . "\n\n";
        $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->boundary\n";
        $this->fullBody .= "Content-Type: " . $this->text_html . "; charset=$this->charset\nContent-Transfer-Encoding: $this->ctencoding\n\n" . $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->boundary\nContent-type: $ctype;\n name=\"$basename\"\nContent-Transfer-Encoding: base64\nContent-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);
    }

}

// class Mail
?>
