added tests, preparing submission confirmation
This commit is contained in:
@@ -1048,13 +1048,13 @@
|
||||
</dia:object>
|
||||
<dia:object type="Flowchart - Box" version="0" id="O22">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="22,16"/>
|
||||
<dia:point val="22,18"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="21.95,15.95;27.05,18.05"/>
|
||||
<dia:rectangle val="21.95,17.95;27.05,20.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_corner">
|
||||
<dia:point val="22,16"/>
|
||||
<dia:point val="22,18"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_width">
|
||||
<dia:real val="5"/>
|
||||
@@ -1080,7 +1080,7 @@
|
||||
<dia:real val="0.80000000000000004"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="pos">
|
||||
<dia:point val="24.5,17.1941"/>
|
||||
<dia:point val="24.5,19.1941"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="color">
|
||||
<dia:color val="#000000ff"/>
|
||||
@@ -1093,13 +1093,13 @@
|
||||
</dia:object>
|
||||
<dia:object type="Flowchart - Box" version="0" id="O23">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="23,20"/>
|
||||
<dia:point val="23,22"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="22.95,19.95;27.05,22.05"/>
|
||||
<dia:rectangle val="22.95,21.95;27.05,24.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_corner">
|
||||
<dia:point val="23,20"/>
|
||||
<dia:point val="23,22"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_width">
|
||||
<dia:real val="4"/>
|
||||
@@ -1125,7 +1125,7 @@
|
||||
<dia:real val="0.80000000000000004"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="pos">
|
||||
<dia:point val="25,21.1941"/>
|
||||
<dia:point val="25,23.1941"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="color">
|
||||
<dia:color val="#000000ff"/>
|
||||
@@ -1138,13 +1138,13 @@
|
||||
</dia:object>
|
||||
<dia:object type="Flowchart - Box" version="0" id="O24">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="23,22"/>
|
||||
<dia:point val="23,24"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="22.95,21.95;27.05,24.05"/>
|
||||
<dia:rectangle val="22.95,23.95;27.05,26.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_corner">
|
||||
<dia:point val="23,22"/>
|
||||
<dia:point val="23,24"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_width">
|
||||
<dia:real val="4"/>
|
||||
@@ -1170,7 +1170,7 @@
|
||||
<dia:real val="0.80000000000000004"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="pos">
|
||||
<dia:point val="25,23.1941"/>
|
||||
<dia:point val="25,25.1941"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="color">
|
||||
<dia:color val="#000000ff"/>
|
||||
@@ -1183,13 +1183,13 @@
|
||||
</dia:object>
|
||||
<dia:object type="Flowchart - Box" version="0" id="O25">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="23,18"/>
|
||||
<dia:point val="23,20"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="22.95,17.95;27.05,20.05"/>
|
||||
<dia:rectangle val="22.95,19.95;27.05,22.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_corner">
|
||||
<dia:point val="23,18"/>
|
||||
<dia:point val="23,20"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_width">
|
||||
<dia:real val="4"/>
|
||||
@@ -1215,7 +1215,7 @@
|
||||
<dia:real val="0.80000000000000004"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="pos">
|
||||
<dia:point val="25,19.1941"/>
|
||||
<dia:point val="25,21.1941"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="color">
|
||||
<dia:color val="#000000ff"/>
|
||||
@@ -1228,16 +1228,16 @@
|
||||
</dia:object>
|
||||
<dia:object type="Standard - ZigZagLine" version="1" id="O26">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="27.0488,21"/>
|
||||
<dia:point val="27.0488,23"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="16.95,8.1382;29.05,21.05"/>
|
||||
<dia:rectangle val="16.95,8.1382;29.05,23.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="orth_points">
|
||||
<dia:point val="27.0488,21"/>
|
||||
<dia:point val="29,21"/>
|
||||
<dia:point val="29,15"/>
|
||||
<dia:point val="19,15"/>
|
||||
<dia:point val="27.0488,23"/>
|
||||
<dia:point val="29,23"/>
|
||||
<dia:point val="29,17"/>
|
||||
<dia:point val="19,17"/>
|
||||
<dia:point val="19,8.5"/>
|
||||
<dia:point val="17,8.5"/>
|
||||
</dia:attribute>
|
||||
@@ -1267,13 +1267,13 @@
|
||||
</dia:object>
|
||||
<dia:object type="Flowchart - Box" version="0" id="O27">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="23,24"/>
|
||||
<dia:point val="23,26"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="22.95,23.95;27.05,26.05"/>
|
||||
<dia:rectangle val="22.95,25.95;27.05,28.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_corner">
|
||||
<dia:point val="23,24"/>
|
||||
<dia:point val="23,26"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_width">
|
||||
<dia:real val="4"/>
|
||||
@@ -1299,7 +1299,7 @@
|
||||
<dia:real val="0.80000000000000004"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="pos">
|
||||
<dia:point val="25,25.1941"/>
|
||||
<dia:point val="25,27.1941"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="color">
|
||||
<dia:color val="#000000ff"/>
|
||||
@@ -1312,13 +1312,13 @@
|
||||
</dia:object>
|
||||
<dia:object type="Flowchart - Box" version="0" id="O28">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="23,26"/>
|
||||
<dia:point val="23,28"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="22.95,25.95;27.05,28.05"/>
|
||||
<dia:rectangle val="22.95,27.95;27.05,30.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_corner">
|
||||
<dia:point val="23,26"/>
|
||||
<dia:point val="23,28"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_width">
|
||||
<dia:real val="4"/>
|
||||
@@ -1344,7 +1344,7 @@
|
||||
<dia:real val="0.80000000000000004"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="pos">
|
||||
<dia:point val="25,27.1941"/>
|
||||
<dia:point val="25,29.1941"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="color">
|
||||
<dia:color val="#000000ff"/>
|
||||
@@ -1357,13 +1357,13 @@
|
||||
</dia:object>
|
||||
<dia:object type="Flowchart - Box" version="0" id="O29">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="23,28"/>
|
||||
<dia:point val="23,30"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="22.95,27.95;27.05,30.05"/>
|
||||
<dia:rectangle val="22.95,29.95;27.05,32.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_corner">
|
||||
<dia:point val="23,28"/>
|
||||
<dia:point val="23,30"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_width">
|
||||
<dia:real val="4"/>
|
||||
@@ -1389,7 +1389,7 @@
|
||||
<dia:real val="0.80000000000000004"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="pos">
|
||||
<dia:point val="25,29.1941"/>
|
||||
<dia:point val="25,31.1941"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="color">
|
||||
<dia:color val="#000000ff"/>
|
||||
@@ -1402,13 +1402,13 @@
|
||||
</dia:object>
|
||||
<dia:object type="Flowchart - Box" version="0" id="O30">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="23,30"/>
|
||||
<dia:point val="23,32"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="22.95,29.95;27.05,32.05"/>
|
||||
<dia:rectangle val="22.95,31.95;27.05,34.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_corner">
|
||||
<dia:point val="23,30"/>
|
||||
<dia:point val="23,32"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_width">
|
||||
<dia:real val="4"/>
|
||||
@@ -1434,7 +1434,7 @@
|
||||
<dia:real val="0.80000000000000004"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="pos">
|
||||
<dia:point val="25,31.1941"/>
|
||||
<dia:point val="25,33.1941"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="color">
|
||||
<dia:color val="#000000ff"/>
|
||||
@@ -1447,16 +1447,16 @@
|
||||
</dia:object>
|
||||
<dia:object type="Standard - ZigZagLine" version="1" id="O31">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="22.9512,25"/>
|
||||
<dia:point val="22.9512,27"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="20.95,18.6382;23.0012,25.05"/>
|
||||
<dia:rectangle val="20.95,20.6382;23.0012,27.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="orth_points">
|
||||
<dia:point val="22.9512,25"/>
|
||||
<dia:point val="21,25"/>
|
||||
<dia:point val="21,19"/>
|
||||
<dia:point val="22.9512,19"/>
|
||||
<dia:point val="22.9512,27"/>
|
||||
<dia:point val="21,27"/>
|
||||
<dia:point val="21,21"/>
|
||||
<dia:point val="22.9512,21"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="orth_orient">
|
||||
<dia:enum val="0"/>
|
||||
@@ -1482,14 +1482,14 @@
|
||||
</dia:object>
|
||||
<dia:object type="Standard - ZigZagLine" version="1" id="O32">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="27.0449,23"/>
|
||||
<dia:point val="27.0449,25"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="26.9949,8.1382;33.05,23.05"/>
|
||||
<dia:rectangle val="26.9949,8.1382;33.05,25.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="orth_points">
|
||||
<dia:point val="27.0449,23"/>
|
||||
<dia:point val="31,23"/>
|
||||
<dia:point val="27.0449,25"/>
|
||||
<dia:point val="31,25"/>
|
||||
<dia:point val="31,8.5"/>
|
||||
<dia:point val="33,8.5"/>
|
||||
</dia:attribute>
|
||||
@@ -1515,5 +1515,50 @@
|
||||
<dia:connection handle="1" to="O4" connection="9"/>
|
||||
</dia:connections>
|
||||
</dia:object>
|
||||
<dia:object type="Flowchart - Box" version="0" id="O33">
|
||||
<dia:attribute name="obj_pos">
|
||||
<dia:point val="22,13"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="obj_bb">
|
||||
<dia:rectangle val="21.95,12.95;27.05,15.05"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_corner">
|
||||
<dia:point val="22,13"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_width">
|
||||
<dia:real val="5"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="elem_height">
|
||||
<dia:real val="2"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="show_background">
|
||||
<dia:boolean val="true"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="padding">
|
||||
<dia:real val="0.5"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="text">
|
||||
<dia:composite type="text">
|
||||
<dia:attribute name="string">
|
||||
<dia:string>#Token#</dia:string>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="font">
|
||||
<dia:font family="sans" style="0" name="Helvetica"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="height">
|
||||
<dia:real val="0.80000000000000004"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="pos">
|
||||
<dia:point val="24.5,14.1941"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="color">
|
||||
<dia:color val="#000000ff"/>
|
||||
</dia:attribute>
|
||||
<dia:attribute name="alignment">
|
||||
<dia:enum val="1"/>
|
||||
</dia:attribute>
|
||||
</dia:composite>
|
||||
</dia:attribute>
|
||||
</dia:object>
|
||||
</dia:layer>
|
||||
</dia:diagram>
|
||||
|
||||
@@ -86,6 +86,12 @@ public class Configuration {
|
||||
return new File((String) locations.get(DB));
|
||||
}
|
||||
|
||||
public Configuration dbFile(File dbFile){
|
||||
var locations = locations();
|
||||
locations.put(DB,dbFile.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public int serverPort() {
|
||||
if (!data.containsKey(PORT)) data.put(PORT,80L);
|
||||
var o = data.get(PORT);
|
||||
|
||||
@@ -11,7 +11,6 @@ import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
import static de.srsoftware.widerhall.Util.t;
|
||||
import static de.srsoftware.widerhall.Constants.*;
|
||||
|
||||
public class Database {
|
||||
public static final String HASHED_PASS = "hashedPassword";
|
||||
@@ -21,28 +20,26 @@ public class Database {
|
||||
private static Database singleton = null;
|
||||
private final Connection conn;
|
||||
|
||||
public Request insertInto(String tbName) {
|
||||
return query("INSERT INTO "+tbName);
|
||||
}
|
||||
|
||||
public class Request{
|
||||
|
||||
private final String sql;
|
||||
private final StringBuilder sql;
|
||||
private final HashMap<String, List<Object>> where = new HashMap<>();
|
||||
private final HashMap<String,Object> values = new HashMap<>();
|
||||
private final HashMap<String,Object> setValues = new HashMap<>();
|
||||
|
||||
public Request(String sql) {
|
||||
this(new StringBuilder(sql));
|
||||
}
|
||||
|
||||
public Request(StringBuilder sql) {
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
|
||||
public ResultSet exec() throws SQLException {
|
||||
var sb = new StringBuilder(sql);
|
||||
var args = new ArrayList<>();
|
||||
if (!where.isEmpty()){
|
||||
var clauses = new ArrayList<String>();
|
||||
sb.append(" WHERE ");
|
||||
sql.append(" WHERE ");
|
||||
|
||||
for (var entry : where.entrySet()){
|
||||
var arr = new String[entry.getValue().size()];
|
||||
@@ -51,13 +48,12 @@ public class Database {
|
||||
clauses.add("("+entry.getKey()+" IN ("+marks+"))");
|
||||
args.addAll(entry.getValue());
|
||||
}
|
||||
sb.append(String.join(" AND ",clauses));
|
||||
sql.append(String.join(" AND ",clauses));
|
||||
|
||||
}
|
||||
var sql = sb.toString();
|
||||
LOG.debug(sql);
|
||||
LOG.debug("SQL: {}",sql);
|
||||
try {
|
||||
var stmt = Database.this.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));
|
||||
}
|
||||
@@ -68,7 +64,6 @@ public class Database {
|
||||
}
|
||||
|
||||
public void run() throws SQLException {
|
||||
var sb = new StringBuilder(sql);
|
||||
var args = new ArrayList<>();
|
||||
|
||||
if (!setValues.isEmpty()){
|
||||
@@ -78,7 +73,7 @@ public class Database {
|
||||
expressions.add(" SET "+entry.getKey()+" = ?");
|
||||
args.add(entry.getValue());
|
||||
}
|
||||
sb.append(String.join(", ",expressions));
|
||||
sql.append(String.join(", ",expressions));
|
||||
}
|
||||
|
||||
if (!values.isEmpty()){
|
||||
@@ -87,19 +82,19 @@ public class Database {
|
||||
keys.add(entry.getKey());
|
||||
args.add(entry.getValue());
|
||||
}
|
||||
sb.append("(")
|
||||
sql.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(")");
|
||||
sql.append("(").append(marks).append(")");
|
||||
}
|
||||
|
||||
if (!where.isEmpty()){
|
||||
var clauses = new ArrayList<String>();
|
||||
sb.append(" WHERE ");
|
||||
sql.append(" WHERE ");
|
||||
|
||||
for (var entry : where.entrySet()){
|
||||
var arr = new String[entry.getValue().size()];
|
||||
@@ -108,13 +103,12 @@ public class Database {
|
||||
clauses.add("("+entry.getKey()+" IN ("+marks+"))");
|
||||
args.addAll(entry.getValue());
|
||||
}
|
||||
sb.append(String.join(" AND ",clauses));
|
||||
sql.append(String.join(" AND ",clauses));
|
||||
|
||||
}
|
||||
var sql = sb.toString();
|
||||
LOG.debug(sql);
|
||||
LOG.debug("SQL: {}",sql);
|
||||
try {
|
||||
var stmt = 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));
|
||||
}
|
||||
@@ -129,6 +123,10 @@ public class Database {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String sql() {
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
public Request where(String key, Object ... values) {
|
||||
for (var val : values) where(key,val);
|
||||
@@ -169,6 +167,18 @@ public class Database {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Request deleteFrom(String tableName){
|
||||
return new Request(new StringBuilder("DELETE FROM ").append(tableName).append(" "));
|
||||
}
|
||||
|
||||
public Request insertInto(String tableName){
|
||||
return new Request(new StringBuilder("INSERT INTO ").append(tableName).append(" "));
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return conn instanceof Connection;
|
||||
}
|
||||
|
||||
public static Database open() {
|
||||
if (singleton == null){
|
||||
Configuration config = Configuration.instance();
|
||||
@@ -186,13 +196,27 @@ public class Database {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
public Request query(String sql) {
|
||||
public Request query(StringBuilder sql) {
|
||||
return new Request(sql);
|
||||
}
|
||||
|
||||
private boolean tableExists(String tbName) throws SQLException {
|
||||
public Request select(String tableName,String ... fields) {
|
||||
StringBuilder sql = new StringBuilder("SELECT ");
|
||||
if (fields == null || fields.length == 0){
|
||||
sql.append("*");
|
||||
} else {
|
||||
sql.append(String.join(", ",fields));
|
||||
}
|
||||
return new Request(sql.append(" FROM ").append(tableName));
|
||||
}
|
||||
|
||||
|
||||
public boolean tableExists(String tbName) throws SQLException {
|
||||
try {
|
||||
ResultSet rs = query("SELECT EXISTS (SELECT name FROM sqlite_schema WHERE type='table' AND name='" + tbName + "')").exec();
|
||||
var sql = new StringBuilder("SELECT EXISTS (SELECT name FROM sqlite_schema WHERE type='table' AND name='")
|
||||
.append(tbName)
|
||||
.append("')");
|
||||
ResultSet rs = query( sql).exec();
|
||||
int val = 0;
|
||||
if (rs.next()) val = rs.getInt(1);
|
||||
rs.close();
|
||||
@@ -201,4 +225,12 @@ public class Database {
|
||||
throw new SQLException(t("Was not able to check existence of table {}!",tbName),e);
|
||||
}
|
||||
}
|
||||
|
||||
public Request update(String tableName,String ...expressions) {
|
||||
var sql = new StringBuilder("UPDATE ").append(tableName);
|
||||
if (expressions != null && expressions.length > 0) {
|
||||
sql.append(" SET ").append(String.join(", ",expressions));
|
||||
}
|
||||
return new Request(sql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,11 @@ public class ListMember {
|
||||
public static final String TABLE_NAME = "ListMembers";
|
||||
public static final int STATE_OWNER = 1;
|
||||
public static final int STATE_SUBSCRIBER = 2;
|
||||
public static final int STATE_UNCONFIRMED = 4;
|
||||
private static final String LIST_EMAIL = "list_email";
|
||||
private static final String USER_EMAIL = "user_email";
|
||||
private static final String STATE = "state";
|
||||
private static final String TOKEN = "token";
|
||||
private final String listEmail;
|
||||
private final String userEmail;
|
||||
private final int state;
|
||||
@@ -32,8 +34,8 @@ public class ListMember {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public static ListMember create(String listEmail, String userEmail, int state) throws SQLException {
|
||||
return new ListMember(listEmail,userEmail,state).save();
|
||||
public static ListMember create(MailingList list, User user, int state) throws SQLException {
|
||||
return new ListMember(list.email(),user.email(),state).save();
|
||||
}
|
||||
|
||||
public static void createTable() throws SQLException {
|
||||
@@ -43,15 +45,15 @@ public class ListMember {
|
||||
.append(LIST_EMAIL).append(" ").append(VARCHAR).append(", ")
|
||||
.append(USER_EMAIL).append(" ").append(VARCHAR).append(", ")
|
||||
.append(STATE).append(" ").append(INT).append(", ")
|
||||
.append(TOKEN).append(" ").append(VARCHAR).append(", ")
|
||||
.append("PRIMARY KEY (").append(LIST_EMAIL).append(", ").append(USER_EMAIL).append("));");
|
||||
Database.open().query(sql.toString()).run();
|
||||
Database.open().query(sql).run();
|
||||
}
|
||||
|
||||
public static List<String> listsOwnedBy(User user) {
|
||||
var list = new ArrayList<String>();
|
||||
try {
|
||||
var rs = Database.open()
|
||||
.query("SELECT "+LIST_EMAIL+" FROM " + TABLE_NAME)
|
||||
var rs = Database.open().select(TABLE_NAME,LIST_EMAIL)
|
||||
.where(USER_EMAIL,user.email())
|
||||
.where(STATE,STATE_OWNER)
|
||||
.exec();
|
||||
@@ -64,7 +66,7 @@ public class ListMember {
|
||||
|
||||
public static Map<User,Integer> of(String listEmail) throws SQLException {
|
||||
var rs = Database.open()
|
||||
.query("SELECT * FROM "+TABLE_NAME)
|
||||
.select(TABLE_NAME)
|
||||
.where(LIST_EMAIL,listEmail)
|
||||
.exec();
|
||||
var temp = new HashMap<String,Integer>();
|
||||
@@ -92,23 +94,23 @@ public class ListMember {
|
||||
return this;
|
||||
}
|
||||
|
||||
public static void unsubscribe(String listEmail, User user) throws SQLException {
|
||||
public static void unsubscribe(MailingList list, User user) throws SQLException {
|
||||
var db = Database.open();
|
||||
var rs = db.query("SELECT * FROM "+TABLE_NAME)
|
||||
.where(LIST_EMAIL,listEmail)
|
||||
var rs = db.select(TABLE_NAME)
|
||||
.where(LIST_EMAIL,list.email())
|
||||
.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)
|
||||
db.deleteFrom(TABLE_NAME)
|
||||
.where(LIST_EMAIL,list.email())
|
||||
.where(USER_EMAIL,user.email())
|
||||
.run();
|
||||
} else { // update entry: whitdraw subscription
|
||||
db.query("UPDATE "+TABLE_NAME)
|
||||
db.update(TABLE_NAME)
|
||||
.set(STATE,state)
|
||||
.where(LIST_EMAIL,listEmail)
|
||||
.where(LIST_EMAIL,list.email())
|
||||
.where(USER_EMAIL,user.email())
|
||||
.run();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -63,19 +64,19 @@ public class MailingList {
|
||||
.append(SMTP_PASS).append(" ").append(VARCHAR).append(", ")
|
||||
.append(STATE).append(" ").append(INT)
|
||||
.append(");");
|
||||
Database.open().query(sql.toString()).run();
|
||||
Database.open().query(sql).run();
|
||||
}
|
||||
|
||||
public static void enable(String listEmail, boolean enable) throws SQLException {
|
||||
// https://stackoverflow.com/questions/16440831/bitwise-xor-in-sqlite-bitwise-not-not-working-as-i-expect
|
||||
String expression = enable ? "state = state | "+ENABLED : "state = (~(state & "+ENABLED+"))&(state|"+ENABLED+")";
|
||||
Database.open().query("UPDATE " + TABLE_NAME + " SET "+expression).where(EMAIL, listEmail).run();
|
||||
Database.open().update(TABLE_NAME,expression).where(EMAIL, listEmail).run();
|
||||
}
|
||||
|
||||
public static void hide(String listEmail, boolean hide) throws SQLException {
|
||||
// https://stackoverflow.com/questions/16440831/bitwise-xor-in-sqlite-bitwise-not-not-working-as-i-expect
|
||||
String expression = hide ? "state = (~(state & "+PUBLIC+"))&(state|"+PUBLIC+")" : ("state = state | "+PUBLIC);
|
||||
Database.open().query("UPDATE " + TABLE_NAME + " SET "+expression).where(EMAIL, listEmail).run();
|
||||
Database.open().update(TABLE_NAME,expression).where(EMAIL, listEmail).run();
|
||||
}
|
||||
|
||||
public static List<MailingList> listsOf(User user) {
|
||||
@@ -84,7 +85,7 @@ public class MailingList {
|
||||
var list = new ArrayList<MailingList>();
|
||||
if (keys != null && keys.isEmpty()) return list;
|
||||
try {
|
||||
Database.Request query = Database.open().query("SELECT * FROM " + TABLE_NAME);
|
||||
Database.Request query = Database.open().select(TABLE_NAME);
|
||||
if (keys != null) query.where(EMAIL, keys);
|
||||
var rs = query.exec();
|
||||
while (rs.next()) {
|
||||
@@ -110,7 +111,10 @@ public class MailingList {
|
||||
public static List<MailingList> openLists() {
|
||||
var list = new ArrayList<MailingList>();
|
||||
try {
|
||||
var rs = Database.open().query("SELECT *, (" + STATE + " & " + PUBLIC + ") as test FROM " + TABLE_NAME).where("test", PUBLIC).exec();
|
||||
var rs = Database.open()
|
||||
.select(TABLE_NAME,"*", "(" + STATE + " & " + PUBLIC + ") as test")
|
||||
.where("test", PUBLIC)
|
||||
.exec();
|
||||
while (rs.next()) {
|
||||
var email = rs.getString(EMAIL);
|
||||
var name = rs.getString(NAME);
|
||||
@@ -127,6 +131,31 @@ public class MailingList {
|
||||
return openLists().stream().filter(ml -> ml.email.equals(list)).count() > 0;
|
||||
}
|
||||
|
||||
public static MailingList load(String listEmail) {
|
||||
try {
|
||||
var rs = Database.open()
|
||||
.select(TABLE_NAME)
|
||||
.where(EMAIL,listEmail)
|
||||
.exec();
|
||||
if (rs.next()){
|
||||
return new MailingList(rs.getString(EMAIL),
|
||||
rs.getString(NAME),
|
||||
rs.getString(IMAP_HOST),
|
||||
rs.getInt(IMAP_PORT),
|
||||
rs.getString(IMAP_USER),
|
||||
rs.getString(IMAP_PASS),
|
||||
rs.getString(SMTP_HOST),
|
||||
rs.getInt(SMTP_PORT),
|
||||
rs.getString(SMTP_USER),
|
||||
rs.getString(SMTP_PASS),
|
||||
rs.getInt(STATE));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOG.debug("Failed to load MailingList: ",e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Map<String, Object> safeMap() {
|
||||
var map = new HashMap<String,Object>();
|
||||
String[] parts = email.split("@", 2);
|
||||
|
||||
@@ -16,6 +16,7 @@ import static de.srsoftware.widerhall.Constants.*;
|
||||
public class User {
|
||||
public static final String TABLE_NAME = "Users";
|
||||
private static final Logger LOG = LoggerFactory.getLogger(User.class);
|
||||
|
||||
private String email, salt, hashedPass, name;
|
||||
|
||||
public User(String email, String name, String salt, String hashedPass) {
|
||||
@@ -25,6 +26,25 @@ public class User {
|
||||
this.hashedPass = hashedPass;
|
||||
}
|
||||
|
||||
/*********** field accessors ***************/
|
||||
public String email() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public String hashedPassword() {
|
||||
return hashedPass;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String salt(){
|
||||
return salt;
|
||||
}
|
||||
|
||||
/************** end of field accessors ****************/
|
||||
|
||||
public static User create(String email, String name, String password) throws SQLException {
|
||||
String salt = null;
|
||||
String hashedPass = null;
|
||||
@@ -44,32 +64,7 @@ public class User {
|
||||
.append(HASHED_PASS).append(" ").append(VARCHAR).append(", ")
|
||||
.append(NAME).append(" ").append(VARCHAR)
|
||||
.append(");");
|
||||
Database.open().query(sql.toString()).run();
|
||||
}
|
||||
|
||||
public static List<User> loadAll(Collection<String> emails) throws SQLException {
|
||||
var rs = Database.open()
|
||||
.query("SELECT * FROM "+TABLE_NAME)
|
||||
.where(EMAIL,emails)
|
||||
.exec();
|
||||
var list = new ArrayList<User>();
|
||||
while (rs.next()){
|
||||
var email = rs.getString(EMAIL);
|
||||
var name = rs.getString(NAME);
|
||||
var salt = rs.getString(SALT);
|
||||
var hashedPass = rs.getString(HASHED_PASS);
|
||||
list.add(new User(email,name,salt,hashedPass));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public String email() {
|
||||
return email;
|
||||
}
|
||||
|
||||
|
||||
public String hashedPassword() {
|
||||
return hashedPass;
|
||||
Database.open().query(sql).run();
|
||||
}
|
||||
|
||||
public boolean is(String test){
|
||||
@@ -77,29 +72,31 @@ public class User {
|
||||
return test.equals(name) || test.equals(email);
|
||||
}
|
||||
|
||||
public static List<User> list() {
|
||||
|
||||
public static List<User> loadAll() throws SQLException {
|
||||
return loadAll(null);
|
||||
}
|
||||
|
||||
public static List<User> loadAll(Collection<String> emails) throws SQLException {
|
||||
var userList = new ArrayList<User>();
|
||||
try {
|
||||
var rs = Database.open().query("SELECT * FROM Users").exec();
|
||||
while (rs.next()){
|
||||
var email = rs.getString(EMAIL);
|
||||
var name = rs.getString(NAME);
|
||||
var salt = rs.getString(SALT);
|
||||
var hashedPassword = rs.getString(HASHED_PASS);
|
||||
userList.add(new User(email,name,salt,hashedPassword));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOG.warn("Error loading user list!",e);
|
||||
}
|
||||
var query = Database.open().select(TABLE_NAME);
|
||||
if (emails != null && !emails.isEmpty()) query.where(EMAIL,emails);
|
||||
var rs = query.exec();
|
||||
while (rs.next()) userList.add(new User(
|
||||
rs.getString(EMAIL),
|
||||
rs.getString(NAME),
|
||||
rs.getString(SALT),
|
||||
rs.getString(HASHED_PASS)));
|
||||
return userList;
|
||||
}
|
||||
|
||||
public static User load(String email, String password) throws InvalidKeyException, SQLException {
|
||||
|
||||
|
||||
public static User loadUser(String email, String password) throws InvalidKeyException, SQLException {
|
||||
ResultSet rs = Database.open()
|
||||
.query("SELECT * FROM Users")
|
||||
.select(TABLE_NAME)
|
||||
.where(EMAIL,email)
|
||||
.exec();
|
||||
|
||||
try {
|
||||
if (rs.next()) {
|
||||
email = rs.getString(EMAIL);
|
||||
@@ -124,12 +121,9 @@ public class User {
|
||||
return hashedPass.equals(Util.sha256(password+salt));
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static boolean noUsers() throws SQLException {
|
||||
var rs = Database.open().query("SELECT count(*) FROM users").exec();
|
||||
var rs = Database.open().select(TABLE_NAME,"count(*)").exec();
|
||||
try {
|
||||
if (rs.next()) {
|
||||
return rs.getInt(1) < 1;
|
||||
|
||||
@@ -53,7 +53,12 @@ public class Rest extends HttpServlet {
|
||||
json.put(USER,user.safeMap());
|
||||
switch (path) {
|
||||
case USER_LIST:
|
||||
json.put("users", (user.is(ADMIN) ? User.list() : List.of(user)).stream().map(User::safeMap).toList());
|
||||
try {
|
||||
json.put("users", (user.is(ADMIN) ? User.loadAll() : List.of(user)).stream().map(User::safeMap).toList());
|
||||
} catch (SQLException e) {
|
||||
LOG.debug("Failed to load user list:",e);
|
||||
json.put(ERROR,"failed to load user list");
|
||||
}
|
||||
break;
|
||||
case LIST_LIST:
|
||||
json.put("lists", MailingList.listsOf(user).stream().map(MailingList::safeMap).toList());
|
||||
|
||||
@@ -116,8 +116,8 @@ public class Web extends HttpServlet {
|
||||
}
|
||||
|
||||
try {
|
||||
MailingList.create(email,name,imapHost,imapPort,imapUser,imapPass,smtpHost,smtpPort,smtpUser,smtpPass);
|
||||
ListMember.create(email,user.email(),ListMember.STATE_OWNER);
|
||||
var list = MailingList.create(email,name,imapHost,imapPort,imapUser,imapPass,smtpHost,smtpPort,smtpUser,smtpPass);
|
||||
ListMember.create(list,user,ListMember.STATE_OWNER);
|
||||
return redirectTo(INDEX,resp);
|
||||
} catch (SQLException e) {
|
||||
return t("Failed to create list '{}': {}",name,e.getMessage());
|
||||
@@ -129,13 +129,13 @@ public class Web extends HttpServlet {
|
||||
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
String error = handleGet(req, resp);
|
||||
if (error != null) resp.sendError(400,error);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
String error = handlePost(req, resp);
|
||||
if (error != null) resp.sendError(400,error);
|
||||
}
|
||||
@@ -208,7 +208,7 @@ public class Web extends HttpServlet {
|
||||
if (email == null || pass == null) return loadTemplate("login", Map.of("error",t("Missing username or password!")), resp);
|
||||
if (!Util.isEmail(email)) return loadTemplate("login", Map.of("error",t("'{}' is not a valid email address!",email)), resp);
|
||||
try {
|
||||
var user = User.load(email,pass);
|
||||
var user = User.loadUser(email,pass);
|
||||
req.getSession().setAttribute("user",user);
|
||||
// loading user successfull: goto index
|
||||
resp.sendRedirect(String.join("/",WEB_ROOT,"admin"));
|
||||
@@ -244,7 +244,6 @@ public class Web extends HttpServlet {
|
||||
|
||||
private String loadFile(String filename, HttpServletResponse resp) {
|
||||
var path = String.join(File.separator,baseDir,"static",filename);
|
||||
LOG.debug("loading {}",path);
|
||||
var file = new File(path);
|
||||
if (!file.exists()) return t("File {} does not exist!",filename);
|
||||
try {
|
||||
@@ -324,12 +323,15 @@ public class Web extends HttpServlet {
|
||||
var name = req.getParameter(NAME);
|
||||
var email = req.getParameter(EMAIL);
|
||||
var pass = req.getParameter(PASSWORD);
|
||||
var list = req.getParameter(LIST);
|
||||
var listEmail = req.getParameter(LIST);
|
||||
var data = new HashMap<String,Object>();
|
||||
data.put(NAME,name);
|
||||
data.put(EMAIL,email);
|
||||
data.put(LIST,list);
|
||||
if (list == null || list.isBlank()){
|
||||
data.put(LIST,listEmail);
|
||||
|
||||
var list = MailingList.load(listEmail);
|
||||
|
||||
if (list == null){
|
||||
data.put(ERROR,"No list provided by form data!");
|
||||
return loadTemplate(SUBSCRIBE,data,resp);
|
||||
|
||||
@@ -340,13 +342,14 @@ public class Web extends HttpServlet {
|
||||
}
|
||||
if (pass != null && pass.isBlank()) pass = null;
|
||||
|
||||
User user = null;
|
||||
try {
|
||||
data.put(USER,User.create(email,name,pass).safeMap());
|
||||
user = User.create(email,name,pass);
|
||||
} catch (SQLException sqle) {
|
||||
var cause = getCausingException(sqle);
|
||||
int code = cause.getErrorCode();
|
||||
if (code == PRIMARY_KEY_CONSTRAINT) try {// user already exists
|
||||
data.put(USER,User.load(email,pass).safeMap());
|
||||
user = User.loadUser(email,pass);
|
||||
// success → subscribe
|
||||
} catch (InvalidKeyException | SQLException e) {
|
||||
// invalid credentials
|
||||
@@ -354,9 +357,11 @@ public class Web extends HttpServlet {
|
||||
return loadTemplate(SUBSCRIBE,data,resp);
|
||||
}
|
||||
}
|
||||
data.put(USER,user.safeMap());
|
||||
|
||||
try {
|
||||
ListMember.create(list,email,ListMember.STATE_SUBSCRIBER);
|
||||
data.put(NOTES,t("Successfully subscribed '{}' to '{}'.",email,list));
|
||||
ListMember.create(list,user,ListMember.STATE_SUBSCRIBER);
|
||||
data.put(NOTES,t("Successfully subscribed '{}' to '{}'.",user.email(),list.email()));
|
||||
return loadTemplate(INDEX,data,resp);
|
||||
} catch (SQLException sqle) {
|
||||
LOG.debug("List subscription failed: ",sqle);
|
||||
@@ -376,11 +381,14 @@ public class Web extends HttpServlet {
|
||||
var user = getSessionUser(req);
|
||||
var email = req.getParameter(EMAIL);
|
||||
var pass = req.getParameter(PASSWORD);
|
||||
var list = req.getParameter(LIST);
|
||||
var listEmail = req.getParameter(LIST);
|
||||
data.put(EMAIL,email);
|
||||
data.put(LIST,list);
|
||||
data.put(LIST,listEmail);
|
||||
|
||||
var list = MailingList.load(listEmail);
|
||||
|
||||
if (user != null) data.put(USER,user.safeMap());
|
||||
if (list == null || list.isBlank()){
|
||||
if (list == null){
|
||||
data.put(ERROR,"No list provided by form data!");
|
||||
return loadTemplate(UNSUBSCRIBE,data,resp);
|
||||
|
||||
@@ -393,7 +401,7 @@ public class Web extends HttpServlet {
|
||||
if (pass != null && pass.isBlank()) pass = null;
|
||||
|
||||
try {
|
||||
user = User.load(email,pass);
|
||||
user = User.loadUser(email,pass);
|
||||
req.getSession().setAttribute(USER,user);
|
||||
data.put(USER,user.safeMap());
|
||||
} catch (InvalidKeyException | SQLException e) {
|
||||
@@ -404,10 +412,10 @@ public class Web extends HttpServlet {
|
||||
// if we get here, we should have a valid user
|
||||
try {
|
||||
ListMember.unsubscribe(list,user);
|
||||
data.put(NOTES,t("Sucessfully un-subscribed from '{}'.",list));
|
||||
data.put(NOTES,t("Sucessfully un-subscribed from '{}'.",list.email()));
|
||||
return loadTemplate(INDEX,data,resp);
|
||||
} catch (SQLException e) {
|
||||
LOG.warn("Problem during unscubsription of {} from {}:",user.email(),list,e);
|
||||
LOG.warn("Problem during unscubsription of {} from {}:",user.email(),list.email(),e);
|
||||
data.put(ERROR,"Failed to unsubscribe!");
|
||||
return loadTemplate(UNSUBSCRIBE,data,resp);
|
||||
|
||||
|
||||
57
src/test/java/de/srsoftware/widerhall/data/DatabaseTest.java
Normal file
57
src/test/java/de/srsoftware/widerhall/data/DatabaseTest.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package de.srsoftware.widerhall.data;
|
||||
|
||||
import de.srsoftware.widerhall.Configuration;
|
||||
import junit.framework.TestCase;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class DatabaseTest extends TestCase {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DatabaseTest.class);
|
||||
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
File dbTemp = File.createTempFile("Widerhall",".sqlite");
|
||||
Configuration.instance().dbFile(dbTemp);
|
||||
}
|
||||
|
||||
public void tearDown() {
|
||||
Configuration.instance().dbFile().delete();
|
||||
}
|
||||
|
||||
public void testOpen() throws SQLException {
|
||||
Database db = Database.open();
|
||||
assertTrue(db.isOpen());
|
||||
assertTrue(db.tableExists(User.TABLE_NAME));
|
||||
assertTrue(db.tableExists(MailingList.TABLE_NAME));
|
||||
assertTrue(db.tableExists(ListMember.TABLE_NAME));
|
||||
}
|
||||
|
||||
public void testDeleteFrom(){
|
||||
assertEquals("DELETE FROM Test ",Database.open().deleteFrom("Test").sql());
|
||||
}
|
||||
|
||||
public void testInsertInto(){
|
||||
assertEquals("INSERT INTO Test ",Database.open().insertInto("Test").sql());
|
||||
}
|
||||
|
||||
public void testQuery() {
|
||||
assertEquals("Test", Database.open().query(new StringBuilder("Test")).sql());
|
||||
}
|
||||
|
||||
public void testSelect(){
|
||||
assertEquals("SELECT * FROM Test",Database.open().select("Test").sql());
|
||||
assertEquals("SELECT * FROM Test",Database.open().select("Test","*").sql());
|
||||
assertEquals("SELECT Field FROM Test",Database.open().select("Test","Field").sql());
|
||||
assertEquals("SELECT Field1, Field2, Field3 FROM Test",Database.open().select("Test","Field1","Field2","Field3").sql());
|
||||
}
|
||||
|
||||
public void testUpdate(){
|
||||
assertEquals("UPDATE Test",Database.open().update("Test").sql());
|
||||
assertEquals("UPDATE Test SET x = 5",Database.open().update("Test","x = 5").sql());
|
||||
assertEquals("UPDATE Test SET x = 5, y = 6",Database.open().update("Test","x = 5","y = 6").sql());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user