当前位置: 首页 > news >正文

用PHP写的POP3电子邮件收取流程

【小蜗牛嗷嗷之作
 
    这里面主要是一个 recvmail() 函数,从POP3服务器检测并收取新的邮件,最后用 update_mail_table() 函数将新邮件信息(发信者, 主题, 发送时间, 邮件文件名, 邮件大小, 附件标志)插入到数据库。
 
    我在 update_mail_table()  函数里写的解释一封电子邮件内容的方法不规范,可能无法正确解释一些不严格遵守MIME规范的邮件。还有一些其它的实用函数,参考了互联网上的别人的一些方法,Thanks the Original Author. ;)
 
<?php
/* 系统默认参数,注意:这里是全局变量 */
$connection=0;   // 保存与主机的连接 
$timeout = 5;    // 连接主机的最大超时时间 
$err_str='';     // 如果出错,这里保存错误信息 
$err_no;         // 如果出错,这里保存错误号码 
$resp;           // 临时保存服务器的响应信息
$new_mail_count = 0; //新邮件数目
/*
 * 函数功能:接收POP3服务器上的(新)邮件
 * 输入参数:$host,$user,$pass
 * 返    回:如果顺利执行,返回true,否则返回false,
 *           错误信息保存在$err_str
 *
 */
function recvmail($host, $user, $pass) {
    /* 使用全局的数据库连接 */
 global $link;
 /* 使用全局的pop3连接参数 */
 global $connection;
 global $timeout;
 global $err_str;
 global $err_no;
 global $resp;
 /* 这次所取回的新邮件数目 */
 global $new_mail_count;
 global $new_mail_dir;
 
    $mail_uidl_stored = array();    // 数据库里的邮件UIDL列表
    $mail_uidl_fresh  = array();    // 邮件服务器上的UIDL列表
    /*===================== 从数据库读取UIDL列表 ========================*/
    $query = "select * from mail_uid_list";
    if ($result = mysqli_query($link, $query)) {
        $i = 0;
        while ($row = mysqli_fetch_row($result)) {
         $mail_uidl_stored[$i++] = $row[0];
     }
     mysqli_free_result($result);
    } else {
        printf("Can't query to MySQL Server. Errorcode: %s ", mysqli_error($link)); 
     exit();
    }
    /*========================= 连接POP3服务器 =========================*/
    if (!$connection=fsockopen($host, 110 , &$err_no, &$err_str, $timeout)) { 
        $err_str="连接到POP服务器失败,错误信息:".$err_str."错误号:".$err_no; 
     return false;
    } else { 
     getresp();
        if (substr($resp,0,3)!="+OK") {
            $err_str="服务器返回无效的信息:".$resp."请检查POP服务器是否正确!"; 
      return false;
        }
    }

    /*========================= 登陆POP3服务器 =========================*/
    if (!login($user, $pass)) {
        $err_str = "帐号或者密码错误!";
  return false;
    }

    /*====================== 获取邮件列表,用UIDL命令 ====================*/
    command("UIDL", 3, "+OK");
    getresp();
    $i = 0; 
    while ($resp != ".") { 
        $mail_uidl_key[$i] = strtok($resp," ");
     $mail_uidl_fresh[$i] = strtok(" "); // 这是邮件的UID
        getresp();
     $i++;
    }
    /*====== 对邮件列表进行分析处理,对比数据库中的信息,收取新邮件 =======*/
    $new_mail_uidl = array_diff($mail_uidl_fresh, $mail_uidl_stored);
    if (empty($new_mail_uidl)) {
     $new_mail_count = 0;
  /* 注意:如果没有新邮件,这里就返回了 */
  return true;
    } else {
        $new_mail_count = count($new_mail_uidl);
    }
    /* 这里巧妙的运用了array_keys函数 */
    $new_mail_num = array_keys($new_mail_uidl);
    $i = 0;
    while (($num = $new_mail_num[$i]) || ($new_mail_num[$i] === 0)) { // 循环收取新邮件
        $num += 1;
        if (!command("RETR $num", 3, "+OK")) {
         return false;
     }
 
     $mail_file_str = "";
     $mail_str = fgets($connection, 100); 
        while ($mail_str != ".\r\n") {  // .\r\n 是邮件结束的标志
         $mail_file_str .= $mail_str; 
      $mail_str = fgets($connection, 100);
        }
 
     /* 输出邮件文件 */
     $mail_file_name_array[$i] = date("YmdHis")."_hoho_".get_radom_str(10);
     file_put_contents($new_mail_dir.$mail_file_name_array[$i], $mail_file_str);
 
     $i ++;
    }
    /*====================== 关闭与服务器连接 =========================*/
 if (!command("QUIT",3,"+OK")) {
     return false;
 }
    fclose($connection);
    /*====================== 更新数据库UID列表 ========================*/
    $i = 0;
    while (($num = $new_mail_num[$i]) || ($new_mail_num[$i] === 0)) { // 即使有一封新邮件,也要更新数据库
        //如果是大批量数据,最好采用数组合并,但目前没有掌握将数据存放msyql的方法
        //$mail_uidl_stored = array_unique(array_merge($mail_uidl_stored, $mail_uidl_fresh));
     //只好笨笨的这样做了
     $query = "insert into mail_uid_list values('".$new_mail_uidl[$new_mail_num[$i]]."', '".$mail_file_name_array[$i]."')";
     $result = mysqli_query($link, $query);
        if (!$result) {
         printf("Can't query to MySQL Server. Errorcode: %s ", mysqli_error($link)); 
            exit();
        }
 
     $i ++;
    }
    /*================ 更新mail列表 =======================*/
    $i = 0;
    while ($mail_file_name_array[$i]) {
        update_mail_table($mail_file_name_array[$i]);
     $i ++;
    }
    /*=============== 注意:这里不能关闭与数据库的连接 ==================*/
    //mysqli_close($link);
 
 return true;
}
function getresp() {
    /* 采用全局变量,再三思考,还是认为这样效率高 */
 global $connection;
 global $resp;
 
    for($resp = "";;) {   
        if(feof($connection)) 
            return false;
        $resp .= fgets($connection,100); 
        $length = strlen($resp); 
  
        if($length >= 2 && substr($resp, $length - 2, 2) == "\r\n") { 
            $resp = strtok($resp,"\r\n");
            return true; 
        }
    }
}
function command($command, $return_lenth=1, $return_code='+') { 
    /* 采用全局变量,再三思考,还是认为这样效率高 */
    global $connection;
 global $resp;
 global $err_str;
 
    if (!fputs($connection, "$command\r\n")) {
        $err_str = "无法发送命令".$command; 
        return false; 
    } else {
        getresp();
        if (substr($resp, 0, $return_lenth) != $return_code) {
            $err_str = $command." 命令服务器返回无效:".$resp;
            return false;
     } else { 
   return true;
  }
    }
}
function login($user, $password) {
    if (!command("USER $user",3,"+OK")) return false; 
    if (!command("PASS $password",3,"+OK")) return false; 
    return true; 
}
function get_radom_str($len){
    $str = 'abcdefghijklmnopqrstuvwxyz0123456789';
    //从以上字串中产生随机串,你如果想要其它字符,可以自行加入,如大写字母
    return substr(str_shuffle($str),0,$len);
}
function update_mail_table($mail_file_name) {
    global $new_mail_dir;
 
    $mail_file_content = file_get_contents($new_mail_dir.$mail_file_name);
 
 $mail_file_size = filesize($new_mail_dir.$mail_file_name);
    /* 记录附件个数和对应名字、大小 */
 if ($mail_file_size > 1048576) {
     $mail_file_size = round($mail_file_size/1048576, "2")." MB ";
 } else if ($mail_file_size > 1024) {
     $mail_file_size = round($mail_file_size/1024, "2")." KB ";
 } else {
     $mail_file_size = $mail_file_size." Bytes ";
 }
 
    /*
     * 注意!这里好像没有先后,所以不能拿一个作为另一个的参考位置出发点
     */
    /*======== 发件人 ========*/
    $from_start = strpos($mail_file_content, "\r\nFrom:");
    $from_end   = strpos($mail_file_content, "\r", $from_start + 7);
    $from_str   = substr($mail_file_content, $from_start, $from_end - $from_start);
    if ($sender_start = strpos($from_str, "?B?")) {
        $sender_start += 3;
     $sender_end = strpos($from_str, "?=", $sender_start);
     $sender_str = substr($from_str, $sender_start, $sender_end - $sender_start);
     $sender = base64_decode($sender_str);
  
  /* 检测字符集类型,在recvmail_func.php中没有这一项 */
  $charset_start = strpos($from_str, "=?") + 2;
  $charset_end = strpos($from_str, "?", $charset_start);
  $charset = substr($from_str, $charset_start, $charset_end - $charset_start);
 } else if ($sender_start = stripos($from_str, "?Q?")) {
     $sender_start += 3;
     $sender_end = strpos($from_str, "?=", $sender_start);
     $sender_str = substr($from_str, $sender_start, $sender_end - $sender_start);
  
  /* 有些变态的名字,还分开几段来写,真郁闷! */
     while ($sender_start = stripos($from_str, "?Q?", $sender_end)) {
      $sender_start += 3;
         $sender_end = strpos($from_str, "?=", $subject_start);
         $sender_str .= substr($from_str, $sender_start, $sender_end - $sender_start);
     }
  
     $sender = quoted_printable_decode($sender_str);
  
  /* 检测字符集类型,在recvmail_func.php中没有这一项 */
  $charset_start = strpos($from_str, "=?") + 2;
  $charset_end = strpos($from_str, "?", $charset_start);
  $charset = substr($from_str, $charset_start, $charset_end - $charset_start);
    } else if ($sender_start = strpos($from_str, "\"")) {
        /* “预防”有的邮件没有 "发件人",虽然自己还没见到例外 2008-10-21 */
     $sender_start += 1;
  $sender_end = strpos($from_str, "\"", $sender_start);
  /* 这里是ascii组成 */
  $sender = substr($from_str, $sender_start, $sender_end - $sender_start);
    } else {
     $sender_start = 8;
  /* 先看看是否有空格,有空格就以空格为边界 */
  if (!$sender_end = strpos($from_str, " ", $sender_start)) {
      $sender_end = $from_end;
  }
  $sender = substr($from_str, $sender_start, $sender_end - $sender_start);
  if (substr($sender, 0, 1) == "<") {
      $sender = "";
  }
 }
 
    /* 发件人email地址 */
    if ($sender_email_start = strpos($from_str, "<")) {
        $sender_email_end   = strpos($from_str, ">");
        $sender_email = substr($from_str, $sender_email_start + 1, $sender_email_end - $sender_email_start - 1);
   
     if (strlen($sender) > 30 || strlen($sender) == 0){
         $sender = $sender_email;
     }
 }
    /*======== 收件人 ========*/
    /*
     *         暂略
     */
    /*========= 主题 =========*/
    $subject_start = strpos($mail_file_content, "\r\nSubject:");
    $subject_end   = strpos($mail_file_content, "\r", $subject_start+10);
    $subject_line  = substr($mail_file_content, $subject_start, $subject_end - $subject_start);
    if ($subject_start = strpos($subject_line, "?B?")) {
        $subject_start += 3;
     $subject_end = strpos($subject_line, "?=", $subject_start);
     $subject_str = substr($subject_line, $subject_start, $subject_end - $subject_start);
     $subject = base64_decode($subject_str);
  
  /* 检测字符集类型,在recvmail_func.php中没有这一项 */
  $charset_start = strpos($subject_line, "=?") + 2;
  $charset_end = strpos($subject_line, "?", $charset_start);
  $charset = substr($subject_line, $charset_start, $charset_end - $charset_start);
 } else if ($subject_start = stripos($subject_line, "?Q?")) {
     $subject_start += 3;
     $subject_end = strpos($subject_line, "?=", $subject_start);
     $subject_str = substr($subject_line, $subject_start, $subject_end - $subject_start);
  
  /* 有些变态的名字,还分开几段来写,真郁闷! */
  while ($subject_start = stripos($subject_line, "?Q?", $subject_end)) {
      $subject_start += 3;
         $subject_end = strpos($subject_line, "?=", $subject_start);
         $subject_str .= substr($subject_line, $subject_start, $subject_end - $subject_start);
  }
  
     $subject = htmlspecialchars(quoted_printable_decode($subject_str));
  
  /* 检测字符集类型,在recvmail_func.php中没有这一项 */
  $charset_start = strpos($subject_line, "=?") + 2;
  $charset_end = strpos($subject_line, "?", $charset_start);
  $charset = substr($subject_line, $charset_start, $charset_end - $charset_start);
    } else {
     $subject = substr($subject_line, 11, $subject_end - $subject_start - 11);
    }
    /*======== 发送日期 =======*/
    if ($date_start = strpos($mail_file_content, "\r\nDate:")) {
     $date_start += 8;
        $date_end   = strpos($mail_file_content, "\r", $date_start);
        $date_str   = substr($mail_file_content, $date_start, $date_end - $date_start);
        $date = date('Y年m月d日 H:i:s', strtotime($date_str));
 } else {
     $date = date('Y年m月d日 H:i:s');
 }
 
 /* 检查是否有附件 */
 if (strpos($mail_file_content, "\r\nContent-Disposition: attachment")) {
     $has_attachment = 1;
 } else {
     $has_attachment = 0;
 }
 
 global $link;
 
    if ($charset == "UTF-8" || $charset == "utf-8") {
     $sender = iconv("UTF-8", "GB2312", $sender);
  $subject = iconv("UTF-8", "GB2312", $subject);
 }
 /* 插入数据库 */
    $query = "insert into mail(Sender, Subject, Sendtime, Filename, Filesize, Attachment) \n
           values('$sender', '$subject', '$date', '$mail_file_name', '$mail_file_size', '$has_attachment')";
        
    $result = mysqli_query($link, $query);
    if (!$result) { 
        printf("Can't query to MySQL Server. Errorcode: %s ", mysqli_error($link)); 
        exit(); 
    }
}
?>
 
------------------------------------------------------------------------------------------

相关文章:

  • linux下安装sniffit
  • linux mail命令
  • 网站不允许上传asp cer cdx htr等文件时
  • WINDOWS服务器安全设置
  • 全面封杀WVS扫描器扫描网站目录
  • webIPS防止扫描软件扫描网站
  • Linux 服务器安全配置
  • MSSQL安全设置的具体步骤和方法
  • Linux菜鸟入门级命令大全
  • ubuntu 10.10正式版
  • 菜鸟入门 Ubuntu 常用命令收集
  • 一系列测试技术
  • Web安全测试知多少
  • 常用的网站功能测试方法
  • dedecms 留言本 XSS 漏洞
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • “大数据应用场景”之隔壁老王(连载四)
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • CSS居中完全指南——构建CSS居中决策树
  • EventListener原理
  • export和import的用法总结
  • FastReport在线报表设计器工作原理
  • Gradle 5.0 正式版发布
  • javascript数组去重/查找/插入/删除
  • maya建模与骨骼动画快速实现人工鱼
  • NSTimer学习笔记
  • 彻底搞懂浏览器Event-loop
  • 基于web的全景—— Pannellum小试
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 批量截取pdf文件
  • 前端面试之闭包
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 深度学习中的信息论知识详解
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 微信小程序--------语音识别(前端自己也能玩)
  • 译有关态射的一切
  • 栈实现走出迷宫(C++)
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #pragma once
  • (安卓)跳转应用市场APP详情页的方式
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (黑马C++)L06 重载与继承
  • (接口封装)
  • (原)Matlab的svmtrain和svmclassify
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (转)项目管理杂谈-我所期望的新人
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .net 验证控件和javaScript的冲突问题