diff --git a/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/BouncedMessageParser.java b/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/BouncedMessageParser.java index ae29b9f15b..bfe67a9df5 100644 --- a/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/BouncedMessageParser.java +++ b/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/BouncedMessageParser.java @@ -8,6 +8,7 @@ import jakarta.mail.internet.InternetHeaders; import jakarta.mail.internet.MimeMessage; import net.ripe.db.whois.api.mail.EmailMessageInfo; +import net.ripe.db.whois.api.mail.exception.MailParsingException; import org.apache.commons.compress.utils.Lists; import org.eclipse.angus.mail.dsn.DeliveryStatus; import org.eclipse.angus.mail.dsn.MultipartReport; @@ -43,8 +44,12 @@ public BouncedMessageParser(@Value("${mail.smtp.from:}") final String smtpFrom) } @Nullable - public EmailMessageInfo parse(final MimeMessage message) throws MessagingException, IOException { - if (enabled && isMultipartReport(message)) { + public EmailMessageInfo parse(final MimeMessage message) throws MessagingException, MailParsingException { + if (!enabled || !isMultipartReport(message) ){ + return null; + } + + try { final MultipartReport multipartReport = multipartReport(message.getContent()); if (isReportDeliveryStatus(multipartReport)) { final DeliveryStatus deliveryStatus = deliveryStatus(message); @@ -55,8 +60,10 @@ public EmailMessageInfo parse(final MimeMessage message) throws MessagingExcepti return new EmailMessageInfo(recipient, messageId); } } + } catch (MessagingException | IOException | IllegalStateException ex){ + throw new MailParsingException("Error parsing multipart report"); } - return null; + throw new MailParsingException("MultiPart message without failure report"); } private boolean isMultipartReport(final MimeMessage message) throws MessagingException { diff --git a/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/MessageDequeue.java b/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/MessageDequeue.java index fd21448144..681dde41a9 100644 --- a/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/MessageDequeue.java +++ b/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/MessageDequeue.java @@ -7,6 +7,7 @@ import net.ripe.db.whois.api.mail.EmailMessageInfo; import net.ripe.db.whois.api.mail.MailMessage; import net.ripe.db.whois.api.mail.dao.MailMessageDao; +import net.ripe.db.whois.api.mail.exception.MailParsingException; import net.ripe.db.whois.common.ApplicationService; import net.ripe.db.whois.common.DateTimeProvider; import net.ripe.db.whois.common.MaintenanceMode; @@ -205,7 +206,15 @@ private void handleMessage(final String messageId) { mailMessageDao.deleteMessage(messageId); return; } + } catch (MailParsingException e){ + LOGGER.info("Error detecting bounce detection or unsubscribing for messageId {}", messageId, e); + mailMessageDao.deleteMessage(messageId); + return; + } catch (MessagingException ex) { + LOGGER.error("There was some kind of error processing the message {}", messageId, ex); + } + try { loggerContext.init(getMessageIdLocalPart(message)); try { handleMessageInContext(messageId, message); diff --git a/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/MessageService.java b/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/MessageService.java index a1720be72d..d52f1bbae2 100644 --- a/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/MessageService.java +++ b/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/MessageService.java @@ -4,6 +4,7 @@ import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import net.ripe.db.whois.api.mail.EmailMessageInfo; +import net.ripe.db.whois.api.mail.exception.MailParsingException; import net.ripe.db.whois.common.dao.EmailStatusDao; import net.ripe.db.whois.common.dao.OutgoingMessageDao; import net.ripe.db.whois.common.mail.EmailStatus; @@ -14,7 +15,6 @@ import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; -import java.io.IOException; import java.util.List; @Service @@ -39,10 +39,10 @@ public MessageService( this.emailStatusDao = emailStatusDao; } - public EmailMessageInfo getBouncedMessageInfo(final MimeMessage message) throws MessagingException, IOException { + public EmailMessageInfo getBouncedMessageInfo(final MimeMessage message) throws MessagingException, MailParsingException { return bouncedMessageParser.parse(message); } - public EmailMessageInfo getUnsubscribedMessageInfo(final MimeMessage message) throws MessagingException { + public EmailMessageInfo getUnsubscribedMessageInfo(final MimeMessage message) throws MessagingException, MailParsingException { return unsubscribeMessageParser.parse(message); } diff --git a/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/UnsubscribeMessageParser.java b/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/UnsubscribeMessageParser.java index 7ad7b06113..34b5bc0a3a 100644 --- a/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/UnsubscribeMessageParser.java +++ b/whois-api/src/main/java/net/ripe/db/whois/api/mail/dequeue/UnsubscribeMessageParser.java @@ -7,6 +7,7 @@ import jakarta.mail.internet.InternetAddress; import jakarta.mail.internet.MimeMessage; import net.ripe.db.whois.api.mail.EmailMessageInfo; +import net.ripe.db.whois.api.mail.exception.MailParsingException; import org.elasticsearch.common.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -32,14 +33,21 @@ public UnsubscribeMessageParser(@Value("${mail.smtp.from:}") final String smtpFr } @Nullable - public EmailMessageInfo parse(final MimeMessage message) throws MessagingException { - if (enabled && isTextPlain(message)) { + public EmailMessageInfo parse(final MimeMessage message) throws MessagingException, MailParsingException { + if (!enabled || !isTextPlain(message)){ + return null; + } + + try { final String messageId = getMessageIdFromSubject(message); final String from = getFrom(message); if ((messageId != null) && (from != null)) { return new EmailMessageInfo(List.of(from), messageId); } + } catch (MessagingException | IllegalStateException ex){ + throw new MailParsingException("Error parsing text plain unsubscribe message"); } + return null; } diff --git a/whois-api/src/main/java/net/ripe/db/whois/api/mail/exception/MailParsingException.java b/whois-api/src/main/java/net/ripe/db/whois/api/mail/exception/MailParsingException.java new file mode 100644 index 0000000000..acf2bfafab --- /dev/null +++ b/whois-api/src/main/java/net/ripe/db/whois/api/mail/exception/MailParsingException.java @@ -0,0 +1,16 @@ +package net.ripe.db.whois.api.mail.exception; + +import org.eclipse.angus.mail.iap.ParsingException; + +import java.io.Serial; + +public class MailParsingException extends ParsingException { + + @Serial + private static final long serialVersionUID = -1263525433251664387L; + + public MailParsingException(final String message) { + super(message); + } + +} diff --git a/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/BouncedMessageParserTest.java b/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/BouncedMessageParserTest.java index 7cacd4edcd..7b46eac94d 100644 --- a/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/BouncedMessageParserTest.java +++ b/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/BouncedMessageParserTest.java @@ -2,6 +2,7 @@ import net.ripe.db.whois.api.MimeMessageProvider; import net.ripe.db.whois.api.mail.EmailMessageInfo; +import net.ripe.db.whois.api.mail.exception.MailParsingException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -67,16 +68,19 @@ public void parse_permanent_delivery_failure_message_rfc822_headers_real() throw @Test public void parse_permanent_delivery_failure_without_message_id() { - final IllegalStateException e = assertThrows(IllegalStateException.class, () -> { + final MailParsingException e = assertThrows(MailParsingException.class, () -> { subject.parse(MimeMessageProvider.getUpdateMessage("permanentFailureWithoutMessageId.mail")); }); - assertThat(e.getMessage(), is("No Message-Id header")); + assertThat(e.getMessage(), is("Error parsing multipart report")); } @Test - public void parse_delayed_delivery() throws Exception { - assertThat(subject.parse(MimeMessageProvider.getUpdateMessage("messageDelayedRfc822Headers.mail")), is(nullValue())); + public void parse_delayed_delivery() { + final MailParsingException exception = assertThrows(MailParsingException.class, () -> { + subject.parse(MimeMessageProvider.getUpdateMessage("messageDelayedRfc822Headers.mail")); + }); + assertThat(exception.getMessage(), is("MultiPart message without failure report")); } @Test diff --git a/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/MailBounceTestIntegration.java b/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/MailBounceTestIntegration.java index cd56f05f8e..acdf0b8c29 100644 --- a/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/MailBounceTestIntegration.java +++ b/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/MailBounceTestIntegration.java @@ -7,7 +7,6 @@ import org.awaitility.Awaitility; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -231,7 +230,6 @@ public void full_email_address_is_normalised() throws MessagingException, IOExce assertThat(mailSenderStub.anyMoreMessages(), is(false)); } - @Disabled("TODO: [ES] message gets stuck") @Test public void invalid_email_do_not_causes_address_to_be_marked_as_undeliverable() { final MimeMessage message = MimeMessageProvider.getUpdateMessage("permanentFailureWithoutMessageId.mail"); diff --git a/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/MessageServiceTestIntegration.java b/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/MessageServiceTestIntegration.java index 4256afe011..db751e2a42 100644 --- a/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/MessageServiceTestIntegration.java +++ b/whois-api/src/test/java/net/ripe/db/whois/api/mail/dequeue/MessageServiceTestIntegration.java @@ -4,14 +4,12 @@ import jakarta.mail.internet.MimeMessage; import net.ripe.db.whois.api.MimeMessageProvider; import net.ripe.db.whois.api.mail.EmailMessageInfo; +import net.ripe.db.whois.api.mail.exception.MailParsingException; import net.ripe.db.whois.update.mail.MailSenderStub; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; - -import java.io.IOException; - import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; @@ -44,7 +42,7 @@ public void setup() { } @Test - public void testNoBouncedEmailFromCorrectEmail() throws MessagingException, IOException { + public void testNoBouncedEmailFromCorrectEmail() throws MessagingException, MailParsingException { final String role = "role: dummy role\n" + "address: Singel 258\n" + @@ -67,7 +65,7 @@ public void testNoBouncedEmailFromCorrectEmail() throws MessagingException, IOEx } @Test - public void testBouncedEmailFromIncorrectEmail() throws MessagingException, IOException { + public void testBouncedEmailFromIncorrectEmail() throws MessagingException, MailParsingException { insertOutgoingMessageId("XXXXXXXX-5AE3-4C58-8E3F-860327BA955D@ripe.net", "enduser@ripe.net"); final MimeMessage message = MimeMessageProvider.getUpdateMessage("permanentFailureMessageRfc822.mail"); @@ -79,7 +77,7 @@ public void testBouncedEmailFromIncorrectEmail() throws MessagingException, IOEx } @Test - public void testBouncedEmailFromCorrectEmail() throws MessagingException, IOException { + public void testBouncedEmailFromCorrectEmail() throws MessagingException, MailParsingException { insertOutgoingMessageId("XXXXXXXX-5AE3-4C58-8E3F-860327BA955D@ripe.net", BOUNCED_MAIL_RECIPIENT); final MimeMessage message = MimeMessageProvider.getUpdateMessage("permanentFailureMessageRfc822.mail"); @@ -91,7 +89,7 @@ public void testBouncedEmailFromCorrectEmail() throws MessagingException, IOExce } @Test - public void testBouncedFailureRecipientFromCorrectEmail() throws MessagingException, IOException { + public void testBouncedFailureRecipientFromCorrectEmail() throws MessagingException, MailParsingException { insertOutgoingMessageId("XXXXXXXX-5AE3-4C58-8E3F-860327BA955D@ripe.net", BOUNCED_MAIL_RECIPIENT); insertOutgoingMessageId("XXXXXXXX-5AE3-4C58-8E3F-860327BA955D@ripe.net", ANOTHER_BOUNCED_MAIL_RECIPIENT); @@ -106,7 +104,7 @@ public void testBouncedFailureRecipientFromCorrectEmail() throws MessagingExcept } @Test - public void testBouncedMultipleFailurePerRecipientFromCorrectEmail() throws MessagingException, IOException { + public void testBouncedMultipleFailurePerRecipientFromCorrectEmail() throws MessagingException, MailParsingException { insertOutgoingMessageId("XXXXXXXX-8553-47AB-A79B-A9896A2DFBAC@ripe.net", BOUNCED_MAIL_RECIPIENT); insertOutgoingMessageId("XXXXXXXX-8553-47AB-A79B-A9896A2DFBAC@ripe.net", ANOTHER_BOUNCED_MAIL_RECIPIENT); @@ -121,7 +119,7 @@ public void testBouncedMultipleFailurePerRecipientFromCorrectEmail() throws Mess } @Test - public void testUnsubscribedEmailFromCorrectEmail() throws MessagingException, IOException { + public void testUnsubscribedEmailFromCorrectEmail() throws MessagingException, MailParsingException { insertOutgoingMessageId("8b8ed6c0-f9cc-4a5f-afbb-fde079b94f44@ripe.net", UNSUBSCRIBED_MAIL_RECIPIENT); final MimeMessage message = MimeMessageProvider.getUpdateMessage("unsubscribeAppleMail.mail"); @@ -136,7 +134,7 @@ public void testUnsubscribedEmailFromCorrectEmail() throws MessagingException, I } @Test - public void testUnsubscribedEmailFromCorrectEmailIsCaseInsensitive() throws MessagingException, IOException { + public void testUnsubscribedEmailFromCorrectEmailIsCaseInsensitive() throws MessagingException, MailParsingException { insertOutgoingMessageId("8b8ed6c0-f9cc-4a5f-afbb-fde079b94f44@ripe.net", "EnDuseR@ripe.net"); final MimeMessage message = MimeMessageProvider.getUpdateMessage("unsubscribeAppleMail.mail");