SmtpClient.php 4.5 KB
<?php

/**
 * 连接代理地址
 * @author:dc
 * @time 2025/3/31 11:27
 * Class ImapClientSwoole
 */
class SmtpClient{

    protected $host = '';

    /**
     * 连接
     * @var \Swoole\Coroutine\Client
     */
    protected $client;

    /**
     * 是否验证了
     */
    public $isAuth = false;

    /**
     * 协程锁
     * @var bool
     */
    private bool $_lock = false;


    public function __construct()
    {

    }

    /**
     * 执行
     * @param $data
     * @return array
     * @author:dc
     * @time 2025/4/17 9:03
     */
    public function exec($data):array {
        if (!$this->isAuth) {

            try {
                $auth = new Auth($data);
            }catch (Throwable $e){
                return [false,$e->getMessage()];
            }

            // 连接客户端
            try {
                $this->open($auth->host,$auth->out_ip, $auth->timeOut);
            }catch (Throwable $e){
                return [false,'500 ' . $e->getMessage()."\r\n"];
            }

            $line = $this->readLine();

            return [true,$line];

        } // 正式请求转发
        else {
            // 没有连接成功
            if(!$this->isAuth){
                return [false,"500 No proxy server.\r\n"];
            }

            $line = false;
            try {
                // 请求数据
                // $num = 0;
                // foreach (explode("\r\n",$data) as $cmd){
                //     if(strlen($cmd) > 0){
                //         if($n = self::$clients[$fd]->write($cmd."\r\n")){
                //             $num += $n;
                //         }
                //     }

                // }
                $num = $this->write($data);

                if($num) $line = $this->readLine();

            }catch (Throwable $e){
                $line = '500 server error '.$e->getMessage()."\r\n";
            }

            if($line===false){
                $line = "500 server error 2 \r\n";
            }
            if(!$line&&$data==".\r\n"){
                $line = "250 Mail OK ok \r\n";
            }
            if(empty($line)){
                $line = "250 Mail OK no reply \r\n";
            }

            return [true,$line];

        }
    }

    /**
     * 排他锁
     * @return bool
     */
    public function lock():void
    {
        while ($this->_lock){
            co::sleep(0.1);
        }

        $this->_lock = true;
    }

    /**
     * 解锁
     * @author:dc
     * @time 2025/4/17 9:09
     */
    public function unlock(){
        $this->_lock = false;
    }

    /**
     * 打开连接
     * @param string $out_ip
     * @param int $timeout
     * @return string
     * @author:dc
     * @time 2025/3/31 10:27
     */
    public function open(string $host, string $out_ip, int $timeout=5){
        $this->host = $host;
        $client = new \Swoole\Coroutine\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);
        $client->set([
            'timeout'=> $timeout,
            'ssl_verify_peer' => false,// 关闭证书验证
//            'bind_address' => $out_ip,
//            'bind_port' => 36002,
        ]);

        $host = parse_url($this->host);

        if(!$client->connect($host['host'],$host['port'],$timeout)){
            throw new Exception($this->host." connection fail. ".$client->errMsg);
        }

        if($client->isConnected()){
            $this->client = $client;
            $this->isAuth = true;
        }else{
            throw new Exception($this->host." connection fail. ");
        }
    }

    /**
     * 写
     * @param string $cmd
     * @return false|int
     * @author:dc
     * @time 2024/9/13 15:47
     */
    public function write(string $cmd){
        if($cmd === "DATA\r\n"){
            $this->is_read = 1;
        }
        if($cmd === ".\r\n"){
            $this->is_read = 0;
        }
        return $this->client->send($cmd);

    }

    protected $is_read = 0;

    /**
     * 读
     * @return false|string
     * @author:dc
     * @time 2024/9/13 15:49
     */
    public function readLine($timeout=5){
        if($this->is_read === 2){
            return "250 DATA OK\r\n";
        }

        if($this->is_read === 1){
            $this->is_read = 2;
        }
        $online =  $this->client->recv($timeout);

        return $online===false ? "500 read time out.\r\n" : $online;
    }


    public function close()
    {
        if(!empty($this->client)) $this->client->close();
        unset($this->client);
        // TODO: Implement __destruct() method.
    }



}