PHP发送邮件

smtp原理

+ smtp邮件协议 : http://www.ietf.org/rfc/rfc2821.txt
+ internet邮件协议 : http://www.ietf.org/rfc/rfc2822.txt
+ 代码参考 : http://blog.csdn.net/kerry0071/article/details/28604267
+ 代码参考 : http://chenall.net/post/cs_smtp/
+ http1.1协议: http://www.ietf.org/rfc/rfc2616.txt
+ 在Internet邮件头非ASCII文本表示 : http://www.ietf.org/rfc/rfc1342.txt

简易smtp实现类

<?php

/*
    简易的SMTP发送邮件类,代码比较少,有助于学习SMTP协议,
    可以带附件,支持需要验证的SMTP服务器(目前的SMTP基本都需要验证)
    编写: chenall
    时间: 2012-12-04
    网址: http://chenall.net/post/cs_smtp/
    修订记录:
        2012-12-08
            添加AddURL函数,可以直接从某个网址上下载文件并作为附件发送。
            修正由于发送人和接收人邮件地址没有使用"<>"126邮箱SMTP无法使用的问题。
        2012-12-06
            添加reset函数,重置连接,这样可以发送多个邮件。
        2012-12-05
           发送附件的代码整合到send函数中,减少变量的使用,快速输出,节省内存占用;
        2012-12-04
           第一个版本

    使用方法:

        1. 初始化:连接到服务器(默认是QQ邮箱)
           $mail = new cs_smtp('smtp.qq.com',25)
           if ($mail->errstr) //如果连接出错
               die($mail->errstr;
        2. 登录到服务器验证,如果失败返回FALSE;
           if (!$mail->login('USERNAME','PASSWORD'))
                die($mail->errstr;
        3. 添加附件如果不指定name自动从指定的文件中取文件名
           $mail->AddFile($file,$name) //服务器上的文件,可以指定文件名;
        4. 发送邮件
            $mail->send($to,$subject,$body)
            $to 收件人,多个使用','分隔
            $subject 邮件主题,可选。
            $body  邮件主体内容,可选
*/
class cs_smtp
{
    private $CRLF = "\r\n";
    private $from;
    private $smtp = null;
    private $attach = array();
    public $debug = true;//调试开关
    public $errstr = '';

    function __construct($host='smtp.qq.com',$port = 25) {
        if (empty($host))
            die('SMTP服务器未指定!');
        $this->smtp = fsockopen($host,$port,$errno,$errstr,5);
        if (empty($this->smtp))
        {
            $this->errstr = '错误'.$errno.':'.$errstr;
            return;
        }
        $this->smtp_log(fread($this->smtp, 515));
        if (intval($this->smtp_cmd('EHLO '.$host)) != 250 && intval($this->smtp_cmd('HELO '.$host)))
            return $this->errstr = '服务器不支持!';
        $this->errstr = '';
    }

    private function AttachURL($url,$name)
    {
        $info = parse_url($url);
        isset($info['port']) || $info['port'] = 80;
        isset($info['path']) || $info['path'] = '/';
        isset($info['query']) || $info['query'] = '';
        $down = fsockopen($info['host'],$info['port'],$errno,$errstr,5);
        if (!$down)
            return false;
        $out = "GET ".$info['path'].'?'.$info['query']." HTTP/1.1\r\n";
        $out .="Host: ".$info['host']."\r\n";
        $out .= "Connection: Close\r\n\r\n";// 头结束
        fwrite($down, $out);
        $filesize = 0;
        while (!feof($down)) {
            $a = fgets($down,515);
            if ($a == "\r\n")  /* fgets 读取一行遇到换行符"\n"结束, "\r"是回车 */
                break;
            $a = explode(':',$a);
            if (strcasecmp($a[0],'Content-Length') == 0)
                $filesize = intval($a[1]);
        }
        $sendsize = 0;
        echo "TotalSize: ".$filesize."\r\n";
        $i = 0;
        // 读取body
        while (!feof($down)) {
            $data = fread($down,0x2000);
            $sendsize += strlen($data);
            if ($filesize)
            {
                echo "$i Send:".$sendsize."\r";
                ob_flush();
                flush();
            }
            ++$i;
            // 使用 RFC 2045 语义格式化 $data
            fwrite($this->smtp,chunk_split(base64_encode($data)));
        }
        echo "\r\n";
        fclose($down);
        return ($filesize>0)?$filesize==$sendsize:true; // content-length 的大小 是否等于 读取body的大小判断是否成功
    }

    function __destruct()
    {
        if ($this->smtp)
            $this->smtp_cmd('QUIT');//发送退出命令
    }

    private function smtp_log($msg)//即时输出调试使用
    {
        if ($this->debug == false)
            return;
        echo $msg."\r\n";
        ob_flush();
        flush();
    }

    function reset()
    {
        $this->attach = null;
        $this->smtp_cmd('RSET');
    }

    function smtp_cmd($msg)//SMTP命令发送和收收
    {
        fputs($this->smtp,$msg.$this->CRLF);// \r\n 结束输入
        $this->smtp_log('SEND:'. substr($msg,0,80));
        $res = fread($this->smtp, 515);
        $this->smtp_log($res);
        $this->errstr = $res;
        return $res;
    }

    function AddURL($url,$name)
    {
        $this->attach[$name] = $url;
    }

    function AddFile($file,$name = '')//添加文件附件
    {
        if (file_exists($file))
        {
            if (!empty($name))
                return $this->attach[$name] = $file;
            $fn = pathinfo($file);
            return $this->attach[$fn['basename']] = $file;
        }
        return false;
    }

    function send($to,$subject='',$body = '')
    {
        $this->smtp_cmd("MAIL FROM: <".$this->from.'>');
        $mailto = explode(',',$to);
        foreach($mailto as $email_to)
            $this->smtp_cmd("RCPT TO: <".$email_to.">");
        if (intval($this->smtp_cmd("DATA")) != 354)//正确的返回必须是354
            return false;
        fwrite($this->smtp,"To:$to\nFrom: ".$this->from."\nSubject: $subject\n");

        $boundary = uniqid("--BY_CHENALL_",true);
        $headers = "MIME-Version: 1.0".$this->CRLF;
        $headers .= "From: <".$this->from.">".$this->CRLF;
        $headers .= "Content-type: multipart/mixed; boundary= $boundary\n\n".$this->CRLF;//headers结束要至少两个换行
        fwrite($this->smtp,$headers);

        $msg = "--$boundary\nContent-Type: text/html;charset=\"ISO-8859-1\"\nContent-Transfer-Encoding: base64\n\n";
        $msg .= chunk_split(base64_encode($body));
        // base64_encode设计此种编码是为了使二进制数据可以通过非纯 8-bit 的传输层传输,例如电子邮件的主体
        fwrite($this->smtp,$msg);
        $files = '';
        $errinfo = '';
        foreach($this->attach as $name=>$file)
        {
            $files .= $name;
            $msg = "--$boundary\n--$boundary\n";
            $msg .= "Content-Type: application/octet-stream; name=\"".$name."\"\n";
            $msg .= "Content-Disposition: attachment; filename=\"".$name."\"\n";
            $msg .= "Content-transfer-encoding: base64\n\n";
            fwrite($this->smtp,$msg);
            if (substr($file,4,1) == ':')//URL like http:///file://
            {
                if (!$this->AttachURL($file,$name))
                    $errinfo .= '文件下载错误:'.$name.",文件可能是错误的\r\n$file";
            }
            else
                fwrite($this->smtp,chunk_split(base64_encode(file_get_contents($file))));//使用BASE64编码,再用chunk_split大卸八块(每行76个字符)
        }
        if (!empty($errinfo))
        {
            $msg = "--$boundary\n--$boundary\n";
            $msg .= "Content-Type: application/octet-stream; name=Error.log\n";
            $msg .= "Content-Disposition: attachment; filename=Error.log\n";
            $msg .= "Content-transfer-encoding: base64\n\n";
            fwrite($this->smtp,$msg);
            fwrite($this->smtp,chunk_split(base64_encode($errinfo)));
        }
        return intval($this->smtp_cmd("--$boundary--\n\r\n.")) == 250;//结束DATA发送,服务器会返回执行结果,如果代码不是250则出错。

        // \r\n.\r\n 表示发送内容结束
    }

    function login($su,$sp)
    {
        if (empty($this->smtp))
            return false;
        $res = $this->smtp_cmd("AUTH LOGIN");
        if (intval($res)>400)
            return !$this->errstr = $res;
        $res = $this->smtp_cmd(base64_encode($su));
        if (intval($res)>400)
            return !$this->errstr = $res;
        $res = $this->smtp_cmd(base64_encode($sp));
        if (intval($res)>400)
            return !$this->errstr = $res;
        $this->from = $su;
        return true;
    }
}

mail+stmp函数

<?
!defined('M_COM') && exit('No Permisson');

function sys_mail(&$to,&$subject,&$msg,&$from){
     
    global $mail_smtp,$mail_mode,$mail_port,$mail_auth,$mail_from,$mail_user,$mail_pwd,$mail_delimiter,$mail_silent,$adminemail,$mcharset,$cmsname;

    if($mail_silent) 
        error_reporting(0);

    $delimiter = $mail_delimiter == 1 ? "\r\n" : ($mail_delimiter == 2 ? "\r" : "\n");

    $user = isset($mail_user) ? $mail_user : 1;

    $subject = '=?'.$mcharset.'?B?'.base64_encode(str_replace(array("\r","\n"),'','['.$cmsname.'] '.$subject)).'?=';

    $msg = chunk_split(base64_encode(str_replace(array("\n\r","\r\n","\r","\n","\r\n.",),array("\r","\n","\n","\r\n"," \r\n..",),$msg)));

    /*
        1. 在Internet邮件头非ASCII文本表示 : http://www.ietf.org/rfc/rfc1342.txt

        2. encoded-word := "=?" charset "?" encoding "?" encoded-text "?="
        
        3. 具体解析:

            encoded-word = "=" "?" charset "?" encoding "?" encoded-text "?" "="
            
            charset      = token    ; legal charsets defined by RFC 1341
            
            encoding     = token   ; Either "B" or "Q"

            token        = 1*<Any CHAR except SPACE, CTLs, and tspecials>

            tspecials    = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "." / "="

            encoded-text = 1*<Any printable ASCII character other than "?" or
                          ; SPACE> (but see "Use of encoded-words in message
                          ; headers", below)
     */
    
    /*
        Examples

            From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>
            To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>
            CC: =?ISO-8859-1?Q?Andr=E9_?= Pirard <PIRARD@vm1.ulg.ac.be>
            Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
            =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=

            From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>
            To: ietf-822@dimacs.rutgers.edu, ojarnef@admin.kth.se
            Subject: Time for ISO 10646?

            To: Dave Crocker <dcrocker@mordor.stanford.edu>
            Cc: ietf-822@dimacs.rutgers.edu, paf@comsol.se
            From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se>
            Subject: Re: RFC-HDR care and feeding    
     */
    
    $from = $from == '' ? '=?'.$mcharset.'?B?'.base64_encode($cmsname)."?= <$adminemail>" : (preg_match('/^(.+?)\<(.+?)\>$/',$from, $v) ? '=?'.$mcharset.'?B?'.base64_encode($v[1])."?= <$v[2]>" : $from);

    $toarr = array();

    foreach(explode(',',$to) as $k) 
        $toarr[] = preg_match('/^(.+?)\<(.+?)\>$/',$k,$v) ? ($user ? '=?'.$mcharset.'?B?'.base64_encode($v[1])."?= <$v[2]>" : $v[2]) : $k;

    $to = implode(',',$toarr);
    $headers = "From: $from{$delimiter}X-Priority: 3{$delimiter}X-Mailer: 08CMS{$delimiter}MIME-Version: 1.0{$delimiter}Content-type: text/html; charset=$mcharset{$delimiter}Content-Transfer-Encoding: base64{$delimiter}";

    $mail_port = $mail_port ? $mail_port : 25;

    if($mail_mode == 1 && function_exists('mail')){
        // 使用mail函数发送
        @mail($to,$subject,$msg,$headers);
     
    }elseif($mail_mode == 2){
        // 使用smtp协议+fsockopen发送
    
        if(!$fp = fsockopen($mail_smtp, $mail_port,$errno,$errstr,30)) 
            return "($mail_smtp:$mail_port) CONNECT - Unable to connect to the SMTP server";

        stream_set_blocking($fp,true);
         
        $nmsg = fgets($fp,512);
        if(substr($nmsg,0,3) != '220') // 220 <domain> Service ready 
            return "$mail_smtp:$mail_port CONNECT - $nmsg";
        
        fputs($fp, ($mail_auth ? 'EHLO' : 'HELO')." 08CMS\r\n");
        $nmsg = fgets($fp,512);
        if(substr($nmsg,0,3) != 220 && substr($nmsg,0,3) != 250) // 250 Requested mail action okay, completed
            return "($mail_smtp:$mail_port) HELO/EHLO - $nmsg";
         
        while(1){
            if(substr($nmsg,3,1) != '-' || empty($nmsg))
                break;
            $nmsg = fgets($fp,512);
        }
         
        if($mail_auth) {
         
            fputs($fp,"AUTH LOGIN\r\n");
            $nmsg = fgets($fp,512);
            if(substr($nmsg, 0, 3) != 334)
                return "($mail_smtp:$mail_port) AUTH LOGIN - $nmsg";
             
            fputs($fp,base64_encode($mail_user)."\r\n");
            $nmsg = fgets($fp,512);
            if(substr($nmsg,0,3) != 334) 
                return "($mail_smtp:$mail_port) USERNAME - $nmsg";
             
            fputs($fp,base64_encode($mail_pwd)."\r\n");
            $nmsg = fgets($fp,512);
            if(substr($nmsg,0,3) != 235) 
                return "($mail_smtp:$mail_port) PASSWORD - $nmsg";
            $from = $mail_from;
        
        }
         
        fputs($fp,"MAIL FROM: <".preg_replace("/.*\<(.+?)\>.*/","\\1",$from).">\r\n");
        $nmsg = fgets($fp,512);
        if(substr($nmsg,0,3) != 250) {
            // 重试了一下?
            fputs($fp,"MAIL FROM: <".preg_replace("/.*\<(.+?)\>.*/","\\1",$from).">\r\n");
            $nmsg = fgets($fp,512);
            if(substr($nmsg,0,3) != 250) 
                return "($mail_smtp:$mail_port) MAIL FROM - $nmsg";
        }
         
        foreach(explode(',',$to) as $v){
            $v = trim($v);
            if($v){
                fputs($fp,"RCPT TO: <".preg_replace("/.*\<(.+?)\>.*/", "\\1", $v).">\r\n");
                $nmsg = fgets($fp,512);
                // 重试了一下?
                if(substr($nmsg,0,3) != 250){
                    fputs($fp, "RCPT TO: <".preg_replace("/.*\<(.+?)\>.*/","\\1",$v).">\r\n");
                    $nmsg = fgets($fp,512);
                    return "($mail_smtp:$mail_port) RCPT TO - $nmsg";
                }
            }
        }
        
        // 邮件内容
        fputs($fp,"DATA\r\n");
        $nmsg = fgets($fp,512);
        // 354 Start mail input; end with <CRLF>.<CRLF>
        if(substr($nmsg,0,3) != 354) 
            return "($mail_smtp:$mail_port) DATA - $nmsg";

        $headers .= 'Message-ID: <'.gmdate('YmdHs').'.'.substr(md5($msg.microtime()),0,6).rand(100000, 999999).'@'.$_SERVER['HTTP_HOST'].">{$delimiter}";
        fputs($fp,"Date: ".gmdate('r')."\r\n");
        fputs($fp,"To: ".$to."\r\n");
        fputs($fp,"Subject: ".$subject."\r\n");
        fputs($fp,$headers."\r\n");
        fputs($fp,"\r\n\r\n");
        // header之后\r\n\r\n?

        fputs($fp,"$msg\r\n.\r\n"); // TODO 增加附件下载功能
        $nmsg = fgets($fp, 512);
        if(substr($nmsg, 0, 3) != 250) 
            return "($mail_smtp:$mail_port) END - $nmsg";
        fputs($fp, "QUIT\r\n");
     
    }elseif($mail_mode == 3){
     
        ini_set('SMTP',$mail_smtp);
        ini_set('smtp_port',$mail_port);
        ini_set('sendmail_from',$from);
        @mail($to,$subject,$msg,$headers);
    
    }
    return '';
}

ecshop mail + smtp 发送邮件

/**
 * 邮件发送
 *
 * @param: $name[string]        接收人姓名
 * @param: $email[string]       接收人邮件地址
 * @param: $subject[string]     邮件标题
 * @param: $content[string]     邮件内容
 * @param: $type[int]           0 普通邮件, 1 HTML邮件
 * @param: $notification[bool]  true 要求回执, false 不用回执
 *
 * @return boolean
 */
function send_mail($name, $email, $subject, $content, $type = 0, $notification=false)
{
    /* 如果邮件编码不是EC_CHARSET,创建字符集转换对象,转换编码 */
    if ($GLOBALS['_CFG']['mail_charset'] != EC_CHARSET)
    {
        $name      = ecs_iconv(EC_CHARSET, $GLOBALS['_CFG']['mail_charset'], $name);
        $subject   = ecs_iconv(EC_CHARSET, $GLOBALS['_CFG']['mail_charset'], $subject);
        $content   = ecs_iconv(EC_CHARSET, $GLOBALS['_CFG']['mail_charset'], $content);
        $shop_name = ecs_iconv(EC_CHARSET, $GLOBALS['_CFG']['mail_charset'], $GLOBALS['_CFG']['shop_name']);
    }
    $charset   = $GLOBALS['_CFG']['mail_charset'];
    /**
     * 使用mail函数发送邮件
     */
    if ($GLOBALS['_CFG']['mail_service'] == 0 && function_exists('mail'))
    {
        /* 邮件的头部信息 */
        $content_type = ($type == 0) ? 'Content-Type: text/plain; charset=' . $charset : 'Content-Type: text/html; charset=' . $charset;
        $headers = array();
        $headers[] = 'From: "' . '=?' . $charset . '?B?' . base64_encode($shop_name) . '?='.'" <' . $GLOBALS['_CFG']['smtp_mail'] . '>';
        $headers[] = $content_type . '; format=flowed';
        if ($notification)
        {
            $headers[] = 'Disposition-Notification-To: ' . '=?' . $charset . '?B?' . base64_encode($shop_name) . '?='.'" <' . $GLOBALS['_CFG']['smtp_mail'] . '>';
            // 回执可以确认对方是否收到该邮件
        }

        $res = @mail($email, '=?' . $charset . '?B?' . base64_encode($subject) . '?=', $content, implode("\r\n", $headers));

        if (!$res)
        {
            $GLOBALS['err'] ->add($GLOBALS['_LANG']['sendemail_false']);

            return false;
        }
        else
        {
            return true;
        }
    }
    /**
     * 使用smtp服务发送邮件
     */
    else
    {
        /* 邮件的头部信息 */
        $content_type = ($type == 0) ?
            'Content-Type: text/plain; charset=' . $charset : 'Content-Type: text/html; charset=' . $charset;
        $content   =  base64_encode($content);

        $headers = array();
        $headers[] = 'Date: ' . gmdate('D, j M Y H:i:s') . ' +0000';
        $headers[] = 'To: "' . '=?' . $charset . '?B?' . base64_encode($name) . '?=' . '" <' . $email. '>';
        $headers[] = 'From: "' . '=?' . $charset . '?B?' . base64_encode($shop_name) . '?='.'" <' . $GLOBALS['_CFG']['smtp_mail'] . '>';
        $headers[] = 'Subject: ' . '=?' . $charset . '?B?' . base64_encode($subject) . '?=';
        $headers[] = $content_type . '; format=flowed';
        $headers[] = 'Content-Transfer-Encoding: base64';
        $headers[] = 'Content-Disposition: inline';
        if ($notification)
        {
            $headers[] = 'Disposition-Notification-To: ' . '=?' . $charset . '?B?' . base64_encode($shop_name) . '?='.'" <' . $GLOBALS['_CFG']['smtp_mail'] . '>';
        }

        /* 获得邮件服务器的参数设置 */
        $params['host'] = $GLOBALS['_CFG']['smtp_host'];
        $params['port'] = $GLOBALS['_CFG']['smtp_port'];
        $params['user'] = $GLOBALS['_CFG']['smtp_user'];
        $params['pass'] = $GLOBALS['_CFG']['smtp_pass'];

        if (empty($params['host']) || empty($params['port']))
        {
            // 如果没有设置主机和端口直接返回 false
            $GLOBALS['err'] ->add($GLOBALS['_LANG']['smtp_setting_error']);

            return false;
        }
        else
        {
            // 发送邮件
            if (!function_exists('fsockopen'))
            {
                //如果fsockopen被禁用,直接返回
                $GLOBALS['err']->add($GLOBALS['_LANG']['disabled_fsockopen']);

                return false;
            }

            include_once(ROOT_PATH . 'includes/cls_smtp.php');
            static $smtp;

            $send_params['recipients'] = $email;
            $send_params['headers']    = $headers;
            $send_params['from']       = $GLOBALS['_CFG']['smtp_mail'];
            $send_params['body']       = $content;

            if (!isset($smtp))
            {
                $smtp = new smtp($params);
            }

            if ($smtp->connect() && $smtp->send($send_params))
            {
                return true;
            }
            else
            {
                $err_msg = $smtp->error_msg();
                if (empty($err_msg))
                {
                    $GLOBALS['err']->add('Unknown Error');
                }
                else
                {
                    if (strpos($err_msg, 'Failed to connect to server') !== false)
                    {
                        $GLOBALS['err']->add(sprintf($GLOBALS['_LANG']['smtp_connect_failure'], $params['host'] . ':' . $params['port']));
                    }
                    else if (strpos($err_msg, 'AUTH command failed') !== false)
                    {
                        $GLOBALS['err']->add($GLOBALS['_LANG']['smtp_login_failure']);
                    }
                    elseif (strpos($err_msg, 'bad sequence of commands') !== false)
                    {
                        $GLOBALS['err']->add($GLOBALS['_LANG']['smtp_refuse']);
                    }
                    else
                    {
                        $GLOBALS['err']->add($err_msg);
                    }
                }

                return false;
            }
        }
    }
}
<?php

/**
 * ECSHOP SMTP 邮件类
 * ============================================================================
 * * 版权所有 2005-2012 上海商派网络科技有限公司,并保留所有权利。
 * 网站地址: http://www.ecshop.com;
 * ----------------------------------------------------------------------------
 * 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和
 * 使用;不允许对程序代码以任何形式任何目的的再发布。
 * ============================================================================
 * $Author: liubo $
 * $Id: cls_smtp.php 17217 2011-01-19 06:29:08Z liubo $
*/

if (!defined('IN_ECS'))
{
    die('Hacking attempt');
}

define('SMTP_STATUS_NOT_CONNECTED', 1, true);
define('SMTP_STATUS_CONNECTED',     2, true);

class smtp
{
    var $connection;
    var $recipients;
    var $headers;
    var $timeout;
    var $errors;
    var $status;
    var $body;
    var $from;
    var $host;
    var $port;
    var $helo;
    var $auth;
    var $user;
    var $pass;

    /**
     *  参数为一个数组
     *  host        SMTP 服务器的主机       默认:localhost
     *  port        SMTP 服务器的端口       默认:25
     *  helo        发送HELO命令的名称      默认:localhost
     *  user        SMTP 服务器的用户名     默认:空值
     *  pass        SMTP 服务器的登陆密码   默认:空值
     *  timeout     连接超时的时间          默认:5
     *  @return  bool
     */
    function smtp($params = array())
    {
        if (!defined('CRLF'))
        {
            define('CRLF', "\r\n", true);
        }

        $this->timeout  = 10;
        $this->status   = SMTP_STATUS_NOT_CONNECTED;
        $this->host     = 'localhost';
        $this->port     = 25;
        $this->auth     = false;
        $this->user     = '';
        $this->pass     = '';
        $this->errors   = array();

        foreach ($params AS $key => $value)
        {
            $this->$key = $value;
        }

        $this->helo     = $this->host;

        //  如果没有设置用户名则不验证
        $this->auth = ('' == $this->user) ? false : true;
    }

    function connect($params = array())
    {
        if (!isset($this->status))
        {
            $obj = new smtp($params);

            if ($obj->connect())
            {
                $obj->status = SMTP_STATUS_CONNECTED;
            }

            return $obj;
        }
        else
        {
            if (!empty($GLOBALS['_CFG']['smtp_ssl']))
            {
                // ssl协议链接
                $this->host = "ssl://" . $this->host;
            }
            $this->connection = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout);

            if ($this->connection === false)
            {
                $this->errors[] = 'Access is denied.';

                return false;
            }

            @socket_set_timeout($this->connection, 0, 250000);

            $greeting = $this->get_data();

            if (is_resource($this->connection))
            {
                $this->status = 2;

                return $this->auth ? $this->ehlo() : $this->helo();
            }
            else
            {
                log_write($errstr, __FILE__, __LINE__);
                $this->errors[] = 'Failed to connect to server: ' . $errstr;

                return false;
            }
        }
    }

    /**
     * 参数为数组
     * recipients      接收人的数组
     * from            发件人的地址,也将作为回复地址
     * headers         头部信息的数组
     * body            邮件的主体
     */

    function send($params = array())
    {
        foreach ($params AS $key => $value)
        {
            $this->$key = $value;
        }

        if ($this->is_connected())
        {
            //  服务器是否需要验证
            if ($this->auth)
            {
                if (!$this->auth())
                {
                    return false;
                }
            }

            $this->mail($this->from);

            if (is_array($this->recipients))
            {
                foreach ($this->recipients AS $value)
                {
                    $this->rcpt($value);
                }
            }
            else
            {
                $this->rcpt($this->recipients);
            }

            if (!$this->data())
            {
                return false;
            }

            $headers = str_replace(CRLF . '.', CRLF . '..', trim(implode(CRLF, $this->headers)));
            $body    = str_replace(CRLF . '.', CRLF . '..', $this->body);
            $body    = substr($body, 0, 1) == '.' ? '.' . $body : $body;

            $this->send_data($headers);
            $this->send_data('');
            $this->send_data($body);
            $this->send_data('.');

            return (substr($this->get_data(), 0, 3) === '250');
        }
        else
        {
            $this->errors[] = 'Not connected!';

            return false;
        }
    }

    function helo()
    {
        if (is_resource($this->connection)
                AND $this->send_data('HELO ' . $this->helo)
                AND substr($error = $this->get_data(), 0, 3) === '250' )
        {
            return true;
        }
        else
        {
            $this->errors[] = 'HELO command failed, output: ' . trim(substr($error, 3));

            return false;
        }
    }

    function ehlo()
    {
        if (is_resource($this->connection)
                AND $this->send_data('EHLO ' . $this->helo)
                AND substr($error = $this->get_data(), 0, 3) === '250' )
        {
            return true;
        }
        else
        {
            $this->errors[] = 'EHLO command failed, output: ' . trim(substr($error, 3));

            return false;
        }
    }

    function auth()
    {
        if (is_resource($this->connection)
                AND $this->send_data('AUTH LOGIN')
                AND substr($error = $this->get_data(), 0, 3) === '334'
                AND $this->send_data(base64_encode($this->user))            // Send username
                AND substr($error = $this->get_data(),0,3) === '334'
                AND $this->send_data(base64_encode($this->pass))            // Send password
                AND substr($error = $this->get_data(),0,3) === '235' )
        {
            return true;
        }
        else
        {
            $this->errors[] = 'AUTH command failed: ' . trim(substr($error, 3));

            return false;
        }
    }

    function mail($from)
    {
        if ($this->is_connected()
            AND $this->send_data('MAIL FROM:<' . $from . '>')
            AND substr($this->get_data(), 0, 2) === '250' )
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    function rcpt($to)
    {
        if ($this->is_connected()
            AND $this->send_data('RCPT TO:<' . $to . '>')
            AND substr($error = $this->get_data(), 0, 2) === '25')
        {
            return true;
        }
        else
        {
            $this->errors[] = trim(substr($error, 3));

            return false;
        }
    }

    function data()
    {
        if ($this->is_connected()
            AND $this->send_data('DATA')
            AND substr($error = $this->get_data(), 0, 3) === '354' )
        {
            return true;
        }
        else
        {
            $this->errors[] = trim(substr($error, 3));

            return false;
        }
    }

    function is_connected()
    {
        return (is_resource($this->connection) AND ($this->status === SMTP_STATUS_CONNECTED));
    }

    function send_data($data)
    {
        if (is_resource($this->connection))
        {
            return fwrite($this->connection, $data . CRLF, strlen($data) + 2);
        }
        else
        {
            return false;
        }
    }

    function get_data()
    {
        $return = '';
        $line   = '';

        if (is_resource($this->connection))
        {
            // 返回以\r\n结束 或 250 OK
            while (strpos($return, CRLF) === false OR $line{3} !== ' ')
            {
                $line    = fgets($this->connection, 512);
                $return .= $line;
            }

            return trim($return);
        }
        else
        {
            return '';
        }
    }

    /**
     * 获得最后一个错误信息
     *
     * @access  public
     * @return  string
     */
    function error_msg()
    {
        if (!empty($this->errors))
        {
            $len = count($this->errors) - 1;
            return $this->errors[$len];
        }
        else
        {
            return '';
        }
    }
}

标签: none