class.smtp.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <?php
  2. /**
  3. * SMTP邮件发送类
  4. * 来源: uhome:http://u.discuz.net/
  5. * 目录: source/function_sendmail.php
  6. * 改造: 改成类的方式,增加对tls的支持,增加socket超时控制,去除多余配置
  7. * 对tls的支持要加上openssl扩展.可以用程序判断: in_array('ssl',stream_get_transports()) ? 'Yes' : 'No!'
  8. * oo::smtp()->send('zjmaslow@gmail.com,zjmaslow@126.com,ColinZhou@boyaa.com', '我是邮件标题', '我是邮件内容');
  9. */
  10. class smtp{
  11. public $tls = true; //是否是tls服务器.不是请改成false
  12. public $server = 'mail.boyaa.com'; //服务器地址.GMail为:ssl://smtp.gmail.com:465 且socket要支持ssl:in_array('ssl',stream_get_transports()) 如果是tls则此处填服务器地址(无需tls://)并把$tls设为true
  13. public $port = '587'; //服务器端口
  14. public $auth = true; //smtp服务器是否需要验证
  15. public $user = 'noreply-game@boyaa.com'; //SMTP用户名
  16. public $password = 'Dd@12wZ#r5'; //SMTP密码
  17. public $from = 'noreply-game@boyaa.com'; //回复邮箱地址,126的强制跟$user相同,gmail的可以不同
  18. public $mailer = "boyaa.com"; //客户端标志
  19. public $errno = 0; //错误代码
  20. public $errstr = ''; //错误信息
  21. public function __construct(){
  22. // $this->server = '113.105.73.52'; //境外用另一个邮件服务器
  23. $this->server = 'mail1.boyaa.com'; //境外用另一个邮件服务器
  24. if (defined('PRODUCTION_SERVER') && PRODUCTION_SERVER == false) {
  25. $this->server = 'mail.boyaa.com'; // 内网用内网的邮件服务器
  26. }
  27. }
  28. /**
  29. * 发送邮件
  30. * @param String $sMails 收件人邮件地址,如果有多个则以,号隔开
  31. * @param String $sSubject 邮件主题
  32. * @param String $sMsg 邮件正文内容.支持HTML格式
  33. * @param String $sFrom 发送者名称.用于对方收件箱中显示
  34. */
  35. public function send($sMails, $sSubject, $sMsg, $sFrom='Boyaa-9K'){
  36. $fromuser = strlen( $sFrom) ? '=?utf-8?B?'.base64_encode( $sFrom)."?= <{$this->from}>" : $this->from; //自定义发送者名字
  37. foreach(explode(',', $sMails) as $touser) {
  38. $tousers[] = preg_match('/^(.+?) \<(.+?)\>$/', $touser, $to) ? '=?utf-8?B?'.base64_encode($to[1])."?= <$to[2]>" : $touser; //使得支持: 接收人名字<***@***.com> 地址形式
  39. }
  40. $sHeader = "From: {$fromuser}\r\nX-Priority: 3\r\nX-Mailer: {$this->mailer}!\r\nMIME-Version: 1.0\r\nContent-type: text/html; charset=UTF-8\r\nContent-Transfer-Encoding: base64\r\n";
  41. $sHeader .= 'Message-ID: <'.gmdate('YmdHs').'.'.substr(md5($sMsg.microtime()), 0, 6).rand(100000, 999999).'@'.$_SERVER['HTTP_HOST'].">\r\n";
  42. $sMails = implode(',', $tousers);
  43. $sSubject = '=?utf-8?B?'.base64_encode(str_replace("\r", '', str_replace("\n", '', $sSubject))).'?=';
  44. $sMsg = chunk_split(base64_encode(str_replace("\r\n.", " \r\n..", str_replace("\n", "\r\n", str_replace("\r", "\n", str_replace("\r\n", "\n", str_replace("\n\r", "\r", $sMsg)))))));
  45. if(! $fp = fsockopen($this->server, $this->port, $errno, $errstr, 5)) { //5秒链接超时
  46. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$errstr);
  47. return false;
  48. }
  49. stream_set_timeout($fp, 5, 0); //读写操作不能超过5秒
  50. stream_set_blocking($fp, true);
  51. $lastmessage = fgets($fp, 512);
  52. if( ($errno = substr($lastmessage, 0, 3)) != '220') {
  53. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  54. return false;
  55. }
  56. fputs($fp, ($this->auth ? 'EHLO' : 'HELO')." {$this->mailer}\r\n");
  57. $lastmessage = fgets($fp, 512);
  58. if( ! in_array( $errno = substr($lastmessage, 0, 3), array(220, 250))) {
  59. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  60. return false;
  61. }
  62. while(1) {
  63. if(substr($lastmessage, 3, 1) != '-' || empty($lastmessage)) {
  64. break;
  65. }
  66. $lastmessage = fgets($fp, 512);
  67. }
  68. if( $this->auth) {
  69. if( $this->tls){
  70. fputs($fp, "STARTTLS\r\n");
  71. $lastmessage = fgets($fp, 512);
  72. if(($errno = substr($lastmessage, 0, 3)) != 220) {
  73. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  74. return false;
  75. }
  76. if(! stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
  77. list($this->errno, $this->errstr) = array($errno, __LINE__);
  78. return false;
  79. }
  80. fputs($fp, ($this->auth ? 'HELO' : 'EHLO')." ".$this->mailer."\r\n"); //HELO是EHLO的扩展,支持验证的
  81. $lastmessage = fgets($fp, 512);
  82. if(($errno = substr($lastmessage, 0, 3)) != 250) {
  83. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  84. return false;
  85. }
  86. }
  87. fputs($fp, "AUTH LOGIN\r\n");
  88. $lastmessage = fgets($fp, 512);
  89. if( ($errno = substr($lastmessage, 0, 3)) != 334) {
  90. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  91. return false;
  92. }
  93. fputs($fp, base64_encode( $this->user)."\r\n");
  94. $lastmessage = fgets($fp, 512);
  95. if( ($errno = substr($lastmessage, 0, 3)) != 334) {
  96. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  97. return false;
  98. }
  99. fputs($fp, base64_encode( $this->password)."\r\n");
  100. $lastmessage = fgets($fp, 512);
  101. if( ($errno = substr($lastmessage, 0, 3)) != 235) {
  102. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  103. return false;
  104. }
  105. }
  106. fputs($fp, "MAIL FROM: <".preg_replace("/.*\<(.+?)\>.*/", "\\1", $fromuser).">\r\n");
  107. $lastmessage = fgets($fp, 512);
  108. if(substr($lastmessage, 0, 3) != 250) {
  109. fputs($fp, "MAIL FROM: <".preg_replace("/.*\<(.+?)\>.*/", "\\1", $fromuser).">\r\n");
  110. $lastmessage = fgets($fp, 512);
  111. if( ($errno = substr($lastmessage, 0, 3)) != 250) {
  112. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  113. return false;
  114. }
  115. }
  116. $email_tos = array();
  117. foreach(explode(',', $sMails) as $touser) {
  118. $touser = trim($touser);
  119. if($touser) {
  120. fputs($fp, "RCPT TO: <".preg_replace("/.*\<(.+?)\>.*/", "\\1", $touser).">\r\n");
  121. $lastmessage = fgets($fp, 512);
  122. if( ($errno = substr($lastmessage, 0, 3)) != 250) {
  123. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  124. return false;
  125. }
  126. }
  127. }
  128. fputs($fp, "DATA\r\n");
  129. $lastmessage = fgets($fp, 512);
  130. if( ($errno = substr($lastmessage, 0, 3)) != 354) {
  131. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  132. return false;
  133. }
  134. fputs($fp, "Date: ".gmdate('r')."\r\n");
  135. fputs($fp, "To: ".$sMails."\r\n");
  136. fputs($fp, "Subject: ".$sSubject."\r\n");
  137. fputs($fp, $sHeader."\r\n");
  138. fputs($fp, "\r\n\r\n");
  139. fputs($fp, "$sMsg\r\n.\r\n");
  140. $lastmessage = fgets($fp, 512);
  141. if( ($errno = substr($lastmessage, 0, 3)) != 250) {
  142. list($this->errno, $this->errstr) = array($errno, __LINE__.':'.$lastmessage);
  143. return false;
  144. }
  145. fputs($fp, "QUIT\r\n");
  146. fclose($fp);
  147. return true;
  148. }
  149. }