Java-basierte Mailinglisten-Anwendung, die auf IMAP+SMTP aufsetzt, und damit (fast) jede Mailbox in eine Mailingliste verwandeln kann.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

264 lines
8.5 KiB

package de.srsoftware.widerhall.data;
import de.srsoftware.widerhall.Util;
import org.antlr.runtime.MismatchedTokenException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.stringtemplate.v4.ST;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static de.srsoftware.widerhall.Constants.*;
import static de.srsoftware.widerhall.Constants.STATE;
/**
* @author Stephan Richter
* This class embodies the ListMembers table
*/
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_AWAITING_CONFIRMATION = 4;
private static final Logger LOG = LoggerFactory.getLogger(ListMember.class);
private static final String LIST_EMAIL = "list_email";
private static final String USER_EMAIL = "user_email";
private static final String STATE = "state";
private MailingList list;
private User user;
private final String token;
private final int state;
/**
* create a new list member object
* @param list
* @param user
* @param state
* @param token
*/
public ListMember(MailingList list, User user, int state, String token){
this.list = list;
this.user = user;
this.state = state;
this.token = token;
}
/**
* tries to confirm the token:
* This method loads the list member, that is assigned with the token.
* If no db entry is found for the token, null is returned.
* If an entry is found, the respective User object is loaded.
* If no User object is assigned, null is returned.
* If a matching User object is present, the member's state is altered from AWAITING_CONFIRMATION to SUBSCRIBER and the token is dropped.
* @param token
* @return
* @throws SQLException
*/
public static User confirm(String token) throws SQLException {
var rs = Database.open().select(TABLE_NAME).where(TOKEN,token).compile().exec();
if (rs.next()){
var lm = ListMember.from(rs);
rs.close();
if (lm.user != null){
int newState = lm.state ^ STATE_AWAITING_CONFIRMATION | STATE_SUBSCRIBER;
Database.open()
.update(TABLE_NAME)
.set(TOKEN,null)
.set(STATE, newState) //drop confirmation state, set subscriber state
.where(LIST_EMAIL,lm.list.email())
.where(USER_EMAIL,lm.user.email())
.compile()
.run();
}
return lm.user;
}
return null;
}
/**
* 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.
* @param list
* @param user
* @param state
* @return
* @throws SQLException
*/
public static ListMember create(MailingList list, User user, int state) throws SQLException {
String token = null;
if ((state & STATE_AWAITING_CONFIRMATION) > 0){
token = Util.sha256(String.join("/",list.email(),user.email(),user.salt()));
}
return new ListMember(list,user,state,token).save();
}
/**
* create the table for ListMember objects
* @throws SQLException
*/
public static void createTable() throws SQLException {
var sql = new StringBuilder()
.append("CREATE TABLE ").append(TABLE_NAME)
.append(" (")
.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).compile().run();
}
/**
* create a new ListMember object from a ResultSet
* @param rs
* @return
* @throws SQLException
*/
public static ListMember from(ResultSet rs) throws SQLException {
return new ListMember(
MailingList.load(rs.getString(LIST_EMAIL)),
User.load(rs.getString(USER_EMAIL)),
rs.getInt(STATE),
rs.getString(TOKEN));
}
/**
* test, if the current object has a given state
* @param testState
* @return
*/
public boolean hasState(int testState) {
return (state & testState) > 0;
}
public boolean isOwner(){
return hasState(STATE_OWNER);
}
/**
* return a set of list emails of MailingLists owned by the given user
* @param user
* @return
*/
public static Set<String> listsOwnedBy(User user) {
var list = new HashSet<String>();
try {
var request = Database.open().select(TABLE_NAME, LIST_EMAIL, STATE+" & "+STATE_OWNER+" as "+STATE);
if (!user.hashPermission(User.PERMISSION_ADMIN)) request = request.where(USER_EMAIL, user.email()).where(STATE, STATE_OWNER);
var rs = request.compile().exec();
while (rs.next()) list.add(rs.getString(LIST_EMAIL));
} catch (SQLException e) {
LOG.warn("Sammeln der Listen von {} fehlgeschlagen: ",user.email(),e);
}
return list;
}
/**
* load the list member specified by a MailingList and as User object
* @param list
* @param user
* @return
* @throws SQLException
*/
public static ListMember load(MailingList list,User user) throws SQLException {
var rs = Database
.open()
.select(TABLE_NAME)
.where(LIST_EMAIL,list.email())
.where(USER_EMAIL,user.email())
.compile()
.exec();
try {
if (rs.next()) return ListMember.from(rs);
} finally {
rs.close();
}
return null;
}
public static Set<ListMember> of(MailingList list) throws SQLException {
var rs = Database.open().select(TABLE_NAME).where(LIST_EMAIL,list.email()).compile().exec();
var set = new HashSet<ListMember>();
try {
while (rs.next()) set.add(ListMember.from(rs));
} finally {
rs.close();
}
return set;
}
public Map<String,Object> safeMap(){
return Map.of(
EMAIL,user.email(),
NAME,user.name(),
STATE,ListMember.stateText(state)
);
}
/**
* Save the current ListMember object to the database
* @return
* @throws SQLException
*/
private ListMember save() throws SQLException {
var req = Database.open()
.insertInto(TABLE_NAME)
.set(LIST_EMAIL,list.email())
.set(USER_EMAIL,user.email())
.set(STATE,state);
if (token != null) req.set(TOKEN,token);
req.compile().run();
return this;
}
/**
* convert state flag to readable text
* @param state
* @return
*/
public static String stateText(int state) {
var words = new ArrayList<String>();
if ((state & STATE_OWNER) > 0) words.add("Besitzer");
if ((state & STATE_SUBSCRIBER) > 0) words.add("Abonniert");
if ((state & STATE_AWAITING_CONFIRMATION) > 0) words.add("erwartet Bestätigung");
return String.join(", ",words);
}
/**
* get the token of the current list member
* @return
*/
public String token() {
return token;
}
/**
* unsubscribe a list member
* @param list
* @param user
* @throws SQLException
*/
public static void unsubscribe(MailingList list, User user) throws SQLException {
var db = Database.open();
var rs = db.select(TABLE_NAME)
.where(LIST_EMAIL,list.email())
.where(USER_EMAIL,user.email())
.compile()
.exec();
while (rs.next()){
int state = Util.unset(rs.getInt(STATE),STATE_SUBSCRIBER,STATE_AWAITING_CONFIRMATION); // drop subscription and awaiting flags
var req = state < 1 ? db.deleteFrom(TABLE_NAME) : db.update(TABLE_NAME).set(STATE,state).set(TOKEN,null);
req.where(LIST_EMAIL,list.email()).where(USER_EMAIL,user.email()).compile().run();
}
}
public User user(){
return user;
}
}