Pop3Util.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. package jnpf.base.util;
  2. import jnpf.base.model.MailAccount;
  3. import jnpf.base.model.MailFile;
  4. import jnpf.config.ConfigValueUtil;
  5. import jnpf.base.entity.EmailReceiveEntity;
  6. import jnpf.constant.FileTypeConstant;
  7. import jnpf.util.*;
  8. import lombok.Cleanup;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.stereotype.Component;
  12. import jakarta.mail.*;
  13. import jakarta.mail.internet.InternetAddress;
  14. import jakarta.mail.internet.MimeMessage;
  15. import jakarta.mail.internet.MimeMultipart;
  16. import jakarta.mail.internet.MimeUtility;
  17. import java.io.*;
  18. import java.text.SimpleDateFormat;
  19. import java.util.*;
  20. /**
  21. *
  22. * @author JNPF开发平台组
  23. * @version V3.1.0
  24. * @copyright 引迈信息技术有限公司
  25. * @date 2021/3/12 15:31
  26. */
  27. @Slf4j
  28. @Component
  29. public class Pop3Util {
  30. @Autowired
  31. private ConfigValueUtil configValueUtil;
  32. /**
  33. * 邮箱验证
  34. *
  35. * @param mailAccount
  36. * @return
  37. */
  38. public String checkConnected(MailAccount mailAccount) {
  39. try {
  40. Properties props = getProperties(mailAccount.getSsl());
  41. Session session = getSession(props);
  42. @Cleanup Store store = getStore(session, mailAccount);
  43. return "true";
  44. } catch (Exception e) {
  45. log.error(e.getMessage());
  46. return e.getMessage();
  47. }
  48. }
  49. /**
  50. * 接收邮件
  51. */
  52. public Map<String, Object> popMail(MailAccount mailAccount) {
  53. List<EmailReceiveEntity> entity = new ArrayList<>();
  54. Map<String, Object> map = new HashMap<>(16);
  55. try {
  56. Properties props = getProperties(mailAccount.getSsl());
  57. Session session = getSession(props);
  58. @Cleanup Store store = getStore(session, mailAccount);
  59. @Cleanup Folder folder = getFolder(store);
  60. int receiveCount = folder.getMessageCount();
  61. Message[] messages = folder.getMessages();
  62. entity = parseMessage(messages);
  63. map.put("receiveCount", receiveCount);
  64. map.put("mailList", entity);
  65. return map;
  66. } catch (Exception e) {
  67. log.error(e.getMessage());
  68. }
  69. return map;
  70. }
  71. /**
  72. * 删除邮件
  73. *
  74. * @param mailAccount
  75. * @param mid
  76. */
  77. public void deleteMessage(MailAccount mailAccount, String mid) {
  78. try {
  79. Properties props = getProperties(false);
  80. Session session = getSession(props);
  81. @Cleanup Store store = getStore(session, mailAccount);
  82. @Cleanup Folder folder = getFolder(store);
  83. Message[] messages = folder.getMessages();
  84. deleteMessage(messages, mid);
  85. } catch (Exception e) {
  86. log.error(e.getMessage());
  87. }
  88. }
  89. /**
  90. * 获取Properties
  91. *
  92. * @param ssl
  93. */
  94. private Properties getProperties(boolean ssl) {
  95. Properties props = new Properties();
  96. props.setProperty("mail.store.protocol", "pop3");
  97. props.setProperty("mail.pop3.auth", "true");
  98. // 设置连接超时时间
  99. props.put("mail.pop3.connectiontimeout", "35000");
  100. // 设置读取超时时间
  101. props.put("mail.pop3.timeout", "10000");
  102. // 设置写入超时时间
  103. props.put("mail.pop3.writetimeout", "10000");
  104. if (ssl) {
  105. props.put("mail.pop3.ssl.enable", "true");
  106. props.put("mail.pop3.socketFactory.fallback", "false");
  107. props.setProperty( "mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
  108. }
  109. return props;
  110. }
  111. /**
  112. * 获取Session
  113. *
  114. * @param props
  115. */
  116. private Session getSession(Properties props) {
  117. Session session = Session.getInstance(props);
  118. session.setDebug(true);
  119. return session;
  120. }
  121. /**
  122. * 获取Store
  123. */
  124. private Store getStore(Session session, MailAccount mailAccount) throws Exception {
  125. Store store = session.getStore();
  126. store.connect(mailAccount.getPop3Host(), mailAccount.getPop3Port(), mailAccount.getAccount(), mailAccount.getPassword());
  127. return store;
  128. }
  129. /**
  130. * 获取Folder
  131. */
  132. private Folder getFolder(Store store) throws Exception {
  133. Folder folder = store.getFolder("INBOX");
  134. folder.open(Folder.READ_ONLY);
  135. return folder;
  136. }
  137. /**
  138. * 解析邮件
  139. *
  140. * @param messages 要解析的邮件列表
  141. */
  142. private List<EmailReceiveEntity> parseMessage(Message... messages) throws MessagingException {
  143. List<EmailReceiveEntity> receiveEntity = new ArrayList<>();
  144. if (messages == null || messages.length < 1) {
  145. throw new MessagingException("未找到要解析的邮件!");
  146. }
  147. List<MailFile> mailFiles = new ArrayList<>();
  148. String mailfiles = "";
  149. for (int i = 0, count = messages.length; i < count; i++) {
  150. try {
  151. MimeMessage msg = (MimeMessage) messages[i];
  152. mailfiles = null;
  153. boolean isContainerAttachment = isContainAttachment(msg);
  154. if (isContainerAttachment) {
  155. //保存附件
  156. mailFiles = saveAttachment(msg, FileUploadUtils.getLocalBasePath() + FilePathUtil.getFilePath(FileTypeConstant.MAIL));
  157. mailfiles = JsonUtil.getObjectToString(mailFiles);
  158. }else {
  159. mailfiles="[]";
  160. }
  161. StringBuilder content = new StringBuilder(30);
  162. getMailTextContent(msg, content);
  163. EmailReceiveEntity entity = new EmailReceiveEntity();
  164. entity.setId(RandomUtil.uuId());
  165. entity.setMAccount(getReceiveAddress(msg, null));
  166. entity.setMID(getMessageId(msg));
  167. if(getFrom(msg)==null){
  168. entity.setSender("00000");
  169. entity.setSenderName("匿名");
  170. }else {
  171. entity.setSender(getFrom(msg).split("_")[0]);
  172. entity.setSenderName(getFrom(msg).split("_")[1]);
  173. }
  174. entity.setSubject(getSubject(msg));
  175. entity.setBodyText(content.toString());
  176. entity.setAttachment(mailfiles);
  177. entity.setFdate(msg.getSentDate());
  178. entity.setIsRead(0);
  179. receiveEntity.add(entity);
  180. }catch (Exception e){
  181. log.error(e.getMessage());
  182. }
  183. }
  184. return receiveEntity;
  185. }
  186. /**
  187. * 解析邮件
  188. *
  189. * @param messages 要解析的邮件列表
  190. */
  191. private void deleteMessage(Message[] messages, String mid) throws MessagingException {
  192. if (messages == null || messages.length < 1) {
  193. throw new MessagingException("未找到要解析的邮件!");
  194. }
  195. for (int i = 0; i < messages.length; i++) {
  196. Message message = messages[i];
  197. MimeMessage msg = (MimeMessage) messages[i];
  198. if (deleteMessageId(msg, mid)) {
  199. message.setFlag(Flags.Flag.DELETED, true);
  200. }
  201. }
  202. }
  203. /**
  204. * 判断mid是否一致
  205. *
  206. * @param msg
  207. * @param mid
  208. * @return
  209. * @throws MessagingException
  210. */
  211. private boolean deleteMessageId(MimeMessage msg, String mid) throws MessagingException {
  212. String messageId = msg.getMessageID();
  213. messageId = messageId.replace("<", "");
  214. messageId = messageId.replace(">", "");
  215. if (messageId.equals(mid)) {
  216. return true;
  217. }
  218. return false;
  219. }
  220. /**
  221. * 获得邮件主题
  222. *
  223. * @param msg 邮件内容
  224. * @return 解码后的邮件主题
  225. */
  226. private String getSubject(MimeMessage msg) throws UnsupportedEncodingException, MessagingException {
  227. return MimeUtility.decodeText(msg.getSubject());
  228. }
  229. /**
  230. * 获得邮件发件人
  231. *
  232. * @param msg 邮件内容
  233. * @return 姓名 <Email地址>
  234. * @throws MessagingException
  235. * @throws UnsupportedEncodingException
  236. */
  237. private String getFrom(MimeMessage msg) throws MessagingException, UnsupportedEncodingException {
  238. String from = "";
  239. Address[] froms = msg.getFrom();
  240. InternetAddress address = (InternetAddress) froms[0];
  241. String person = address.getPersonal();
  242. if (person != null) {
  243. person = MimeUtility.decodeText(person) + " ";
  244. } else {
  245. person = "";
  246. }
  247. from = person + "_" + address.getAddress();
  248. return from;
  249. }
  250. /**
  251. * 获取邮件的id
  252. */
  253. private String getMessageId(MimeMessage msg) throws MessagingException {
  254. String messageId = msg.getMessageID();
  255. if (messageId == null) {
  256. return "";
  257. }
  258. messageId = messageId.replace("<", "");
  259. messageId = messageId.replace(">", "");
  260. return messageId;
  261. }
  262. /**
  263. * 根据收件人类型,获取邮件收件人、抄送和密送地址。如果收件人类型为空,则获得所有的收件人
  264. * <p>Message.RecipientType.TO 收件人</p>
  265. * <p>Message.RecipientType.CC 抄送</p>
  266. * <p>Message.RecipientType.BCC 密送</p>
  267. *
  268. * @param msg 邮件内容
  269. * @param type 收件人类型
  270. * @return 收件人1 <邮件地址1>, 收件人2 <邮件地址2>, ...
  271. * @throws MessagingException
  272. */
  273. private String getReceiveAddress(MimeMessage msg, Message.RecipientType type) throws MessagingException {
  274. StringBuilder receiveAddress = new StringBuilder();
  275. Address[] addresss = null;
  276. if (type == null) {
  277. addresss = msg.getAllRecipients();
  278. } else {
  279. addresss = msg.getRecipients(type);
  280. }
  281. if (addresss == null || addresss.length < 1) {
  282. return null;
  283. }
  284. for (Address address : addresss) {
  285. InternetAddress internetAddress = (InternetAddress) address;
  286. receiveAddress.append(internetAddress.toUnicodeString()).append(",");
  287. }
  288. //删除最后一个逗号
  289. receiveAddress.deleteCharAt(receiveAddress.length() - 1);
  290. return receiveAddress.toString();
  291. }
  292. /**
  293. * 获得邮件发送时间
  294. *
  295. * @param msg 邮件内容
  296. * @return yyyy年mm月dd日 星期X HH:mm
  297. * @throws MessagingException
  298. */
  299. private String getSentDate(MimeMessage msg, String pattern) throws MessagingException {
  300. Date receivedDate = msg.getSentDate();
  301. if (receivedDate == null) {
  302. return "";
  303. }
  304. if (pattern == null || "".equals(pattern)) {
  305. pattern = "yyyy年MM月dd日 E HH:mm ";
  306. }
  307. return new SimpleDateFormat(pattern).format(receivedDate);
  308. }
  309. /**
  310. * 判断邮件中是否包含附件
  311. *
  312. * @return 邮件中存在附件返回true,不存在返回false
  313. * @throws MessagingException
  314. * @throws IOException
  315. */
  316. private boolean isContainAttachment(Part part) {
  317. boolean flag = false;
  318. try {
  319. if (part.isMimeType("multipart/*")) {
  320. MimeMultipart multipart = (MimeMultipart) part.getContent();
  321. int partCount = multipart.getCount();
  322. for (int i = 0; i < partCount; i++) {
  323. BodyPart bodyPart = multipart.getBodyPart(i);
  324. String disp = bodyPart.getDisposition();
  325. if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
  326. flag = true;
  327. } else if (bodyPart.isMimeType("multipart/*")) {
  328. flag = isContainAttachment(bodyPart);
  329. } else {
  330. String contentType = bodyPart.getContentType();
  331. if (contentType.contains("application")) {
  332. flag = true;
  333. }
  334. if (contentType.contains("name")) {
  335. flag = true;
  336. }
  337. }
  338. if (flag) {
  339. break;
  340. }
  341. }
  342. } else if (part.isMimeType("message/rfc822")) {
  343. flag = isContainAttachment((Part) part.getContent());
  344. }
  345. }catch (Exception e){
  346. log.error(e.getMessage());
  347. }
  348. return flag;
  349. }
  350. /**
  351. * 判断邮件是否已读
  352. *
  353. * @param msg 邮件内容
  354. * @return 如果邮件已读返回true, 否则返回false
  355. * @throws MessagingException
  356. */
  357. private boolean isSeen(MimeMessage msg) throws MessagingException {
  358. return msg.getFlags().contains(Flags.Flag.SEEN);
  359. }
  360. /**
  361. * 判断邮件是否需要阅读回执
  362. *
  363. * @param msg 邮件内容
  364. * @return 需要回执返回true, 否则返回false
  365. * @throws MessagingException
  366. */
  367. private boolean isReplySign(MimeMessage msg) throws MessagingException {
  368. boolean replySign = false;
  369. String[] headers = msg.getHeader("Disposition-Notification-To");
  370. if (headers != null) {
  371. replySign = true;
  372. }
  373. return replySign;
  374. }
  375. /**
  376. * 获得邮件的优先级
  377. *
  378. * @param msg 邮件内容
  379. * @return 1(High):紧急 3:普通(Normal) 5:低(Low)
  380. * @throws MessagingException
  381. */
  382. private String getPriority(MimeMessage msg) throws MessagingException {
  383. String priority = "普通";
  384. String[] headers = msg.getHeader("X-Priority");
  385. if (headers != null) {
  386. String headerPriority = headers[0];
  387. if (headerPriority.indexOf("1") != -1 || headerPriority.indexOf("High") != -1) {
  388. priority = "紧急";
  389. } else if (headerPriority.indexOf("5") != -1 || headerPriority.indexOf("Low") != -1) {
  390. priority = "低";
  391. } else {
  392. priority = "普通";
  393. }
  394. }
  395. return priority;
  396. }
  397. /**
  398. * 获得邮件文本内容
  399. *
  400. * @param part 邮件体
  401. * @param content 存储邮件文本内容的字符串
  402. * @throws MessagingException
  403. * @throws IOException
  404. */
  405. private void getMailTextContent(Part part, StringBuilder content) {
  406. try {
  407. boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;
  408. if (part.isMimeType("text/html") && !isContainTextAttach) {
  409. content.append(part.getContent().toString());
  410. } else if (part.isMimeType("message/rfc822")) {
  411. getMailTextContent((Part) part.getContent(), content);
  412. } else if (part.isMimeType("multipart/*")) {
  413. Multipart multipart = (Multipart) part.getContent();
  414. int partCount = multipart.getCount();
  415. for (int i = 0; i < partCount; i++) {
  416. BodyPart bodyPart = multipart.getBodyPart(i);
  417. getMailTextContent(bodyPart, content);
  418. }
  419. }
  420. }catch (Exception e){
  421. e.printStackTrace();
  422. }
  423. }
  424. /**
  425. * 保存附件
  426. *
  427. * @param part 邮件中多个组合体中的其中一个组合体
  428. * @param destDir 附件保存目录
  429. * @throws UnsupportedEncodingException
  430. * @throws MessagingException
  431. * @throws FileNotFoundException
  432. * @throws IOException
  433. */
  434. private List<MailFile> saveAttachment(Part part, String destDir) {
  435. List<MailFile> mailFiles = new ArrayList<>();
  436. try {
  437. if (part.isMimeType("multipart/*")) {
  438. Multipart multipart = (Multipart) part.getContent();
  439. int partCount = multipart.getCount();
  440. for (int i = 0; i < partCount; i++) {
  441. BodyPart bodyPart = multipart.getBodyPart(i);
  442. String disp = bodyPart.getDisposition();
  443. if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
  444. MailFile mailFile = new MailFile();
  445. @Cleanup InputStream is = bodyPart.getInputStream();
  446. //解决附件中文乱码
  447. String fileName=MimeUtility.decodeText(bodyPart.getFileName());
  448. String fileType=fileName.split("\\.")[1];
  449. mailFile.setFileId(RandomUtil.uuId()+"."+fileType);
  450. saveFile(is, destDir, decodeText(mailFile.getFileId()));
  451. File file = new File(destDir + decodeText(fileName));
  452. mailFile.setFileName(fileName);
  453. mailFile.setFileSize(String.valueOf(file.length()));
  454. mailFile.setFileState("-1");
  455. mailFile.setFileTime(DateUtil.getNow());
  456. mailFiles.add(mailFile);
  457. } else if (bodyPart.isMimeType("multipart/*")) {
  458. saveAttachment(bodyPart, destDir);
  459. } else {
  460. String contentType = bodyPart.getContentType();
  461. if (contentType.indexOf("name") != -1 || contentType.indexOf("application") != -1) {
  462. saveFile(bodyPart.getInputStream(), destDir, decodeText(bodyPart.getFileName()));
  463. File file = new File(destDir + decodeText(bodyPart.getFileName()));
  464. MailFile mailFile = new MailFile();
  465. mailFile.setFileId(RandomUtil.uuId());
  466. mailFile.setFileName(file.getName());
  467. mailFile.setFileSize(String.valueOf(file.length()));
  468. mailFile.setFileState("-1");
  469. mailFile.setFileTime(DateUtil.getNow());
  470. mailFiles.add(mailFile);
  471. }
  472. }
  473. }
  474. } else if (part.isMimeType("message/rfc822")) {
  475. saveAttachment((Part) part.getContent(), destDir);
  476. }
  477. }catch (Exception e){
  478. e.printStackTrace();
  479. }
  480. return mailFiles;
  481. }
  482. /**
  483. * 读取输入流中的数据保存至指定目录
  484. *
  485. * @param is 输入流
  486. * @param fileName 文件名
  487. * @param destDir 文件存储目录
  488. * @throws FileNotFoundException
  489. * @throws IOException
  490. */
  491. private void saveFile(InputStream is, String destDir, String fileName)
  492. throws FileNotFoundException, IOException {
  493. @Cleanup BufferedInputStream bis = new BufferedInputStream(is);
  494. @Cleanup FileOutputStream fileOutputStream = new FileOutputStream(new File(destDir + fileName));
  495. @Cleanup BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream);
  496. int len = -1;
  497. while ((len = bis.read()) != -1) {
  498. bos.write(len);
  499. bos.flush();
  500. }
  501. }
  502. /**
  503. * 文本解码
  504. *
  505. * @param encodeText 解码MimeUtility.encodeText(String text)方法编码后的文本
  506. * @return 解码后的文本
  507. * @throws UnsupportedEncodingException
  508. */
  509. private String decodeText(String encodeText) throws UnsupportedEncodingException {
  510. if (encodeText == null || "".equals(encodeText)) {
  511. return "";
  512. } else {
  513. return MimeUtility.decodeText(encodeText);
  514. }
  515. }
  516. }