diff --git a/pom.xml b/pom.xml index 8d6f2ca..f63bd10 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.example Widerhall - 0.0.14 + 0.0.15 diff --git a/src/main/java/de/srsoftware/widerhall/Util.java b/src/main/java/de/srsoftware/widerhall/Util.java index 829b278..b182ef4 100644 --- a/src/main/java/de/srsoftware/widerhall/Util.java +++ b/src/main/java/de/srsoftware/widerhall/Util.java @@ -2,6 +2,8 @@ package de.srsoftware.widerhall; import de.srsoftware.tools.translations.Translation; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; diff --git a/src/main/java/de/srsoftware/widerhall/data/MailingList.java b/src/main/java/de/srsoftware/widerhall/data/MailingList.java index c6845e4..915f386 100644 --- a/src/main/java/de/srsoftware/widerhall/data/MailingList.java +++ b/src/main/java/de/srsoftware/widerhall/data/MailingList.java @@ -2,15 +2,22 @@ package de.srsoftware.widerhall.data; import de.srsoftware.widerhall.Configuration; import de.srsoftware.widerhall.mail.ImapClient; +import de.srsoftware.widerhall.mail.MessageHandler; import de.srsoftware.widerhall.mail.SmtpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.mail.Address; +import javax.mail.Message; import javax.mail.MessagingException; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; import java.io.UnsupportedEncodingException; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; +import java.util.stream.Stream; import static de.srsoftware.widerhall.Constants.*; import static de.srsoftware.widerhall.Util.t; @@ -19,7 +26,7 @@ import static de.srsoftware.widerhall.data.User.PERMISSION_ADMIN; /** * this class encapsulates a MailingList db object */ -public class MailingList { +public class MailingList implements MessageHandler { private static final Logger LOG = LoggerFactory.getLogger(MailingList.class); private static final String IMAP_HOST = "imap_host"; private static final String IMAP_PORT = "imap_port"; @@ -106,6 +113,17 @@ public class MailingList { Database.open().query(sql).compile().run(); } + /** + * load the set of mailing lists a given user is allowed to edit + * @param user + * @return + */ + public static Set editableBy(User user) { + var list = new HashSet(); + for (String key : ListMember.listsOwnedBy(user)) list.add(load(key)); + return list; + } + public String email() { return email; } @@ -114,41 +132,23 @@ public class MailingList { public void enable(boolean enable) throws SQLException { state = enable ? state | STATE_ENABLED : state ^ (state & STATE_ENABLED); Database.open().update(TABLE_NAME).set(STATE,state).where(EMAIL, email()).compile().run(); - } - public void hide(boolean hide) throws SQLException { - state = hide ? state ^ (state & STATE_PUBLIC) : state | STATE_PUBLIC; - Database.open().update(TABLE_NAME).set(STATE,state).where(EMAIL, email()).compile().run(); + if (enable) { + imap.start().addListener(this); + } else { + imap.stop(); + } } - /** - * test, whether the current ML is subscribable by a given user - * @param user - * @return - */ - public boolean isOpenFor(User user) { - if ((state & STATE_PUBLIC) > 0) return true; // all users may subscribe public mailing lists - if (user == null) return false; + private void forward(Message message) throws MessagingException { try { - var member = ListMember.load(this,user); - return member.hasState(ListMember.STATE_OWNER|ListMember.STATE_SUBSCRIBER); // owners may subscribe their own mailing lists + var emails = members().stream().map(User::email).toList(); + smtp.bccForward(email(),message,emails); } catch (SQLException e) { - LOG.warn("Was not able to load ListMember: ",e); - return false; + LOG.error("Failed to read list members of {} from database. Cannot forward message!",email(),e); } } - /** - * load the set of mailing lists a given user is allowed to edit - * @param user - * @return - */ - public static Set editableBy(User user) { - var list = new HashSet(); - for (String key : ListMember.listsOwnedBy(user)) list.add(load(key)); - return list; - } - /** * Create a mailing list object from a ResultSet. * This is a cached method: if the ML has been loaded before, the already-loaded object will be returned. @@ -175,6 +175,28 @@ public class MailingList { return ml; } + public void hide(boolean hide) throws SQLException { + state = hide ? state ^ (state & STATE_PUBLIC) : state | STATE_PUBLIC; + Database.open().update(TABLE_NAME).set(STATE,state).where(EMAIL, email()).compile().run(); + } + + /** + * test, whether the current ML is subscribable by a given user + * @param user + * @return + */ + public boolean isOpenFor(User user) { + if ((state & STATE_PUBLIC) > 0) return true; // all users may subscribe public mailing lists + if (user == null) return false; + try { + var member = ListMember.load(this,user); + return member.hasState(ListMember.STATE_OWNER|ListMember.STATE_SUBSCRIBER); // owners may subscribe their own mailing lists + } catch (SQLException e) { + LOG.warn("Was not able to load ListMember: ",e); + return false; + } + } + /** * Load a ML object by it's identifying email address. * This is a cached method: if the ML has been loaded before, the already-loaded object will be returned. @@ -196,6 +218,10 @@ public class MailingList { return ml; } + private Set members() throws SQLException { + return ListMember.of(email()).keySet(); + } + /** * creates a map from the current ML object containing only email and name of the ML * @return @@ -213,6 +239,13 @@ public class MailingList { return name; } + @Override + public void onMessageReceived(Message message) throws MessagingException { + LOG.debug("Message received: {}",message.getFrom()); + storeMessage(message); + forward(message); + } + /** * provide the set of mailing lists that are publicy open to subscriptions * @return @@ -362,6 +395,10 @@ public class MailingList { } } + private void storeMessage(Message message){ + // TODO: implement + } + /** * send a test email to the provided user @@ -374,4 +411,14 @@ public class MailingList { var text = "If you received this mail, the SMTP settings of your mailing list are correct."; smtp.login().send(email(),name(),user.email(),subject,text); } + + public static Stream toAddress(String email) { + try { + return Arrays.asList(InternetAddress.parse(email)).stream(); + } catch (AddressException e) { + LOG.debug("Was not able to parse {}",email,e); + return new ArrayList().stream(); + } + } + } diff --git a/src/main/java/de/srsoftware/widerhall/mail/Archiver.java b/src/main/java/de/srsoftware/widerhall/mail/Archiver.java deleted file mode 100644 index 0bb8c07..0000000 --- a/src/main/java/de/srsoftware/widerhall/mail/Archiver.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.srsoftware.widerhall.mail; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.mail.Message; -import javax.mail.MessagingException; -import java.io.UnsupportedEncodingException; - -public class Archiver implements MessageHandler { - private static final Logger LOG = LoggerFactory.getLogger(Archiver.class); - private final SmtpClient smtp; - private final String receiver,sender; - - public Archiver(String host, int port, String username, String password, String sender, String receiver) { - this.sender = sender; - this.receiver = receiver; - SmtpClient smtp = new SmtpClient(host,port,username,password); - this.smtp = smtp; - } - - @Override - public void onMessageReceived(Message message) throws MessagingException { - LOG.debug("storing {}",message.getSubject()); - } -} diff --git a/src/main/java/de/srsoftware/widerhall/mail/Forwarder.java b/src/main/java/de/srsoftware/widerhall/mail/Forwarder.java deleted file mode 100644 index 4dca6bd..0000000 --- a/src/main/java/de/srsoftware/widerhall/mail/Forwarder.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.srsoftware.widerhall.mail; - -import org.json.simple.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.mail.Message; -import javax.mail.MessagingException; -import java.io.UnsupportedEncodingException; - -public class Forwarder implements MessageHandler { - private static final Logger LOG = LoggerFactory.getLogger(Forwarder.class); - private final SmtpClient smtp; - private final String receiver,sender; - - public Forwarder(String host, int port, String username, String password, String sender, String receiver) { - this.sender = sender; - this.receiver = receiver; - SmtpClient smtp = new SmtpClient(host,port,username,password); - this.smtp = smtp; - } - - @Override - public void onMessageReceived(Message message) throws MessagingException { - LOG.debug("forwarding {}",message.getSubject()); - } -} diff --git a/src/main/java/de/srsoftware/widerhall/mail/ImapClient.java b/src/main/java/de/srsoftware/widerhall/mail/ImapClient.java index 1a5c009..0c0c2a7 100644 --- a/src/main/java/de/srsoftware/widerhall/mail/ImapClient.java +++ b/src/main/java/de/srsoftware/widerhall/mail/ImapClient.java @@ -2,7 +2,6 @@ package de.srsoftware.widerhall.mail; import com.sun.mail.imap.IMAPFolder; import de.srsoftware.widerhall.Constants; -import org.json.simple.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,26 +14,38 @@ public class ImapClient { private static final Logger LOG = LoggerFactory.getLogger(ImapClient.class); private final int port; private final String host, username, password, folderName; - private boolean stopped = true; private IMAPFolder inbox; - private HashSet listeners = new HashSet<>(); + private ListeningThread listeningThread; + private Heartbeat heartbeat; private class ListeningThread extends Thread { private static final Logger LOG = LoggerFactory.getLogger(ListeningThread.class); + private HashSet listeners = new HashSet<>(); + private boolean stopped = false; + + public ListeningThread addListener(MessageHandler messageHandler) { + listeners.add(messageHandler); + return this; + } + + public ListeningThread dropListeners() { + listeners.clear(); + return this; + } @Override public void run() { while (!stopped) { + try { + sleep(5000); + } catch (InterruptedException interruptedException) { + interruptedException.printStackTrace(); + } try { openInbox(); } catch (MessagingException e){ LOG.warn("Connection problem:",e); - try { - sleep(1000); - } catch (InterruptedException interruptedException) { - interruptedException.printStackTrace(); - } } } } @@ -78,11 +89,18 @@ public class ImapClient { props.put(Constants.PROTOCOL,Constants.IMAPS); return props; } + + public ListeningThread doStop() { + stopped = true; + return this; + } } private class Heartbeat extends Thread{ private static final Logger LOG = LoggerFactory.getLogger(Heartbeat.class); + private boolean stopped = false; + @Override public void run() { while (!stopped){ @@ -99,6 +117,11 @@ public class ImapClient { } } } + + public Heartbeat doStop() { + stopped = true; + return this; + } } public ImapClient(String host, int port, String username, String password, String folderName) { @@ -109,9 +132,9 @@ public class ImapClient { this.folderName = folderName; } - public ImapClient addListener(MessageHandler messageHandler) { - listeners.add(messageHandler); + if (listeningThread == null) listeningThread = new ListeningThread(); + listeningThread.addListener(messageHandler); return this; } @@ -135,15 +158,28 @@ public class ImapClient { return folderName; } - public void start() { - stopped = false; + public ImapClient start() { + stop(); + LOG.debug("Creating ListeningThread…"); - new ListeningThread().start(); + (listeningThread = new ListeningThread()).start(); + LOG.debug("Creating Heartbeat…"); - new Heartbeat().start(); + (heartbeat = new Heartbeat()).start(); + + return this; } - public void stop(){ - stopped = true; + public ImapClient stop(){ + if (listeningThread != null) { + LOG.debug("Stopping old ListeningThread…"); + listeningThread.dropListeners().doStop(); + listeningThread = null; + } + if (heartbeat != null){ + heartbeat.doStop(); + heartbeat = null; + } + return this; } } diff --git a/src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java b/src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java index 526ef22..4033f0a 100644 --- a/src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java +++ b/src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java @@ -4,10 +4,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.mail.*; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; +import javax.mail.internet.*; import java.io.UnsupportedEncodingException; import java.util.Date; +import java.util.List; import java.util.Properties; public class SmtpClient { @@ -29,6 +29,25 @@ public class SmtpClient { this.port = port; } + public void bccForward(String from, Message message, List emails) throws MessagingException { + if (session == null) login(); + MimeMessage forward = new MimeMessage(session); + var addresses = InternetAddress.parse(String.join(", ",emails)); + var senders = message.getFrom(); + forward.setFrom(senders[0]); + //forward.setFrom(InternetAddress.parse(from)[0]); + forward.setRecipients(Message.RecipientType.BCC,addresses); + forward.setSubject(message.getSubject()); + MimeBodyPart body = new MimeBodyPart(); + body.setContent(message,"message/rfc822"); + Multipart multipart = new MimeMultipart(); + multipart.addBodyPart(body); + forward.setContent(multipart); + forward.saveChanges(); + send(forward); + } + + public SmtpClient login(){ if (session == null) { Properties props = new Properties(); @@ -55,10 +74,14 @@ public class SmtpClient { message.setText(content,UTF8); message.setSentDate(new Date()); message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(receivers,false)); + send(message); + } + public void send(Message message) throws MessagingException { LOG.debug("Versende Mail…"); Transport.send(message,username,password); LOG.debug("…versendet"); + } public String host() { @@ -76,4 +99,5 @@ public class SmtpClient { public String password() { return password; } + } diff --git a/src/main/java/de/srsoftware/widerhall/web/Web.java b/src/main/java/de/srsoftware/widerhall/web/Web.java index a976397..85c13f2 100644 --- a/src/main/java/de/srsoftware/widerhall/web/Web.java +++ b/src/main/java/de/srsoftware/widerhall/web/Web.java @@ -66,9 +66,11 @@ public class Web extends TemplateServlet { var imapUser = req.getParameter(IMAP_USER); data.put(IMAP_USER, imapUser); var imapPass = req.getParameter(IMAP_PASS); - var smtpHost = req.getParameter(SMTP_HOST); var inbox = req.getParameter(INBOX); - if (inbox == null) inbox = DEFAULT_INBOX; + if (inbox == null || inbox.isBlank()) inbox = DEFAULT_INBOX; + data.put(INBOX, inbox); + + var smtpHost = req.getParameter(SMTP_HOST); data.put(SMTP_HOST, smtpHost); var smtpUser = req.getParameter(SMTP_USER); data.put(SMTP_USER, smtpUser); diff --git a/static/templates/add_list.st b/static/templates/add_list.st index 5b20816..8831334 100644 --- a/static/templates/add_list.st +++ b/static/templates/add_list.st @@ -44,7 +44,7 @@ Password diff --git a/static/templates/frontpage.st b/static/templates/frontpage.st index 2f7cbe6..741afb3 100644 --- a/static/templates/frontpage.st +++ b/static/templates/frontpage.st @@ -36,5 +36,6 @@ That's it.

+ «footer()» \ No newline at end of file