正在显示
14 个修改的文件
包含
379 行增加
和
2723 行删除
| @@ -29,9 +29,19 @@ class Demo extends Command | @@ -29,9 +29,19 @@ class Demo extends Command | ||
| 29 | public function handle() | 29 | public function handle() |
| 30 | { | 30 | { |
| 31 | 31 | ||
| 32 | -// run(function (){ | ||
| 33 | -// | ||
| 34 | -// }); | 32 | + $i = 0; |
| 33 | + $n = 'a'; | ||
| 34 | + run(function () use (&$i,&$n){ | ||
| 35 | + | ||
| 36 | + go(function () use (&$i,&$n){ | ||
| 37 | + $n = 'b'; | ||
| 38 | + $i = 2; | ||
| 39 | + }); | ||
| 40 | + | ||
| 41 | + }); | ||
| 42 | + | ||
| 43 | + echo $i; | ||
| 44 | + echo $n; | ||
| 35 | 45 | ||
| 36 | return Command::SUCCESS; | 46 | return Command::SUCCESS; |
| 37 | } | 47 | } |
app/Console/Commands/PushSync.php
0 → 100644
| 1 | +<?php | ||
| 2 | +// | ||
| 3 | +//namespace App\Console\Commands; | ||
| 4 | +// | ||
| 5 | +//use App\Models\Email; | ||
| 6 | +//use App\Models\Folder; | ||
| 7 | +//use Helper\Mail\Mail; | ||
| 8 | +//use Illuminate\Console\Command; | ||
| 9 | +//use Illuminate\Support\Facades\Cache; | ||
| 10 | +//use Illuminate\Support\Facades\Log; | ||
| 11 | +//use function Co\run; | ||
| 12 | +// | ||
| 13 | +//class PushSync extends Command | ||
| 14 | +//{ | ||
| 15 | +// /** | ||
| 16 | +// * The name and signature of the console command. | ||
| 17 | +// * | ||
| 18 | +// * @var string | ||
| 19 | +// */ | ||
| 20 | +// protected $signature = 'PushSync'; | ||
| 21 | +// | ||
| 22 | +// /** | ||
| 23 | +// * The console command description. | ||
| 24 | +// * | ||
| 25 | +// * @var string | ||
| 26 | +// */ | ||
| 27 | +// protected $description = '发布同步任务到队列中'; | ||
| 28 | +// | ||
| 29 | +// /** | ||
| 30 | +// * Execute the console command. | ||
| 31 | +// * | ||
| 32 | +// * @return int | ||
| 33 | +// */ | ||
| 34 | +// public function handle() | ||
| 35 | +// { | ||
| 36 | +// // 最大协程 1000 | ||
| 37 | +// \co::set(['max_coroutine'=>1000]); | ||
| 38 | +// // 开启协程池 | ||
| 39 | +// run(function (){ | ||
| 40 | +// // 统计邮箱总数量 | ||
| 41 | +// $size = Email::select(['id'])->count(); | ||
| 42 | +// if($size){ | ||
| 43 | +// // 开启协程的数量 | ||
| 44 | +// $i = ceil($size/100); | ||
| 45 | +// while ($i >= 0){ | ||
| 46 | +// // 开启一个协程 | ||
| 47 | +// go(function () use (&$i){ | ||
| 48 | +// // 取得邮箱列表 | ||
| 49 | +// $emails = Email::getPushEmailList($i); | ||
| 50 | +// if($emails){ | ||
| 51 | +// foreach ($emails as $email) { | ||
| 52 | +// | ||
| 53 | +// } | ||
| 54 | +// } | ||
| 55 | +// }); | ||
| 56 | +// | ||
| 57 | +// $i--; | ||
| 58 | +// } | ||
| 59 | +// } | ||
| 60 | +// | ||
| 61 | +// }); | ||
| 62 | +// | ||
| 63 | +// return Command::SUCCESS; | ||
| 64 | +// } | ||
| 65 | +// | ||
| 66 | +// /** | ||
| 67 | +// * 开始同步执行 | ||
| 68 | +// * @param int $id 邮箱id | ||
| 69 | +// * @author:dc | ||
| 70 | +// * @time 2023/2/5 17:21 | ||
| 71 | +// */ | ||
| 72 | +// private function push(int $id = 0){ | ||
| 73 | +// | ||
| 74 | +// $email = Email::where(['status'=>1,'pwd_error'=>0]) | ||
| 75 | +// ->where('id','>',$id) | ||
| 76 | +// ->orderBy('id','asc') | ||
| 77 | +// ->select() | ||
| 78 | +// ->first(); | ||
| 79 | +// | ||
| 80 | +// if($email){ | ||
| 81 | +// // 登录imap服务器 | ||
| 82 | +// Mail::login($email->email,$email->password,$email->imap); | ||
| 83 | +// // 设置id | ||
| 84 | +// Mail::$client[$email]->setId($email->id); | ||
| 85 | +// // 同步文件夹 | ||
| 86 | +// Mail::syncFolder($email->email); | ||
| 87 | +// | ||
| 88 | +// // 获取当前邮箱的所有文件夹 | ||
| 89 | +// $folders = Folder::_all($email->id); | ||
| 90 | +// // 目前只发现最高2级 | ||
| 91 | +// foreach ($folders as $folder){ | ||
| 92 | +// if(empty($folder['_child'])){ | ||
| 93 | +// // 同步邮件 | ||
| 94 | +// Mail::syncMail($email->email,$folder['origin_folder']); | ||
| 95 | +// }else{ | ||
| 96 | +// // 循环子级目录,有子级的情况,父级不可操作,且不会有邮件 | ||
| 97 | +// foreach ($folder['_child'] as $f){ | ||
| 98 | +// // 同步邮件 | ||
| 99 | +// Mail::syncMail($email->email,$folder['origin_folder'].'/'.$f['origin_folder']); | ||
| 100 | +// } | ||
| 101 | +// } | ||
| 102 | +// | ||
| 103 | +// } | ||
| 104 | +// | ||
| 105 | +// // 再次执行 | ||
| 106 | +// $this->push($email->id); | ||
| 107 | +// | ||
| 108 | +// } | ||
| 109 | +// | ||
| 110 | +// } | ||
| 111 | +// | ||
| 112 | +// | ||
| 113 | +// | ||
| 114 | +//} |
app/Console/Commands/SyncFolder.php
0 → 100644
| 1 | +<?php | ||
| 2 | +// | ||
| 3 | +//namespace App\Console\Commands; | ||
| 4 | +// | ||
| 5 | +//use App\Models\Email; | ||
| 6 | +//use App\Models\Folder; | ||
| 7 | +//use Helper\Mail\Mail; | ||
| 8 | +//use Illuminate\Console\Command; | ||
| 9 | +//use Illuminate\Support\Facades\Cache; | ||
| 10 | +//use Illuminate\Support\Facades\Log; | ||
| 11 | +//use function Co\run; | ||
| 12 | +// | ||
| 13 | +//class SyncFolder extends Command | ||
| 14 | +//{ | ||
| 15 | +// /** | ||
| 16 | +// * The name and signature of the console command. | ||
| 17 | +// * | ||
| 18 | +// * @var string | ||
| 19 | +// */ | ||
| 20 | +// protected $signature = 'SyncFolder'; | ||
| 21 | +// | ||
| 22 | +// /** | ||
| 23 | +// * The console command description. | ||
| 24 | +// * | ||
| 25 | +// * @var string | ||
| 26 | +// */ | ||
| 27 | +// protected $description = '同步邮箱的文件夹'; | ||
| 28 | +// | ||
| 29 | +// /** | ||
| 30 | +// * Execute the console command. | ||
| 31 | +// * | ||
| 32 | +// * @return int | ||
| 33 | +// */ | ||
| 34 | +// public function handle() | ||
| 35 | +// { | ||
| 36 | +// | ||
| 37 | +// // 这个是防止前一个任务没有完成,反而无法取得资源。 | ||
| 38 | +// $rand = date('dHi'); | ||
| 39 | +// // 携程数量越大,需要的内存越大 | ||
| 40 | +// $max_coroutine = 10000; | ||
| 41 | +// // 最大携程数量 | ||
| 42 | +// \co::set(['max_coroutine'=>$max_coroutine]); | ||
| 43 | +// | ||
| 44 | +// run(function () use ($rand){ | ||
| 45 | +// // 获取邮箱总数量 | ||
| 46 | +// $size = Email::where(['status'=>1,'pwd_error'=>0])->count(); | ||
| 47 | +//// Cache::set('syncMail:total',$size); | ||
| 48 | +// // 创建的携程数量 | ||
| 49 | +// $max_coroutine = $size > 3000 ? 3000 : $size; | ||
| 50 | +// if ($max_coroutine){ | ||
| 51 | +// for ($i = $max_coroutine; $i > 0; $i--) { | ||
| 52 | +// // 创建携程 | ||
| 53 | +// go(function () use ($size,$rand){ | ||
| 54 | +//// Log::error('携程:'.\co::getCid()); | ||
| 55 | +// | ||
| 56 | +// $n = 0; | ||
| 57 | +// while ($n <= $size){ | ||
| 58 | +// // 是否已存在 | ||
| 59 | +// if(Cache::add('syncMail'.$rand.':'.$n,$n,7200)) { | ||
| 60 | +// $this->sync($n); | ||
| 61 | +// } | ||
| 62 | +// $n++; | ||
| 63 | +// } | ||
| 64 | +// | ||
| 65 | +// }); | ||
| 66 | +// } | ||
| 67 | +// } | ||
| 68 | +// | ||
| 69 | +// }); | ||
| 70 | +// | ||
| 71 | +// return Command::SUCCESS; | ||
| 72 | +// } | ||
| 73 | +// | ||
| 74 | +// /** | ||
| 75 | +// * 开始同步执行 | ||
| 76 | +// * @param int $n 第几条数据 | ||
| 77 | +// * @author:dc | ||
| 78 | +// * @time 2023/2/5 17:21 | ||
| 79 | +// */ | ||
| 80 | +// private function sync(int $n = 0){ | ||
| 81 | +// | ||
| 82 | +// $email = Email::where(['status'=>1,'pwd_error'=>0]) | ||
| 83 | +// ->orderBy('id','asc') | ||
| 84 | +// ->select()->limit($n,1) | ||
| 85 | +// ->first(); | ||
| 86 | +// | ||
| 87 | +// if($email){ | ||
| 88 | +// // 登录imap服务器 | ||
| 89 | +// Mail::login($email->email,$email->password,$email->imap); | ||
| 90 | +// // 设置id | ||
| 91 | +// Mail::$client[$email]->setId($email->id); | ||
| 92 | +// // 同步文件夹 | ||
| 93 | +// Mail::syncFolder($email->email); | ||
| 94 | +// | ||
| 95 | +// // 获取当前邮箱的所有文件夹 | ||
| 96 | +// $folders = Folder::_all($email->id); | ||
| 97 | +// // 目前只发现最高2级 | ||
| 98 | +// foreach ($folders as $folder){ | ||
| 99 | +// if(empty($folder['_child'])){ | ||
| 100 | +// // 同步邮件 | ||
| 101 | +// Mail::syncMail($email->email,$folder['origin_folder']); | ||
| 102 | +// }else{ | ||
| 103 | +// // 循环子级目录,有子级的情况,父级不可操作,且不会有邮件 | ||
| 104 | +// foreach ($folder['_child'] as $f){ | ||
| 105 | +// // 同步邮件 | ||
| 106 | +// Mail::syncMail($email->email,$folder['origin_folder'].'/'.$f['origin_folder']); | ||
| 107 | +// } | ||
| 108 | +// } | ||
| 109 | +// | ||
| 110 | +// } | ||
| 111 | +// | ||
| 112 | +// | ||
| 113 | +// | ||
| 114 | +// } | ||
| 115 | +// | ||
| 116 | +// } | ||
| 117 | +// | ||
| 118 | +// | ||
| 119 | +// | ||
| 120 | +//} |
app/Console/Commands/SyncMailList.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace App\Console\Commands; | ||
| 4 | + | ||
| 5 | +use App\Models\Email; | ||
| 6 | +use App\Models\Folder; | ||
| 7 | +use Helper\Mail\Mail; | ||
| 8 | +use Illuminate\Console\Command; | ||
| 9 | +use Illuminate\Support\Facades\Cache; | ||
| 10 | +use Illuminate\Support\Facades\Log; | ||
| 11 | +use function Co\run; | ||
| 12 | + | ||
| 13 | +class SyncMailList extends Command | ||
| 14 | +{ | ||
| 15 | + /** | ||
| 16 | + * The name and signature of the console command. | ||
| 17 | + * | ||
| 18 | + * @var string | ||
| 19 | + */ | ||
| 20 | + protected $signature = 'SyncMailList'; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * The console command description. | ||
| 24 | + * | ||
| 25 | + * @var string | ||
| 26 | + */ | ||
| 27 | + protected $description = '同步邮箱的邮件到本地'; | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * Execute the console command. | ||
| 31 | + * | ||
| 32 | + * @return int | ||
| 33 | + */ | ||
| 34 | + public function handle() | ||
| 35 | + { | ||
| 36 | + | ||
| 37 | + // 这个是防止前一个任务没有完成,反而无法取得资源。 | ||
| 38 | + $rand = date('dHi'); | ||
| 39 | + // 携程数量越大,需要的内存越大 | ||
| 40 | + $max_coroutine = 10000; | ||
| 41 | + // 最大携程数量 | ||
| 42 | + \co::set(['max_coroutine'=>$max_coroutine]); | ||
| 43 | + | ||
| 44 | + run(function () use ($rand,$max_coroutine){ | ||
| 45 | + // 获取邮箱总数量 | ||
| 46 | + $size = Email::where([])->count(); | ||
| 47 | + // 最后一条数据的id | ||
| 48 | + $lastId = Email::where([])->orderBy('id','desc')->value('id'); | ||
| 49 | + // 创建的携程数量 | ||
| 50 | + $max_coroutine = $size > $max_coroutine ? $max_coroutine : $size; | ||
| 51 | + if ($max_coroutine){ | ||
| 52 | + for ($i = $max_coroutine; $i > 0; $i--) { | ||
| 53 | + // 创建携程 | ||
| 54 | + go(function () use ($size,$rand,$lastId){ | ||
| 55 | + | ||
| 56 | + $n = 1; | ||
| 57 | + while ($n <= $lastId){ | ||
| 58 | + // 是否已存在 | ||
| 59 | + if(Cache::add('syncMail'.$rand.':'.$n,$n,7200)) { | ||
| 60 | + $this->sync($n); | ||
| 61 | + } | ||
| 62 | + $n++; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + }); | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + }); | ||
| 70 | + | ||
| 71 | + return Command::SUCCESS; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + /** | ||
| 75 | + * 开始同步执行 | ||
| 76 | + * @param int $n 第几条数据 | ||
| 77 | + * @author:dc | ||
| 78 | + * @time 2023/2/5 17:21 | ||
| 79 | + */ | ||
| 80 | + private function sync(int $n = 0){ | ||
| 81 | + | ||
| 82 | + $email = Email::where(['id'=>$n])->first(); | ||
| 83 | + // 密码没有错误,且状态正常的 | ||
| 84 | + if ($email && $email->pwd_error == 0 && $email->status == Email::STATUS_ACTIVE){ | ||
| 85 | + // 登录imap服务器 | ||
| 86 | + Mail::login($email->email,$email->password,$email->imap); | ||
| 87 | + // 设置id | ||
| 88 | + Mail::$client[$email]->setId($email->id); | ||
| 89 | + // 同步文件夹 | ||
| 90 | + Mail::syncFolder($email->email); | ||
| 91 | + | ||
| 92 | + // 获取当前邮箱的所有文件夹 | ||
| 93 | + $folders = Folder::_all($email->id); | ||
| 94 | + // 目前只发现最高2级 | ||
| 95 | + foreach ($folders as $folder){ | ||
| 96 | + if(empty($folder['_child'])){ | ||
| 97 | + // 同步邮件 | ||
| 98 | + Mail::syncMail([],$email->email,$email->id,$folder['id'],$folder['origin_folder']); | ||
| 99 | + }else{ | ||
| 100 | + // 循环子级目录,有子级的情况,父级不可操作,且不会有邮件 | ||
| 101 | + foreach ($folder['_child'] as $f){ | ||
| 102 | + // 同步邮件 | ||
| 103 | + Mail::syncMail([],$email->email,$email->id,$f['id'],$folder['origin_folder'].'/'.$f['origin_folder']); | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + | ||
| 113 | + | ||
| 114 | +} |
| @@ -4,8 +4,8 @@ namespace App\Http\Controllers; | @@ -4,8 +4,8 @@ namespace App\Http\Controllers; | ||
| 4 | 4 | ||
| 5 | use App\Models\Email; | 5 | use App\Models\Email; |
| 6 | use App\Models\Host; | 6 | use App\Models\Host; |
| 7 | -use Helper\Fun; | ||
| 8 | use Helper\Mail\Imap; | 7 | use Helper\Mail\Imap; |
| 8 | +use Helper\Mail\Mail; | ||
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| 11 | * 提供邮件各项数据 | 11 | * 提供邮件各项数据 |
| @@ -35,10 +35,10 @@ class MailApi | @@ -35,10 +35,10 @@ class MailApi | ||
| 35 | ]); | 35 | ]); |
| 36 | 36 | ||
| 37 | if($validator->fails()){ | 37 | if($validator->fails()){ |
| 38 | - Fun::response() | 38 | + return res() |
| 39 | ->message($validator->errors()->first()) | 39 | ->message($validator->errors()->first()) |
| 40 | ->status(400) | 40 | ->status(400) |
| 41 | - ->throw(); | 41 | + ->toJson(); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | // host | 44 | // host |
| @@ -50,30 +50,33 @@ class MailApi | @@ -50,30 +50,33 @@ class MailApi | ||
| 50 | 50 | ||
| 51 | $model->imap = $formData['imap']; | 51 | $model->imap = $formData['imap']; |
| 52 | $model->smtp = $formData['smtp']; | 52 | $model->smtp = $formData['smtp']; |
| 53 | - $model->password = encrypt($formData['password']); | 53 | + $model->password = @base64_encode($formData['password']); |
| 54 | 54 | ||
| 55 | - $imap = new Imap(); | ||
| 56 | - // 是否初始成功 | ||
| 57 | try { | 55 | try { |
| 58 | - $imap->login("ssl://{$formData['imap']}:993",$model->email,$model->password); | 56 | + Mail::login($model->email,$model->password,$model->imap); |
| 59 | }catch (\Throwable $e){ | 57 | }catch (\Throwable $e){ |
| 60 | - Fun::response() | 58 | + return res() |
| 61 | ->message($e->getMessage()) | 59 | ->message($e->getMessage()) |
| 62 | ->status(400) | 60 | ->status(400) |
| 63 | - ->throw(); | 61 | + ->toJson(); |
| 64 | } | 62 | } |
| 65 | 63 | ||
| 66 | // 登录成功了,密码验证字段通过 | 64 | // 登录成功了,密码验证字段通过 |
| 67 | - $model->pass_error = 0; | 65 | + $model->pwd_error = 0; |
| 68 | // 保存好邮箱 | 66 | // 保存好邮箱 |
| 69 | $model->save(); | 67 | $model->save(); |
| 70 | 68 | ||
| 69 | + // 设置上id,方便后面使用 | ||
| 70 | + Mail::$client[$model->email]->setId($model->id); | ||
| 71 | + | ||
| 71 | // 开始同步文件夹 | 72 | // 开始同步文件夹 |
| 72 | - $folder = $imap->getFolder(); | 73 | +// $folder = Mail::syncFolder($model->email); |
| 73 | 74 | ||
| 74 | - Fun::response() | ||
| 75 | - ->data($folder) | ||
| 76 | - ->throw(); | 75 | + return res() |
| 76 | + ->data([ | ||
| 77 | + 'token' => token_en($model->id.','.$model->email.','.time()) | ||
| 78 | + ]) | ||
| 79 | + ->toJson(); | ||
| 77 | } | 80 | } |
| 78 | 81 | ||
| 79 | 82 | ||
| @@ -86,7 +89,7 @@ class MailApi | @@ -86,7 +89,7 @@ class MailApi | ||
| 86 | 89 | ||
| 87 | $host = Host::_all(); | 90 | $host = Host::_all(); |
| 88 | 91 | ||
| 89 | - Fun::response()->data($host)->throw(); | 92 | + res()->data($host)->throw(); |
| 90 | 93 | ||
| 91 | } | 94 | } |
| 92 | 95 |
app/Mail/Jobs/ImapApi.php
已删除
100644 → 0
| 1 | -<?php | ||
| 2 | - | ||
| 3 | -namespace App\Mail\Jobs; | ||
| 4 | - | ||
| 5 | -/** | ||
| 6 | - * | ||
| 7 | - * @time 2022/7/29 15:11 | ||
| 8 | - * Class ImapApi | ||
| 9 | - * @package App\Mail\Jobs | ||
| 10 | - */ | ||
| 11 | -class ImapApi { | ||
| 12 | - | ||
| 13 | - /** | ||
| 14 | - * @var ImapApi | ||
| 15 | - */ | ||
| 16 | - private static $Instance; | ||
| 17 | - | ||
| 18 | - /** | ||
| 19 | - * 如 imap.qq.com | ||
| 20 | - * @var string | ||
| 21 | - */ | ||
| 22 | - private $host; | ||
| 23 | - | ||
| 24 | - /** | ||
| 25 | - * 邮箱地址 | ||
| 26 | - * @var string | ||
| 27 | - */ | ||
| 28 | - private $username; | ||
| 29 | - | ||
| 30 | - /** | ||
| 31 | - * 密码 | ||
| 32 | - * @var string | ||
| 33 | - */ | ||
| 34 | - private $password; | ||
| 35 | - | ||
| 36 | - /** | ||
| 37 | - * 端口 | ||
| 38 | - * @var int | ||
| 39 | - */ | ||
| 40 | - private $port = 993; | ||
| 41 | - | ||
| 42 | - | ||
| 43 | - private function __construct(){} | ||
| 44 | - | ||
| 45 | - | ||
| 46 | - /** | ||
| 47 | - * @return ImapApi | ||
| 48 | - */ | ||
| 49 | - public static function getInstance(): ImapApi | ||
| 50 | - { | ||
| 51 | - if(!self::$Instance){ | ||
| 52 | - self::$Instance = new static(); | ||
| 53 | - } | ||
| 54 | - | ||
| 55 | - return self::$Instance; | ||
| 56 | - } | ||
| 57 | - | ||
| 58 | - | ||
| 59 | - | ||
| 60 | -} |
app/Mail/Jobs/SendJob.php
已删除
100644 → 0
| 1 | -<?php | ||
| 2 | -namespace App\Mail\Jobs; | ||
| 3 | - | ||
| 4 | - | ||
| 5 | -use App\Http\Mail\lib\MailFun; | ||
| 6 | -use App\Http\Mail\Models\Email; | ||
| 7 | -use App\Http\Mail\Models\EmailSendJob; | ||
| 8 | -use App\Http\Mail\Models\EmailSendJobStatu; | ||
| 9 | -use App\Http\Models\EmailSendTemplate; | ||
| 10 | -use App\Sk; | ||
| 11 | -use Illuminate\Bus\Queueable; | ||
| 12 | -use Illuminate\Contracts\Queue\ShouldQueue; | ||
| 13 | -use Illuminate\Foundation\Bus\Dispatchable; | ||
| 14 | -use Illuminate\Queue\InteractsWithQueue; | ||
| 15 | -use Illuminate\Queue\SerializesModels; | ||
| 16 | -use Illuminate\Support\Facades\Cache; | ||
| 17 | - | ||
| 18 | -/** | ||
| 19 | - * @author:dc | ||
| 20 | - * @time 2022/11/9 11:52 | ||
| 21 | - * Class SendJob | ||
| 22 | - */ | ||
| 23 | -class SendJob implements ShouldQueue | ||
| 24 | -{ | ||
| 25 | - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||
| 26 | - | ||
| 27 | - public $job_id; | ||
| 28 | - | ||
| 29 | - /** | ||
| 30 | - * SendServiceMsg constructor. | ||
| 31 | - * @param $id | ||
| 32 | - */ | ||
| 33 | - public function __construct($id) | ||
| 34 | - { | ||
| 35 | - // 使用的链接 | ||
| 36 | - $this->connection = Sk::QUEUE_EMAIL_QUN; | ||
| 37 | - | ||
| 38 | - $this->job_id = $id; | ||
| 39 | - } | ||
| 40 | - | ||
| 41 | - /** | ||
| 42 | - * Execute the job. | ||
| 43 | - * | ||
| 44 | - * @return bool | ||
| 45 | - */ | ||
| 46 | - public function handle() | ||
| 47 | - { | ||
| 48 | - // 查询邮件任务主体 | ||
| 49 | - $jobData = EmailSendJob::where('id',$this->job_id) | ||
| 50 | - ->whereIn('status',[EmailSendJob::STATUS_WAIT,EmailSendJob::STATUS_RUNING]) | ||
| 51 | - ->first(); | ||
| 52 | - | ||
| 53 | - if(!$jobData){ | ||
| 54 | - $this->log('任务不存在'); | ||
| 55 | - return false; | ||
| 56 | - } | ||
| 57 | - | ||
| 58 | - // 发送时间 | ||
| 59 | - if(trim($jobData->send_time) != 'now'){ | ||
| 60 | - // 定时发送 | ||
| 61 | - list($start,$end) = explode(',',$jobData->send_time); | ||
| 62 | - // 系统是中国时区,按照美国时区要慢13个小时 | ||
| 63 | - if(!(date('H') >= $start || date('H') < $end)){ | ||
| 64 | - // 也就是 美国早上 8点到晚上10点 | ||
| 65 | -// $this->log('休息中'); | ||
| 66 | - SendJob::dispatch($this->job_id)->delay(600); | ||
| 67 | - return true; | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | - } | ||
| 71 | - | ||
| 72 | - $this->log('开始检查任务:'.$this->job_id); | ||
| 73 | - if(!$jobData){ | ||
| 74 | - $this->log('没有找到任务'); | ||
| 75 | - return true; | ||
| 76 | - } | ||
| 77 | - // 查询需要发送的邮件 | ||
| 78 | - $jobStdata = EmailSendJobStatu::where(['job_id'=>$this->job_id,'status'=>EmailSendJobStatu::STATUS_WAIT])->first(); | ||
| 79 | - if(!$jobStdata){ | ||
| 80 | - $this->log('完成所有了'); | ||
| 81 | - // 是否完成 | ||
| 82 | - $jobData->status = EmailSendJob::STATUS_SUCCESS; | ||
| 83 | - $jobData->save(); | ||
| 84 | - return true; | ||
| 85 | - } | ||
| 86 | - $this->log('找到任务:'.$jobStdata['id']); | ||
| 87 | - | ||
| 88 | - // 防止重复 | ||
| 89 | - $cachekey = 'email_send_job:'.$this->job_id.":".$jobStdata['id']; | ||
| 90 | - // 存在 | ||
| 91 | - if(Cache::has($cachekey)){ | ||
| 92 | - $this->log("任务:{$jobStdata['id']}正在发送,跳过"); | ||
| 93 | - SendJob::dispatch($this->job_id)->delay(30); | ||
| 94 | - return true; | ||
| 95 | - } | ||
| 96 | - // 占有2分钟 | ||
| 97 | - Cache::set($cachekey,$jobStdata['id'],120); | ||
| 98 | - | ||
| 99 | - | ||
| 100 | - // 是否等待状态 | ||
| 101 | - if($jobData->status !== EmailSendJob::STATUS_RUNING){ | ||
| 102 | - $this->log('开始运行脚本了'); | ||
| 103 | - $jobData->status = EmailSendJob::STATUS_RUNING;; | ||
| 104 | - $jobData->save(); | ||
| 105 | - } | ||
| 106 | - | ||
| 107 | - // 当前管理账号下所有绑定的邮件 | ||
| 108 | - $emailinfos = Email::_get($jobData->user_id,Email::STATUS_ACTIVE,['e.id','e.email','e.email_name','e.smtp','e.password','e.pwd_error']); | ||
| 109 | - | ||
| 110 | - $smtpErrorNum = 0;// 错误次数 | ||
| 111 | - | ||
| 112 | - // 标签 | ||
| 113 | - $tags = $jobData->tags; | ||
| 114 | - $tags = is_array($tags) ? $tags : explode(',',$tags); | ||
| 115 | - // 模板列表 | ||
| 116 | - $tempLists = EmailSendTemplate::getAdminTagsList($tags); | ||
| 117 | - if(!$tempLists){ | ||
| 118 | - // 是否完成 | ||
| 119 | - $jobData->status = EmailSendJob::STATUS_SUCCESS; | ||
| 120 | - $jobData->save(); | ||
| 121 | - $this->log('没有可用的模板'); | ||
| 122 | - return false; | ||
| 123 | - } | ||
| 124 | - // 随机一个模板 | ||
| 125 | - $template = $tempLists[array_rand($tempLists->toArray())]; | ||
| 126 | - | ||
| 127 | - | ||
| 128 | - // 节点 | ||
| 129 | - EMAILRESETRANG: | ||
| 130 | - | ||
| 131 | - if(!$emailinfos){ | ||
| 132 | - $this->log('暂时没有可分配的账号'); | ||
| 133 | - // 再次发布任务,延时10分钟 | ||
| 134 | - SendJob::dispatch($this->job_id)->delay(120); | ||
| 135 | - return true; | ||
| 136 | - } | ||
| 137 | - | ||
| 138 | - // 随机一个邮件来当发送 | ||
| 139 | - $key = array_rand($emailinfos,1); | ||
| 140 | - $emailinfo = $emailinfos ? $emailinfos[$key] : []; | ||
| 141 | - if(!$emailinfo){ | ||
| 142 | - $this->log('没有分配到账号'); | ||
| 143 | - // 再次发布任务,延时10分钟 | ||
| 144 | - SendJob::dispatch($this->job_id)->delay(600); | ||
| 145 | - return true; | ||
| 146 | - } | ||
| 147 | - unset($emailinfos[$key]); | ||
| 148 | - $emailinfos = array_values($emailinfos); | ||
| 149 | - | ||
| 150 | - // 密码是否需要验证 | ||
| 151 | - if($emailinfo['pwd_error']){ | ||
| 152 | - $this->log('账号密码验证失败,需要修改密码 '.$emailinfo['email']); | ||
| 153 | - // 重新随机一个, | ||
| 154 | - goto EMAILRESETRANG; | ||
| 155 | - } | ||
| 156 | - | ||
| 157 | - // 每个小时不能超过20 | ||
| 158 | - $cacheEmailSuccesshkey = 'email_job_email:'.$emailinfo['email'].":success_h"; | ||
| 159 | - if(Cache::get($cacheEmailSuccesshkey,0) >= 8){ | ||
| 160 | - $this->log('账号超过每小时8封了'.$emailinfo['email']); | ||
| 161 | - // 重新随机一个, | ||
| 162 | - goto EMAILRESETRANG; | ||
| 163 | - } | ||
| 164 | - | ||
| 165 | - // 一天中是否超过100, | ||
| 166 | -// $cacheEmailSuccessdkey = 'email_job_email:'.$emailinfo['email'].":success_d"; | ||
| 167 | -// if(Cache::get($cacheEmailSuccessdkey,0) >= 100){ | ||
| 168 | -// $this->log('账号超过每天100封了'.$emailinfo['email']); | ||
| 169 | -// // 重新随机一个, | ||
| 170 | -// goto EMAILRESETRANG; | ||
| 171 | -// } | ||
| 172 | - | ||
| 173 | - $cachemailikey = 'email_job_email:'.$emailinfo['email'].":success_i"; | ||
| 174 | - if (Cache::has($cachemailikey)){ | ||
| 175 | - $this->log('账号没超过10分钟间隔 '.$emailinfo['email']); | ||
| 176 | - // 重新随机一个, | ||
| 177 | - goto EMAILRESETRANG; | ||
| 178 | - } | ||
| 179 | - | ||
| 180 | - // 是否错误了 | ||
| 181 | - $cachemailierrordkey = 'email_job_email:'.$emailinfo['email'].":error_d"; | ||
| 182 | - if(Cache::has($cachemailierrordkey)){ | ||
| 183 | - $this->log('账号记录错误了,跳过 1小时'.$emailinfo['email']); | ||
| 184 | - goto EMAILRESETRANG; | ||
| 185 | - } | ||
| 186 | - | ||
| 187 | - Cache::set($cachemailikey,$jobStdata['id'],550); | ||
| 188 | - | ||
| 189 | - $this->log('找到账号:'.$emailinfo['email']); | ||
| 190 | - // 数据发送的email | ||
| 191 | - $data = json_decode($jobData->maildata,true); | ||
| 192 | - | ||
| 193 | - $data['body'] = $template['body']; | ||
| 194 | - $data['subject'] = $template['subject']; | ||
| 195 | - | ||
| 196 | - try { | ||
| 197 | - | ||
| 198 | - // 替换邮件规则, | ||
| 199 | - $data['body'] = str_replace('[read][/read]','<div style="opacity: 0;width: 1px;height: 1px;overflow: hidden;"><img src="https://king.shopk.com/_shopk_?mail='.base64_encode('logo|'.$jobStdata['id']).'" /></div>',$data['body']); | ||
| 200 | - // 匹配链接规则 | ||
| 201 | - if (preg_match_all("/\[link:(.*)\](.*)\[\/link\]/U",$data['body'],$m)){ | ||
| 202 | - foreach ($m[0] as $mk=>$item){ | ||
| 203 | - $data['body'] = str_replace($item,'<a target="_blank" href="https://king.shopk.com/_shopk_?mail='.base64_encode('link|'.$jobStdata['id'].'|'.urlencode($m[1][$mk])).'">'.$m[2][$mk].'</a>',$data['body']); | ||
| 204 | - } | ||
| 205 | - } | ||
| 206 | - | ||
| 207 | - // 发送邮件 | ||
| 208 | - MailFun::sendEmail( | ||
| 209 | - $emailinfo['smtp'],$emailinfo['email'],decrypt($emailinfo['password']) | ||
| 210 | - ,$emailinfo['email_name'],$jobStdata['to_email'],$data['subject'], | ||
| 211 | - $data['body'],$data['file']??[],false | ||
| 212 | - ,($data['priority']??0) ? 1 : 3 | ||
| 213 | - ); | ||
| 214 | - | ||
| 215 | - // 记录 | ||
| 216 | - $jobStdata->status = EmailSendJobStatu::STATUS_SUCCESS; | ||
| 217 | - $jobStdata->send_email = $emailinfo['email']; | ||
| 218 | - $jobStdata->time = date('Y-m-d H:i:s'); | ||
| 219 | - $this->log('成功了'); | ||
| 220 | - // 成功 | ||
| 221 | - $jobData->success = $jobData->success+1; | ||
| 222 | - | ||
| 223 | - // 24小时内不能超过100 | ||
| 224 | -// if(Cache::has($cacheEmailSuccessdkey)){ | ||
| 225 | -// // 加1 | ||
| 226 | -// Cache::increment($cacheEmailSuccessdkey); | ||
| 227 | -// }else{ | ||
| 228 | -// Cache::set($cacheEmailSuccessdkey,1,86400); | ||
| 229 | -// } | ||
| 230 | - | ||
| 231 | - // 每小时内不能超过20 | ||
| 232 | - if(Cache::has($cacheEmailSuccesshkey)){ | ||
| 233 | - // 加1 | ||
| 234 | - Cache::increment($cacheEmailSuccesshkey); | ||
| 235 | - }else{ | ||
| 236 | - Cache::set($cacheEmailSuccesshkey,1,3600); | ||
| 237 | - } | ||
| 238 | - | ||
| 239 | - | ||
| 240 | - | ||
| 241 | - } catch (\Exception $e) { | ||
| 242 | - if($e->getMessage()=='SMTP Error: data not accepted.'){ | ||
| 243 | - // 下次跳过账号 | ||
| 244 | - if(!Cache::has($cachemailierrordkey)) { | ||
| 245 | - Cache::set($cachemailierrordkey, $e->getMessage(), 3600); | ||
| 246 | - } | ||
| 247 | - } | ||
| 248 | - // 无法验证,密码错误了,处理密码 | ||
| 249 | - if($e->getMessage()=='SMTP Error: Could not authenticate.'){ | ||
| 250 | - Email::_update(['id'=>$emailinfo['id']],['pwd_error'=>1]); | ||
| 251 | - } | ||
| 252 | - | ||
| 253 | - | ||
| 254 | - $smtpErrorNum++; | ||
| 255 | - // 超过失败3次的 | ||
| 256 | - $error = $jobStdata->error; | ||
| 257 | - $error[] = [ | ||
| 258 | - 'email' => $emailinfo['email'], | ||
| 259 | - 'message' => $e->getMessage(), | ||
| 260 | - 'file' => $e->getFile(), | ||
| 261 | - 'line' => $e->getLine() | ||
| 262 | - ]; | ||
| 263 | - $jobStdata->time = date('Y-m-d H:i:s'); | ||
| 264 | - $jobStdata->error = json_encode($error); | ||
| 265 | - | ||
| 266 | - // 如果失败了,重试3次 | ||
| 267 | - if($smtpErrorNum <= 20){ | ||
| 268 | - // 记录 | ||
| 269 | - $jobStdata->save(); | ||
| 270 | - $this->log('错误次数'.$smtpErrorNum); | ||
| 271 | - goto EMAILRESETRANG; | ||
| 272 | - } | ||
| 273 | - $this->log('错误超过次数'.$smtpErrorNum); | ||
| 274 | - // 记录 | ||
| 275 | - $jobStdata->status = EmailSendJobStatu::STATUS_ERROR; | ||
| 276 | - $jobStdata->send_email = $emailinfo['email']; | ||
| 277 | - | ||
| 278 | - // 错误 | ||
| 279 | - $jobData->error = $jobData->error+1; | ||
| 280 | - } | ||
| 281 | - | ||
| 282 | - // 保存 | ||
| 283 | - $jobStdata->save(); | ||
| 284 | - $jobData->save(); | ||
| 285 | - $this->log('完成'); | ||
| 286 | - | ||
| 287 | - // 再次发布任务 | ||
| 288 | - SendJob::dispatch($this->job_id); | ||
| 289 | - | ||
| 290 | - | ||
| 291 | - } | ||
| 292 | - | ||
| 293 | - | ||
| 294 | - protected function log($content){ | ||
| 295 | - @file_put_contents(storage_path('logs/send_email_job_'.$this->job_id.'_.log'),date('Y-m-d H:i:s ').$content.PHP_EOL,FILE_APPEND); | ||
| 296 | - } | ||
| 297 | - | ||
| 298 | -} |
app/Mail/Mail.php
已删除
100644 → 0
| 1 | -<?php | ||
| 2 | - | ||
| 3 | -namespace App\Mail; | ||
| 4 | - | ||
| 5 | -use App\Mail\Jobs\SendJob; | ||
| 6 | -use App\Mail\lib\Lang; | ||
| 7 | -use App\Mail\lib\MailFun; | ||
| 8 | -use App\Mail\lib\MailParse\Body; | ||
| 9 | -use App\Models\Email; | ||
| 10 | -use App\Models\EmailBody; | ||
| 11 | -use App\Models\EmailContact; | ||
| 12 | -use App\Models\EmailContactGroup; | ||
| 13 | -use App\Models\EmailFolder; | ||
| 14 | -use App\Models\Host; | ||
| 15 | -use App\Models\EmailList; | ||
| 16 | -use App\Models\EmailLog; | ||
| 17 | -use App\Models\EmailSendJob; | ||
| 18 | -use Illuminate\Http\UploadedFile; | ||
| 19 | -use Illuminate\Support\Facades\Config; | ||
| 20 | -use Illuminate\Support\Facades\DB; | ||
| 21 | -use Illuminate\Support\Facades\Log; | ||
| 22 | -use Illuminate\Support\Facades\Validator; | ||
| 23 | - | ||
| 24 | -/** | ||
| 25 | - * 邮件服务 | ||
| 26 | - * @time 2022/7/29 15:05 | ||
| 27 | - * Class Mail | ||
| 28 | - * @package app\Mail | ||
| 29 | - */ | ||
| 30 | -class Mail { | ||
| 31 | - | ||
| 32 | - /** | ||
| 33 | - * @var Mail | ||
| 34 | - */ | ||
| 35 | - private static $Instance; | ||
| 36 | - | ||
| 37 | - /** | ||
| 38 | - * 如 imap.qq.com | ||
| 39 | - * @var array | ||
| 40 | - */ | ||
| 41 | - private $host = [ | ||
| 42 | - 'imap'=>'', | ||
| 43 | - 'smtp'=>'' | ||
| 44 | - ]; | ||
| 45 | - | ||
| 46 | - /** | ||
| 47 | - * 用户email表的id | ||
| 48 | - * @var int | ||
| 49 | - */ | ||
| 50 | - private $id = 0; | ||
| 51 | - | ||
| 52 | - /** | ||
| 53 | - * 邮箱地址 | ||
| 54 | - * @var string | ||
| 55 | - */ | ||
| 56 | - private $username = ''; | ||
| 57 | - private $nickname = ''; | ||
| 58 | - | ||
| 59 | - /** | ||
| 60 | - * 密码 | ||
| 61 | - * @var string | ||
| 62 | - */ | ||
| 63 | - private $password = ''; | ||
| 64 | - | ||
| 65 | - /** | ||
| 66 | - * 端口 | ||
| 67 | - * @var int 143 993 | ||
| 68 | - */ | ||
| 69 | - private $port = 993; | ||
| 70 | - /** | ||
| 71 | - * 协议 | ||
| 72 | - * @var string | ||
| 73 | - */ | ||
| 74 | - private $ssl = 'ssl://'; | ||
| 75 | - | ||
| 76 | - /** | ||
| 77 | - * 用户id | ||
| 78 | - * @var int | ||
| 79 | - */ | ||
| 80 | - private $user_id = 0; | ||
| 81 | - | ||
| 82 | - /** | ||
| 83 | - * 附件保存目录 | ||
| 84 | - * @var string | ||
| 85 | - */ | ||
| 86 | - private $filePath; | ||
| 87 | - | ||
| 88 | - | ||
| 89 | - | ||
| 90 | - /** | ||
| 91 | - * imap服务器连接 | ||
| 92 | - * @var \App\Http\Mail\lib\client\Imap[] | ||
| 93 | - */ | ||
| 94 | - private $client; | ||
| 95 | - | ||
| 96 | - | ||
| 97 | - /** | ||
| 98 | - * 连接目录 INBOX | ||
| 99 | - * @var string | ||
| 100 | - */ | ||
| 101 | - private $folder = 'INBOX'; | ||
| 102 | - | ||
| 103 | - /** | ||
| 104 | - * 目录编号 | ||
| 105 | - * @var int | ||
| 106 | - */ | ||
| 107 | - private $folderId = 0; | ||
| 108 | - | ||
| 109 | - /** | ||
| 110 | - * @var array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Http\Request|string|null | ||
| 111 | - */ | ||
| 112 | - private $request; | ||
| 113 | - | ||
| 114 | - /** | ||
| 115 | - * 所有绑定邮箱的id | ||
| 116 | - * @var array | ||
| 117 | - */ | ||
| 118 | - private $email_ids = []; | ||
| 119 | - | ||
| 120 | - | ||
| 121 | - private function __construct(){ | ||
| 122 | - $this->request = request(); | ||
| 123 | - } | ||
| 124 | - | ||
| 125 | - | ||
| 126 | - /** | ||
| 127 | - * @return Mail | ||
| 128 | - */ | ||
| 129 | - public static function getInstance(int $user_id, string $email=''): Mail | ||
| 130 | - { | ||
| 131 | - $key = md5($user_id.$email); | ||
| 132 | - | ||
| 133 | - if(empty(self::$Instance[$key])){ | ||
| 134 | - $mail = self::$Instance[$key] = new static(); | ||
| 135 | - | ||
| 136 | - $mail->user_id = $user_id; | ||
| 137 | - | ||
| 138 | - // 绑定的所有邮箱id | ||
| 139 | - $mail->email_ids = array_column(Email::_getById($mail->user_id),'id'); | ||
| 140 | - | ||
| 141 | - if($email){ | ||
| 142 | - $data = Email::_first($email); | ||
| 143 | - // 是否存在并绑定了 | ||
| 144 | - if($data && in_array($data['id'],$mail->email_ids)){ | ||
| 145 | - $mail->id = $data['id']; | ||
| 146 | - $mail->username = $email; | ||
| 147 | - $mail->nickname = $data['email_name']??''; | ||
| 148 | - $mail->password = $data['password']; | ||
| 149 | - // 当前选择的邮箱 | ||
| 150 | - $mail->host = [ | ||
| 151 | - 'imap' => $data['imap'], | ||
| 152 | - 'smtp' => $data['smtp'], | ||
| 153 | - ]; | ||
| 154 | - // 设置目录 | ||
| 155 | - $mail->selectFolder($mail->folder); | ||
| 156 | - }else{ | ||
| 157 | - throw new \Exception(Lang::__('email_not_bind',$email)); | ||
| 158 | - } | ||
| 159 | - } | ||
| 160 | - } | ||
| 161 | - | ||
| 162 | - // 设置邮件附件地址 | ||
| 163 | - self::$Instance[$key]->setFilePath(storage_path('/imap')); | ||
| 164 | - | ||
| 165 | - return self::$Instance[$key]; | ||
| 166 | - } | ||
| 167 | - | ||
| 168 | - /** | ||
| 169 | - * 设置目录 | ||
| 170 | - * @param $path | ||
| 171 | - * @time 2022/8/1 15:39 | ||
| 172 | - */ | ||
| 173 | - public function setFilePath($path){ | ||
| 174 | - $this->filePath = $path; | ||
| 175 | -// if(!is_dir($this->filePath)){ | ||
| 176 | -// @mkdir($this->filePath,0775,true); | ||
| 177 | -// } | ||
| 178 | - } | ||
| 179 | - | ||
| 180 | - /** | ||
| 181 | - * @return string | ||
| 182 | - */ | ||
| 183 | - public function getFilePath(): string | ||
| 184 | - { | ||
| 185 | - $path = $this->filePath.'/'.$this->username.'/'; | ||
| 186 | -// if(is_dir($path)){ | ||
| 187 | -// mkdir($path,0775,true); | ||
| 188 | -// } | ||
| 189 | - return $path; | ||
| 190 | - } | ||
| 191 | - | ||
| 192 | - | ||
| 193 | - /** | ||
| 194 | - * 选择使用的用户 | ||
| 195 | - * @param int $user_id | ||
| 196 | - * @param string $email | ||
| 197 | - * @return Mail | ||
| 198 | - * @author:dc | ||
| 199 | - * @time 2022/7/29 17:42 | ||
| 200 | - */ | ||
| 201 | - public static function use(int $user_id, string $email=''):Mail { | ||
| 202 | - $mail = self::getInstance($user_id,$email); | ||
| 203 | - | ||
| 204 | - | ||
| 205 | - return $mail; | ||
| 206 | - } | ||
| 207 | - | ||
| 208 | - /** | ||
| 209 | - * 选择文件夹 | ||
| 210 | - * @param string|int $folder | ||
| 211 | - * @time 2022/8/4 14:54 | ||
| 212 | - */ | ||
| 213 | - public function selectFolder($folder){ | ||
| 214 | - if($folder) { | ||
| 215 | - $lists = EmailFolder::_all($this->id, false); | ||
| 216 | - // 找到目标文件的id,pid | ||
| 217 | - foreach ($lists as $list) { | ||
| 218 | - if ($list[is_numeric($folder)?'id':'folder'] == $folder) { | ||
| 219 | - $id = $list['id'];// id | ||
| 220 | - $pid = $list['pid']; // 上级id | ||
| 221 | - $folder = $list['origin_folder']; | ||
| 222 | - } | ||
| 223 | - } | ||
| 224 | - | ||
| 225 | - // 不存在 | ||
| 226 | - if(!$lists){ | ||
| 227 | - $folder = 'INBOX'; | ||
| 228 | - $pid = 0; | ||
| 229 | - // 这个名字是每个邮箱默认的,不可更改 | ||
| 230 | - $id = EmailFolder::_insert($this->user_id,$this->id,'INBOX','INBOX'); | ||
| 231 | - if($folder != 'INBOX'){ | ||
| 232 | - $id = 0; | ||
| 233 | - } | ||
| 234 | - } | ||
| 235 | - | ||
| 236 | - // 是否存 | ||
| 237 | - if ($id ?? 0) { | ||
| 238 | - // 拿到上级,成为 dir/dir/dir | ||
| 239 | - if ($pid ?? 0) { | ||
| 240 | - EmailFolder::_firstTree($lists, $pid, $folder,'origin_folder'); | ||
| 241 | - } | ||
| 242 | - $this->folderId = $id; | ||
| 243 | - $this->folder = $folder; | ||
| 244 | - $this->folderInfo = EmailFolder::_first($id); | ||
| 245 | - } else { | ||
| 246 | - throw new \Exception(Lang::__('email_folder_not', $folder)); | ||
| 247 | - } | ||
| 248 | - } | ||
| 249 | - } | ||
| 250 | - | ||
| 251 | - /** | ||
| 252 | - * @return \App\Http\Mail\lib\client\Imap | ||
| 253 | - * @throws \Exception | ||
| 254 | - * @time 2022/8/5 17:22 | ||
| 255 | - */ | ||
| 256 | - public function client($email=''){ | ||
| 257 | - | ||
| 258 | - $email = $email ? : $this->username; | ||
| 259 | - | ||
| 260 | - if(empty($this->client[$email])){ | ||
| 261 | - if($email == $this->username){ | ||
| 262 | - $this->login(); | ||
| 263 | - }else{ | ||
| 264 | - $data = Email::_first($email); | ||
| 265 | - if($data){ | ||
| 266 | - $this->loginImap($data['imap'],$data['email'],$data['password']); | ||
| 267 | - } | ||
| 268 | - } | ||
| 269 | - } | ||
| 270 | - return $this->client[$email]; | ||
| 271 | - } | ||
| 272 | - | ||
| 273 | - /** | ||
| 274 | - * 登录邮箱 | ||
| 275 | - * @param string $password | ||
| 276 | - * @param string $imap | ||
| 277 | - * @param string $smtp | ||
| 278 | - * @return bool | ||
| 279 | - * @throws \Exception | ||
| 280 | - * @time 2022/8/5 17:21 | ||
| 281 | - */ | ||
| 282 | - public function login(string $password='',string $imap='',string $smtp=''):bool{ | ||
| 283 | - // host | ||
| 284 | - if($imap && $this->username){ | ||
| 285 | - $this->hostAdd(explode('@',$this->username)[1],$imap,$smtp); | ||
| 286 | - } | ||
| 287 | - | ||
| 288 | - if(!$password && !$this->password){ | ||
| 289 | - throw new \Exception(Lang::__('password_required')); | ||
| 290 | - } | ||
| 291 | - | ||
| 292 | - if($password){ | ||
| 293 | - // 如果密码不一致,更新 | ||
| 294 | - if($password != $this->password){ | ||
| 295 | - Email::_changePwd($this->username, $password); | ||
| 296 | - } | ||
| 297 | - | ||
| 298 | - $this->password = $password; | ||
| 299 | - } | ||
| 300 | - | ||
| 301 | - if(!$this->hostMy()){ | ||
| 302 | - throw new \Exception(Lang::__('login_host')); | ||
| 303 | - } | ||
| 304 | - | ||
| 305 | - // imap imap.qq.com | ||
| 306 | - $this->loginImap($this->hostMy('imap'),$this->username,$this->password); | ||
| 307 | - // 密码没验证成功 | ||
| 308 | -// if($this->id){ | ||
| 309 | -// Email::_update(['id'=>$this->id],['pwd_error'=>1]); | ||
| 310 | -// } | ||
| 311 | - return true; | ||
| 312 | - | ||
| 313 | - } | ||
| 314 | - | ||
| 315 | - | ||
| 316 | - /** | ||
| 317 | - * 公用链接 | ||
| 318 | - * @param $host | ||
| 319 | - * @param $email | ||
| 320 | - * @param $password | ||
| 321 | - * @throws \Exception | ||
| 322 | - * @author:dc | ||
| 323 | - * @time 2022/12/7 9:35 | ||
| 324 | - */ | ||
| 325 | - public function loginImap($host,$email,$password):void { | ||
| 326 | - | ||
| 327 | - $this->client[$email] = new \App\Http\Mail\lib\client\Imap(); | ||
| 328 | - // 是否初始成功 | ||
| 329 | - $this->client[$email]->login($this->ssl.$host.':'.$this->port,$email,$password); | ||
| 330 | - | ||
| 331 | - } | ||
| 332 | - | ||
| 333 | - /** | ||
| 334 | - * 自动路由 | ||
| 335 | - * @return \Illuminate\Http\JsonResponse|string|\Symfony\Component\HttpFoundation\BinaryFileResponse | ||
| 336 | - * @throws \Illuminate\Contracts\Container\BindingResolutionException | ||
| 337 | - * @throws \Psr\SimpleCache\InvalidArgumentException | ||
| 338 | - * @throws \Throwable | ||
| 339 | - * @author:dc | ||
| 340 | - * @time 2022/8/1 11:07 | ||
| 341 | - */ | ||
| 342 | - public function autoRoute(){ | ||
| 343 | - $task = $this->request->get('_task'); | ||
| 344 | - $action = $this->request->get('_action'); | ||
| 345 | - $result = []; | ||
| 346 | - | ||
| 347 | - switch ($task){ | ||
| 348 | - case 'sync': { | ||
| 349 | - // 同步操作 | ||
| 350 | - switch ($action){ | ||
| 351 | - case 'list': { | ||
| 352 | - // 同步邮件列表 | ||
| 353 | - $msgno = $this->request->post('msgno',[]); | ||
| 354 | - if(is_string($msgno)){ | ||
| 355 | - $msgno = explode(',',$msgno); | ||
| 356 | - } | ||
| 357 | - $result = $this->syncEmailList($msgno); | ||
| 358 | - break; | ||
| 359 | - } | ||
| 360 | - // 同步文件夹 | ||
| 361 | - case 'folder': { | ||
| 362 | - $folder = $this->request->post('folder'); | ||
| 363 | - $result = $this->syncFolder($folder); | ||
| 364 | - break; | ||
| 365 | - } | ||
| 366 | - // 更新最新的 | ||
| 367 | - case 'new':{ | ||
| 368 | - $result = $this->syncEmailList(); | ||
| 369 | - break; | ||
| 370 | - } | ||
| 371 | - } | ||
| 372 | - break; | ||
| 373 | - } | ||
| 374 | - // 设置标签,已读,未读,删除等 | ||
| 375 | - case 'flags':{ | ||
| 376 | - $ids = $this->request->post('ids'); | ||
| 377 | - $ids = is_string($ids) ? explode(',',$ids) : $ids; | ||
| 378 | - $ids = is_array($ids) ? $ids : [$ids]; | ||
| 379 | - // 获取到邮件的uid | ||
| 380 | - $uids = EmailList::_getUidsByIds($ids,$this->id); | ||
| 381 | - $filed = ''; | ||
| 382 | - $value = 0; | ||
| 383 | - $mod = ''; | ||
| 384 | - $flags = ''; | ||
| 385 | - // 标记操作 | ||
| 386 | - switch ($action){ | ||
| 387 | - // 标记已读 | ||
| 388 | - case 'setSeen':{ | ||
| 389 | - $filed = 'seen'; | ||
| 390 | - $value = 1; | ||
| 391 | - $flags = 'seen'; | ||
| 392 | - $mod = '+'; | ||
| 393 | - break; | ||
| 394 | - } | ||
| 395 | - // 标记未读 | ||
| 396 | - case 'delSeen':{ | ||
| 397 | - $filed = 'seen'; | ||
| 398 | - $value = 0; | ||
| 399 | - $flags = 'seen'; | ||
| 400 | - $mod = '-'; | ||
| 401 | - break; | ||
| 402 | - } | ||
| 403 | - case 'setFlagged':{ | ||
| 404 | - $filed = 'flagged'; | ||
| 405 | - $value = 1; | ||
| 406 | - $flags = 'flagged'; | ||
| 407 | - $mod = '+'; | ||
| 408 | - break; | ||
| 409 | - } | ||
| 410 | - case 'delFlagged':{ | ||
| 411 | - $filed = 'flagged'; | ||
| 412 | - $value = 0; | ||
| 413 | - $flags = 'flagged'; | ||
| 414 | - $mod = '-'; | ||
| 415 | - break; | ||
| 416 | - } | ||
| 417 | - } | ||
| 418 | - | ||
| 419 | - if($this->setflagged($uids,$flags,$mod)){ | ||
| 420 | - // 更新标签 | ||
| 421 | - if(EmailList::_setFlags(array_keys($uids),$this->id,$filed,$value)){ | ||
| 422 | - $result = array_keys($uids); | ||
| 423 | - } | ||
| 424 | - } | ||
| 425 | - break; | ||
| 426 | - } | ||
| 427 | - case 'info': { | ||
| 428 | - $id = (int) $this->request->get('id'); | ||
| 429 | - // 读取邮件 | ||
| 430 | - $result = $this->emailInfo($id); | ||
| 431 | - | ||
| 432 | - if(!$result){ | ||
| 433 | - $result = new static(); | ||
| 434 | - }else{ | ||
| 435 | - // 是否是数组 | ||
| 436 | - if(!is_array($result)) $result = $result->toArray(); | ||
| 437 | - | ||
| 438 | - // 缓存一下 | ||
| 439 | - \Cache::set('app_email_info_'.$this->user_id.":".$result['id'],$result,3000); | ||
| 440 | - | ||
| 441 | - // 渲染视图 | ||
| 442 | - $result['body'] = view('admin/email/info',[ | ||
| 443 | - 'data' => $result | ||
| 444 | - ])->render(); | ||
| 445 | - // 有邮件编码bug,必须要转 | ||
| 446 | - $result['body'] = base64_encode($result['body']); | ||
| 447 | - | ||
| 448 | - } | ||
| 449 | - ///////////////////////// 这里是测试 | ||
| 450 | -// print_r($result['body']['text_html']);exit(); | ||
| 451 | -// foreach ($result['body']['text_html'] as $item){ | ||
| 452 | -// if(!empty($item['type']) && ($item['type'] == 'text/html' || $item['type'] == 'text/plain')){ | ||
| 453 | -// header("content-type:text/html;charset=".($item['charset']??'utf-8')); | ||
| 454 | -// echo $item['body']; | ||
| 455 | -// } | ||
| 456 | -// } | ||
| 457 | -// exit(); | ||
| 458 | - /////////////////////// | ||
| 459 | - | ||
| 460 | - break; | ||
| 461 | - } | ||
| 462 | - case 'list': { | ||
| 463 | - if($this->id){ | ||
| 464 | - $eids = [$this->id]; | ||
| 465 | - }else{ | ||
| 466 | - $eids = array_column(Email::_get($this->user_id),'id'); | ||
| 467 | - } | ||
| 468 | - | ||
| 469 | - $total = 0; | ||
| 470 | - | ||
| 471 | - if($this->folder=='INBOX'){ | ||
| 472 | - $fids = EmailFolder::_user_folders($this->email_ids); | ||
| 473 | -// $total = array_sum(array_column($fids,'exsts')); | ||
| 474 | - $fids = array_column($fids,'id'); | ||
| 475 | - | ||
| 476 | - $unseen = array_sum(array_column($fids,'unseen')); // 未读数量 | ||
| 477 | -// $unseen = EmailList::_getUnseenNum($eids); // 未读数量 | ||
| 478 | - }else{ | ||
| 479 | - $fids = [$this->folderId]; | ||
| 480 | - | ||
| 481 | - $total = $this->folderInfo['exsts']??0; | ||
| 482 | - } | ||
| 483 | - | ||
| 484 | - // 搜索 | ||
| 485 | - $search = []; | ||
| 486 | - $search['search'] = $this->request->get('search'); | ||
| 487 | - $search['seen'] = $this->request->get('seen'); | ||
| 488 | - | ||
| 489 | - | ||
| 490 | - $result = $this->emailLists($eids,$fids,$total,$search)->toArray(); | ||
| 491 | - | ||
| 492 | - | ||
| 493 | -// if($this->folder == 'INBOX'){ | ||
| 494 | - $this->allemail = []; | ||
| 495 | - foreach ($result['data'] as $k=>$datum){ | ||
| 496 | - if(!$datum['uid']){ | ||
| 497 | - // 邮件 | ||
| 498 | - if (empty($this->allemail[$datum['email_id']])){ | ||
| 499 | - $this->allemail[$datum['email_id']] = Email::_firstById($datum['email_id']); | ||
| 500 | - } | ||
| 501 | - if(empty($this->allemail[$datum['email_id']])){ | ||
| 502 | - continue; | ||
| 503 | - } | ||
| 504 | - // 同步 | ||
| 505 | - $id = $this->syncEmailList( | ||
| 506 | - [$datum['msgno']], | ||
| 507 | - $datum['email_id'], | ||
| 508 | - $this->allemail[$datum['email_id']]['email'], | ||
| 509 | - $datum['folder_id'], | ||
| 510 | - $this->folder | ||
| 511 | - ); | ||
| 512 | - if($id['ids']){ | ||
| 513 | - // 重新获取 | ||
| 514 | - $result['data'][$k] = EmailList::_first($id['ids'][0]); | ||
| 515 | - } | ||
| 516 | - } | ||
| 517 | - // 干掉 -snv 和 (Failure) | ||
| 518 | - if(preg_match("/(\-\ssnv)|(\(Failure\))|(\(Delay\))$/",$datum['subject'])){ | ||
| 519 | - unset($result['data'][$k]); | ||
| 520 | - continue; | ||
| 521 | - } | ||
| 522 | - } | ||
| 523 | - $result['data'] = array_values($result['data']); | ||
| 524 | -// } | ||
| 525 | - | ||
| 526 | - // 未读邮件数量 | ||
| 527 | - $result['unseen'] = $unseen??0; | ||
| 528 | - break; | ||
| 529 | - } | ||
| 530 | - case 'mail': { | ||
| 531 | - $result = $this->emails(); | ||
| 532 | - break; | ||
| 533 | - } | ||
| 534 | - case 'add': { | ||
| 535 | - $result = $this->emailAdd(); | ||
| 536 | - break; | ||
| 537 | - } | ||
| 538 | - case 'folder': { | ||
| 539 | - | ||
| 540 | - $email = $this->request->post('email'); | ||
| 541 | - if($email){ | ||
| 542 | - $ids = Email::_getIds($email); | ||
| 543 | - }else{ | ||
| 544 | - $ids = $this->id; | ||
| 545 | - } | ||
| 546 | - | ||
| 547 | - $result = $this->folders($ids); | ||
| 548 | - break; | ||
| 549 | - } | ||
| 550 | - case 'contact': { | ||
| 551 | - switch ($action){ | ||
| 552 | - case 'info': { | ||
| 553 | - $id = $this->request->get('id'); | ||
| 554 | - $result = $this->contactInfo($id); | ||
| 555 | - break; | ||
| 556 | - } | ||
| 557 | - case 'add': { | ||
| 558 | - $result = $this->contactAdd(); | ||
| 559 | - break; | ||
| 560 | - } | ||
| 561 | - case 'del': { | ||
| 562 | - $result = $this->contactDel(); | ||
| 563 | - break; | ||
| 564 | - } | ||
| 565 | - case 'group':{ | ||
| 566 | - $is_contact = $this->request->get('is_contact'); | ||
| 567 | - $result = $this->contactGroup($is_contact); | ||
| 568 | - break; | ||
| 569 | - } | ||
| 570 | - case 'group_save': { | ||
| 571 | - $name = $this->request->post('group_name'); | ||
| 572 | - $id = $this->request->post('id',0); | ||
| 573 | - $result = $this->contactGroupSave($name,$id); | ||
| 574 | - break; | ||
| 575 | - } | ||
| 576 | - case 'group_del': { | ||
| 577 | - $group_id = (int) $this->request->get('group_id'); | ||
| 578 | - $result = $this->contactGroupDel($group_id); | ||
| 579 | - break; | ||
| 580 | - } | ||
| 581 | - default: { | ||
| 582 | - $is_group = $this->request->get('is_group',false); | ||
| 583 | - $result = $this->contact($is_group); | ||
| 584 | - break; | ||
| 585 | - } | ||
| 586 | - } | ||
| 587 | - break; | ||
| 588 | - } | ||
| 589 | - // 下载附件 | ||
| 590 | - case 'download':{ | ||
| 591 | - $name = $this->request->get('name'); | ||
| 592 | - $originname = $this->request->get('originname'); | ||
| 593 | - return $this->download($name,$originname); | ||
| 594 | - } | ||
| 595 | - case 'contact_view':{ | ||
| 596 | - return $this->contact_view(); | ||
| 597 | - } | ||
| 598 | - // 发送邮件 | ||
| 599 | - case 'send_email':{ | ||
| 600 | - $data = $this->request->post('to'); | ||
| 601 | - | ||
| 602 | - // 文件收件人 | ||
| 603 | - $to = $this->request->file()['to']['to_email_file']??[]; | ||
| 604 | - if($to && $to instanceof UploadedFile){ | ||
| 605 | - $data['to'] = ($data['to']??'')."\n".$to->getContent(); | ||
| 606 | - } | ||
| 607 | - | ||
| 608 | - | ||
| 609 | - // 附件 | ||
| 610 | - $data['file'] = $this->request->file()['to']['file']??[]; | ||
| 611 | - if($data['file']){ | ||
| 612 | - | ||
| 613 | - $upconfig = [ | ||
| 614 | - // 上传文件的大小范围 | ||
| 615 | - 'size' => [ | ||
| 616 | - 'max' => 1024 * 1024 * 100, // 100M大了服务器不知道会发生什么 | ||
| 617 | - 'min' => 1, // 0k | ||
| 618 | - ], | ||
| 619 | - // 扩展名 | ||
| 620 | - 'ext' => [ | ||
| 621 | - 'xlsx', 'xltx', 'potx', 'ppsx', 'pptx', 'sldx', 'docx', 'dotx', 'xlam', 'xlsb', | ||
| 622 | - 'apk', 'doc', 'pdf', 'xls', 'ppt', /*'jar', 'js', 'json', 'rpm',*/ | ||
| 623 | - 'swf', 'tar', 'zip', 'gif', 'png', 'flv', 'avi', 'ai', 'gz', | ||
| 624 | - 'jpg', 'mov', 'mp3', 'mp4', 'txt', 'webm', 'webp', | ||
| 625 | - ], | ||
| 626 | - // mime类型 | ||
| 627 | - 'mime' => [ | ||
| 628 | - /*'xlsx' => */'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', | ||
| 629 | - /*'xltx' => */'application/vnd.openxmlformats-officedocument.spreadsheetml.template', | ||
| 630 | - /*'potx' => */'application/vnd.openxmlformats-officedocument.presentationml.template', | ||
| 631 | - /*'ppsx' => */'application/vnd.openxmlformats-officedocument.presentationml.slideshow', | ||
| 632 | - /*'pptx' => */'application/vnd.openxmlformats-officedocument.presentationml.presentation', | ||
| 633 | - /*'sldx' => */'application/vnd.openxmlformats-officedocument.presentationml.slide', | ||
| 634 | - /*'docx' => */'application/vnd.openxmlformats-officedocument.wordprocessingml.document', | ||
| 635 | - /*'dotx' => */'application/vnd.openxmlformats-officedocument.wordprocessingml.template', | ||
| 636 | - /*'xlam' => */'application/vnd.ms-excel.addin.macroEnabled.12', | ||
| 637 | - /*'xlsb' => */'application/vnd.ms-excel.sheet.binary.macroEnabled.12', | ||
| 638 | - /*'apk' => */'application/vnd.android.package-archive', | ||
| 639 | - /*'doc' => */'application/msword', | ||
| 640 | - /*'pdf' => */'application/pdf', | ||
| 641 | - /*'xls' => */'application/vnd.ms-excel', | ||
| 642 | - /*'ppt' => */'application/vnd.ms-powerpoint', | ||
| 643 | -// /*'jar' => */'application/java-archive', | ||
| 644 | -// /*'js' => */'application/javascript', | ||
| 645 | -// /*'json' => */'application/json', | ||
| 646 | -// /*'rpm' => */'application/x-rpm', | ||
| 647 | - /*'swf' => */'application/x-shockwave-flash', | ||
| 648 | - /*'tar' => */'application/x-tar', | ||
| 649 | - /*'zip' => */'application/zip', | ||
| 650 | - /*'gif' => */'image/gif', | ||
| 651 | - /*'png' => */'image/png', | ||
| 652 | - /*'flv' => */'video/x-flv', | ||
| 653 | - /*'avi' => */'video/x-msvideo', | ||
| 654 | - /*'ai' => */'application/postscript', | ||
| 655 | - /*'gz' => */'application/x-gzip', | ||
| 656 | - /*'jpg' => */'image/jpeg', | ||
| 657 | - /*'mov' => */'video/quicktime', | ||
| 658 | - /*'mp3' => */'audio/mpeg', | ||
| 659 | - /*'mp4' => */'video/mp4', | ||
| 660 | - /*'txt' => */'text/plain', | ||
| 661 | - /*'webm' => */'video/webm', | ||
| 662 | - /*'webp' => */'image/webp', | ||
| 663 | - | ||
| 664 | - ], | ||
| 665 | - // 磁盘 | ||
| 666 | - 'disk' => 'local2', | ||
| 667 | - // 目录 | ||
| 668 | - 'path' => '/email/'.$this->username.'/', | ||
| 669 | - ]; | ||
| 670 | - // 设置配置 | ||
| 671 | - Config::set('upload.email_upload',$upconfig); | ||
| 672 | - | ||
| 673 | - foreach ($data['file'] as $k=>$file){ | ||
| 674 | - if($file instanceof UploadedFile){ | ||
| 675 | - $data['file'][$k] = [ | ||
| 676 | - 'path' => Upload::put($file,'email_upload'), | ||
| 677 | - 'origin_name' => $file->getClientOriginalName(), | ||
| 678 | - ]; | ||
| 679 | - } | ||
| 680 | - | ||
| 681 | - } | ||
| 682 | - | ||
| 683 | - // 保存的目录 | ||
| 684 | - $path = config('filesystems.disks.local2.root'); | ||
| 685 | - foreach ($data['file'] as &$f){ | ||
| 686 | - $f['path'] = realpath($path.'/'.$f['path']); | ||
| 687 | - } | ||
| 688 | - | ||
| 689 | - } | ||
| 690 | - | ||
| 691 | - | ||
| 692 | - if($this->send($data)){ | ||
| 693 | - return $this->echoJson(1,'邮件发送成功'); | ||
| 694 | - } | ||
| 695 | - } | ||
| 696 | - } | ||
| 697 | - | ||
| 698 | - return $this->echoJson($result); | ||
| 699 | - | ||
| 700 | - } | ||
| 701 | - | ||
| 702 | - /** | ||
| 703 | - * 自动路由时 | ||
| 704 | - * @return array | ||
| 705 | - * @time 2022/8/1 16:28 | ||
| 706 | - */ | ||
| 707 | - public function getRoute(){ | ||
| 708 | - return [ | ||
| 709 | - [ | ||
| 710 | - 'name' => '同步文件夹', | ||
| 711 | - 'route' => '?_task=sync&_action=folder' | ||
| 712 | - ], | ||
| 713 | - [ | ||
| 714 | - 'name' => '同步邮件列表', | ||
| 715 | - 'route' => '?_task=sync&_action=list' | ||
| 716 | - ], | ||
| 717 | - [ | ||
| 718 | - 'name' => '邮件详情', | ||
| 719 | - 'route' => '?_task=info&id=1972' | ||
| 720 | - ], | ||
| 721 | - [ | ||
| 722 | - 'name' => '邮件列表', | ||
| 723 | - 'route' => '?_task=list' | ||
| 724 | - ], | ||
| 725 | - [ | ||
| 726 | - 'name' => '邮箱', | ||
| 727 | - 'route' => '?_task=mail' | ||
| 728 | - ], | ||
| 729 | - [ | ||
| 730 | - 'name' => '添加邮箱', | ||
| 731 | - 'route' => '?_task=add' | ||
| 732 | - ], | ||
| 733 | - [ | ||
| 734 | - 'name' => '邮箱联系人', | ||
| 735 | - 'route' => '?_task=contact' | ||
| 736 | - ], | ||
| 737 | - ]; | ||
| 738 | - } | ||
| 739 | - | ||
| 740 | - | ||
| 741 | - /** | ||
| 742 | - * 同步文件夹 | ||
| 743 | - * 缺点,远程修改文件名称后,同步回来,就会新建文件夹,会导致邮件混乱, | ||
| 744 | - * 处理方法,删除本地的,重新同步该文件夹下面的所有邮件,虽然不友好,也只能这样做。 | ||
| 745 | - * @time 2022/8/1 16:09 | ||
| 746 | - */ | ||
| 747 | - public function syncFolder($folder=''){ | ||
| 748 | - | ||
| 749 | - // 同步单个文件夹 | ||
| 750 | - if($folder){ | ||
| 751 | - $status = $this->client()->selectFolder($this->folder); | ||
| 752 | - // 更新数量 | ||
| 753 | - EmailFolder::_updateNum($this->folderId,$status['EXISTS']??null, $status['UNSEEN']??null); | ||
| 754 | - | ||
| 755 | - return EmailFolder::_firstAndEmailId($this->folderId,$this->id); | ||
| 756 | - } | ||
| 757 | - | ||
| 758 | - | ||
| 759 | - // 读取所有文件夹,未解密 | ||
| 760 | - $folders = $this->client()->getFolder(); | ||
| 761 | - | ||
| 762 | - DB::beginTransaction(); | ||
| 763 | - foreach ($folders as $folder){ | ||
| 764 | - | ||
| 765 | - // 处理子父文件夹 | ||
| 766 | - $folder['id'] = explode('/',$folder['folder']); | ||
| 767 | - $folder['name'] = explode('/',$folder['parseFolder']); | ||
| 768 | - $pid = 0; | ||
| 769 | - foreach ($folder['id'] as $k=>$item){ | ||
| 770 | - // 插入到数据库 | ||
| 771 | - $pid = EmailFolder::_insert( | ||
| 772 | - $this->user_id, | ||
| 773 | - $this->id, | ||
| 774 | - $folder['name'][$k], | ||
| 775 | - $item, | ||
| 776 | - $pid | ||
| 777 | - ); | ||
| 778 | - } | ||
| 779 | - } | ||
| 780 | - DB::commit(); | ||
| 781 | - | ||
| 782 | - return EmailFolder::_all($this->id); | ||
| 783 | - } | ||
| 784 | - | ||
| 785 | - /** | ||
| 786 | - * 同步当前用户的邮件数量 | ||
| 787 | - * @return array | ||
| 788 | - * [surplus_num] 剩余待拉取数量 | ||
| 789 | - * [ids] 本次拉取后保存后的id | ||
| 790 | - * @time 2022/8/2 14:06 | ||
| 791 | - */ | ||
| 792 | - public function syncEmailList($msgno=[],$use_email_id=0,$use_email='',$use_folder_id=0,$use_folder=''){ | ||
| 793 | - | ||
| 794 | - $use_email_id = $use_email_id ? : $this->id; | ||
| 795 | - $use_email = $use_email ? : $this->username; | ||
| 796 | - $use_folder_id = $use_folder_id ? : $this->folderId; | ||
| 797 | - $use_folder = $use_folder ? : $this->folder; | ||
| 798 | - | ||
| 799 | - // 零时增加时长 | ||
| 800 | -// set_time_limit(180); | ||
| 801 | - // 选择文件夹 | ||
| 802 | - $status = $this->client($use_email)->selectFolder($use_folder); | ||
| 803 | - if (!isset($status['EXISTS']) || !$status['EXISTS']){ | ||
| 804 | - return [ | ||
| 805 | - 'ids' => [], | ||
| 806 | - 'folder' => $status, | ||
| 807 | - 'end' => true | ||
| 808 | - ]; | ||
| 809 | - } | ||
| 810 | - $end = false; | ||
| 811 | - | ||
| 812 | - if($msgno){ | ||
| 813 | - goto SYNCEMAILLIST; | ||
| 814 | - } | ||
| 815 | - | ||
| 816 | - // 最后拉取的时间,如果是第一次 | ||
| 817 | - $lastMsgno = EmailList::_lastMsgno($use_email_id, $use_folder_id); | ||
| 818 | - | ||
| 819 | - $nu = 20; | ||
| 820 | - if(!$msgno){ | ||
| 821 | - if(!$lastMsgno){ | ||
| 822 | - $msgno = range(1,$nu); | ||
| 823 | - }else{ | ||
| 824 | - $msgno = range($lastMsgno,$lastMsgno+$nu); | ||
| 825 | - | ||
| 826 | - if($lastMsgno > $status['EXISTS']){ | ||
| 827 | - $msgno = range($status['EXISTS'] > $nu ? $status['EXISTS'] - $nu : 1,$status['EXISTS']); | ||
| 828 | - } | ||
| 829 | - // 一样就不拉新的 | ||
| 830 | - if($lastMsgno == $status['EXISTS']){ | ||
| 831 | - return [ | ||
| 832 | - 'ids' => [], | ||
| 833 | - 'folder' => $status, | ||
| 834 | - 'end' => true, | ||
| 835 | - ]; | ||
| 836 | - } | ||
| 837 | - } | ||
| 838 | - } | ||
| 839 | - | ||
| 840 | - // 更新数量 | ||
| 841 | - EmailFolder::_updateNum($use_folder_id,$status['EXISTS'], $status['UNSEEN']??null); | ||
| 842 | - | ||
| 843 | - // 说明是第一次 | ||
| 844 | - if(!$lastMsgno){ | ||
| 845 | - $pgnu = 1000; | ||
| 846 | - DB::beginTransaction(); | ||
| 847 | - for ($n=0;$n<$status['EXISTS']/$pgnu;$n++){ | ||
| 848 | - | ||
| 849 | - $i = ($pgnu*$n)+1; | ||
| 850 | - $max = $i+$pgnu; | ||
| 851 | - $max = $max > $status['EXISTS'] ? $status['EXISTS']+1 : $max; | ||
| 852 | - $sql = []; | ||
| 853 | - while ($i<$max){ | ||
| 854 | - $sql[] = "(".$use_email_id.",{$i},".$use_folder_id.",1)"; | ||
| 855 | - $i++; | ||
| 856 | - } | ||
| 857 | -// bug 使用事务,无法插入数据 | ||
| 858 | - $ret = DB::insert("insert INTO email_lists (`email_id`,`msgno`,`folder_id`,`seen`) VALUES ".implode(',',$sql)); | ||
| 859 | - if(!$ret){ | ||
| 860 | - DB::rollBack(); | ||
| 861 | - abort(500,'同步失败'); | ||
| 862 | - } | ||
| 863 | - } | ||
| 864 | - DB::commit(); | ||
| 865 | - // 最新的数量 | ||
| 866 | - $msgno = range($status['EXISTS'] > $nu ? $status['EXISTS'] - $nu : 1,$status['EXISTS']); | ||
| 867 | - // 不同步 | ||
| 868 | - return [ | ||
| 869 | - 'ids' => [1], | ||
| 870 | - 'folder' => $status, | ||
| 871 | - 'end' => $end, | ||
| 872 | - ]; | ||
| 873 | - } | ||
| 874 | - SYNCEMAILLIST: | ||
| 875 | - // 是否有id | ||
| 876 | - $dataids = EmailList::_getIdsByMsgno($use_email_id,$use_folder_id,$msgno); | ||
| 877 | - | ||
| 878 | - $this->client($use_email)->debug(false,storage_path('logs')); | ||
| 879 | - // 循环 | ||
| 880 | - $results = $this->client($use_email)->fetchHeader($msgno); | ||
| 881 | - | ||
| 882 | - if($results){ | ||
| 883 | - DB::beginTransaction(); | ||
| 884 | - // 批量插入 | ||
| 885 | - foreach ($results as $key=>$result){ | ||
| 886 | - if($key == $status['EXISTS']){ | ||
| 887 | - $end = true; | ||
| 888 | - } | ||
| 889 | -// $header = $this->client($use_email)->getHeader($result['RFC822.HEADER']??''); | ||
| 890 | - $header = &$result['HEADER.FIELDS']; | ||
| 891 | - | ||
| 892 | - foreach ($result['FLAGS'] as $k=>$FLAG){ | ||
| 893 | - $result['FLAGS'][$k] = strtolower(str_replace('\\','',$FLAG)); | ||
| 894 | - } | ||
| 895 | - try { | ||
| 896 | - | ||
| 897 | - $file_header = &$result['BODYSTRUCTURE']; | ||
| 898 | - | ||
| 899 | - // 没有收件人 | ||
| 900 | - if(!empty($header['To'])){ | ||
| 901 | - $header['To'] = MailFun::toOrFrom($header['To']); | ||
| 902 | - }else{ | ||
| 903 | - $header['To'] = []; | ||
| 904 | - } | ||
| 905 | - | ||
| 906 | - | ||
| 907 | - $header['From'] = MailFun::toOrFrom($header['From']); | ||
| 908 | - | ||
| 909 | - $data = [ | ||
| 910 | - 'id' => $dataids[$key]??0, | ||
| 911 | - 'msgno' => $key, | ||
| 912 | - 'uid' => $result['UID'], | ||
| 913 | - 'subject' => $header['Subject'], | ||
| 914 | -// 'cc' => $header['Cc']??'', | ||
| 915 | - 'from' => $header['From'][0]['email']??'', | ||
| 916 | - 'from_name' => $header['From'][0]['name']??'', | ||
| 917 | - 'to' => $header['To']?implode(',',array_column($header['To'],'email')):'', | ||
| 918 | - 'to_name' => json_encode($header['To']), | ||
| 919 | - 'date' => isset($header['Date'])&&$header['Date'] ? strtotime(is_array($header['Date']) ? $header['Date'][0] : $header['Date']) : strtotime($result['INTERNALDATE']), | ||
| 920 | - 'message_id' => $header['Message-ID']??'', | ||
| 921 | - 'udate' => strtotime($result['INTERNALDATE']), | ||
| 922 | -// 'size' => $result['RFC822.SIZE'], | ||
| 923 | - 'recent' => in_array('recent',$result['FLAGS']), | ||
| 924 | - 'seen' => in_array('seen',$result['FLAGS']), | ||
| 925 | - 'draft' => in_array('draft',$result['FLAGS']), | ||
| 926 | - 'flagged' => in_array('flagged',$result['FLAGS']), | ||
| 927 | - 'answered' => in_array('answered',$result['FLAGS']), | ||
| 928 | - 'folder_id' => $use_folder_id, | ||
| 929 | - 'email_id' => $use_email_id, | ||
| 930 | - 'uuid' => $use_email_id.$use_folder_id.$result['UID'], | ||
| 931 | - 'is_file' => MailFun::isFile($file_header[$key]['BODYSTRUCTURE']??[]) //是否附件 | ||
| 932 | - ]; | ||
| 933 | - }catch (\Throwable $e){ | ||
| 934 | - Log::error('邮件解析失败:'.$e->getMessage().print_r($result,true)); | ||
| 935 | - unset($results[$key]); | ||
| 936 | - continue; | ||
| 937 | - } | ||
| 938 | - | ||
| 939 | - $results[$key] = $data; | ||
| 940 | - } | ||
| 941 | - $ids = EmailList::_insertAll(array_values($results)); | ||
| 942 | - // 提交 | ||
| 943 | - DB::commit(); | ||
| 944 | - }else{ | ||
| 945 | - $end = true; | ||
| 946 | - } | ||
| 947 | - | ||
| 948 | - | ||
| 949 | - | ||
| 950 | - | ||
| 951 | - return [ | ||
| 952 | - 'ids' => $ids??[], | ||
| 953 | - 'folder' => $status, | ||
| 954 | - 'end' => $end, | ||
| 955 | - ]; | ||
| 956 | - | ||
| 957 | - } | ||
| 958 | - | ||
| 959 | - | ||
| 960 | - /** | ||
| 961 | - * 同步body | ||
| 962 | - * @param int $uid | ||
| 963 | - * @param int $id | ||
| 964 | - * @return mixed | ||
| 965 | - * @time 2022/8/2 15:11 | ||
| 966 | - */ | ||
| 967 | - public function syncEMailBody(int $uid, $id = 0 ){ | ||
| 968 | - | ||
| 969 | -// $this->client()->debug(true,storage_path('logs')); | ||
| 970 | - | ||
| 971 | - $this->client()->selectFolder($this->folder); | ||
| 972 | - $body = $this->client()->fetch([$uid],'body',true); | ||
| 973 | - | ||
| 974 | - /******* start ********/ | ||
| 975 | - // 记录原始数据,方便分析 | ||
| 976 | - $path = storage_path('logs/email/'.$this->username.'/'); | ||
| 977 | - if(!is_dir($path)){ | ||
| 978 | - mkdir($path,0775,true); | ||
| 979 | - } | ||
| 980 | - file_put_contents($path.$id.'.'.time().'.log',print_r($body,true)); | ||
| 981 | - /******** end *******/ | ||
| 982 | - | ||
| 983 | - if($body && $id){ | ||
| 984 | - $body = array_values($body); | ||
| 985 | - | ||
| 986 | - $body = (new Body($body[0]['RFC822.TEXT'],$this->getFilePath()))->getItem(); | ||
| 987 | - | ||
| 988 | - | ||
| 989 | - EmailBody::_insert( | ||
| 990 | - $id, | ||
| 991 | - $body | ||
| 992 | - ); | ||
| 993 | - } | ||
| 994 | - | ||
| 995 | - return EmailBody::_first($id); | ||
| 996 | - } | ||
| 997 | - | ||
| 998 | - | ||
| 999 | - /** | ||
| 1000 | - * 设置标记 | ||
| 1001 | - * @param array $uids | ||
| 1002 | - * @param string $flags | ||
| 1003 | - * @param string $mod +|- | ||
| 1004 | - * @return bool | ||
| 1005 | - * @throws \Exception | ||
| 1006 | - * @author:dc | ||
| 1007 | - * @time 2022/10/27 14:22 | ||
| 1008 | - */ | ||
| 1009 | - public function setflagged(array $uids,string $flags,string $mod){ | ||
| 1010 | - // 选择目录 | ||
| 1011 | - $status = $this->client()->selectFolder($this->folder); | ||
| 1012 | - | ||
| 1013 | - return $this->client()->flags($uids,[$flags],$mod,true); | ||
| 1014 | - } | ||
| 1015 | - | ||
| 1016 | - /** | ||
| 1017 | - * 设置为已读 | ||
| 1018 | - * @param int|array $uids | ||
| 1019 | - * @return bool | ||
| 1020 | - * @throws \Exception | ||
| 1021 | - * @author:dc | ||
| 1022 | - * @time 2022/10/26 17:08 | ||
| 1023 | - */ | ||
| 1024 | - public function setSeen($uids):bool{ | ||
| 1025 | - // 选择目录 | ||
| 1026 | - $status = $this->client()->selectFolder($this->folder); | ||
| 1027 | - | ||
| 1028 | - return $this->client()->flags($uids,[\App\Http\Mail\lib\client\Imap::FLAGS_SEEN],'+',true); | ||
| 1029 | - } | ||
| 1030 | - | ||
| 1031 | - /** | ||
| 1032 | - * 设置为未读 | ||
| 1033 | - * @param $uids | ||
| 1034 | - * @return bool | ||
| 1035 | - * @throws \Exception | ||
| 1036 | - * @author:dc | ||
| 1037 | - * @time 2022/10/26 17:11 | ||
| 1038 | - */ | ||
| 1039 | - public function delSeen($uids):bool{ | ||
| 1040 | - // 选择目录 | ||
| 1041 | - $status = $this->client()->selectFolder($this->folder); | ||
| 1042 | - | ||
| 1043 | - return $this->client()->flags($uids,[\App\Http\Mail\lib\client\Imap::FLAGS_SEEN],'-',true); | ||
| 1044 | - } | ||
| 1045 | - | ||
| 1046 | - | ||
| 1047 | - | ||
| 1048 | - /** | ||
| 1049 | - * 邮件列表 | ||
| 1050 | - * @param array $mail_id | ||
| 1051 | - * @param array $folder | ||
| 1052 | - * @param int $total | ||
| 1053 | - * @return mixed|object | ||
| 1054 | - * @throws \Illuminate\Contracts\Container\BindingResolutionException | ||
| 1055 | - * @time 2022/8/16 17:34 | ||
| 1056 | - */ | ||
| 1057 | - public function emailLists($mail_id = [], $folder = [], $total = 0, $search = []){ | ||
| 1058 | - | ||
| 1059 | - return EmailList::_paginate(function ($query) use ($mail_id, $folder,$search){ | ||
| 1060 | - $query->whereIn('email_id',$mail_id)->whereIn('folder_id',$folder); | ||
| 1061 | - // 搜索 | ||
| 1062 | - if(!empty($search['search'])){ | ||
| 1063 | - $search = htmlspecialchars($search); | ||
| 1064 | - $query->where('subject','like',"%{$search}%"); | ||
| 1065 | - } | ||
| 1066 | - // 已读 | ||
| 1067 | - if(!empty($search['seen']) && $search['seen']){ | ||
| 1068 | - $query->where('seen',0); | ||
| 1069 | - } | ||
| 1070 | - // 过滤 | ||
| 1071 | - $query->where([ | ||
| 1072 | - ['subject','not like','%- snv'], | ||
| 1073 | - ['subject','not like','%(Failure)'], | ||
| 1074 | - ['subject','not like','%(Delay)'] | ||
| 1075 | - ]); | ||
| 1076 | - },20,$total); | ||
| 1077 | - } | ||
| 1078 | - | ||
| 1079 | - /** | ||
| 1080 | - * 读取邮件详情 | ||
| 1081 | - * @param $id | ||
| 1082 | - * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null | ||
| 1083 | - * @time 2022/8/2 15:13 | ||
| 1084 | - */ | ||
| 1085 | - public function emailInfo($id){ | ||
| 1086 | - $email = EmailList::_firstWithBody($id); | ||
| 1087 | - // 是否存在,并且是否属于自己 | ||
| 1088 | - if($email && in_array($email->email_id,$this->email_ids)){ | ||
| 1089 | - if(!$email->body && $email['uid']){ | ||
| 1090 | - $email = $email->toArray(); | ||
| 1091 | - try { | ||
| 1092 | - $email['body'] = $this->syncEMailBody($email['uid'],$email['id']); | ||
| 1093 | - }catch (\Throwable $e){ | ||
| 1094 | - $email['body'] = $this->syncEMailBody($email['uid'],$email['id']); | ||
| 1095 | - } | ||
| 1096 | - } | ||
| 1097 | - return $email; | ||
| 1098 | - } | ||
| 1099 | - return null; | ||
| 1100 | - } | ||
| 1101 | - | ||
| 1102 | - | ||
| 1103 | - | ||
| 1104 | - | ||
| 1105 | - /** | ||
| 1106 | - * 获取邮件服务器 | ||
| 1107 | - * @param string $suffix | ||
| 1108 | - * @return array | ||
| 1109 | - * @time 2022/7/29 16:24 | ||
| 1110 | - */ | ||
| 1111 | - public function host(string $suffix=''):array { | ||
| 1112 | - if($suffix){ | ||
| 1113 | - return EmailHost::_get($suffix); | ||
| 1114 | - } | ||
| 1115 | - return EmailHost::_all(); | ||
| 1116 | - } | ||
| 1117 | - | ||
| 1118 | - /** | ||
| 1119 | - * 获取自己的服务器地址 | ||
| 1120 | - * @param null $name | ||
| 1121 | - * @return array|mixed|string | ||
| 1122 | - * @time 2022/8/1 15:46 | ||
| 1123 | - */ | ||
| 1124 | - public function hostMy($name=null) { | ||
| 1125 | - | ||
| 1126 | -// if(!$this->host){ | ||
| 1127 | -// $data = Email::_first($this->id); | ||
| 1128 | -// $this->host = [ | ||
| 1129 | -// 'imap' => $data['imap'], | ||
| 1130 | -// 'smtp' => $data['smtp'], | ||
| 1131 | -// ]; | ||
| 1132 | -// } | ||
| 1133 | - | ||
| 1134 | - if($name){ | ||
| 1135 | - return $this->host[$name]??''; | ||
| 1136 | - } | ||
| 1137 | - | ||
| 1138 | - return $this->host; | ||
| 1139 | - } | ||
| 1140 | - | ||
| 1141 | - /** | ||
| 1142 | - * 添加邮件服务器 | ||
| 1143 | - * @param string $suffix | ||
| 1144 | - * @param string $imap | ||
| 1145 | - * @param string $smtp | ||
| 1146 | - * @return int email host 表的id | ||
| 1147 | - * @time 2022/7/29 16:28 | ||
| 1148 | - */ | ||
| 1149 | - public function hostAdd(string $suffix, string $imap, string $smtp):int{ | ||
| 1150 | - return EmailHost::_insert($suffix, $imap, $smtp); | ||
| 1151 | - } | ||
| 1152 | - | ||
| 1153 | - | ||
| 1154 | - /** | ||
| 1155 | - * 添加一个邮箱地址 | ||
| 1156 | - * @time 2022/7/29 16:51 | ||
| 1157 | - */ | ||
| 1158 | - public function emailAdd($data=[]){ | ||
| 1159 | - $data = $data ? $data : $this->request->post(); | ||
| 1160 | - if(!$data){ | ||
| 1161 | - throw new \Exception(Lang::__('empty_form')); | ||
| 1162 | - } | ||
| 1163 | - | ||
| 1164 | - $validator = Validator::make($data,[ | ||
| 1165 | - 'email' => ['required','email'], | ||
| 1166 | - 'password' => ['required'], | ||
| 1167 | - ],[ | ||
| 1168 | - 'email.required' => 'email_required', | ||
| 1169 | - 'email.email' => 'email_validator', | ||
| 1170 | - 'password.required' => 'password_required', | ||
| 1171 | - ]); | ||
| 1172 | - | ||
| 1173 | - if($validator->fails()){ | ||
| 1174 | - throw new \Exception(Lang::__($validator->errors()->first())); | ||
| 1175 | - } | ||
| 1176 | - $data['email'] = trim($data['email']); | ||
| 1177 | - $data['password'] = trim($data['password']); | ||
| 1178 | - $data['email_name'] = trim($data['email_name']??$data['email']); | ||
| 1179 | - | ||
| 1180 | - // 检查imap服务器 | ||
| 1181 | - if($data['host']??''){ | ||
| 1182 | - $host = [ | ||
| 1183 | - 'imap' => trim($data['host']), | ||
| 1184 | - 'smtp' => trim($data['smtp']??'') | ||
| 1185 | - ]; | ||
| 1186 | - }else{ | ||
| 1187 | - // 获取数据库中有的 | ||
| 1188 | - $host = $this->host(explode('@',$data['email'])[1]); | ||
| 1189 | - } | ||
| 1190 | - | ||
| 1191 | - if(!$host){ | ||
| 1192 | - // 是否存在imap服务器 | ||
| 1193 | - throw new \Exception(Lang::__('host_required')); | ||
| 1194 | - } | ||
| 1195 | - | ||
| 1196 | - // 登录名 | ||
| 1197 | - $this->username = $data['email']; | ||
| 1198 | - | ||
| 1199 | - // 进行远程登录 | ||
| 1200 | - // imap imap.qq.com | ||
| 1201 | - $imap = new \App\Http\Mail\lib\client\Imap(); | ||
| 1202 | - $imap->login($this->ssl.$host['imap'].':'.$this->port,$data['email'],$data['password']); | ||
| 1203 | - | ||
| 1204 | - // 添加邮箱,并绑定user_id | ||
| 1205 | - $model = Email::_add($this->user_id, $data['email'], $data['password'], $data['email_name'],$host); | ||
| 1206 | - | ||
| 1207 | - try { | ||
| 1208 | - // 添加一个联系人的默认分组 | ||
| 1209 | - $this->contactGroupSave('默认分组'); | ||
| 1210 | - }catch (\Throwable $e){ | ||
| 1211 | - | ||
| 1212 | - } | ||
| 1213 | - | ||
| 1214 | - | ||
| 1215 | - // 退出imap登录 | ||
| 1216 | - $imap->loginOut(); | ||
| 1217 | - | ||
| 1218 | - return $model; | ||
| 1219 | - | ||
| 1220 | -// throw new \Exception(Lang::__('email_insert_error')); | ||
| 1221 | - } | ||
| 1222 | - | ||
| 1223 | - /** | ||
| 1224 | - * 读取所有邮箱 | ||
| 1225 | - * @return array | ||
| 1226 | - * @time 2022/8/1 9:34 | ||
| 1227 | - */ | ||
| 1228 | - public function emails():array { | ||
| 1229 | - return Email::_get($this->user_id); | ||
| 1230 | - } | ||
| 1231 | - | ||
| 1232 | - /** | ||
| 1233 | - * 文件夹 | ||
| 1234 | - * @return array | ||
| 1235 | - * @time 2022/8/4 17:34 | ||
| 1236 | - */ | ||
| 1237 | - public function folders($id){ | ||
| 1238 | - | ||
| 1239 | - $result = EmailFolder::_all($id); | ||
| 1240 | - if(!$result){ | ||
| 1241 | - $result = $this->syncFolder(); | ||
| 1242 | - } | ||
| 1243 | - return $result; | ||
| 1244 | - } | ||
| 1245 | - | ||
| 1246 | - | ||
| 1247 | - /** | ||
| 1248 | - * 联系人列表 | ||
| 1249 | - * @return mixed | ||
| 1250 | - * @time 2022/8/4 9:05 | ||
| 1251 | - */ | ||
| 1252 | - public function contact($is_group=false){ | ||
| 1253 | - | ||
| 1254 | - return EmailContact::_all($this->id,$is_group); | ||
| 1255 | - } | ||
| 1256 | - | ||
| 1257 | - /** | ||
| 1258 | - * 联系人管理视图 | ||
| 1259 | - * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||
| 1260 | - * @author:dc | ||
| 1261 | - * @time 2022/11/2 16:37 | ||
| 1262 | - */ | ||
| 1263 | - public function contact_view(){ | ||
| 1264 | - | ||
| 1265 | - | ||
| 1266 | - return view('admin/email/contacts',[ | ||
| 1267 | -// 'emails' => $this->emails(), | ||
| 1268 | - 'email' => $this->username, | ||
| 1269 | -// 'groups' => $this->contactGroup(), | ||
| 1270 | -// 'contacts' => $this->contact() | ||
| 1271 | - ]); | ||
| 1272 | - } | ||
| 1273 | - | ||
| 1274 | - | ||
| 1275 | - /** | ||
| 1276 | - * 添加联系人 | ||
| 1277 | - * @return EmailContact | ||
| 1278 | - * @throws \Exception | ||
| 1279 | - * @time 2022/8/4 9:46 | ||
| 1280 | - */ | ||
| 1281 | - public function contactAdd(){ | ||
| 1282 | - | ||
| 1283 | - $data = $this->request->post(); | ||
| 1284 | - | ||
| 1285 | - $validator = Validator::make($data,[ | ||
| 1286 | - 'group_id' => ['required'], | ||
| 1287 | - 'email' => ['required','email'], | ||
| 1288 | - 'email_name' => ['required','max:100'], | ||
| 1289 | - 'remark' => ['max:200'], | ||
| 1290 | - ],[ | ||
| 1291 | - 'group_id.required' => 'contact_group_exists', | ||
| 1292 | - 'email.required' => 'contact_email_required', | ||
| 1293 | - 'email.email' => 'contact_email_error', | ||
| 1294 | - 'email_name.required' => 'contact_email_name_required', | ||
| 1295 | - 'email_name.max' => 'contact_email_name_max', | ||
| 1296 | - 'remark.max' => 'contact_remark_max', | ||
| 1297 | - ]); | ||
| 1298 | - // 验证数据 | ||
| 1299 | - if($validator->fails()){ | ||
| 1300 | - | ||
| 1301 | - $p = ''; | ||
| 1302 | - | ||
| 1303 | - switch ($validator->errors()->first()){ | ||
| 1304 | - case 'contact_email_name_max': $p = '100';break; | ||
| 1305 | - case 'contact_remark_max': $p = '200';break; | ||
| 1306 | - } | ||
| 1307 | - | ||
| 1308 | - throw new \Exception( | ||
| 1309 | - Lang::__( | ||
| 1310 | - $validator->errors()->first(), | ||
| 1311 | - $p | ||
| 1312 | - ) | ||
| 1313 | - ); | ||
| 1314 | - } | ||
| 1315 | - | ||
| 1316 | - $group = EmailContactGroup::_first($data['group_id']); | ||
| 1317 | - if(!$group || $group['email_id']!=$this->id){ | ||
| 1318 | - abort(600,Lang::__('contact_group_exists')); | ||
| 1319 | - } | ||
| 1320 | - | ||
| 1321 | - | ||
| 1322 | - return EmailContact::_save($this->id,$data); | ||
| 1323 | - } | ||
| 1324 | - | ||
| 1325 | - | ||
| 1326 | - /** | ||
| 1327 | - * 联系人详情 | ||
| 1328 | - * @param $id | ||
| 1329 | - * @return array | ||
| 1330 | - * @author:dc | ||
| 1331 | - * @time 2022/11/4 17:02 | ||
| 1332 | - */ | ||
| 1333 | - public function contactInfo($id){ | ||
| 1334 | - return EmailContact::_first($this->id,$id); | ||
| 1335 | - } | ||
| 1336 | - | ||
| 1337 | - | ||
| 1338 | - /** | ||
| 1339 | - * 删除联系人 | ||
| 1340 | - * @time 2022/8/4 9:52 | ||
| 1341 | - */ | ||
| 1342 | - public function contactDel(){ | ||
| 1343 | - $contact_id = (int) $this->request->get('contact_id'); | ||
| 1344 | - if(!$contact_id || EmailContact::_del($this->id,$contact_id)){ | ||
| 1345 | - abort(600,Lang::__('del_error')); | ||
| 1346 | - } | ||
| 1347 | - return true; | ||
| 1348 | - } | ||
| 1349 | - | ||
| 1350 | - /** | ||
| 1351 | - * 联系人分组 | ||
| 1352 | - * @return mixed | ||
| 1353 | - * @time 2022/8/4 10:13 | ||
| 1354 | - */ | ||
| 1355 | - public function contactGroup($is_contact=false){ | ||
| 1356 | - return EmailContactGroup::_all($this->id,$is_contact); | ||
| 1357 | - } | ||
| 1358 | - | ||
| 1359 | - /** | ||
| 1360 | - * 添加联系人分组 | ||
| 1361 | - * @return EmailContactGroup | ||
| 1362 | - * @throws \Exception | ||
| 1363 | - * @time 2022/8/4 10:47 | ||
| 1364 | - */ | ||
| 1365 | - public function contactGroupSave($name,$id=0){ | ||
| 1366 | - if(!$name || mb_strlen($name)>100){ | ||
| 1367 | - throw new \Exception(Lang::__('contact_group_name_error',1,100)); | ||
| 1368 | - } | ||
| 1369 | - | ||
| 1370 | - $name = htmlspecialchars($name); | ||
| 1371 | - | ||
| 1372 | - $data = EmailContactGroup::_firstByName($this->id,$name); | ||
| 1373 | - if(($data && !$id) || ($data && $id && $data['id'] != $id)){ | ||
| 1374 | - throw new \Exception(Lang::__('contact_group_name_unique')); | ||
| 1375 | - } | ||
| 1376 | - | ||
| 1377 | - return EmailContactGroup::_save($this->id,$name,$id); | ||
| 1378 | - } | ||
| 1379 | - | ||
| 1380 | - /** | ||
| 1381 | - * 删除 | ||
| 1382 | - * @return false|mixed | ||
| 1383 | - * @time 2022/8/4 10:49 | ||
| 1384 | - */ | ||
| 1385 | - public function contactGroupDel($group_id){ | ||
| 1386 | - | ||
| 1387 | - if(!$group_id){ | ||
| 1388 | - GROUP_DEL_ABORT: | ||
| 1389 | - abort(600,Lang::__('contact_group_del_id')); | ||
| 1390 | - } | ||
| 1391 | - | ||
| 1392 | - // 是否有联系人在其中 | ||
| 1393 | - if(EmailContact::_count($group_id, $this->id)){ | ||
| 1394 | - abort(600,Lang::__('contact_group_del_contact')); | ||
| 1395 | - } | ||
| 1396 | - | ||
| 1397 | - // 删除 | ||
| 1398 | - if(!EmailContactGroup::_del($group_id, $this->id)){ | ||
| 1399 | - goto GROUP_DEL_ABORT; | ||
| 1400 | - } | ||
| 1401 | - | ||
| 1402 | - return true; | ||
| 1403 | - } | ||
| 1404 | - | ||
| 1405 | - | ||
| 1406 | - | ||
| 1407 | - | ||
| 1408 | - | ||
| 1409 | - /** | ||
| 1410 | - * 发送邮件 | ||
| 1411 | - * @time 2022/8/3 16:08 | ||
| 1412 | - */ | ||
| 1413 | - public function send($data=[]){ | ||
| 1414 | - | ||
| 1415 | - $data = $data ? $data : $this->request->post(); | ||
| 1416 | - | ||
| 1417 | - $data['is_text'] = intval($data['is_text']??0);//纯文本 | ||
| 1418 | -// $data['save_send'] = intval($data['save_send']??0);//保存到已发送 | ||
| 1419 | - $data['priority'] = intval($data['priority']??0);//紧急 | ||
| 1420 | - $data['receipt'] = intval($data['receipt']??0);//需要回执 | ||
| 1421 | - $data['encrypt'] = intval($data['encrypt']??0);//加密邮件 | ||
| 1422 | - | ||
| 1423 | - | ||
| 1424 | - | ||
| 1425 | - // 是纯文本还是html | ||
| 1426 | - $data['body'] = $data['is_text'] ? ($data['text']??'') : ($data['html']??''); | ||
| 1427 | - | ||
| 1428 | -// 是否是回复邮件 | ||
| 1429 | - if(!empty($data['reply']['id']) && !empty($data['reply']['email_id'])){ | ||
| 1430 | - | ||
| 1431 | - // 标题,主题 | ||
| 1432 | - if(empty($data['subject'])){ | ||
| 1433 | - throw new \Exception(Lang::__('send_email_subject_error',500)); | ||
| 1434 | - } | ||
| 1435 | - | ||
| 1436 | - // 查询 | ||
| 1437 | - $originData = EmailList::_first($data['reply']['id']); | ||
| 1438 | - // 是否存在邮件 | ||
| 1439 | - if($originData && $originData['email_id'] == $data['reply']['email_id'] && in_array($originData['email_id'],$this->email_ids)){ | ||
| 1440 | - $emaildata = Email::_firstById($originData['email_id']); | ||
| 1441 | - if(!$emaildata){ | ||
| 1442 | - throw new \Exception('发件人异常',600); | ||
| 1443 | - } | ||
| 1444 | - try { | ||
| 1445 | - // 立刻发送邮件 | ||
| 1446 | - MailFun::sendEmail( | ||
| 1447 | - $emaildata['smtp'],$emaildata['email'],$emaildata['password'], | ||
| 1448 | - $emaildata['email_name'],['email'=>$originData['from'],'name'=>$originData['from_name']] | ||
| 1449 | - ,$data['subject'],$data['body'],$data['file']??[],$data['receipt'],$data['priority']?1:3 | ||
| 1450 | - ); | ||
| 1451 | - // 记录下 | ||
| 1452 | - EmailLog::error(print_r([ | ||
| 1453 | - 'manage_id'=>the_manage('id'), | ||
| 1454 | - 'data' => $data, | ||
| 1455 | - '回复邮件' | ||
| 1456 | - ],true)); | ||
| 1457 | - return true; | ||
| 1458 | - }catch (\Throwable $e){ | ||
| 1459 | - throw new \Exception($e->getMessage()); | ||
| 1460 | - } | ||
| 1461 | - } | ||
| 1462 | - } | ||
| 1463 | - | ||
| 1464 | - | ||
| 1465 | - // 收件人 | ||
| 1466 | - if(empty($data['to'])){ | ||
| 1467 | - SEND_EMAIL_TO_ERROR: | ||
| 1468 | - throw new \Exception(Lang::__('send_email_to_error')); | ||
| 1469 | - } | ||
| 1470 | - // 是否是数组 | ||
| 1471 | - $data['to'] = explode("\n",trim($data['to'])); | ||
| 1472 | - if(!$data['to']){ | ||
| 1473 | - goto SEND_EMAIL_TO_ERROR; | ||
| 1474 | - } | ||
| 1475 | -// $to = [ | ||
| 1476 | -// 'email' => 'xxx@qq.com', | ||
| 1477 | -// 'name' => 'xxx' | ||
| 1478 | -// ]; | ||
| 1479 | - foreach ($data['to'] as $k=>$to){ | ||
| 1480 | - $to = trim($to); | ||
| 1481 | - $data['to'][$to] = []; | ||
| 1482 | - $data['to'][$to]['email'] = $to; | ||
| 1483 | - $data['to'][$to]['name'] = explode('@',$to)[0]; | ||
| 1484 | - | ||
| 1485 | - // 是否是邮箱 | ||
| 1486 | - if(!preg_match('/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/',$data['to'][$to]['email'])){ | ||
| 1487 | -// throw new \Exception(Lang::__('send_email_to_error',$data['to'][$to]['email'])); | ||
| 1488 | - unset($data['to'][$to]); | ||
| 1489 | - } | ||
| 1490 | - | ||
| 1491 | - unset($data['to'][$k]); | ||
| 1492 | - } | ||
| 1493 | - | ||
| 1494 | - $data['to'] = array_values($data['to']); | ||
| 1495 | - if(!$data['to']){ | ||
| 1496 | - goto SEND_EMAIL_TO_ERROR; | ||
| 1497 | - } | ||
| 1498 | - | ||
| 1499 | - // 时间 | ||
| 1500 | - if($data['send_time'] != 'now'){ | ||
| 1501 | - $data['send_time'] = explode(',',$data['send_time']); | ||
| 1502 | - if($data['send_time'][0] < 24 && $data['send_time'][1] < 24){ | ||
| 1503 | - $data['send_time'] = implode(',',$data['send_time']); | ||
| 1504 | - }else{ | ||
| 1505 | - $data['send_time'] = '21,11'; | ||
| 1506 | - } | ||
| 1507 | - } | ||
| 1508 | - | ||
| 1509 | - $tags = get('tags'); | ||
| 1510 | - if (!$tags || !is_array($tags)){ | ||
| 1511 | - throw new \Exception('请选择tag标签'); | ||
| 1512 | - } | ||
| 1513 | - | ||
| 1514 | - | ||
| 1515 | - // 加入任务 | ||
| 1516 | - $job_id = EmailSendJob::_insert([ | ||
| 1517 | - 'title' => $data['subject']??'', | ||
| 1518 | - 'to' => $data['to'], | ||
| 1519 | - 'the_manage_id' => the_manage('id'), | ||
| 1520 | - 'email_id' => $this->id, | ||
| 1521 | - 'user_id' => $this->user_id, | ||
| 1522 | - 'data' => $data, | ||
| 1523 | - 'send_time' => $data['send_time'], | ||
| 1524 | - 'tags' => $tags | ||
| 1525 | - ]); | ||
| 1526 | - if($job_id){ | ||
| 1527 | - // 发送一个任务 | ||
| 1528 | - SendJob::dispatch($job_id); | ||
| 1529 | - return $job_id; | ||
| 1530 | - } | ||
| 1531 | - | ||
| 1532 | - throw new \Exception(Lang::__('email_send_error')); | ||
| 1533 | - } | ||
| 1534 | - | ||
| 1535 | - | ||
| 1536 | - /** | ||
| 1537 | - * 下载附件 | ||
| 1538 | - * @param $name | ||
| 1539 | - * @param string $originname | ||
| 1540 | - * @return string|\Symfony\Component\HttpFoundation\BinaryFileResponse | ||
| 1541 | - * @author:dc | ||
| 1542 | - * @time 2022/11/2 10:59 | ||
| 1543 | - */ | ||
| 1544 | - public function download($name,$originname=''){ | ||
| 1545 | - $file = $this->getFilePath().$name; | ||
| 1546 | - if(is_file($file)){ | ||
| 1547 | - return response()->download($file,$originname ? : $name); | ||
| 1548 | - } | ||
| 1549 | - return ''; | ||
| 1550 | - } | ||
| 1551 | - | ||
| 1552 | - | ||
| 1553 | - | ||
| 1554 | - | ||
| 1555 | - | ||
| 1556 | - /** | ||
| 1557 | - * @param mixed $data | ||
| 1558 | - * @param string $message | ||
| 1559 | - * @param int $status | ||
| 1560 | - * @return \Illuminate\Http\JsonResponse | ||
| 1561 | - * @author:dc | ||
| 1562 | - * @time 2022/8/1 11:05 | ||
| 1563 | - */ | ||
| 1564 | - private function echoJson($data='', $message='', $status=200):\Illuminate\Http\JsonResponse{ | ||
| 1565 | - $data = [ | ||
| 1566 | - 'data' => $data, | ||
| 1567 | - 'message' => $message, | ||
| 1568 | - 'status' => $status | ||
| 1569 | - ]; | ||
| 1570 | - return response()->json($data,200,[]); | ||
| 1571 | - } | ||
| 1572 | - | ||
| 1573 | -} |
app/Mail/lang/en.php
已删除
100644 → 0
| 1 | -<?php | ||
| 2 | - | ||
| 3 | - | ||
| 4 | - | ||
| 5 | -return $message = [ | ||
| 6 | - 'imap_server_error' => 'IMAP server error: %s', | ||
| 7 | - 'login_host' => 'Enter the IMAP server address.', | ||
| 8 | - 'empty_form' => 'The form data is empty.', | ||
| 9 | - 'password_required' => 'The password field is required.', | ||
| 10 | - 'password_error' => 'wrong password.', | ||
| 11 | - 'host_required' => 'The IMAP server address must be.', | ||
| 12 | - 'email_required' => 'Email address must be.', | ||
| 13 | - 'email_validator' => 'Email format error.', | ||
| 14 | - 'email_insert_error' => 'Failed to Add Mailbox.', | ||
| 15 | - 'contact_email_required' => 'Contact email address must be specified.', | ||
| 16 | - 'contact_group_exists' => 'The selected group does not exist.', | ||
| 17 | - 'contact_email_error' => 'The email format of the contact is incorrect.', | ||
| 18 | - 'contact_email_name_required' => 'Contact name must be entered.', | ||
| 19 | - 'contact_email_name_max' => 'Contact name Maximum character %s.', | ||
| 20 | - 'contact_group_name_error' => 'The contact group must be longer than %s and smaller than %s characters.', | ||
| 21 | - 'contact_group_name_unique' => 'The contact group already exists.', | ||
| 22 | - 'contact_group_del_id' => 'Grouping does not exist.', | ||
| 23 | - 'contact_remark_max' => 'The contact remarks must contain %s characters.', | ||
| 24 | - 'contact_group_del_contact' => 'There are contacts in the group and cannot be deleted.', | ||
| 25 | - 'email_folder_not' => 'Folder (%s) does not exist.', | ||
| 26 | - 'email_not_bind' => 'Mailbox (%s) is not bound.', | ||
| 27 | - 'email_send_error' => 'Failed to send message (%s).', | ||
| 28 | - 'del_error' => 'fail to delete.', | ||
| 29 | - 'send_email_subject_error' => 'The message subject must be within the %s character.', | ||
| 30 | - 'send_email_to_error' => 'The recipient must or is formatted incorrectly, %s.', | ||
| 31 | -]; |
app/Mail/lang/zh-cn.php
已删除
100644 → 0
| 1 | -<?php | ||
| 2 | - | ||
| 3 | - | ||
| 4 | - | ||
| 5 | -return $message = [ | ||
| 6 | - 'imap_server_error' => 'IMAP服务器错误: %s', | ||
| 7 | - 'login_host' => '请输入imap服务器地址', | ||
| 8 | - 'empty_form' => '表单数据为空', | ||
| 9 | - 'password_required' => '密码必须', | ||
| 10 | - 'password_error' => '密码错误', | ||
| 11 | - 'host_required' => 'imap服务器地址必须', | ||
| 12 | - 'email_required' => '邮箱必须', | ||
| 13 | - 'email_validator' => '邮箱格式错误', | ||
| 14 | - 'email_insert_error' => '添加邮箱失败', | ||
| 15 | - 'contact_email_required' => '联系人邮箱必须填写', | ||
| 16 | - 'contact_group_exists' => '选择的分组不存在', | ||
| 17 | - 'contact_email_error' => '联系人邮箱格式错误', | ||
| 18 | - 'contact_email_name_required' => '联系人姓名必须填写', | ||
| 19 | - 'contact_email_name_max' => '联系人姓名最大字符%s', | ||
| 20 | - 'contact_group_name_error' => '联系人分组必须大于%s且小于%s字符', | ||
| 21 | - 'contact_group_name_unique' => '联系人分组已存在', | ||
| 22 | - 'contact_group_del_id' => '分组不存在', | ||
| 23 | - 'contact_group_del_contact' => '分组下还有联系人,无法删除', | ||
| 24 | - 'contact_remark_max' => '联系人备注在%s字符内', | ||
| 25 | - 'email_folder_not' => '文件夹(%s)不存在', | ||
| 26 | - 'email_not_bind' => '邮箱(%s)未绑定', | ||
| 27 | - 'email_send_error' => '邮件发送失败(%s)', | ||
| 28 | - 'del_error' => '删除失败', | ||
| 29 | - 'send_email_subject_error' => '邮件主题必须且在%s字符内', | ||
| 30 | - 'send_email_to_error' => '收件人必须或格式错误,%s', | ||
| 31 | -]; |
app/Mail/lib/ImapUtf7.php
已删除
100644 → 0
| 1 | -<?php | ||
| 2 | - | ||
| 3 | -namespace App\Mail\lib; | ||
| 4 | - | ||
| 5 | -/** | ||
| 6 | - * 中文情况 | ||
| 7 | - * @time 2022/8/1 15:32 | ||
| 8 | - * Class ImapUtf7 | ||
| 9 | - * @package App\Mail\lib | ||
| 10 | - */ | ||
| 11 | -class ImapUtf7 { | ||
| 12 | - static $imap_base64 = | ||
| 13 | - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,'; | ||
| 14 | - static private function encode_b64imap($s) { | ||
| 15 | - $a=0; $al=0; $res=''; $n=strlen($s); | ||
| 16 | - for($i=0;$i<$n;$i++) { | ||
| 17 | - $a=($a<<8)|ord($s[$i]); $al+=8; | ||
| 18 | - for(;$al>=6;$al-=6) $res.=self::$imap_base64[($a>>($al-6))&0x3F]; | ||
| 19 | - } | ||
| 20 | - if ($al>0) { $res.=self::$imap_base64[($a<<(6-$al))&0x3F]; } | ||
| 21 | - return $res; | ||
| 22 | - } | ||
| 23 | - static private function encode_utf8_char($w) { | ||
| 24 | - if ($w&0x80000000) return ''; | ||
| 25 | - if ($w&0xFC000000) $n=5; else | ||
| 26 | - if ($w&0xFFE00000) $n=4; else | ||
| 27 | - if ($w&0xFFFF0000) $n=3; else | ||
| 28 | - if ($w&0xFFFFF800) $n=2; else | ||
| 29 | - if ($w&0xFFFFFF80) $n=1; else return chr($w); | ||
| 30 | - $res=chr(( (255<<(7-$n)) | ($w>>($n*6)) )&255); | ||
| 31 | - while(--$n>=0) $res.=chr((($w>>($n*6))&0x3F)|0x80); | ||
| 32 | - return $res; | ||
| 33 | - } | ||
| 34 | - static private function decode_b64imap($s) { | ||
| 35 | - $a=0; $al=0; $res=''; $n=strlen($s); | ||
| 36 | - for($i=0;$i<$n;$i++) { | ||
| 37 | - $k=strpos(self::$imap_base64,$s[$i]); if ($k===FALSE) continue; | ||
| 38 | - $a=($a<<6)|$k; $al+=6; | ||
| 39 | - if ($al>=8) { $res.=chr(($a>>($al-8))&255);$al-=8; } | ||
| 40 | - } | ||
| 41 | - $r2=''; $n=strlen($res); | ||
| 42 | - for($i=0;$i<$n;$i++) { | ||
| 43 | - $c=ord($res[$i]); $i++; | ||
| 44 | - if ($i<$n) $c=($c<<8) | ord($res[$i]); | ||
| 45 | - $r2.=self::encode_utf8_char($c); | ||
| 46 | - } | ||
| 47 | - return $r2; | ||
| 48 | - } | ||
| 49 | - static function encode($s) { | ||
| 50 | - $n=strlen($s);$err=0;$buf='';$res=''; | ||
| 51 | - for($i=0;$i<$n;) { | ||
| 52 | - $x=ord($s[$i++]); | ||
| 53 | - if (($x&0x80)==0x00) { $r=$x;$w=0; } | ||
| 54 | - else if (($x&0xE0)==0xC0) { $w=1; $r=$x &0x1F; } | ||
| 55 | - else if (($x&0xF0)==0xE0) { $w=2; $r=$x &0x0F; } | ||
| 56 | - else if (($x&0xF8)==0xF0) { $w=3; $r=$x &0x07; } | ||
| 57 | - else if (($x&0xFC)==0xF8) { $w=4; $r=$x &0x03; } | ||
| 58 | - else if (($x&0xFE)==0xFC) { $w=5; $r=$x &0x01; } | ||
| 59 | - else if (($x&0xC0)==0x80) { $w=0; $r=-1; $err++; } | ||
| 60 | - else { $w=0;$r=-2;$err++; } | ||
| 61 | - for($k=0;$k<$w && $i<$n; $k++) { | ||
| 62 | - $x=ord($s[$i++]); if ($x&0xE0!=0x80) { $err++; } | ||
| 63 | - $r=($r<<6)|($x&0x3F); | ||
| 64 | - } | ||
| 65 | - if ($r<0x20 || $r>0x7E ) { | ||
| 66 | - $buf.=chr(($r>>8)&0xFF); $buf.=chr($r&0xFF); | ||
| 67 | - } else { | ||
| 68 | - if (strlen($buf)) { | ||
| 69 | - $res.='&'.self::encode_b64imap($buf).'-'; | ||
| 70 | - $buf=''; | ||
| 71 | - } | ||
| 72 | - if ($r==0x26) { $res.='&-'; } else $res.=chr($r); | ||
| 73 | - } | ||
| 74 | - } | ||
| 75 | - if (strlen($buf)) $res.='&'.self::encode_b64imap($buf).'-'; | ||
| 76 | - return $res; | ||
| 77 | - } | ||
| 78 | - static function decode($s) { | ||
| 79 | - $res=''; $n=strlen($s); $h=0; | ||
| 80 | - while($h<$n) { | ||
| 81 | - $t=strpos($s,'&',$h); if ($t===false) $t=$n; | ||
| 82 | - $res.=substr($s,$h,$t-$h); $h=$t+1; if ($h>=$n) break; | ||
| 83 | - $t=strpos($s,'-',$h); if ($t===false) $t=$n; | ||
| 84 | - $k=$t-$h; | ||
| 85 | - if ($k==0) $res.='&'; | ||
| 86 | - else $res.=self::decode_b64imap(substr($s,$h,$k)); | ||
| 87 | - $h=$t+1; | ||
| 88 | - } | ||
| 89 | - return $res; | ||
| 90 | - } | ||
| 91 | -} |
app/Mail/lib/Lang.php
已删除
100644 → 0
| 1 | -<?php | ||
| 2 | - | ||
| 3 | -namespace App\Mail\lib; | ||
| 4 | - | ||
| 5 | -/** | ||
| 6 | - * @time 2022/8/3 9:42 | ||
| 7 | - * Class Lang | ||
| 8 | - * @package App\Mail\lib | ||
| 9 | - */ | ||
| 10 | -class Lang { | ||
| 11 | - | ||
| 12 | - public static $l; | ||
| 13 | - | ||
| 14 | - public static $data; | ||
| 15 | - | ||
| 16 | - /** | ||
| 17 | - * @param $key | ||
| 18 | - * @param mixed ...$var | ||
| 19 | - * @return mixed | ||
| 20 | - * @time 2022/8/3 9:41 | ||
| 21 | - */ | ||
| 22 | - public static function __($key,...$var){ | ||
| 23 | - // 是否加载语言 | ||
| 24 | - if (!isset(self::$data[self::$l])) self::load(); | ||
| 25 | - // 获取语言 | ||
| 26 | - $str = self::$data[self::$l][$key]??self::$data['en'][$key]??$key; | ||
| 27 | - | ||
| 28 | - // 数组填充到10个元素,避免出现元素不够而返回空字符 | ||
| 29 | - $var = array_pad($var,10,''); | ||
| 30 | - // 替换 | ||
| 31 | - return @sprintf($str, ...$var); | ||
| 32 | - | ||
| 33 | - } | ||
| 34 | - | ||
| 35 | - | ||
| 36 | - /** | ||
| 37 | - * 加载 | ||
| 38 | - * @param string $l | ||
| 39 | - * @time 2022/8/4 15:55 | ||
| 40 | - */ | ||
| 41 | - public static function load(string $l = ''){ | ||
| 42 | - self::$l = strtolower($l ? $l : app()->getLocale()); | ||
| 43 | - | ||
| 44 | - self::$data[self::$l] = require_once __DIR__.'/../lang/'.self::$l.'.php'; | ||
| 45 | - | ||
| 46 | - // default | ||
| 47 | - if(!isset(self::$data['en'])){ | ||
| 48 | - self::$data['en'] = require_once __DIR__.'/../lang/en.php'; | ||
| 49 | - } | ||
| 50 | - | ||
| 51 | - } | ||
| 52 | - | ||
| 53 | -} |
app/Mail/lib/MailParse/Body.php
已删除
100644 → 0
| 1 | -<?php | ||
| 2 | - | ||
| 3 | -namespace App\Mail\lib\MailParse; | ||
| 4 | - | ||
| 5 | -use App\Mail\lib\MailFun; | ||
| 6 | - | ||
| 7 | -/** | ||
| 8 | - * 解析邮件body内容 | ||
| 9 | - * 通过 fetch msgno RFC822.text 获取到的内容 | ||
| 10 | - * 此内容包含html 文本 附件 | ||
| 11 | - * @author:dc | ||
| 12 | - * @time 2022/8/12 9:15 | ||
| 13 | - * Class Body | ||
| 14 | - * @package App\Mail\lib | ||
| 15 | - */ | ||
| 16 | -class Body { | ||
| 17 | - | ||
| 18 | - /** | ||
| 19 | - * @var string | ||
| 20 | - */ | ||
| 21 | - private $body; | ||
| 22 | - | ||
| 23 | - /** | ||
| 24 | - * | ||
| 25 | - * @var array | ||
| 26 | - */ | ||
| 27 | - private $item = []; | ||
| 28 | - | ||
| 29 | - /** | ||
| 30 | - * 保存的目录 | ||
| 31 | - * @var string | ||
| 32 | - */ | ||
| 33 | - private $fileSavePath; | ||
| 34 | - | ||
| 35 | - | ||
| 36 | - /** | ||
| 37 | - * Body constructor. | ||
| 38 | - * @param string $body | ||
| 39 | - * @param string $fileSavePath | ||
| 40 | - */ | ||
| 41 | - public function __construct(string $body, string $fileSavePath='/') | ||
| 42 | - { | ||
| 43 | - $this->body = $body = trim($body); | ||
| 44 | - | ||
| 45 | - $this->fileSavePath = $fileSavePath; | ||
| 46 | - | ||
| 47 | - // 这个是描述特殊文本 | ||
| 48 | - if(strpos($body,'This is a multi-part message in MIME format.')===0){ | ||
| 49 | - $body = trim($body,'This is a multi-part message in MIME format.'); | ||
| 50 | - $body = trim($body); | ||
| 51 | - } | ||
| 52 | - | ||
| 53 | - // 163 有 | ||
| 54 | - if(strpos($body,'------=_Part')!==false){ | ||
| 55 | - $this->parse($body,'------=_Part'); | ||
| 56 | - } | ||
| 57 | - elseif (mb_strpos($body,'------=_NextPart')!==false){ | ||
| 58 | - $this->parse($body,'------=_NextPart'); | ||
| 59 | - } | ||
| 60 | - elseif (mb_strpos($body,'----_NmP')!==false){ | ||
| 61 | - $this->parse($body,'----_NmP'); | ||
| 62 | - } | ||
| 63 | - elseif (mb_strpos($body,'--_=_swift')!==false){ | ||
| 64 | - $this->parse($body,'--_=_swift'); | ||
| 65 | - } | ||
| 66 | - elseif (mb_strpos($body,'----==_mimepart')!==false){ | ||
| 67 | - $this->parse($body,'----==_mimepart'); | ||
| 68 | - } | ||
| 69 | - elseif (mb_strpos($body,'--------------Boundary')!==false){ | ||
| 70 | - $this->parse($body,'--------------Boundary'); | ||
| 71 | - } | ||
| 72 | - elseif (mb_strpos($body,'--=-')!==false){ | ||
| 73 | - $this->parse($body,'--=-'); | ||
| 74 | - } | ||
| 75 | - // 很多--开始的,且不规则 | ||
| 76 | - elseif(strpos($body,'--')===0){ | ||
| 77 | - // 获取第一行 | ||
| 78 | - $tag = $this->body_get_tag($body,'--'); | ||
| 79 | - // 以第一行为标准 | ||
| 80 | - $this->parse($body,trim($tag)); | ||
| 81 | - } | ||
| 82 | - // 直接html | ||
| 83 | - elseif (mb_strpos($body,'<')===0){ | ||
| 84 | - $body = quoted_printable_decode($body); | ||
| 85 | -// preg_match("/<meta(?!\s*(?:name|value)\s*=)(?:[^>]*?content\s*=[\s\"']*)?([^>]*?)[\s\"';]*charset\s*=[\s\"']*([^\s\"'\/>]*)/",$body,$icon); | ||
| 86 | -// if(!empty($icon[2])){ | ||
| 87 | -// // 解码 | ||
| 88 | -// $body = mb_convert_encoding($body,'utf-8',$icon[2]); | ||
| 89 | -// } | ||
| 90 | - $this->setItem(['type'=>'text/html','body'=>$body]); | ||
| 91 | - } | ||
| 92 | - else{ | ||
| 93 | - // qq的是base64 | ||
| 94 | - if(rtrim($body,'=') == rtrim(base64_encode(base64_decode($body)),'=')){ | ||
| 95 | - $this->setItem(['type'=>'text/plain','body'=>base64_decode($body)]); | ||
| 96 | - }else{ | ||
| 97 | - $this->setItem(['type'=>'text/plain','body'=>$body]); | ||
| 98 | - } | ||
| 99 | - | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - | ||
| 103 | - } | ||
| 104 | - | ||
| 105 | - /** | ||
| 106 | - * 获取标签 | ||
| 107 | - * @param $body | ||
| 108 | - * @param $tag | ||
| 109 | - * @return mixed|string | ||
| 110 | - * @author:dc | ||
| 111 | - * @time 2022/8/12 10:49 | ||
| 112 | - */ | ||
| 113 | - private function body_get_tag($body,$tag){ | ||
| 114 | - preg_match("/{$tag}[\w\W].*/i",$body,$result); | ||
| 115 | - if(!empty($result[0])) { | ||
| 116 | - return $result[0]; | ||
| 117 | - } | ||
| 118 | - return ''; | ||
| 119 | - } | ||
| 120 | - | ||
| 121 | - /** | ||
| 122 | - * @param $item | ||
| 123 | - */ | ||
| 124 | - private function setItem($item): void | ||
| 125 | - { | ||
| 126 | - $this->item[] = $item; | ||
| 127 | - } | ||
| 128 | - | ||
| 129 | - | ||
| 130 | - /** | ||
| 131 | - * @return [] | ||
| 132 | - */ | ||
| 133 | - public function getItem(): array | ||
| 134 | - { | ||
| 135 | - return $this->item; | ||
| 136 | - } | ||
| 137 | - | ||
| 138 | - | ||
| 139 | - /** | ||
| 140 | - * 开始解析 | ||
| 141 | - * @param string $body | ||
| 142 | - * @param string $tag | ||
| 143 | - * @return array | ||
| 144 | - * @author:dc | ||
| 145 | - * @time 2022/8/12 9:50 | ||
| 146 | - */ | ||
| 147 | - private function parse(string $body, string $tag){ | ||
| 148 | - | ||
| 149 | - // 删除第一个标签前面的数据,一般情况无用 | ||
| 150 | - $body = mb_substr($this->body,strpos($this->body,$tag),99999999999); | ||
| 151 | - | ||
| 152 | - // 有附件的情况 | ||
| 153 | - preg_match('/boundary="([-_A-Za-z0-9=\.]{1,})"/i',$body,$boundary); | ||
| 154 | - if($boundary[0]??''){ | ||
| 155 | - $body = str_replace($boundary[0],'',$body); | ||
| 156 | -// $body = mb_substr($body,mb_strpos($body,$boundary[0])+strlen($boundary[0]),99999999999); | ||
| 157 | - } | ||
| 158 | - // 附件情况 | ||
| 159 | - if(!empty($boundary[1])){ | ||
| 160 | - preg_match_all('/.*'.$boundary[1].'.*/i',$body,$boundary_tag); | ||
| 161 | - $body = str_replace($boundary_tag[0],'{--tag--}',$body); | ||
| 162 | - } | ||
| 163 | - // 查找tag块 | ||
| 164 | - preg_match_all("/(".$tag.".*+\n)/i",$body."\r\n\r\n",$he); | ||
| 165 | - // 把每个tag块分开成数组 | ||
| 166 | - if(!empty($he[0])){ | ||
| 167 | - foreach ($he[0] as $hk=>$h){ | ||
| 168 | - $he[0][$hk] = trim($h); | ||
| 169 | - } | ||
| 170 | - arsort($he[0]); | ||
| 171 | - $body = str_replace($he[0],'{--tag--}',$body); | ||
| 172 | - } | ||
| 173 | - $body = explode('{--tag--}',$body); | ||
| 174 | - // 处理 | ||
| 175 | - foreach ($body as $key=>$item){ | ||
| 176 | - $data = []; | ||
| 177 | - | ||
| 178 | - $item = trim($item); | ||
| 179 | - // 附件的头 | ||
| 180 | - if(!$item) { continue; } | ||
| 181 | - | ||
| 182 | - // 邮件体包含邮件体 | ||
| 183 | - if(preg_match("/boundary=\"([-_a-z0-9]{5,})\"/Ui",$item,$bm)){ | ||
| 184 | - if (strpos($item,$bm[1].'--')!==false){ | ||
| 185 | - $data = (new self('--'.$bm[1]."\r\n".$item,$this->fileSavePath))->getItem(); | ||
| 186 | -// $this->setItem($data); | ||
| 187 | - // 合并邮件体 | ||
| 188 | - $this->item = array_merge($this->item,$data); | ||
| 189 | - } | ||
| 190 | - continue; | ||
| 191 | - } | ||
| 192 | - | ||
| 193 | - // 先解码解码 | ||
| 194 | - $encode = $this->body_match_tag('Content-Transfer-Encoding:',$item); | ||
| 195 | - if($encode){ | ||
| 196 | - $data['encode'] = strtolower($encode['text']); | ||
| 197 | - $item = str_replace($encode['origin'],'',$item); | ||
| 198 | - } | ||
| 199 | - | ||
| 200 | - // 内容类型 | ||
| 201 | - $type = $this->preg_match_type($item); | ||
| 202 | - if($type){ | ||
| 203 | - $data['type'] = strtolower($type['type']); | ||
| 204 | - // 编码 | ||
| 205 | - if(isset($type['charset'])){ | ||
| 206 | - $data['charset'] = strtolower($type['charset']); | ||
| 207 | - } | ||
| 208 | - // nama。附件 | ||
| 209 | - if(isset($type['name'])){ | ||
| 210 | - $data['name'] = $type['name']; | ||
| 211 | - } | ||
| 212 | - // 删除 | ||
| 213 | - $item = str_replace($type['origin'],'',$item); | ||
| 214 | - } | ||
| 215 | - | ||
| 216 | - // | ||
| 217 | - if(empty($data['charset'])){ | ||
| 218 | - // 编码 | ||
| 219 | - $code = $this->preg_match_charset($item); | ||
| 220 | - if($code){ | ||
| 221 | - $data['charset'] = strtolower($code['charset']); | ||
| 222 | - | ||
| 223 | - $item = str_replace($code['origin'],'',$item); | ||
| 224 | - } | ||
| 225 | - } | ||
| 226 | - | ||
| 227 | - // 先匹配留存文件名称 | ||
| 228 | - preg_match('/filename="(\w?.*)"/',$item,$filename); | ||
| 229 | - if(!empty($filename[1])){ | ||
| 230 | - $filename = MailFun::decodeMimeStr($filename[1]); | ||
| 231 | - } | ||
| 232 | - | ||
| 233 | - | ||
| 234 | - // 删除不需要的tag属性,如果需要进进行解析 | ||
| 235 | - $item = $this->body_remove_tag($item,'Content-Description:'); | ||
| 236 | - $item = $this->body_remove_tag($item,'Content-Disposition:'); | ||
| 237 | - $item = $this->body_remove_tag($item,'Mime-Version:'); | ||
| 238 | - | ||
| 239 | - $data['body'] = trim($item); | ||
| 240 | - | ||
| 241 | - if(!empty($data['type'])){ | ||
| 242 | - // 邮件头 | ||
| 243 | - if($data['type'] == 'multipart/alternative'){ | ||
| 244 | - | ||
| 245 | - } | ||
| 246 | - // 是文本还是附件 | ||
| 247 | - else if(strpos($data['type'],'text/') === 0 ){ | ||
| 248 | - // body解密 | ||
| 249 | - switch($data['encode']??''){ | ||
| 250 | - case 'base64': { | ||
| 251 | - $data['body'] = base64_decode($data['body']); | ||
| 252 | - break; | ||
| 253 | - } | ||
| 254 | - case 'quoted-printable': { | ||
| 255 | - $data['body'] = quoted_printable_decode($data['body']); | ||
| 256 | - break; | ||
| 257 | - } | ||
| 258 | - case '8bit': { | ||
| 259 | - try { | ||
| 260 | - $data['body'] = DeCoding::de8bit($data['body']); | ||
| 261 | - $data['body'] = quoted_printable_decode($data['body']); | ||
| 262 | - }catch (\Throwable $e){ | ||
| 263 | - | ||
| 264 | - } | ||
| 265 | - | ||
| 266 | - break; | ||
| 267 | - } | ||
| 268 | - } | ||
| 269 | - | ||
| 270 | - // 转码 | ||
| 271 | -// if(isset($data['charset']) && $data['charset']){ | ||
| 272 | -// $debody = @mb_convert_encoding($data['body'],'utf-8',$data['charset']); | ||
| 273 | -// if($debody){ | ||
| 274 | -// $data['body'] = $debody; | ||
| 275 | -// $debody = null; | ||
| 276 | -// } | ||
| 277 | -// } | ||
| 278 | - | ||
| 279 | - | ||
| 280 | - } | ||
| 281 | - // 系统退信//里面包含了发送邮件所有内容,这里不记录 | ||
| 282 | - elseif (strpos($data['type'],'message') === 0){ | ||
| 283 | - $data['body'] = '';// 一般不需要这些内容,如有需要就要重新解析 | ||
| 284 | - } | ||
| 285 | - elseif (!empty($data['type']) && $data['body']){ | ||
| 286 | - // 解析附件 | ||
| 287 | - $data = $this->parseFile($data,$filename); | ||
| 288 | - } | ||
| 289 | - } | ||
| 290 | - | ||
| 291 | - $this->setItem($data); | ||
| 292 | - | ||
| 293 | - } | ||
| 294 | - | ||
| 295 | - } | ||
| 296 | - | ||
| 297 | - | ||
| 298 | - /** | ||
| 299 | - * 解析文件 | ||
| 300 | - * @param $item | ||
| 301 | - * @return array|mixed | ||
| 302 | - * @author:dc | ||
| 303 | - * @time 2022/8/12 10:40 | ||
| 304 | - */ | ||
| 305 | - private function parseFile($item,$filename=''){ | ||
| 306 | - $data = []; | ||
| 307 | - // 查找文件名 | ||
| 308 | - | ||
| 309 | - $data['filename'] = $this->file_save_name($item['body'],'filename'); | ||
| 310 | - $data['name'] = $this->file_save_name($item['body'],'name'); | ||
| 311 | - $data['name'] = $data['name'] ? : ($item['name']??$filename); | ||
| 312 | - $data['filename'] = $data['filename'] ? : $data['name']; | ||
| 313 | - | ||
| 314 | - // 是否有文件名 | ||
| 315 | - if(empty($data['filename']) || strpos($data['filename'],'.')===false){ | ||
| 316 | - return $item; | ||
| 317 | - } | ||
| 318 | - | ||
| 319 | - $ext = explode('.',$data['filename']); | ||
| 320 | - $ext = end($ext); | ||
| 321 | - | ||
| 322 | -// if(!empty($item['type'])){ | ||
| 323 | -// // 文件类型来判断后缀 | ||
| 324 | -// // // download it from http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types | ||
| 325 | -// if(is_readable(__DIR__.'/mime.types')){ | ||
| 326 | -// $f = fopen(__DIR__.'/mime.types','r'); | ||
| 327 | -// while(!feof($f)){ | ||
| 328 | -// $fext = fgets($f); | ||
| 329 | -// if($fext){ | ||
| 330 | -// $fext = strtolower($fext); | ||
| 331 | -// $item['type'] = strtolower($item['type']); | ||
| 332 | -// // 找到了类型后缀 | ||
| 333 | -// if(strpos($fext,$item['type']) === 0){ | ||
| 334 | -// $ext = trim(str_replace($item['type'],'',$fext)); | ||
| 335 | -// break;//找到了要跳出循环 | ||
| 336 | -// } | ||
| 337 | -// } | ||
| 338 | -// } | ||
| 339 | -// // 关闭文件 | ||
| 340 | -// fclose($f); | ||
| 341 | -// } | ||
| 342 | -// } | ||
| 343 | - | ||
| 344 | - // 找不到后缀,说明不是文件 | ||
| 345 | -// if(empty($ext)){ | ||
| 346 | - // 文件后缀 | ||
| 347 | -// $ext = explode('.',$data['filename']); | ||
| 348 | -// $ext = count($ext) > 1 ? ($ext[count($ext)-1]??'') : ''; | ||
| 349 | - // 直接返回 | ||
| 350 | -// return $item['body']; | ||
| 351 | -// } | ||
| 352 | - | ||
| 353 | - | ||
| 354 | - // content id | ||
| 355 | - preg_match("/Content-ID:[\s].*<[\w\W]{1,}>/i",$item['body'],$result); | ||
| 356 | - if (!empty($result[0])){ | ||
| 357 | - $data['content-id'] = explode('<',$result[0]); | ||
| 358 | - $data['content-id'] = $data['content-id'][1]; | ||
| 359 | - $data['content-id'] = trim($data['content-id']); | ||
| 360 | - $data['content-id'] = trim($data['content-id'],'>'); | ||
| 361 | - | ||
| 362 | - $item['body'] = str_replace($result[0],'',$item['body']); | ||
| 363 | - } | ||
| 364 | - | ||
| 365 | - $item['body'] = str_replace($result,'',$item['body']); | ||
| 366 | - | ||
| 367 | - | ||
| 368 | - $content = base64_decode(trim($item['body'])); | ||
| 369 | - | ||
| 370 | - if($content){ | ||
| 371 | - // 目录 | ||
| 372 | - $data['path'] = $this->fileSavePath; | ||
| 373 | - if(!is_dir($data['path'])){ | ||
| 374 | - mkdir($data['path'],0775,true); | ||
| 375 | - } | ||
| 376 | - $data['signName'] = md5($content).($ext ? '.'.$ext : ''); | ||
| 377 | - $data['path'] = $data['path'].'/'.$data['signName']; | ||
| 378 | - // 保存文件 | ||
| 379 | - @file_put_contents($data['path'],$content); | ||
| 380 | - } | ||
| 381 | - | ||
| 382 | - | ||
| 383 | - return $data; | ||
| 384 | - } | ||
| 385 | - | ||
| 386 | - // 获取文件名称 | ||
| 387 | - private function file_save_name(&$body,$tag){ | ||
| 388 | - preg_match('/'.$tag.'="[(\S\W.*\s.*)]{1,}"/i',$body,$result); | ||
| 389 | - if($result[0]??''){ | ||
| 390 | - $body = str_replace($result[0],'',$body); | ||
| 391 | - } | ||
| 392 | - $val = trim(str_replace([$tag.'=','"',"'"],'',$result[0]??'')); | ||
| 393 | - if ($val && strpos($val,'=?')===0){ | ||
| 394 | - | ||
| 395 | - $val = iconv_mime_decode($val,ICONV_MIME_DECODE_CONTINUE_ON_ERROR,'utf-8'); | ||
| 396 | - } | ||
| 397 | - | ||
| 398 | - return $val; | ||
| 399 | - } | ||
| 400 | - | ||
| 401 | - /** | ||
| 402 | - * 删除tag | ||
| 403 | - * @param $body | ||
| 404 | - * @param $tag | ||
| 405 | - * @return mixed|string|string[] | ||
| 406 | - * @author:dc | ||
| 407 | - * @time 2022/8/12 10:34 | ||
| 408 | - */ | ||
| 409 | - private function body_remove_tag($body,$tag){ | ||
| 410 | - preg_match("/{$tag}[\w\W].*/i",$body,$result); | ||
| 411 | - if(!empty($result[0])) { | ||
| 412 | - $body = str_replace($result, '', $body); | ||
| 413 | - } | ||
| 414 | - return $body; | ||
| 415 | - } | ||
| 416 | - | ||
| 417 | - /** | ||
| 418 | - * 读取编码 | ||
| 419 | - * @param $item | ||
| 420 | - * @return array | ||
| 421 | - * @author:dc | ||
| 422 | - * @time 2022/8/12 10:28 | ||
| 423 | - */ | ||
| 424 | - private static function preg_match_charset($item){ | ||
| 425 | - // 匹配内容 type | ||
| 426 | - preg_match('/charset[ \t]{0,}=[ \t]{0,}"?[ \t0-9a-zA-Z-]{1,}"?/i',$item,$result); | ||
| 427 | - | ||
| 428 | - if(!empty($result[0])){ | ||
| 429 | - $ret['origin'] = trim($result[0]); | ||
| 430 | - // charset | ||
| 431 | - $ret['charset'] = trim(str_replace(['charset','=','"',"'"],'',$ret['origin'])); | ||
| 432 | - | ||
| 433 | - return $ret; | ||
| 434 | - } | ||
| 435 | - return []; | ||
| 436 | - } | ||
| 437 | - | ||
| 438 | - /** | ||
| 439 | - * 解析type | ||
| 440 | - * @param $item | ||
| 441 | - * @return array | ||
| 442 | - * @author:dc | ||
| 443 | - * @time 2022/8/12 10:26 | ||
| 444 | - */ | ||
| 445 | - private function preg_match_type($item){ | ||
| 446 | - // 匹配内容 type | ||
| 447 | - preg_match("/Content-Type:[\w\W].*/i",$item,$result); | ||
| 448 | - | ||
| 449 | - if(!empty($result[0])){ | ||
| 450 | - $ret['origin'] = trim($result[0]); | ||
| 451 | - // type | ||
| 452 | - $type = str_replace(['Content-Type:','"',"'"],'',$ret['origin']); | ||
| 453 | - $type = explode(';',$type); | ||
| 454 | - // 类型 | ||
| 455 | - $ret['type'] = trim($type[0]); | ||
| 456 | - if(isset($type[1]) && $type[1]){ | ||
| 457 | - // 编码 | ||
| 458 | - $r = explode('=',$type[1]); | ||
| 459 | - $ret[strtolower(trim($r[0]))] = trim($r[1]??''); | ||
| 460 | - } | ||
| 461 | - return $ret; | ||
| 462 | - } | ||
| 463 | - return []; | ||
| 464 | - } | ||
| 465 | - | ||
| 466 | - /** | ||
| 467 | - * 匹配tag | ||
| 468 | - * @param $tag | ||
| 469 | - * @param $item | ||
| 470 | - * @return array | ||
| 471 | - * @author:dc | ||
| 472 | - * @time 2022/8/12 10:05 | ||
| 473 | - */ | ||
| 474 | - private function body_match_tag($tag,$item){ | ||
| 475 | - // tag Content-Transfer-Encoding: | ||
| 476 | - preg_match("/".$tag."[\w\W].*/i",$item,$result); | ||
| 477 | - | ||
| 478 | - if(!empty($result[0])){ | ||
| 479 | - $ret['origin'] = trim($result[0]); | ||
| 480 | - // charset | ||
| 481 | - $ret['text'] = trim(str_replace([$tag,'"',"'"],'',$ret['origin'])); | ||
| 482 | - | ||
| 483 | - return $ret; | ||
| 484 | - } | ||
| 485 | - return []; | ||
| 486 | - } | ||
| 487 | - | ||
| 488 | - | ||
| 489 | -} |
app/Mail/lib/MailParse/DeCoding.php
已删除
100644 → 0
| 1 | -<?php | ||
| 2 | - | ||
| 3 | -namespace App\Mail\lib\MailParse; | ||
| 4 | - | ||
| 5 | -/** | ||
| 6 | - * 解码邮件内容 | ||
| 7 | - * @author:dc | ||
| 8 | - * @time 2022/8/12 9:33 | ||
| 9 | - * Class DeCoding | ||
| 10 | - * @package App\Mail\lib\MailParse | ||
| 11 | - */ | ||
| 12 | -class DeCoding { | ||
| 13 | - | ||
| 14 | - | ||
| 15 | - /** | ||
| 16 | - * @param $sText | ||
| 17 | - * @param bool $bEmulate_imap_8bit | ||
| 18 | - * @return string | ||
| 19 | - * @author:dc | ||
| 20 | - * @time 2022/8/12 9:34 | ||
| 21 | - */ | ||
| 22 | - public static function de8bit($sText,$bEmulate_imap_8bit=true) { | ||
| 23 | - // split text into lines | ||
| 24 | - $aLines=explode(chr(13).chr(10),$sText); | ||
| 25 | - | ||
| 26 | - for ($i=0;$i<count($aLines);$i++) { | ||
| 27 | - $sLine =& $aLines[$i]; | ||
| 28 | - if (strlen($sLine)===0) continue; // do nothing, if empty | ||
| 29 | - | ||
| 30 | - $sRegExp = '/[^\x09\x20\x21-\x3C\x3E-\x7E]/e'; | ||
| 31 | - | ||
| 32 | - // imap_8bit encodes x09 everywhere, not only at lineends, | ||
| 33 | - // for EBCDIC safeness encode !"#$@[\]^`{|}~, | ||
| 34 | - // for complete safeness encode every character :) | ||
| 35 | - if ($bEmulate_imap_8bit) | ||
| 36 | - $sRegExp = '/[^\x20\x21-\x3C\x3E-\x7E]/e'; | ||
| 37 | - | ||
| 38 | - $sReplmt = 'sprintf( "=%02X", ord ( "$0" ) ) ;'; | ||
| 39 | - $sLine = preg_replace( $sRegExp, $sReplmt, $sLine ); | ||
| 40 | - | ||
| 41 | - // encode x09,x20 at lineends | ||
| 42 | - { | ||
| 43 | - $iLength = strlen($sLine); | ||
| 44 | - $iLastChar = ord($sLine{$iLength-1}); | ||
| 45 | - | ||
| 46 | - // !!!!!!!! | ||
| 47 | - // imap_8_bit does not encode x20 at the very end of a text, | ||
| 48 | - // here is, where I don't agree with imap_8_bit, | ||
| 49 | - // please correct me, if I'm wrong, | ||
| 50 | - // or comment next line for RFC2045 conformance, if you like | ||
| 51 | - if (!($bEmulate_imap_8bit && ($i==count($aLines)-1))) | ||
| 52 | - | ||
| 53 | - if (($iLastChar==0x09)||($iLastChar==0x20)) { | ||
| 54 | - $sLine{$iLength-1}='='; | ||
| 55 | - $sLine .= ($iLastChar==0x09)?'09':'20'; | ||
| 56 | - } | ||
| 57 | - } // imap_8bit encodes x20 before chr(13), too | ||
| 58 | - // although IMHO not requested by RFC2045, why not do it safer :) | ||
| 59 | - // and why not encode any x20 around chr(10) or chr(13) | ||
| 60 | - if ($bEmulate_imap_8bit) { | ||
| 61 | - $sLine=str_replace(' =0D','=20=0D',$sLine); | ||
| 62 | - //$sLine=str_replace(' =0A','=20=0A',$sLine); | ||
| 63 | - //$sLine=str_replace('=0D ','=0D=20',$sLine); | ||
| 64 | - //$sLine=str_replace('=0A ','=0A=20',$sLine); | ||
| 65 | - } | ||
| 66 | - | ||
| 67 | - // finally split into softlines no longer than 76 chars, | ||
| 68 | - // for even more safeness one could encode x09,x20 | ||
| 69 | - // at the very first character of the line | ||
| 70 | - // and after soft linebreaks, as well, | ||
| 71 | - // but this wouldn't be caught by such an easy RegExp | ||
| 72 | - preg_match_all( '/.{1,73}([^=]{0,2})?/', $sLine, $aMatch ); | ||
| 73 | - $sLine = implode( '=' . chr(13).chr(10), $aMatch[0] ); // add soft crlf's | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - // join lines into text | ||
| 77 | - return implode(chr(13).chr(10),$aLines); | ||
| 78 | - } | ||
| 79 | -} |
-
请 注册 或 登录 后发表评论