|
|
<?php
|
|
|
|
|
|
namespace Lib\Imap\Parse;
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 解析邮件
|
|
|
* @author:dc
|
|
|
* @time 2024/9/10 16:54
|
|
|
* Class Header
|
|
|
*/
|
|
|
class Fetch{
|
|
|
|
|
|
/**
|
|
|
* @var array
|
|
|
*/
|
|
|
private array $attributes = [];
|
|
|
|
|
|
/**
|
|
|
* 原始头信息
|
|
|
* @var string
|
|
|
*/
|
|
|
protected string $raw_header;
|
|
|
|
|
|
/**
|
|
|
* Header constructor.
|
|
|
* @param string $raw_header
|
|
|
*/
|
|
|
public function __construct(string $raw_header)
|
|
|
{
|
|
|
$this->raw_header = $raw_header;
|
|
|
|
|
|
$this->rfc822_parse_headers();
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* @param string $name
|
|
|
* @param string $value
|
|
|
* @param bool $append
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 16:24
|
|
|
*/
|
|
|
public function setAttribute(string $name, string $value, bool $append = true){
|
|
|
$name = strtolower($name);
|
|
|
|
|
|
if(!$append){
|
|
|
$this->attributes[$name] = $value;
|
|
|
}else{
|
|
|
if(isset($this->attributes[$name]))
|
|
|
$this->attributes[$name] .= "\r\n".$value;
|
|
|
else
|
|
|
$this->attributes[$name] = $value;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 解析邮件头部
|
|
|
* @author:dc
|
|
|
* @time 2024/9/10 16:29
|
|
|
*/
|
|
|
private function rfc822_parse_headers(){
|
|
|
$header = explode("\r\n",$this->raw_header);
|
|
|
$name = '';
|
|
|
foreach ($header as $str){
|
|
|
// 判断是否是上一行的
|
|
|
if(str_starts_with($str,' ') || str_starts_with($str,"\t")){
|
|
|
$this->setAttribute($name,$str);
|
|
|
}else{
|
|
|
$str = explode(":",$str);
|
|
|
$name = $str[0];
|
|
|
unset($str[0]);
|
|
|
$this->setAttribute($name,implode(':',$str));
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach ($this->attributes as $name => $attribute){
|
|
|
$attribute = trim($attribute);
|
|
|
$this->attributes[$name] = $attribute;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
public function __get(string $name)
|
|
|
{
|
|
|
return $this->get($name);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 读取字段
|
|
|
* @param string $name
|
|
|
* @return mixed
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 15:06
|
|
|
*/
|
|
|
public function get(string $name):mixed {
|
|
|
$name = strtolower($name);
|
|
|
|
|
|
$m = 'get'.str_replace(' ','',ucwords(str_replace('-',' ',$name)));
|
|
|
|
|
|
if(method_exists($this,$m)){
|
|
|
return $this->{$m}();
|
|
|
}
|
|
|
|
|
|
return $this->attributes[$name]??'';
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 获取收件地址 可能是假地址
|
|
|
* @param bool $is_obj 是否解析成对象 Address
|
|
|
* @return Address[]
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 15:03
|
|
|
*/
|
|
|
public function getTo() {
|
|
|
// 如果有这个字段,就用这个字段 to字段的邮箱可能被伪造
|
|
|
if(!empty($this->attributes['delivered-to'])){
|
|
|
$to = $this->attributes['delivered-to'];
|
|
|
}else{
|
|
|
$to = $this->attributes['to']??'';
|
|
|
}
|
|
|
|
|
|
return $this->parseAddress($to);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 解析地址
|
|
|
* @param string $address
|
|
|
* @return array
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 15:53
|
|
|
*/
|
|
|
private function parseAddress(string $address){
|
|
|
$arr = [];
|
|
|
foreach (explode(',',$address) as $str){
|
|
|
$arr[] = Address::make($str);
|
|
|
}
|
|
|
return $arr;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 抄送人
|
|
|
* @return array
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 15:53
|
|
|
*/
|
|
|
public function getCc()
|
|
|
{
|
|
|
return $this->parseAddress($this->attributes['cc']??'');
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 密送人
|
|
|
* @return array
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 15:54
|
|
|
*/
|
|
|
public function getBcc()
|
|
|
{
|
|
|
return $this->parseAddress($this->attributes['bcc']??'');
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 发件人 可能是欺炸 发件人
|
|
|
* @return Address
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 15:19
|
|
|
*/
|
|
|
public function getFrom():Address {
|
|
|
return Address::make($this->attributes['from']??'');
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取解码后的主题
|
|
|
* @return string
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 15:22
|
|
|
*/
|
|
|
public function getSubject():string {
|
|
|
$subject = explode("\n",$this->attributes['subject']??'');
|
|
|
foreach ($subject as $ak=>$str){
|
|
|
$subject[$ak] = DeCode::decode($str);
|
|
|
}
|
|
|
|
|
|
return implode("\n",$subject);
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* Boundary
|
|
|
* @param string $str
|
|
|
* @return string
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 16:05
|
|
|
*/
|
|
|
public function getBoundary(string $str = ''): string
|
|
|
{
|
|
|
$pre = "/boundary=(.*?(?=;)|(.*))/i";
|
|
|
// 在内容类型中读取 大多少情况都在这里面
|
|
|
$contentType = $str ? $str: ($this->attributes['content-type']??'');
|
|
|
if($contentType){
|
|
|
preg_match($pre,$contentType,$b);
|
|
|
if(!empty($b[1])){
|
|
|
return trim(str_replace(['"',';'],'',$b[1]));
|
|
|
}
|
|
|
}
|
|
|
if($this->raw_header && !$str){
|
|
|
return $this->getBoundary($this->raw_header);
|
|
|
}
|
|
|
|
|
|
return '';
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 这个是是否加急 1加急 3普通 5不急
|
|
|
* @return string
|
|
|
*/
|
|
|
public function getPriority(): string
|
|
|
{
|
|
|
if(!empty($this->attributes['priority'])){
|
|
|
return $this->attributes['priority'];
|
|
|
}
|
|
|
|
|
|
if(!empty($this->attributes['x-priority'])){
|
|
|
return $this->attributes['x-priority'];
|
|
|
}
|
|
|
|
|
|
return '3';
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
* @return array
|
|
|
*/
|
|
|
public function getAttributes(): array
|
|
|
{
|
|
|
return $this->attributes;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @return array
|
|
|
* @author:dc
|
|
|
* @time 2024/9/11 16:15
|
|
|
*/
|
|
|
public function toArray():array {
|
|
|
$arr = [];
|
|
|
foreach ($this->attributes as $key=>$attr){
|
|
|
$arr[$key] = $this->get($key);
|
|
|
}
|
|
|
|
|
|
return $arr;
|
|
|
}
|
|
|
|
|
|
} |