diff --git a/.env b/.env index aa97d0f..767e0cf 100644 --- a/.env +++ b/.env @@ -6,6 +6,10 @@ send_email_password = Wu999999tmrwe ;审核建议邮箱 editor_email = publisher@tmrjournals.com +;读取的企业邮箱 +mail_server = imap.qq.com + + [journal] ;官网服务器地址 base_url = http://journalapi.tmrjournals.com/public/index.php \ No newline at end of file diff --git a/application/api/controller/Inbox.php b/application/api/controller/Inbox.php new file mode 100644 index 0000000..0a2a81f --- /dev/null +++ b/application/api/controller/Inbox.php @@ -0,0 +1,202 @@ +inbox_obj = Db::name('inbox'); + $this->inbox_attachment_obj = Db::name('inbox_attachment'); + $this->journal_obj = Db::name('journal'); + + + } + + /** + * 获取收件箱邮件 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getInbox(){ + + $server = new receiveMails(); + $journals=[ + [ 'journal_id'=>2, + 'email'=>'1586428462@qq.com', + 'epassword'=>'rthlprelzzfphigh' + ] + ]; + foreach ($journals as $journal){ + + //1. 连接邮箱 传入参数 + $server->connect($journal); + + //2. 获取邮件数 + $emailNums = $server->getTotalMails(); + + if($emailNums){ + + $insertData=[]; + + foreach ($emailNums as $key=>$value){ + + //2.1 获取邮件头部信息 + $head = $server->getHeaders($value); + + //2.2 处理邮件附件 + $files = $server->getAttach($value); + // 附件 + $head['url'] =[]; + + //2.3 处理正文中的图片 + $imageList=array(); + $section = 2; + if($files){ + foreach($files as $k => $file) + { + + //type=1为附件,0为邮件内容图片 + if($file['type'] == 0) + { + $imageList[$k]=$file['pathname']; + } + + if($file['type'] == 1) + { + array_push($head['url'],$file['url']); + } + + if(($file['type'] == 0 || $file['type'] == 1) && !empty($file['url'])){ + + $section = 1.2 ; + } + + } + } + + $webPath = ROOT_PATH.'public' . DS . 'contentImg' . DS . date('Ymd') .DS ; + // 正文内容 + $head['content'] = $server->getBody($value,$webPath,$imageList,$section); + array_push($insertData,$head); + } + $this->insertData($insertData,$journal); + } + + } + + } + + // 后台展示 收件箱 + public function getInboxList(){ + + $data = $this->request->post(); + // 验证规则 + $rule = new Validate([ + 'pageIndex'=>'require|number', + 'pageSize'=>'require|number' + ]); + if(!$rule->check($data)){ + return json(['code' => 1,'msg'=>$rule->getError()]); + } + + $res['data'] = $this->inbox_obj->field('t_inbox.*,t_inbox_attachment.attachmentUrl') + ->join('t_inbox_attachment','t_inbox_attachment.emailId = t_inbox.id','LEFT') + ->page($data['pageIndex'],$data['pageSize']) + ->select(); + + $res['data']= $this->inbox_obj->field('id,sendEmail,title,content,creatTime,isaAtachment')->page($data['pageIndex'],$data['pageSize'])->select(); + + foreach ($res['data'] as $key=>$value ){ + $res['data'][$key]['attachmentUrl'] = $this->inbox_attachment_obj->where('emailId',$value['id'])->column('attachmentUrl'); + } + + $res['total'] = $this->inbox_obj->count(); + + return jsonSuccess($res); + + } + + /** + * 已经存在邮件去除 + * @param $datas + * @return mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function removeData($datas,$journal){ + + foreach ($datas as $key => $value){ + $res = $this->inbox_obj->where(['journalId'=>$journal['journal_id'],'eid'=>$value['emailId']])->find(); + if($res){ + unset($datas[$key]); + } + } + return $datas; + } + + /** + * 写入数据库 + * @param $datas + */ + private function insertData($datas,$journal){ + + $datas = $this->removeData($datas,$journal); + + Db::startTrans(); + + try{ + foreach ($datas as $key=>$value){ + + $insert = [ + 'journalId'=>$journal['journal_id'], + 'eid'=>$value['emailId'], + 'sendEmail'=>$value['from'], + 'title'=>$value['subject'], + 'content'=>$value['content'], + 'sendTime'=>$value['sendTime'], + 'creatTime'=>time(), + 'isaAtachment'=>empty($value['url'])?0:1, + 'isReply'=>$value['isReply'] + ]; + + $id = $this->inbox_obj->insertGetId($insert); + + if(!empty($value['url']) && is_array($value['url'])){ + $urls=[]; + foreach ($value['url'] as $k => $url){ + + $urls[$k]=[ + 'emailId'=>$id, + 'attachmentUrl'=>$url + ]; + } + $this->inbox_attachment_obj->insertAll($urls); + } + } + + //提交事务 + Db::commit(); + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + } + + } + + + +} + diff --git a/extend/email/receiveMails.php b/extend/email/receiveMails.php new file mode 100644 index 0000000..bdb02d4 --- /dev/null +++ b/extend/email/receiveMails.php @@ -0,0 +1,425 @@ +marubox = imap_open($mailLink,$mailUser,$mailPass); + + if (!$this->marubox) { + + echo "Error: Connecting to mail server"; + exit; + } + + return true; + } + + /** + * 获取邮件数 + * @return false|int + */ + function getTotalMails() + { + if (!$this->marubox){ + return false; + } + $date = date("j F Y"); + $emails = imap_search($this->marubox,'SINCE "'.$date.'"'); + return $emails; + } + + + /** + * 获取邮件头部信息 + * @param $mid + * @return array|false + */ + + function getHeaders($mid) // Get Header info + { + if (!$this->marubox){ + return false; + } + + $mail_header = imap_header($this->marubox, $mid); + $sender = $mail_header->from[0]; + if (strtolower($sender->mailbox) != 'mailer-daemon' && strtolower($sender->mailbox) != 'postmaster') { + + $subject = $this->decode_mime($mail_header->subject); + $mail_details = array( + + 'emailId'=>$mid, + 'from' => strtolower($sender->mailbox) . '@' . $sender->host, + 'subject'=>$subject,//imap_mime_header_decode($mail_header->subject)[0]->text, + 'sendTime' => $mail_header->udate, + 'isReply'=>0 + + ); + + $reply = substr($mail_details['subject'],0,2); + if($reply == 'RE' || $reply == 'Re' ){ + + $mail_details['isReply'] = 1; + + } + + } + return $mail_details; + } + + /** + * 处理邮件附件 + * @param $mid + * @return array|false + */ + function getAttach($mid) // Get Atteced File from Mail + { + if (!$this->marubox){ + return false; + } + $struckture = imap_fetchstructure($this->marubox, $mid); + + $files = array(); + if ($struckture->parts) { + + foreach ($struckture->parts as $key => $value) { + $enc = $struckture->parts[$key]->encoding; + + //取邮件附件 + if ($struckture->parts[$key]->ifdparameters) { + + //命名附件,转码 + $name = $this->decode_mime($struckture->parts[$key]->dparameters[0]->value); + + $extend = explode(".", $name); + + $file['extension'] = $extend[count($extend) - 1]; + + $file['pathname'] = $this->setPathName($mid, $key, $file['extension']); + + $file['title'] = !empty($name) ? htmlspecialchars($name) : str_replace('.' . $file['extension'], '', $name); + + if (@$struckture->parts[$key]->disposition == "attachment") { // 是附件 + $path = ROOT_PATH.'public' . DS . 'downAttachment' . DS . date('Ymd') .DS; + $file['type'] = 1; + } else { // 是正文内容 + $file['type'] = 0; + $path = ROOT_PATH.'public' . DS . 'contentImg' . DS . date('Ymd') .DS; + } + + $message = imap_fetchbody($this->marubox, $mid, $key + 1); + if ($enc == 0){ + $message = imap_8bit($message); + } + if ($enc == 1){ + $message = imap_8bit($message); + } + if ($enc == 2){ + $message = imap_binary($message); + } + if ($enc == 3){//图片 + $message = imap_base64($message); + } + if ($enc == 4){ + $message = quoted_printable_decode($message); + } + if ($enc == 5){ + $message = $message; + } + + + if (!file_exists($path)){ + mkdir($path,0777,true); + } + $fp = fopen($path . $file['pathname'], "w+"); + fwrite($fp, $message); + fclose($fp); + + $file['url']=$path . $file['pathname']; + + $files[] = $file; + } + + // 处理内容中包含图片的部分 + if($key == 0){ + + if (isset($struckture->parts[$key]->parts)) { + + foreach ($struckture->parts[$key]->parts as $keyb => $valueb) { + $enc = $struckture->parts[$key]->parts[$keyb]->encoding; + if ($struckture->parts[$key]->parts[$keyb]->ifdparameters) { + //命名图片 + $name = $this->decode_mime($struckture->parts[0]->parts[$keyb]->dparameters[0]->value); + $extend = explode(".", $name); + $file['extension'] = $extend[count($extend) - 1]; + $file['pathname'] = $this->setPathName($mid,$key, $file['extension'],0); + $file['title'] = !empty($name) ? htmlspecialchars($name) : str_replace('.' . $file['extension'], '', $name); + + // $file['size'] = $struckture->parts[$key]->parts[$keyb]->dparameters[1]->value; + // $file['tmpname'] = $struckture->parts[$key]->dparameters[0]->value; + + $file['type'] = 0; + + + $partnro = ($key + 1) . "." . ($keyb + 1); + + $message = imap_fetchbody($this->marubox, $mid, $partnro); + if ($enc == 0){ + $message = imap_8bit($message); + } + if ($enc == 1){ + $message = imap_8bit($message); + } + if ($enc == 2){ + $message = imap_binary($message); + } + if ($enc == 3){ + $message = imap_base64($message); + } + if ($enc == 4){ + $message = quoted_printable_decode($message); + } + if ($enc == 5){ + $message = $message; + } + + $path = ROOT_PATH.'public' . DS . 'contentImg' . DS . date('Ymd') .DS; + + if (!file_exists($path)){ + mkdir($path,0777,true); + } + + $fp = fopen($path . $file['pathname'], "w+"); + fwrite($fp, $message); + fclose($fp); + + $file['url']=$path . $file['pathname']; + + $files[] = $file; + + } + } + } + } + + } + } + return $files; + } + + /** + * 获取正文 + * @param $mid + * @param $path + * @param $imageList + * @return false|mixed|string|string[]|void + */ + function getBody($mid,$path,$imageList,$section) // Get Message Body + { + + if (!$this->marubox){ + return false; + } + + $struckture = imap_fetchstructure($this->marubox, $mid); + + + if(isset($struckture->parts)) + { + + if($struckture->subtype == 'MIXED' || $struckture->subtype == 'RELATED' ){ + $encoding= $struckture->parts[0]->parts[0]->encoding; + } + if($struckture->subtype == 'ALTERNATIVE'){ + $encoding = $struckture->parts[0]->encoding; + } + + $body = $this->contentDecoder($encoding,$mid,$section); + + //处理图片 + $body = $this->embed_images($body, $path, $imageList); + return $body; + + } + + } + + private function contentDecoder($encoding, $num,$section) //$this->mbox, $num, $section + { + switch ($encoding) { + case 0: + case 1: + $message = imap_8bit(imap_fetchbody($this->marubox, $num, $section)); + break; + case 2: + $message = imap_binary(imap_fetchbody($this->marubox, $num, $section)); + break; + case 3: + $message = iconv('gbk', 'utf-8',imap_base64(imap_fetchbody($this->marubox, $num, $section))); + break; + case 4: + $message = quoted_printable_decode(imap_fetchbody($this->marubox, $num, $section)); + break; + } + + return $message; + } + + + + function embed_images(&$body,&$path,$imageList) + { + + // get all img tags + preg_match_all('//', $body, $matches); + if (!isset($matches[0])) return; + + foreach ($matches[0] as $key=>$img) + { +// halt($path.$imageList[$key]); + // replace image web path with local path + preg_match('/src="(.*?)"/', $img, $m); + + if (!isset($m[1])) continue; + $arr = parse_url($m[1]); + if (!isset($arr['scheme']) || !isset($arr['path']))continue; + +// if (!isset($arr['host']) || !isset($arr['path']))continue; + if ($arr['scheme']!="http") + { + $filename=explode("@", $arr['path']); + $body = str_replace($img, '', $body); +// $body = str_replace($img, '', $body); + } + } + return $body; + } + function get_part($stream, $msg_number, $mime_type, $structure = false, $part_number = false) //Get Part Of Message Internal Private Use + { + + if (!$structure) { + $structure = imap_fetchstructure($stream, $msg_number); + } + if ($structure) { + if ($mime_type == $this->get_mime_type($structure)) { + if (!$part_number) { + $part_number = "1"; + } + $text = imap_fetchbody($stream, $msg_number, $part_number); + + if ($structure->encoding == 3) { + return imap_base64($text); +// if ($structure->parameters[0]->value!="utf-8") +// { +// return imap_base64($text); +// } +// else +// { +// return imap_base64($text); +// } + } else if ($structure->encoding == 4) { + return iconv('gb2312', 'utf8', imap_qprint($text)); + } else { + return iconv('gb2312', 'utf8', $text); + } + } + if ($structure->type == 1) /* multipart */ { + while (list($index, $sub_structure) = $this->new_each($structure->parts)) { + if ($part_number) { + $prefix = $part_number . '.'; + }else{ + $prefix = "1"; + } + $data = $this->get_part($stream, $msg_number, $mime_type, $sub_structure, $prefix . ($index + 1)); + if ($data) { + return $data; + } + } + } + } + return false; + } + function get_mime_type(&$structure) //Get Mime type Internal Private Use + { + $primary_mime_type = array("TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "OTHER"); + + if($structure->subtype && $structure->subtype!="PNG") { + return $primary_mime_type[(int) $structure->type] . '/' . $structure->subtype; + } + return "TEXT/PLAIN"; + } + + function new_each(&$array){ + $res = array(); + $key = key($array); + if($key !== null){ + next($array); + $res[1] = $res['value'] = $array[$key]; + $res[0] = $res['key'] = $key; + }else{ + $res = false; + } + return $res; + } + + + /** + * 标题编码转化 + * @param $str + * @return false|string + */ + function decode_mime($str) + { + $str = imap_mime_header_decode($str); + + return $str[0]->text; + + if ($str[0]->charset != "default") { + + return iconv($str[0]->charset, 'utf8', $str[0]->text); + } else { + return $str[0]->text; + } + } + + + /** + * Set path name of the uploaded file to be saved. + * + * @param int $mid + * @param int $fileID + * @param string $extension + * @access public + * @return string + */ + public function setPathName($mid, $fileID, $extension,$type = 1) + { + if($type == 1){ + return $mid."-".$fileID .'.' . $extension; + }else{ + return $mid."-".$fileID .mt_rand(0, 10000) .'.' . $extension; + } + } + + +} \ No newline at end of file