diff options
Diffstat (limited to 'src/main/java/com/juick/EmailManager.java')
-rw-r--r-- | src/main/java/com/juick/EmailManager.java | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/main/java/com/juick/EmailManager.java b/src/main/java/com/juick/EmailManager.java new file mode 100644 index 00000000..e5b527f4 --- /dev/null +++ b/src/main/java/com/juick/EmailManager.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2008-2020, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.juick; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.juick.model.Message; +import com.juick.model.User; +import com.juick.www.api.SystemActivity; +import com.juick.util.HttpBadRequestException; +import com.juick.www.WebApp; +import com.juick.service.EmailService; +import com.juick.service.MessagesService; +import com.juick.service.UserService; +import com.juick.service.component.*; +import com.juick.util.MessageUtils; +import com.mitchellbosecke.pebble.PebbleEngine; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; +import java.util.*; + +import static com.juick.util.formatters.PlainTextFormatter.formatPost; +import static com.juick.util.formatters.PlainTextFormatter.formatUrl; + +public class EmailManager implements NotificationListener { + + public static final String MSGID_PATTERN = "\\.|@|<"; + + private static final Logger logger = LoggerFactory.getLogger(EmailManager.class); + @Inject + private EmailService emailService; + @Inject + private MessagesService messagesService; + @Inject + private UserService userService; + @Inject + private PebbleEngine pebbleEngine; + @Inject + private ObjectMapper jsonMapper; + @Inject + private WebApp webApp; + @Value("${service_email:}") + private String serviceEmail; + + @Override + public void processSystemEvent(@Nonnull SystemEvent systemEvent) { + var activity = systemEvent.getActivity(); + var msg = activity.getMessage(); + var subscribers = activity.getTo(); + if (activity.getType().equals(SystemActivity.ActivityType.message)) { + processMessage(msg, subscribers); + } + try { + var eventHeader = Collections.singletonMap("X-Event-Version", "1.0"); + sendEmail("noreply@juick.com", serviceEmail, "New system event", + jsonMapper.writeValueAsString(systemEvent.getActivity()), null, eventHeader); + } catch (JsonProcessingException e) { + logger.warn("JSON exception", e); + } + } + private void processMessage(Message msg, List<User> subscribedUsers) { + if (msg.isService()) { + return; + } + if (MessageUtils.isPM(msg)) { + String subject = String.format("Private message from %s", msg.getUser().getName()); + emailService.getEmails(msg.getTo().getUid(), true).forEach(email -> emailNotify(email, subject, msg)); + } else if (MessageUtils.isReply(msg)) { + Message originalMessage = messagesService.getMessage(msg.getMid()).orElseThrow(IllegalStateException::new); + String subject = String.format("New reply to %s", originalMessage.getUser().getName()); + subscribedUsers.stream() + .flatMap(user -> emailService.getEmails(user.getUid(), true).stream()) + .forEach(email -> emailNotify(email, subject, msg)); + } else { + String subject = String.format("New message from %s", msg.getUser().getName()); + subscribedUsers + .forEach(user -> emailService.getEmails(user.getUid(), true) + .forEach(email -> emailNotify(email, subject, msg))); + } + } + + @Override + public void processPingEvent(PingEvent pingEvent) { + + } + + @Async + @EventListener + public void processMailVerificationEvent(MailVerificationEvent mailVerificationEvent) { + if (!sendEmail("noreply@juick.com", mailVerificationEvent.getEmail(), "Juick authorization link", + String.format("Follow link to attach this email to Juick account:\n" + + "http://juick.com/settings?page=auth-email&code=%s\n\n" + + "If you don't know, what this mean - just ignore this mail.\n", mailVerificationEvent.getCode()), + StringUtils.EMPTY, Collections.emptyMap())) { + throw new HttpBadRequestException(); + } + } + @Async + @EventListener + public void processAccountVerificationEvent(AccountVerificationEvent accountVerificationEvent) { + String signupUrl = String.format("Follow this link to create Juick account: https://juick.com/signup?type=email&hash=%s", accountVerificationEvent.getCode()); + sendEmail("noreply@juick.com", accountVerificationEvent.getEmail(), "Juick registration", signupUrl, StringUtils.EMPTY, Collections.emptyMap()); + } + + private void emailNotify(String email, String subject, Message msg) { + Map<String, String> headers = new HashMap<>(); + if (!MessageUtils.isPM(msg)) { + headers.put("Message-ID", String.format("<%d.%d@juick.com>", msg.getMid(), msg.getRid())); + } + if (MessageUtils.isReply(msg)) { + if (msg.getReplyto() > 0) { + Message replyto = messagesService.getReply(msg.getMid(), msg.getReplyto()); + headers.put("In-Reply-To", String.format("<%d.%d@juick.com>", replyto.getMid(), replyto.getRid())); + } else { + Message original = messagesService.getMessage(msg.getMid()).orElseThrow(IllegalStateException::new); + headers.put("In-Reply-To", String.format("<%d.%d@juick.com>", original.getMid(), original.getRid())); + } + } + String plainText = webApp.renderPlaintext(formatPost(msg), formatUrl(msg)).orElseThrow(IllegalStateException::new); + String hash = userService.getHashByUID(userService.getUserByEmail(email).getUid()); + String htmlText = webApp.renderHtml(MessageUtils.formatHtml(msg), formatUrl(msg), msg, hash).orElseThrow(IllegalStateException::new); + sendEmail(StringUtils.EMPTY, email, subject, plainText, htmlText, headers); + } + public boolean sendEmail(String from, String to, String subject, String textPart, String htmlPart, Map<String, String> messageHeaders) { + Properties prop = System.getProperties(); + prop.put("mail.smtp.starttls.enable", "true"); + Session session = Session.getDefaultInstance(prop); + try { + Transport transport = session.getTransport("smtp"); + MimeMessage message = new MimeMessage(session) { + protected void updateMessageID() throws MessagingException { + for (Map.Entry<String, String> entry: messageHeaders.entrySet()) { + setHeader(entry.getKey(), entry.getValue()); + } + } + }; + String fromAddress = StringUtils.isNotEmpty(from) ? from : "juick@juick.com"; + message.setFrom(fromAddress); + message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); + message.setSubject(subject); + MimeBodyPart textBodyPart = new MimeBodyPart(); + textBodyPart.setContent(textPart, "text/plain; charset=UTF-8"); + + Multipart multipart = new MimeMultipart("alternative"); + multipart.addBodyPart(textBodyPart); + if (StringUtils.isNotBlank(htmlPart)) { + MimeBodyPart htmlBodyPart = new MimeBodyPart(); + htmlBodyPart.setContent(htmlPart, "text/html; charset=UTF-8"); + multipart.addBodyPart(htmlBodyPart); + } + message.setContent(multipart); + User emailUser = userService.getUserByEmail(to); + if (!emailUser.isAnonymous()) { + message.setHeader("List-Id", "Juick notifications <mail-notifications.juick.com>"); + message.setHeader("List-Post", "<mailto:juick@juick.com>"); + message.setHeader("List-Owner", "<mailto:support@juick.com>"); + message.setHeader("List-Archive", "<https://juick.com/>"); + message.setHeader("List-Unsubscribe", + String.format("<mailto:unsubscribe@juick.com>,<https://juick.com/settings/unsubscribe?hash=%s>", + userService.getHashByUID(emailUser.getUid()))); + message.setHeader("List-Unsubscribe-Post", "List-Unsubscribe=One-Click"); + } + message.saveChanges(); + transport.connect(); + transport.sendMessage(message, message.getAllRecipients()); + return true; + } catch (MessagingException ex) { + logger.error("mail exception", ex); + return false; + } + } +} |