From ea50fabc95fe0d2a6ac517f3c61bfba6c220e018 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Wed, 24 Mar 2021 20:22:07 +0300 Subject: Service: handle invalid from headers in emails --- src/main/java/com/juick/www/api/Service.java | 157 +++++++++++++++------------ 1 file changed, 86 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/www/api/Service.java b/src/main/java/com/juick/www/api/Service.java index c3ce1242..3844d003 100644 --- a/src/main/java/com/juick/www/api/Service.java +++ b/src/main/java/com/juick/www/api/Service.java @@ -51,6 +51,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import javax.inject.Inject; import javax.mail.Session; +import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.io.FileOutputStream; @@ -98,96 +99,108 @@ public class Service { return; } } - if (msg.getFrom() == null || msg.getFrom().length == 0) { - logger.info("Missing from/sender headers"); - return; - } - String from = ((InternetAddress) msg.getFrom()[0]).getAddress(); + try { + var messageAddresses = msg.getFrom(); + if (messageAddresses == null || messageAddresses.length == 0) { + logger.info("Missing from/sender headers"); + return; + } + String from = ((InternetAddress) messageAddresses[0]).getAddress(); - User visitor = userService.getUserByEmail(from); - if (!visitor.isAnonymous()) { - MimeMessageParser parser = new MimeMessageParser(msg); - parser.parse(); - final String[] body = {parser.getPlainContent()}; - if (body[0] == null) { - parser.getAttachmentList().stream() - .filter(a -> a.getContentType().equals("text/plain")).findFirst() - .ifPresent(a -> { + User visitor = userService.getUserByEmail(from); + if (!visitor.isAnonymous()) { + MimeMessageParser parser = new MimeMessageParser(msg); + parser.parse(); + final String[] body = { parser.getPlainContent() }; + if (body[0] == null) { + parser.getAttachmentList().stream().filter(a -> a.getContentType().equals("text/plain")) + .findFirst().ifPresent(a -> { + try { + body[0] = IOUtils.toString(a.getInputStream(), StandardCharsets.UTF_8); + logger.info("got text: {}", body[0]); + } catch (IOException e) { + logger.info("attachment error", e); + } + }); + } + final String[] attachmentFName = new String[1]; + parser.getAttachmentList().stream().filter( + a -> a.getContentType().equals("image/jpeg") || a.getContentType().equals("image/png")) + .findFirst().ifPresent(a -> { + logger.info("got attachment: {}", a.getContentType()); + String attachmentType; + if (a.getContentType().equals("image/jpeg")) { + attachmentType = "jpg"; + } else { + attachmentType = "png"; + } + attachmentFName[0] = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + + attachmentType; try { - body[0] = IOUtils.toString(a.getInputStream(), StandardCharsets.UTF_8); - logger.info("got text: {}", body[0]); + logger.info("got inputstream: {}", a.getInputStream()); + FileOutputStream fos = new FileOutputStream( + Paths.get(tmpDir, attachmentFName[0]).toString()); + IOUtils.copy(a.getInputStream(), fos); + fos.close(); } catch (IOException e) { logger.info("attachment error", e); } }); - } - final String[] attachmentFName = new String[1]; - parser.getAttachmentList().stream().filter(a -> - a.getContentType().equals("image/jpeg") || a.getContentType().equals("image/png")) - .findFirst().ifPresent(a -> { - logger.info("got attachment: {}", a.getContentType()); - String attachmentType; - if (a.getContentType().equals("image/jpeg")) { - attachmentType = "jpg"; - } else { - attachmentType = "png"; + String[] inReplyToHeaders = msg.getHeader("In-Reply-To"); + if (inReplyToHeaders != null && inReplyToHeaders.length > 0) { + int mid, rid; + var originalMessage = messagesService.findMessageByProperty("messageId", inReplyToHeaders[0]); + if (originalMessage.isPresent()) { + mid = originalMessage.get().getLeft(); + rid = originalMessage.get().getRight(); + } else { + Scanner inReplyToScanner = new Scanner(inReplyToHeaders[0].trim()) + .useDelimiter(EmailManager.MSGID_PATTERN); + mid = Integer.parseInt(inReplyToScanner.next()); + rid = Integer.parseInt(inReplyToScanner.next()); + } + logger.info("Message is reply to #{}/{}", mid, rid); + body[0] = rid > 0 ? String.format("#%d/%d %s", mid, rid, body[0]) + : String.format("#%d %s", mid, body[0]); } - attachmentFName[0] = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType; - try { - logger.info("got inputstream: {}", a.getInputStream()); - FileOutputStream fos = new FileOutputStream(Paths.get(tmpDir, attachmentFName[0]).toString()); - IOUtils.copy(a.getInputStream(), fos); - fos.close(); - } catch (IOException e) { - logger.info("attachment error", e); + URI attachmentUri = StringUtils.isNotEmpty(attachmentFName[0]) + ? URI.create(String.format("juick://%s", attachmentFName[0])) + : URI.create(StringUtils.EMPTY); + CommandResult result = commandsManager.processCommand(visitor, body[0], attachmentUri); + if (result.getNewMessage().isPresent()) { + String[] messageIds = msg.getHeader("Message-Id"); + if (messageIds.length == 1) { + Message message = result.getNewMessage().get(); + messagesService.setMessageProperty(message.getMid(), message.getRid(), "messageId", + messageIds[0]); + } else { + logger.warn("Wrong number of Message-Id headers"); + } } - }); - String[] inReplyToHeaders = msg.getHeader("In-Reply-To"); - if (inReplyToHeaders != null && inReplyToHeaders.length > 0) { - int mid, rid; - var originalMessage = messagesService.findMessageByProperty("messageId", inReplyToHeaders[0]); - if (originalMessage.isPresent()) { - mid = originalMessage.get().getLeft(); - rid = originalMessage.get().getRight(); - } else { - Scanner inReplyToScanner = new Scanner(inReplyToHeaders[0].trim()).useDelimiter(EmailManager.MSGID_PATTERN); - mid = Integer.parseInt(inReplyToScanner.next()); - rid = Integer.parseInt(inReplyToScanner.next()); + } else { + if (!Arrays.asList(ignoredEmails).contains(from)) { + String verificationCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase(); + emailService.addVerificationCode(null, from, verificationCode); + applicationEventPublisher + .publishEvent(new AccountVerificationEvent(this, from, verificationCode)); } - logger.info("Message is reply to #{}/{}", mid, rid); - body[0] = rid > 0 ? String.format("#%d/%d %s", mid, rid, body[0]) - : String.format("#%d %s", mid, body[0]); - } - URI attachmentUri = StringUtils.isNotEmpty(attachmentFName[0]) ? URI.create(String.format("juick://%s", attachmentFName[0])) - : URI.create(StringUtils.EMPTY); - CommandResult result = commandsManager.processCommand(visitor, body[0], attachmentUri); - if (result.getNewMessage().isPresent()) { - String[] messageIds = msg.getHeader("Message-Id"); - if (messageIds.length == 1) { - Message message = result.getNewMessage().get(); - messagesService.setMessageProperty(message.getMid(), message.getRid(), "messageId", messageIds[0]); - } else { - logger.warn("Wrong number of Message-Id headers"); - } - } - } else { - if (!Arrays.asList(ignoredEmails).contains(from)) { - String verificationCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase(); - emailService.addVerificationCode(null, from, verificationCode); - applicationEventPublisher.publishEvent(new AccountVerificationEvent(this, from, verificationCode)); } + } catch (AddressException e) { + logger.info("Got email with invalid from address"); } } else { throw new HttpForbiddenException(); } } + @Hidden @PostMapping("/api/mail/unsubscribe") @ResponseStatus(value = HttpStatus.OK) public void processMailUnsubscribe(@Visitor User current, InputStream data) throws Exception { if (current.getName().equals(serviceUser)) { MimeMessage msg = new MimeMessage(session, data); - String from = msg.getFrom() == null || msg.getFrom().length > 1 ? ((InternetAddress) msg.getSender()).getAddress() + String from = msg.getFrom() == null || msg.getFrom().length > 1 + ? ((InternetAddress) msg.getSender()).getAddress() : ((InternetAddress) msg.getFrom()[0]).getAddress(); User visitor = userService.getUserByEmail(from); @@ -200,11 +213,12 @@ public class Service { throw new HttpForbiddenException(); } } + private void endSession(SseEmitter emitter) { - serverManager.getSessions().stream() - .filter(s -> s.getEmitter().equals(emitter)) + serverManager.getSessions().stream().filter(s -> s.getEmitter().equals(emitter)) .forEach(session -> serverManager.getSessions().remove(session)); } + @GetMapping("/api/events") public SseEmitter handle(@Visitor User visitor) { logger.info("{} connected", visitor.getName()); @@ -219,6 +233,7 @@ public class Service { return emitter; } + @ExceptionHandler(AsyncRequestTimeoutException.class) public void eventErrorHandler(Exception ex) { logger.debug("SSE timeout", ex); -- cgit v1.2.3