diff --git a/src/main/java/de/srsoftware/widerhall/Configuration.java b/src/main/java/de/srsoftware/widerhall/Configuration.java index c0b9d29..51eaff4 100644 --- a/src/main/java/de/srsoftware/widerhall/Configuration.java +++ b/src/main/java/de/srsoftware/widerhall/Configuration.java @@ -17,6 +17,45 @@ public class Configuration { private JSONObject data; private File file; + public File archiveDir() { + var locations = locations(); + if (!locations.containsKey(ARCHIVE)) locations.put(ARCHIVE, String.join(File.separator,baseDir(),"archive")); + return new File((String) locations.get(ARCHIVE)); + } + + public String baseDir() { + var locations = locations(); + if (!locations.containsKey(BASE)) locations.put(BASE,System.getProperty("user.dir")); + return (String) locations.get(BASE); + } + + public String baseUrl() { + if (!data.containsKey(BASE_URL)) data.put(BASE_URL,"http://localhost"); + return (String) data.get(BASE_URL); + } + + public File configFile() { + var locations = locations(); + if (!locations.containsKey(CONFIG)) locations.put(CONFIG, String.join(File.separator,baseDir(),"config","config.json")); + return new File((String) locations.get(CONFIG)); + } + + public File dbFile() { + var locations = locations(); + if (!locations.containsKey(DB)) locations.put(DB, String.join(File.separator,baseDir(),"db","db.sqlite3")); + return new File((String) locations.get(DB)); + } + + public Configuration dbFile(File dbFile){ + var locations = locations(); + locations.put(DB,dbFile.toString()); + return this; + } + + public File file() { + return file; + } + public static Configuration instance() { if (singleton == null) singleton = new Configuration().setDefaults(); return singleton; @@ -42,6 +81,12 @@ public class Configuration { return this; } + private JSONObject locations() { + Object o = data.get(LOCATIONS); + if (!(o instanceof JSONObject)) data.put(LOCATIONS,o = new JSONObject()); + return (JSONObject) o; + } + public Configuration save(File file) throws IOException { this.file = file; return save(); @@ -64,57 +109,9 @@ public class Configuration { return this; } - private JSONObject locations() { - Object o = data.get(LOCATIONS); - if (!(o instanceof JSONObject)) data.put(LOCATIONS,o = new JSONObject()); - return (JSONObject) o; - } - - public String baseDir() { - var locations = locations(); - if (!locations.containsKey(BASE)) locations.put(BASE,System.getProperty("user.dir")); - return (String) locations.get(BASE); - } - - public File configFile() { - var locations = locations(); - if (!locations.containsKey(CONFIG)) locations.put(CONFIG, String.join(File.separator,baseDir(),"config","config.json")); - return new File((String) locations.get(CONFIG)); - } - - public File dbFile() { - var locations = locations(); - if (!locations.containsKey(DB)) locations.put(DB, String.join(File.separator,baseDir(),"db","db.sqlite3")); - return new File((String) locations.get(DB)); - } - - public Configuration dbFile(File dbFile){ - var locations = locations(); - locations.put(DB,dbFile.toString()); - return this; - } - - public File archiveDir() { - var locations = locations(); - if (!locations.containsKey(ARCHIVE)) locations.put(ARCHIVE, String.join(File.separator,baseDir(),"archive")); - return new File((String) locations.get(ARCHIVE)); - } - - public int serverPort() { if (!data.containsKey(PORT)) data.put(PORT,80L); var o = data.get(PORT); return (int) (long) o; } - - public String baseUrl() { - if (!data.containsKey(BASE_URL)) data.put(BASE_URL,"http://localhost"); - return (String) data.get(BASE_URL); - } - - - public File file() { - return file; - } - } diff --git a/src/main/java/de/srsoftware/widerhall/Util.java b/src/main/java/de/srsoftware/widerhall/Util.java index eaa48fc..c49ce75 100644 --- a/src/main/java/de/srsoftware/widerhall/Util.java +++ b/src/main/java/de/srsoftware/widerhall/Util.java @@ -28,19 +28,33 @@ public class Util { private static final MessageDigest SHA256 = getSha256(); private static final String EMAIL_PATTERN = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^-]+(?:\\.[a-zA-Z0-9_!#$%&'*+/=?`{|}~^-]+)*@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"; - public static String urlEncode(Map data) { - String params = data.entrySet() - .stream() - .map(entry -> encode(entry.getKey()) + "=" + encode(entry.getValue())) - .collect(Collectors.joining("&")); - return params; + public static String dropEmail(String tx) { + return tx.replaceAll( "[.\\-\\w]+@[.\\-\\w]+", "[email_removed]"); } private static String encode(Object value) { return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8); } + public static boolean getCheckbox(HttpServletRequest req, String key) { + return "on".equals(req.getParameter(key)); + } + + public static MailingList getMailingList(HttpServletRequest req) { + var listEmail = req.getParameter(LIST); + if (listEmail == null || listEmail.isBlank()) return null; + return MailingList.load(listEmail); + } + + public static T getNullable(ResultSet rs, String colName) throws SQLException { + final T val = (T) rs.getObject(colName); + return rs.wasNull() ? null : val; + } + public static String getPath(HttpServletRequest req) { + var path = req.getPathInfo(); + return path == null ? INDEX : path.substring(1); + } public static MessageDigest getSha256() { try { return MessageDigest.getInstance("SHA-256"); @@ -50,9 +64,43 @@ public class Util { } } - public static String sha256(String s) { - byte[] bytes = SHA256.digest(s.getBytes(StandardCharsets.UTF_8)); - return hex(bytes); + /** + * Return the primary text content of the message. + */ + public static String getText(Part p) throws MessagingException, IOException { + // https://javaee.github.io/javamail/FAQ + if (p.isMimeType("text/*")) return (String)p.getContent(); + + if (p.isMimeType("multipart/alternative")) { + // prefer html text over plain text + Multipart mp = (Multipart)p.getContent(); + String text = null; + for (int i = 0; i < mp.getCount(); i++) { + Part bp = mp.getBodyPart(i); + if (bp.isMimeType("text/plain")) { + if (text == null) text = getText(bp); + continue; + } else if (bp.isMimeType("text/html")) { + String s = getText(bp); + if (s != null) return s; + } else { + return getText(bp); + } + } + return text; + } else if (p.isMimeType("multipart/*")) { + Multipart mp = (Multipart)p.getContent(); + for (int i = 0; i < mp.getCount(); i++) { + String s = getText(mp.getBodyPart(i)); + if (s != null) return s; + } + } + return null; + } + + public static User getUser(HttpServletRequest req) { + var o = req.getSession().getAttribute(USER); + return o instanceof User ? (User) o : null; } private static String hex(byte[] bytes) { @@ -67,14 +115,15 @@ public class Util { return (char)(upper < 10 ? '0'+upper : 'A'+upper-10)+""+(char)(lower < 10 ? '0'+lower : 'A'+lower-10); } - public static String t(String tx, Object ... fills){ - return Translation.get(Application.class,tx,fills); - } - public static boolean isEmail(String email) { return email.matches(EMAIL_PATTERN); } + public static String sha256(String s) { + byte[] bytes = SHA256.digest(s.getBytes(StandardCharsets.UTF_8)); + return hex(bytes); + } + public static boolean simplePassword(String pass) { if (pass.length() < 6) return true; if (pass.length() < 8){ @@ -98,75 +147,23 @@ public class Util { return false; } - public static int unset(int value, int...flags) { - for (int flag : flags){ - if ((value & flag) > 0) value ^= flag; - } - return value; - } - - public static User getUser(HttpServletRequest req) { - var o = req.getSession().getAttribute(USER); - return o instanceof User ? (User) o : null; - } - - public static String getPath(HttpServletRequest req) { - var path = req.getPathInfo(); - return path == null ? INDEX : path.substring(1); - - } - - public static MailingList getMailingList(HttpServletRequest req) { - var listEmail = req.getParameter(LIST); - if (listEmail == null || listEmail.isBlank()) return null; - return MailingList.load(listEmail); + public static String t(String tx, Object ... fills){ + return Translation.get(Application.class,tx,fills); } - public static boolean getCheckbox(HttpServletRequest req, String key) { - return "on".equals(req.getParameter(key)); + public static String urlEncode(Map data) { + String params = data.entrySet() + .stream() + .map(entry -> encode(entry.getKey()) + "=" + encode(entry.getValue())) + .collect(Collectors.joining("&")); + return params; } - /** - * Return the primary text content of the message. - */ - public static String getText(Part p) throws MessagingException, IOException { - // https://javaee.github.io/javamail/FAQ - if (p.isMimeType("text/*")) return (String)p.getContent(); - if (p.isMimeType("multipart/alternative")) { - // prefer html text over plain text - Multipart mp = (Multipart)p.getContent(); - String text = null; - for (int i = 0; i < mp.getCount(); i++) { - Part bp = mp.getBodyPart(i); - if (bp.isMimeType("text/plain")) { - if (text == null) text = getText(bp); - continue; - } else if (bp.isMimeType("text/html")) { - String s = getText(bp); - if (s != null) return s; - } else { - return getText(bp); - } - } - return text; - } else if (p.isMimeType("multipart/*")) { - Multipart mp = (Multipart)p.getContent(); - for (int i = 0; i < mp.getCount(); i++) { - String s = getText(mp.getBodyPart(i)); - if (s != null) return s; - } + public static int unset(int value, int...flags) { + for (int flag : flags){ + if ((value & flag) > 0) value ^= flag; } - - return null; - } - - public static String dropEmail(String tx) { - return tx.replaceAll( "[.\\-\\w]+@[.\\-\\w]+", "[email_removed]"); - } - - public static T getNullable(ResultSet rs, String colName) throws SQLException { - final T val = (T) rs.getObject(colName); - return rs.wasNull() ? null : val; + return value; } } diff --git a/src/main/java/de/srsoftware/widerhall/data/Database.java b/src/main/java/de/srsoftware/widerhall/data/Database.java index b4bbe28..8318d8a 100644 --- a/src/main/java/de/srsoftware/widerhall/data/Database.java +++ b/src/main/java/de/srsoftware/widerhall/data/Database.java @@ -137,30 +137,6 @@ public class Database { if (!sortFields.isEmpty()) sql.append(" ORDER BY ").append(String.join(", ",sortFields)); } - @Override - protected Request clone() { - Request clone = new Request(new StringBuilder(sql)); - clone.where.putAll(where); - clone.values.putAll(values); - return clone; - } - - /** - * finalize sql, save sql and arguments as compiled request - * @return - */ - public CompiledRequest compile(Object ...additionalArgs){ - var args = new ArrayList<>(); - applyValues(args); - applyConditions(args); - applyGrouping(); - applySorting(); - if (additionalArgs != null) { - for (Object arg : additionalArgs) args.add(arg); - } - return new CompiledRequest(sql.toString(),args); - } - /** * apply values (for insert or update statements) * @param args @@ -193,6 +169,30 @@ public class Database { } } + @Override + protected Request clone() { + Request clone = new Request(new StringBuilder(sql)); + clone.where.putAll(where); + clone.values.putAll(values); + return clone; + } + + /** + * finalize sql, save sql and arguments as compiled request + * @return + */ + public CompiledRequest compile(Object ...additionalArgs){ + var args = new ArrayList<>(); + applyValues(args); + applyConditions(args); + applyGrouping(); + applySorting(); + if (additionalArgs != null) { + for (Object arg : additionalArgs) args.add(arg); + } + return new CompiledRequest(sql.toString(),args); + } + public Request groupBy(String column) { groupBy = column; return this; diff --git a/src/main/java/de/srsoftware/widerhall/data/ListMember.java b/src/main/java/de/srsoftware/widerhall/data/ListMember.java index a5eb99c..ce2134e 100644 --- a/src/main/java/de/srsoftware/widerhall/data/ListMember.java +++ b/src/main/java/de/srsoftware/widerhall/data/ListMember.java @@ -112,20 +112,6 @@ public class ListMember { return null; } - public void sendConfirmationMail(ST template) throws SQLException, MessagingException { - var subject = t("[{}] Subscription complete!",list.name()); - var data = new HashMap(); - data.put(USER,user.safeMap()); - data.put(LIST,list.minimalMap()); - data.put(URL,Configuration.instance().baseUrl()+"/web/index"); - if (list.isOpenForSubscribers()) data.put("open_list",true); - var text = template.add("data",data).render(); - try { - list.smtp().send(list.email(),list.name(),user.email(),subject,text); - } catch (UnsupportedEncodingException e) { - LOG.warn("Failed to send list subscription confirmation!",e); - } - } /** * Create a new list member entry in the database. * If the member has the state AWAITING_CONFIRMATION, a token is assigned with the member, too. @@ -374,6 +360,21 @@ public class ListMember { return this; } + public void sendConfirmationMail(ST template) throws SQLException, MessagingException { + var subject = t("[{}] Subscription complete!",list.name()); + var data = new HashMap(); + data.put(USER,user.safeMap()); + data.put(LIST,list.minimalMap()); + data.put(URL,Configuration.instance().baseUrl()+"/web/index"); + if (list.isOpenForSubscribers()) data.put("open_list",true); + var text = template.add("data",data).render(); + try { + list.smtp().send(list.email(),list.name(),user.email(),subject,text); + } catch (UnsupportedEncodingException e) { + LOG.warn("Failed to send list subscription confirmation!",e); + } + } + public ListMember setState(int newState) throws SQLException { Database.open() .update(TABLE_NAME) diff --git a/src/main/java/de/srsoftware/widerhall/mail/ImapClient.java b/src/main/java/de/srsoftware/widerhall/mail/ImapClient.java index 63ceab2..1b30638 100644 --- a/src/main/java/de/srsoftware/widerhall/mail/ImapClient.java +++ b/src/main/java/de/srsoftware/widerhall/mail/ImapClient.java @@ -33,28 +33,40 @@ public class ImapClient { return this; } + public ListeningThread doStop() { + stopped = true; + 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); - problemListener.onImapException(e); - } + + private void handle(Message message) throws MessagingException { + LOG.debug("Handling {}",message.getSubject()); + for (MessageHandler listener : listeners) listener.onMessageReceived(message); + } + + private void handleMessages() throws MessagingException { + LOG.debug("Reading email of {}:",username); + if (!inbox.isOpen()) inbox.open(IMAPFolder.READ_WRITE); + for (Message message : inbox.getMessages()){ + if (message.isSet(Flags.Flag.SEEN)) continue; + handle(message); + Folder folder = message.getFolder(); + if (!folder.isOpen()) folder.open(Folder.READ_WRITE); + message.setFlag(Flags.Flag.SEEN,true); } } + private Properties imapProps() { + Properties props = new Properties(); + props.put(Constants.PROTOCOL,Constants.IMAPS); + return props; + } + private void openInbox() throws MessagingException { LOG.debug("Connecting and logging in…"); Properties props = imapProps(); @@ -73,40 +85,33 @@ public class ImapClient { } } - private void handleMessages() throws MessagingException { - LOG.debug("Reading email of {}:",username); - if (!inbox.isOpen()) inbox.open(IMAPFolder.READ_WRITE); - for (Message message : inbox.getMessages()){ - if (message.isSet(Flags.Flag.SEEN)) continue; - handle(message); - Folder folder = message.getFolder(); - if (!folder.isOpen()) folder.open(Folder.READ_WRITE); - message.setFlag(Flags.Flag.SEEN,true); + @Override + public void run() { + while (!stopped) { + try { + sleep(5000); + } catch (InterruptedException interruptedException) { + interruptedException.printStackTrace(); + } + try { + openInbox(); + } catch (MessagingException e){ + LOG.warn("Connection problem:",e); + problemListener.onImapException(e); + } } } - - private void handle(Message message) throws MessagingException { - LOG.debug("Handling {}",message.getSubject()); - for (MessageHandler listener : listeners) listener.onMessageReceived(message); - } - - private Properties imapProps() { - Properties props = new Properties(); - 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; + public Heartbeat doStop() { + stopped = true; + return this; + } + @Override public void run() { while (!stopped){ @@ -123,11 +128,6 @@ public class ImapClient { } } } - - public Heartbeat doStop() { - stopped = true; - return this; - } } public ImapClient(String host, int port, String username, String password, String folderName,ProblemListener listener) { @@ -165,25 +165,13 @@ public class ImapClient { inbox.expunge(); } - - public String host(){ - return host; - } - - public String username(){ - return username; - } - - public String password(){ - return password; + public String folderName(){ + return folderName; } - public int port(){ - return port; - } - public String folderName(){ - return folderName; + public String host(){ + return host; } public ImapClient move(Message message, String destinationFolder) throws MessagingException { @@ -200,6 +188,14 @@ public class ImapClient { return this; } + public String password(){ + return password; + } + + public int port(){ + return port; + } + public ImapClient start() { stop(); @@ -225,4 +221,8 @@ public class ImapClient { } return this; } + + public String username(){ + return username; + } } diff --git a/src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java b/src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java index 71c01cd..df278c5 100644 --- a/src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java +++ b/src/main/java/de/srsoftware/widerhall/mail/SmtpClient.java @@ -81,6 +81,9 @@ public class SmtpClient { send(forward); } + public String host() { + return host; + } public SmtpClient login(){ if (session == null) { @@ -97,6 +100,20 @@ public class SmtpClient { return this; } + public String password() { + return password; + } + + public int port() { + return port; + } + + public void send(Message message) throws MessagingException { + LOG.debug("Versende Mail…"); + Transport.send(message,username,password); + LOG.debug("…versendet"); + } + public void send(String senderAdress, String senderName, String receivers, String subject, String content) throws MessagingException, UnsupportedEncodingException { login(); MimeMessage message = new MimeMessage(session); @@ -113,27 +130,7 @@ public class SmtpClient { send(message); } - public void send(Message message) throws MessagingException { - LOG.debug("Versende Mail…"); - Transport.send(message,username,password); - LOG.debug("…versendet"); - - } - - public String host() { - return host; - } - - public int port() { - return port; - } - public String username() { return username; } - - public String password() { - return password; - } - }