diff --git a/src/main/java/de/srsoftware/widerhall/data/Database.java b/src/main/java/de/srsoftware/widerhall/data/Database.java index 4ef857b..350f403 100644 --- a/src/main/java/de/srsoftware/widerhall/data/Database.java +++ b/src/main/java/de/srsoftware/widerhall/data/Database.java @@ -30,56 +30,16 @@ public class Database { private final String sql; private final HashMap> where = new HashMap<>(); private final HashMap values = new HashMap<>(); + private final HashMap setValues = new HashMap<>(); public Request(String sql) { this.sql = sql; } - public Request where(String key, Object ... values) { - for (var val : values) where(key,val); - return this; - } - - public Request where(String key, Collection values) { - for (var val : values) where(key,val); - return this; - } - - private Request where(String key, Object value) { - var list = where.get(key); - if (list == null) where.put(key,list = new ArrayList<>()); - list.add(value); - return this; - } - public Request values(Map newValues) { - values.putAll(newValues); - return this; - } - - public Request values(String key, Object value) { - values.put(key,value); - return this; - } - - public void run() throws SQLException { + public ResultSet exec() throws SQLException { var sb = new StringBuilder(sql); var args = new ArrayList<>(); - if (!values.isEmpty()){ - var keys = new ArrayList(); - for (var entry : values.entrySet()) { - keys.add(entry.getKey()); - args.add(entry.getValue()); - } - sb.append("(") - .append(String.join(", ",keys)) - .append(")") - .append(" VALUES "); - var arr = new String[args.size()]; - Arrays.fill(arr,"?"); - var marks = String.join(", ",arr); - sb.append("(").append(marks).append(")"); - } if (!where.isEmpty()){ var clauses = new ArrayList(); sb.append(" WHERE "); @@ -97,19 +57,46 @@ public class Database { var sql = sb.toString(); LOG.debug(sql); try { - var stmt = conn.prepareStatement(sql); + var stmt = Database.this.conn.prepareStatement(sql); if (!args.isEmpty()) { for (int i = 0; i < args.size(); i++) stmt.setObject(i+1, args.get(i)); } - stmt.execute(); + return stmt.executeQuery(); } catch (SQLException sqle) { throw new SQLException(t("Query '{}' failed:",sql),sqle); } } - public ResultSet exec() throws SQLException { + public void run() throws SQLException { var sb = new StringBuilder(sql); var args = new ArrayList<>(); + + if (!setValues.isEmpty()){ + var keys = new ArrayList(); + var expressions = new ArrayList(); + for (var entry : setValues.entrySet()) { + expressions.add(" SET "+entry.getKey()+" = ?"); + args.add(entry.getValue()); + } + sb.append(String.join(", ",expressions)); + } + + if (!values.isEmpty()){ + var keys = new ArrayList(); + for (var entry : values.entrySet()) { + keys.add(entry.getKey()); + args.add(entry.getValue()); + } + sb.append("(") + .append(String.join(", ",keys)) + .append(")") + .append(" VALUES "); + var arr = new String[args.size()]; + Arrays.fill(arr,"?"); + var marks = String.join(", ",arr); + sb.append("(").append(marks).append(")"); + } + if (!where.isEmpty()){ var clauses = new ArrayList(); sb.append(" WHERE "); @@ -127,21 +114,61 @@ public class Database { var sql = sb.toString(); LOG.debug(sql); try { - var stmt = Database.this.conn.prepareStatement(sql); + var stmt = conn.prepareStatement(sql); if (!args.isEmpty()) { for (int i = 0; i < args.size(); i++) stmt.setObject(i+1, args.get(i)); } - return stmt.executeQuery(); + stmt.execute(); } catch (SQLException sqle) { throw new SQLException(t("Query '{}' failed:",sql),sqle); } } + + public Request set(String key, Object value) { + setValues.put(key,value); + return this; + } + + + public Request where(String key, Object ... values) { + for (var val : values) where(key,val); + return this; + } + + public Request where(String key, Collection values) { + for (var val : values) where(key,val); + return this; + } + + private Request where(String key, Object value) { + var list = where.get(key); + if (list == null) where.put(key,list = new ArrayList<>()); + list.add(value); + return this; + } + + public Request values(Map newValues) { + values.putAll(newValues); + return this; + } + + public Request values(String key, Object value) { + values.put(key,value); + return this; + } } public Database(Connection connection) { this.conn = connection; } + private Database assertTables() throws SQLException { + if (!tableExists(User.TABLE_NAME)) User.createTable(); + if (!tableExists(MailingList.TABLE_NAME)) MailingList.createTable(); + if (!tableExists(ListMember.TABLE_NAME)) ListMember.createTable(); + return this; + } + public static Database open() { if (singleton == null){ Configuration config = Configuration.instance(); @@ -159,15 +186,10 @@ public class Database { return singleton; } - private Database assertTables() throws SQLException { - if (!tableExists(User.TABLE_NAME)) User.createTable(); - if (!tableExists(MailingList.TABLE_NAME)) MailingList.createTable(); - if (!tableExists(ListMember.TABLE_NAME)) ListMember.createTable(); - return this; + public Request query(String sql) { + return new Request(sql); } - - private boolean tableExists(String tbName) throws SQLException { try { ResultSet rs = query("SELECT EXISTS (SELECT name FROM sqlite_schema WHERE type='table' AND name='" + tbName + "')").exec(); @@ -179,8 +201,4 @@ public class Database { throw new SQLException(t("Was not able to check existence of table {}!",tbName),e); } } - - public Request query(String sql) { - return new Request(sql); - } } diff --git a/src/main/java/de/srsoftware/widerhall/data/ListMember.java b/src/main/java/de/srsoftware/widerhall/data/ListMember.java index 7e1105a..9851de3 100644 --- a/src/main/java/de/srsoftware/widerhall/data/ListMember.java +++ b/src/main/java/de/srsoftware/widerhall/data/ListMember.java @@ -3,6 +3,7 @@ package de.srsoftware.widerhall.data; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.xml.crypto.Data; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; @@ -90,4 +91,27 @@ public class ListMember { .run(); return this; } + + public static void unsubscribe(String listEmail, User user) throws SQLException { + var db = Database.open(); + var rs = db.query("SELECT * FROM "+TABLE_NAME) + .where(LIST_EMAIL,listEmail) + .where(USER_EMAIL,user.email()) + .exec(); + while (rs.next()){ + int state = rs.getInt(STATE) ^ STATE_SUBSCRIBER; + if (state < 1) { // drop entry + db.query("DELETE FROM "+TABLE_NAME) + .where(LIST_EMAIL,listEmail) + .where(USER_EMAIL,user.email()) + .run(); + } else { // update entry: whitdraw subscription + db.query("UPDATE "+TABLE_NAME) + .set(STATE,state) + .where(LIST_EMAIL,listEmail) + .where(USER_EMAIL,user.email()) + .run(); + } + } + } } diff --git a/src/main/java/de/srsoftware/widerhall/web/Web.java b/src/main/java/de/srsoftware/widerhall/web/Web.java index e3d9311..9036d3b 100644 --- a/src/main/java/de/srsoftware/widerhall/web/Web.java +++ b/src/main/java/de/srsoftware/widerhall/web/Web.java @@ -32,8 +32,8 @@ public class Web extends HttpServlet { private static final String LOGOUT = "logout"; private static final String REGISTER = "register"; private static final String SUBSCRIBE = "subscribe"; + private static final String UNSUBSCRIBE = "unsubscribe"; private static final String RELOAD = "reload"; - private static final String INSPECT = "inspect"; private static final String IMAP_HOST = "imap_host"; private static final String IMAP_PORT = "imap_port"; private static final String IMAP_USER = "imap_user"; @@ -140,6 +140,15 @@ public class Web extends HttpServlet { if (error != null) resp.sendError(400,error); } + private SQLException getCausingException(SQLException sqle) { + Throwable cause = sqle.getCause(); + while (cause instanceof SQLException){ + sqle = (SQLException) cause; + cause = sqle.getCause(); + } + return sqle; + } + private String handleGet(HttpServletRequest req, HttpServletResponse resp) { var o = req.getSession().getAttribute("user"); User user = o instanceof User ? (User) o : null; @@ -148,6 +157,8 @@ public class Web extends HttpServlet { var path = req.getPathInfo(); path = (path == null || path.equals("/")) ? INDEX : path.substring(1); String notes = null; + var list = req.getParameter(LIST); + if (list != null && !list.isBlank()) data.put(LIST,list); switch (path){ case RELOAD: loadTemplates(); @@ -155,9 +166,9 @@ public class Web extends HttpServlet { path = INDEX; case "css": case INDEX: + case UNSUBSCRIBE: return loadTemplate(path,data,resp); case SUBSCRIBE: - var list = req.getParameter(LIST); // TODO check permission if (MailingList.isOpen(list)) { data.put(LIST, list); @@ -183,9 +194,8 @@ public class Web extends HttpServlet { } if (user != null){ - var list = req.getParameter(LIST); if (list != null) data.put(LIST,req.getParameter(LIST)); - data.put(NOTES,notes); + //data.put(NOTES,notes); return loadTemplate(path,data,resp); } return redirectTo(LOGIN,resp); @@ -225,6 +235,8 @@ public class Web extends HttpServlet { return registerUser(req,resp); case SUBSCRIBE: return subscribe(req,resp); + case UNSUBSCRIBE: + return unsubscribe(req,resp); } return t("No handler for path {}!",path); @@ -357,12 +369,52 @@ public class Web extends HttpServlet { } } - private SQLException getCausingException(SQLException sqle) { - Throwable cause = sqle.getCause(); - while (cause instanceof SQLException){ - sqle = (SQLException) cause; - cause = sqle.getCause(); + + + private String unsubscribe(HttpServletRequest req, HttpServletResponse resp) { + var data = new HashMap(); + var user = getSessionUser(req); + var email = req.getParameter(EMAIL); + var pass = req.getParameter(PASSWORD); + var list = req.getParameter(LIST); + data.put(EMAIL,email); + data.put(LIST,list); + if (user != null) data.put(USER,user.safeMap()); + if (list == null || list.isBlank()){ + data.put(ERROR,"No list provided by form data!"); + return loadTemplate(UNSUBSCRIBE,data,resp); + } - return sqle; + if (user == null) { + if (email == null || email.isBlank()) { + data.put(ERROR, "Email is required for list un-subscription!"); + return loadTemplate(UNSUBSCRIBE, data, resp); + } + if (pass != null && pass.isBlank()) pass = null; + + try { + user = User.load(email,pass); + req.getSession().setAttribute(USER,user); + data.put(USER,user.safeMap()); + } catch (InvalidKeyException | SQLException e) { + data.put(ERROR,"Invalid email/password combination!"); + return loadTemplate(UNSUBSCRIBE,data,resp); + } + } + // if we get here, we should have a valid user + try { + ListMember.unsubscribe(list,user); + data.put(NOTES,t("Sucessfully un-subscribed from '{}'.",list)); + return loadTemplate(INDEX,data,resp); + } catch (SQLException e) { + LOG.warn("Problem during unscubsription of {} from {}:",user.email(),list,e); + data.put(ERROR,"Failed to unsubscribe!"); + return loadTemplate(UNSUBSCRIBE,data,resp); + + } + } + + private User getSessionUser(HttpServletRequest req) { + return req.getSession().getAttribute(USER) instanceof User user ? user : null; } } diff --git a/static/templates/js.st b/static/templates/js.st index 9de6fdc..1da2843 100644 --- a/static/templates/js.st +++ b/static/templates/js.st @@ -145,5 +145,9 @@ function subscribeTo(domain,prefix){ window.location.href='subscribe?list='+prefix+'@'+domain; } +function unsubscribeFrom(domain,prefix){ + window.location.href='unsubscribe?list='+prefix+'@'+domain; +} + $(start); // document.on ready \ No newline at end of file diff --git a/static/templates/subscribe.st b/static/templates/subscribe.st index 5cecc56..2b29306 100644 --- a/static/templates/subscribe.st +++ b/static/templates/subscribe.st @@ -16,11 +16,11 @@
Suscribe to "«data.list»"