<?php namespace Lib\Imap\Parse; /** * 解析邮件 * @author:dc * @time 2024/9/10 16:54 * Class Header */ class Header{ /** * @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); }elseif(str_contains($str,':')){ list($name, $str) = explode(":",$str,2); $this->setAttribute($name,$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(bool $isArray = false):array { // 如果有这个字段,就用这个字段 to字段的邮箱可能被伪造 if(!empty($this->attributes['delivered-to'])){ $to = $this->attributes['delivered-to']; }else{ $to = $this->attributes['to']??''; } return $this->parseAddress($to,$isArray); } /** * 解析地址 * @param string $address * @param false $isArray 是否返回数组 * @return array * @author:dc * @time 2024/9/29 14:54 */ private function parseAddress(string $address, $isArray = false){ $arr = []; foreach (explode(',',$address) as $k=>$str){ $arr[$k] = Address::make($str); if($isArray) $arr[$k] = $arr[$k]->toArray(); } return $arr; } /** * 抄送人 * @return array * @author:dc * @time 2024/9/11 15:53 */ public function getCc(bool $isArray = false):array { return $this->parseAddress($this->attributes['cc']??'',$isArray); } /** * 密送人 * @return array * @author:dc * @time 2024/9/11 15:54 */ public function getBcc(bool $isArray = false):array { return $this->parseAddress($this->attributes['bcc']??'',$isArray); } /** * 发件人 可能是欺炸 发件人 * @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 { return static::mime_decode($this->attributes['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; } /** * @return string */ public function getRaw(): string { return $this->raw_header; } /** * 解析加密字符 * @param string $str * @return string * @author:dc * @time 2024/9/29 14:21 */ public static function mime_decode(string $str):string { $str = trim($str); if(preg_match("/^=\?([a-z0-9-]{3,})\?[bq]\?/i",$str,$code)){ // 解码 这个函数好像已经转码了, $str = mb_decode_mimeheader($str); return $str; // 转字符编码 // return mb_convert_encoding($str,'utf-8',$code[1]); } return $str; } }