MTA
[ class tree: MTA ] [ index: MTA ] [ all elements ]

Source for file MTA_Socket.php

Documentation is available at MTA_Socket.php

  1. <?php
  2. /**
  3.  * Project: MTA_Socket :: MTA socket abstraction layer<br />
  4.  * File:    MTA/MTA_Socket.php
  5.  *
  6.  * eSNMP_Socket class는 소켓으로 메일을 발송하기 위한 추상
  7.  * 레이어를 제공한다.
  8.  *
  9.  * @category   Networking
  10.  * @package    MTA
  11.  * @subpackage MTA_Socket
  12.  * @author     JoungKyun.Kim <http://oops.org>
  13.  * @copyright  (c) 2018, OOPS.org
  14.  * @license    BSD License
  15.  * @version    SVN: $Id$
  16.  * @link       http://pear.oops.org/package/MTA
  17.  * @filesource
  18.  */
  19.  
  20. /**
  21.  * Socket for send mail of MTA API
  22.  *
  23.  * 소켓으로 메일을 발송하기 위한 추상 레이어
  24.  *
  25.  * @package MTA
  26.  */
  27. Class MTA_Socket {
  28.     // {{{ properties
  29.     /**
  30.      * 에러 메시지
  31.      * @access public
  32.      * @var string 
  33.      */
  34.     public $error = null;
  35.     // }}}
  36.  
  37.     // {{{ protected MTA_Socket::target_object ($o)
  38.     /**
  39.      * 동일한 호스트에 여러번 접속하지 않도록 하기 위하여 메일 호스트
  40.      * 별로 모음
  41.      *
  42.      * @access protected
  43.      * @return stdClass 
  44.      * @param  stdClass $o mail object
  45.      *    <pre>
  46.      *    stdClass Object
  47.      *    (
  48.      *        [to]  => (array) to list
  49.      *        [cc]  => (array) cc list
  50.      *        [bcc] => (array) bcc list
  51.      *    )
  52.      *    </pre>
  53.      */
  54.     protected function target_object ($o{
  55.         if is_array ($o->to|| is_object ($o->to) ) {
  56.             foreach $o->to as $ad )
  57.                 $tmp[$ad;
  58.         else
  59.             $tmp array ($o->to);
  60.  
  61.         if $o->cc {
  62.             if is_array ($o->cc|| is_object ($o->cc) ) {
  63.                 foreach $o->cc as $ad )
  64.                     $tmp[$ad;
  65.             else
  66.                 $tmp[$o->cc;
  67.         }
  68.  
  69.         if $o->bcc {
  70.             if is_array ($o->bcc|| is_object ($o->bcc) ) {
  71.                 foreach $o->bcc as $ad )
  72.                     $tmp[$ad;
  73.             else
  74.                 $tmp[$o->bcc;
  75.         }
  76.  
  77.         $target new stdClass;
  78.         foreach $tmp as $addr {
  79.             $mail preg_replace ('/^[^<]+<|>[^>]*$/'''$addr);
  80.             list ($local$hostpreg_split ('/@/'$mail);
  81.  
  82.             if $this->check_local ($localtrue== false )
  83.                 continue;
  84.  
  85.             if $this->check_domain ($hosttrue== false )
  86.                 continue;
  87.  
  88.             if $target->$host {
  89.                 $target->$host new stdClass;
  90.  
  91.                 if getmxrr ($host$mx$weight) ) {
  92.                     foreach $weight as $key => $val {
  93.                         $target->$host->mx[$val ' ' $mx[$key];
  94.                     }
  95.                     sort ($target->$host->mxSORT_NUMERIC);
  96.  
  97.  
  98.                 else {
  99.                     $target->$host->mx array ('10 ' $host);
  100.                 }
  101.             }
  102.  
  103.             $target->$host->rcpt['<' $mail '>';
  104.         }
  105.  
  106.         return $target;
  107.     }
  108.     // }}}
  109.  
  110.     // {{{ (object) protected MTA_Socket::socket_send ($o, &$template)
  111.     /**
  112.      * 메일 발송
  113.      *
  114.      * @access protected
  115.      * @return stdClass  발송 결과를 object로 반환한다.
  116.      *    <pre>
  117.      *    stdClass Object
  118.      *    (
  119.      *        [status]  => (boolean) 성공 실패 여부
  120.      *        [error]   => (string|null) status false시 에러 메시지
  121.      *        [rcptlog] => (array) rcpt to에 대한 log
  122.      *    )
  123.      *    </pre>
  124.      *
  125.      *    RCPT list별로 확인을 위해서 status가 true이더라도 rcptlog를
  126.      *    확인하는 것이 필요
  127.      *
  128.      * @param  stdClass $o mail object
  129.      *    <pre>
  130.      *    stdClass Object
  131.      *    (
  132.      *        [rpath]  => (string) return path (optional)
  133.      *        [from]   => (string) Sender address
  134.      *        [to]     => (array) Reciever address
  135.      *        [cc]     => (array) See also reciever address
  136.      *        [bcc]    => (array) Hidden see also reciever address
  137.      *        [subjet] => (string) mail subject
  138.      *        [body]   => (string) mail contents
  139.      *        [pbody]  => (string) planin/text mail contents (optional)
  140.      *        [attach] => (array) attached files (optional)
  141.      *    )
  142.      *    </pre>
  143.      * @param string &$template 메일 본문
  144.      */
  145.     protected function socket_send ($o&$template{
  146.         $this->error = false;
  147.         $t $this->target_object ($o);
  148.         $r new stdClass;
  149.  
  150.         foreach $t as $vendor => $val {
  151.             $this->debug (str_repeat ('-'60));
  152.             $this->debug ('Trying to ' $vendor);
  153.             $this->debug (str_repeat ('-'60));
  154.  
  155.             $r->$vendor new stdClass;
  156.  
  157.             if $this->open ($val=== false {
  158.                 $r->$vendor->status false;
  159.                 $r->$vendor->error $this->error;
  160.                 $this->error = null;
  161.                 continue;
  162.             }
  163.  
  164.             if $this->ehlo (=== false {
  165.                 $r->$vendor->status false;
  166.                 $r->$vendor->error $this->error;
  167.                 $this->error = null;
  168.                 $this->close ();
  169.                 continue;
  170.             }
  171.  
  172.             if $this->mailfrom ($o=== false {
  173.                 $r->$vendor->status false;
  174.                 $r->$vendor->error $this->error;
  175.                 $this->error = null;
  176.                 $this->close ();
  177.                 continue;
  178.             }
  179.  
  180.             if isset ($log) )
  181.                 unset ($log);
  182.  
  183.             if $this->rcptto ($val$log=== false {
  184.                 $r->$vendor->status false;
  185.                 $r->$vendor->error $this->error;
  186.                 $r->$vendor->rcptlog $log;
  187.                 $this->error = null;
  188.                 $this->close ();
  189.                 continue;
  190.             else {
  191.                 # 일부 실패를 위해서 확인은 해야 함!
  192.                 $r->$vendor->rcptlog $log;
  193.             }
  194.  
  195.             if $this->data ($template=== false {
  196.                 $r->$vendor->status false;
  197.                 $r->$vendor->error $this->error;
  198.                 $this->error = null;
  199.                 $this->close ();
  200.                 continue;
  201.             }
  202.             $this->close ();
  203.  
  204.             $r->$vendor->status true;
  205.         }
  206.  
  207.         return $r;
  208.     }
  209.     // }}}
  210.  
  211.     // {{{ (bool) protected MTA_Socket::ehlo ()
  212.     /**
  213.      * ehlo 명령 전송
  214.      *
  215.      * @access protected
  216.      * @return bool 
  217.      */
  218.     protected function ehlo ({
  219.         $this->error = false;
  220.         $this->write ('EHLO ' gethostname ());
  221.  
  222.         if $this->read (=== false )
  223.             return false;
  224.  
  225.         return true;
  226.     }
  227.     // }}}
  228.  
  229.     // {{{ (bool) protected MTA_Socket::mailfrom ($o)
  230.     /**
  231.      * mailfrom 명령 전송
  232.      * 
  233.      * @access protected
  234.      * @return bool 
  235.      * @param  stdClass $o mail object
  236.      *        - o->from   : Sender address
  237.      *        - o->to     : array of Reciever address
  238.      *        - o->cc     : array of See also reciever address
  239.      *        - o->bcc    : array of Hidden see also reciever address
  240.      *        - o->subjet : mail subject
  241.      *        - o->body   : mail contents
  242.      *        - o->pbody  : planin/text mail contents (optional)
  243.      *        - o->attach : attached files (array / optional)
  244.      */
  245.     protected function mailfrom ($o{
  246.         $this->error = false;
  247.         $rv $o->rpath $o->rpath $o->from;
  248.         $rv preg_replace ('/^[^<]*<|>[^>]*$/'''$rv);
  249.  
  250.         $chk preg_split ('/@/'$rv);
  251.  
  252.         if $this->check_local ($chk[0]true=== false {
  253.             $this->error = 'Invalid local part of Return-Path';
  254.             return false;
  255.         }
  256.  
  257.         if $this->check_domain ($chk[1]true=== false {
  258.             $this->error = 'Invalid domain part of Return-Path';
  259.             return false;
  260.         }
  261.  
  262.         $this->write ('MAIL From:<' $rv '>');
  263.  
  264.         if $this->read (=== false )
  265.             return false;
  266.  
  267.         return true;
  268.     }
  269.     // }}}
  270.  
  271.     // {{{ (bool) protected MTA_Socket::rcptto ($o, &$log)
  272.     /**
  273.      * RCPT 명령 전송
  274.      * 
  275.      * @access protected
  276.      * @return bool 
  277.      * @param  stdClass $o MTA_Socket::target_object method의 rcpt list
  278.      * @param  array &$log 발송 로그
  279.      */
  280.     protected function rcptto ($o&$log{
  281.         $status array ();
  282.         $this->error = false;
  283.         if is_array ($o->rcpt&& is_object ($o->rcpt|| count ($o->rcpt) ) {
  284.             $this->error = 'No RCPT list';
  285.             return false;
  286.         }
  287.  
  288.         $no count ($o->rcpt);
  289.         $fno 0;
  290.  
  291.         foreach $o->rcpt as $addr {
  292.             $this->write ('RCPT To:' $addr);
  293.  
  294.             if $this->read (=== false {
  295.                 $log[$this->error;
  296.                 $fno++;
  297.             else
  298.                 $log[sprintf ('%s Success'$addr);
  299.         }
  300.  
  301.         # 모든 rcpt list가 실패
  302.         if $fno == $no {
  303.             $this->error = 'Failure all RCPT list';
  304.             return false;
  305.         }
  306.  
  307.         return true;
  308.     }
  309.     // }}}
  310.  
  311.     // {{{ (bool) protected MTA_Socket::data (&$v)
  312.     /**
  313.      * DATA 명령 전송 및 mail body 전송
  314.      *
  315.      * @access protected
  316.      * @return bool 
  317.      * @param  string $v 메일 본문 원본
  318.      */
  319.     protected function data (&$v{
  320.         $this->error = false;
  321.  
  322.         $this->write ('DATA');
  323.         if $this->read (=== false )
  324.             return false;
  325.  
  326.         // self recieve
  327.         $smtp gethostname ();
  328.         $remote $_SERVER['REMOTE_ADDR'$_SERVER['REMOTE_ADDR''127.0.0.1';
  329.  
  330.         $received =
  331.             "Received: from localhost ([{$remote}])\r\n.
  332.             "    by {$smtp} with ESMTP id uniqid (";\r\n" .
  333.             '    ' $this->date ("\r\n";
  334.  
  335.         $this->write ($received $v "\r\n.");
  336.  
  337.         if $this->read (=== false )
  338.             return false;
  339.  
  340.         return true;
  341.     }
  342.     // }}}
  343.  
  344.     // {{{ (bool) protected MTA_Socket::open ($o)
  345.     /**
  346.      * @access protected
  347.      * @return bool 
  348.      * @param stdClass $o MTA_Socket::target_object method 반환값
  349.      */
  350.     protected function open ($o{
  351.         $this->error = false;
  352.         $mxno count ($o->mx);
  353.  
  354.         for $i=0$i<$mxno$i++ {
  355.             $mx preg_replace ('/^[0-9]+[\s]*/'''$o->mx[$i]);
  356.  
  357.  
  358.             $this->sock @stream_socket_client (
  359.                 'tcp://' $mx .':25'$errno$errstr3STREAM_CLIENT_CONNECT
  360.             );
  361.  
  362.             if is_resource ($this->sock) ) {
  363.                 $this->debug ('  - Connecting to ' $mx '... Success');
  364.                 if ( ($buf $this->read ()) === false {
  365.                     fclose ($this->sokc);
  366.                     continue;
  367.                 }
  368.                 return true;
  369.             else
  370.                 $this->debug ('  - Connecting to ' $mx '... Failure');
  371.         }
  372.  
  373.         $this->error = $errstr;
  374.  
  375.         return false;
  376.     }
  377.     // }}}
  378.  
  379.     // {{{ (string) protected MTA_Socket::read ()
  380.     /**
  381.      * 소켓을 읽는다.
  382.      *
  383.      * @access protected
  384.      * @return string 
  385.      */
  386.     protected function read ({
  387.         $this->error = null;
  388.         $buf fread ($this->sock8102);
  389.  
  390.         $code substr ($buf03);
  391.         if preg_match ('/^(220|221|250|251|354)$/'$code) ) {
  392.             $this->error = $buf;
  393.             return false;
  394.         }
  395.  
  396.         $rbuf preg_replace ('/^/m',    '           '$buf);
  397.         $rbuf preg_replace ('/^           /''  * '$rbuf);
  398.         $this->debug ($rbuf);
  399.         return $buf;
  400.     }
  401.     // }}}
  402.  
  403.     // {{{ (int) protected MTA_Socket::write ($m)
  404.     /**
  405.      * 데이터의 끝에 "\r\n"을 붙여서 전송한다.
  406.      *
  407.      * @access protected
  408.      * @return int 전송한 데이터 길이
  409.      * @param string $m 전송할 데이터
  410.      */
  411.     protected function write ($m{
  412.         $this->debug ('  * ' $m);
  413.         $m .= "\r\n";
  414.         return fwrite ($this->sock$mstrlen ($m));
  415.     }
  416.     // }}}
  417.  
  418.     // {{{ (void) protected MTA_Socket::close ()
  419.     /**
  420.      * Quit 명령을 실행한 후, socket을 닫는다.
  421.      *
  422.      * @access protected
  423.      * @return void 
  424.      */
  425.     protected function close ({
  426.         $this->write ('Quit');
  427.         if is_resource ($this->sock) )
  428.             fclose ($this->sock);
  429.     }
  430.     // }}}
  431.  
  432.     // {{{ (void) protected MTA_Socket::debug ($m)
  433.     /**
  434.      * verbose properity가 true로 설정이 되면 debug 메시지를
  435.      * 출력한다.
  436.      *
  437.      * @access protected
  438.      * @return void 
  439.      * @param string $m 메시지
  440.      */
  441.     protected function debug ($m{
  442.         if $this->verbose )
  443.             return;
  444.  
  445.         echo 'DEBUG: ' preg_replace ("/\r?\n$/"''$mPHP_EOL;
  446.     }
  447.     // }}}
  448. }
  449.  
  450. /*
  451.  * Local variables:
  452.  * tab-width: 4
  453.  * c-basic-offset: 4
  454.  * End:
  455.  * vim: set filetype=php noet sw=4 ts=4 fdm=marker:
  456.  * vim600: noet sw=4 ts=4 fdm=marker
  457.  * vim<600: noet sw=4 ts=4
  458.  */
  459. ?>

Documentation generated on Tue, 14 May 2019 02:00:35 +0900 by phpDocumentor 1.4.4