Compare commits

...

4 Commits

Author SHA1 Message Date
Stephan Richter 745ce65a75 fixed use of non-accessible exception 2 months ago
Stephan Richter 0a1e210503 improved permission checks on archive 2 months ago
Stephan Richter 1b484760b9 backported some code from lang_de branch 2 months ago
Stephan Richter 89fda62c9d Löschen von Nachrichten aus dem Archiv implementiert. Dies Löst #2 2 months ago
  1. 2
      pom.xml
  2. 2
      src/main/java/de/srsoftware/widerhall/Constants.java
  3. 10
      src/main/java/de/srsoftware/widerhall/data/Database.java
  4. 16
      src/main/java/de/srsoftware/widerhall/data/MailingList.java
  5. 49
      src/main/java/de/srsoftware/widerhall/data/Post.java
  6. 1
      src/main/java/de/srsoftware/widerhall/mail/ImapClient.java
  7. 64
      src/main/java/de/srsoftware/widerhall/web/Rest.java
  8. 16
      src/main/java/de/srsoftware/widerhall/web/Web.java
  9. 3
      static/templates/archive.st
  10. 9
      static/templates/js.st

2
pom.xml

@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
<groupId>org.example</groupId>
<artifactId>Widerhall</artifactId>
<version>0.2.61</version>
<version>1.0.1</version>
<build>
<plugins>
<plugin>

2
src/main/java/de/srsoftware/widerhall/Constants.java

@ -18,6 +18,8 @@ public class Constants { @@ -18,6 +18,8 @@ public class Constants {
public static final String LIST = "list";
public static final String LOCATIONS = "locations";
public static final String NAME = "name";
public static final String MESSAGE_ID = "message_id";
public static final String MODERATOR = "moderator";
public static final String MONTH = "month";
public static final String NOTES = "notes";
public static final String PASSWORD = "password";

10
src/main/java/de/srsoftware/widerhall/data/Database.java

@ -91,6 +91,7 @@ public class Database { @@ -91,6 +91,7 @@ public class Database {
*/
public class Request{
private String groupBy = null;
private final StringBuilder sql; // buffer the sql to be built
private final HashMap<String, List<Object>> where = new HashMap<>(); // buffer condition statements for select
private final HashMap<String,Object> values = new HashMap<>(); // buffer values for insert/update statements
@ -128,6 +129,10 @@ public class Database { @@ -128,6 +129,10 @@ public class Database {
}
}
private void applyGrouping(){
if (groupBy != null && !groupBy.isBlank()) sql.append(" GROUP BY ").append(groupBy.trim());
}
private void applySorting(){
if (!sortFields.isEmpty()) sql.append(" ORDER BY ").append(String.join(", ",sortFields));
}
@ -148,6 +153,7 @@ public class Database { @@ -148,6 +153,7 @@ public class Database {
var args = new ArrayList<>();
applyValues(args);
applyConditions(args);
applyGrouping();
applySorting();
if (additionalArgs != null) {
for (Object arg : additionalArgs) args.add(arg);
@ -187,6 +193,10 @@ public class Database { @@ -187,6 +193,10 @@ public class Database {
}
}
public Request groupBy(String column) {
groupBy = column;
return this;
}
public void run() throws SQLException {
compile().run();

16
src/main/java/de/srsoftware/widerhall/data/MailingList.java

@ -13,8 +13,7 @@ import org.stringtemplate.v4.ST; @@ -13,8 +13,7 @@ import org.stringtemplate.v4.ST;
import javax.mail.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.xml.crypto.Data;
import java.io.*;
import java.io.UnsupportedEncodingException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
@ -188,7 +187,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -188,7 +187,7 @@ public class MailingList implements MessageHandler, ProblemListener {
}
private void forward(Message message, Stream<ListMember> members) throws MessagingException {
if (hasState(STATE_PUBLIC_ARCHIVE)) storeMessage(message);
if (hasPublicArchive()) storeMessage(message);
String newSender = !hasState(STATE_FORWARD_FROM) ? email() : null;
var receivers = members
.map(ListMember::user)
@ -239,6 +238,10 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -239,6 +238,10 @@ public class MailingList implements MessageHandler, ProblemListener {
return ml;
}
public boolean hasPublicArchive() {
return hasState(STATE_PUBLIC_ARCHIVE);
}
public boolean hasState(int test){
return (state & test) > 0;
}
@ -328,6 +331,7 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -328,6 +331,7 @@ public class MailingList implements MessageHandler, ProblemListener {
}
public boolean mayBeAlteredBy(User user) {
if (user == null) return false;
if (user.hashPermission(PERMISSION_ADMIN)) return true;
try {
if (ListMember.load(this,user).isModerator()) return true;
@ -603,9 +607,9 @@ public class MailingList implements MessageHandler, ProblemListener { @@ -603,9 +607,9 @@ public class MailingList implements MessageHandler, ProblemListener {
if (hasState(STATE_FORWARD_ATTACHED)) map.put(t("forward_attached"),HIDDEN);
if (hasState(STATE_HIDE_RECEIVERS)) map.put(t("hide_receivers"),HIDDEN);
if (hasState(STATE_REPLY_TO_LIST)) map.put(t("reply_to_list"),HIDDEN);
if (hasState(STATE_OPEN_FOR_GUESTS)) map.put(t("open_for_guests"),HIDDEN);
if (hasState(STATE_OPEN_FOR_SUBSCRIBERS)) map.put(t("open_for_subscribers"),HIDDEN);
if (hasState(STATE_PUBLIC_ARCHIVE)) map.put(t("archive"),VISIBLE);
if (isOpenForGuests()) map.put(t("open_for_guests"),HIDDEN);
if (isOpenForSubscribers()) map.put(t("open_for_subscribers"),HIDDEN);
if (hasPublicArchive()) map.put(t("archive"),VISIBLE);
return map;
}

49
src/main/java/de/srsoftware/widerhall/data/Post.java

@ -15,10 +15,7 @@ import java.nio.file.Files; @@ -15,10 +15,7 @@ import java.nio.file.Files;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.*;
import static de.srsoftware.widerhall.Constants.*;
import static de.srsoftware.widerhall.Constants.VARCHAR;
@ -26,12 +23,13 @@ import static de.srsoftware.widerhall.Constants.VARCHAR; @@ -26,12 +23,13 @@ import static de.srsoftware.widerhall.Constants.VARCHAR;
public class Post {
public static final Logger LOG = LoggerFactory.getLogger(Post.class);
public static final String TABLE_NAME = "Posts";
private static final String DATE = "date";
private static final String FILE = "file";
private static final String FROM_ADDR = "from_addr";
private static final String FROM_NAME = "from_name";
private static final String PARENT = "parent";
private static final String LONG = "LONG";
private static final String DATE = "date";
private static final String FILE = "file";
private static final String PARENT = "parent";
private static HashMap<String, Post> cache = new HashMap<>();
private String id, fromAddr, fromName, subject, filename;
@ -115,6 +113,24 @@ public class Post { @@ -115,6 +113,24 @@ public class Post {
}
}
public static ArrayList<Post> find(MailingList list, String month, List<String> allowedSenders) throws SQLException {
var query = Database.open()
.select(TABLE_NAME,"*","strftime('%Y-%m',date/1000,'unixepoch') as month")
.where(LIST,list.email())
.where(MONTH,month);
if (allowedSenders != null) query = query.where(FROM_ADDR,allowedSenders);
var rs = query.sort(DATE)
.compile()
.exec();
try {
var result = new ArrayList<Post>();
while (rs.next()) result.add(Post.from(rs));
return result;
} finally {
rs.close();
}
}
private static Post from(ResultSet rs) {
try {
var id = rs.getString(ID);
@ -141,6 +157,10 @@ public class Post { @@ -141,6 +157,10 @@ public class Post {
return id;
}
public MailingList list() {
return list;
}
public static Post load(String id) throws SQLException {
var rs = Database.open().select(TABLE_NAME).where(ID,id).compile().exec();
try {
@ -161,6 +181,11 @@ public class Post { @@ -161,6 +181,11 @@ public class Post {
FILE,filename);
}
public void remove() throws SQLException {
Database.open().deleteFrom(TABLE_NAME).where(ID,id).compile().run();
file().delete();
}
public Map<String,Object> safeMap() {
return Map.of(ID,id,
LIST,list.name(),
@ -174,10 +199,13 @@ public class Post { @@ -174,10 +199,13 @@ public class Post {
return this;
}
public static Map<String, Object> summarize(MailingList list) throws SQLException {
var sql = new StringBuilder("SELECT count(*) as count,strftime('%Y-%m',date/1000,'unixepoch') as month FROM Posts WHERE list = ? GROUP BY month ORDER BY month;");
public static Map<String, Object> summarize(MailingList list,List<String> limitedUsers) throws SQLException {
var sql = new StringBuilder("SELECT count(*) as count,strftime('%Y-%m',date/1000,'unixepoch') as month FROM Posts");
var query = Database.open().query(sql).where(LIST,list.email()).groupBy(MONTH).sort(MONTH);
if (limitedUsers != null) query.where(FROM_ADDR,limitedUsers);
var rs = query.compile().exec();
var map = new TreeMap<String,Object>();
var rs = Database.open().query(sql).compile(list.email()).exec();
while (rs.next()) map.put(rs.getString("month"),rs.getInt("count"));
rs.close();
return map;
@ -186,5 +214,4 @@ public class Post { @@ -186,5 +214,4 @@ public class Post {
public long timestamp(){
return timestamp;
}
}

1
src/main/java/de/srsoftware/widerhall/mail/ImapClient.java

@ -156,6 +156,7 @@ public class ImapClient { @@ -156,6 +156,7 @@ public class ImapClient {
var days = duration.toDays();
LOG.info("Message {} is {} days old!",message.getSubject(),days);
if (days > holdTime){
LOG.info("…removing");
Folder folder = message.getFolder();
if (!folder.isOpen()) folder.open(Folder.READ_WRITE);
message.setFlag(Flags.Flag.DELETED, true);

64
src/main/java/de/srsoftware/widerhall/web/Rest.java

@ -39,6 +39,7 @@ public class Rest extends HttpServlet { @@ -39,6 +39,7 @@ public class Rest extends HttpServlet {
private static final String LIST_SHOW = "list/show";
private static final String LIST_TEST = "list/test";
private static final String LIST_SUBSCRIBABLE = "list/subscribable";
private static final String MAIL_DROP = "mail/drop";
private static final String USER_ADD_PERMISSION = "user/addpermission";
private static final String USER_DROP_PERMISSION = "user/droppermission";
private static final String USER_LIST = "user/list";
@ -61,27 +62,29 @@ public class Rest extends HttpServlet { @@ -61,27 +62,29 @@ public class Rest extends HttpServlet {
return Map.of(SUCCESS,"Updated user permissions");
}
private Map<String, Object> archive(HttpServletRequest req) {
private Map<String,Object> archive(HttpServletRequest req, User user) throws SQLException {
var list = Util.getMailingList(req);
if (list != null){
try {
var month = req.getParameter(MONTH);
if (month == null || month.isBlank()) {
return Map.of(LIST,list.email(),"summary",Post.summarize(list));
} else {
return Map.of(LIST,list.email(),"posts",Post.find(list,month).stream().map(Post::safeMap).toList());
}
} catch (SQLException e) {
e.printStackTrace();
}
}
LOG.debug("list: {}",list.email());
return Map.of();
if (list == null) throw new IllegalArgumentException(t("You are trying to access a non-existing list!"));
var allowed = list.hasPublicArchive() || list.mayBeAlteredBy(user);
if (!allowed) throw new IllegalAccessError(t("You are not allowed to access the archive of this list!"));
var allEmails = user != null || list.hasState(STATE_OPEN_FOR_SUBSCRIBERS) || list.hasState(STATE_OPEN_FOR_GUESTS);
var limitedSenders = allEmails ? null : list.moderators().map(ListMember::user).map(User::email).toList();
boolean userIsMod = list.mayBeAlteredBy(user);
String month = req.getParameter(MONTH);
if (month == null || month.isBlank()) return Map.of(LIST,list.email(),MODERATOR,userIsMod,"summary",Post.summarize(list,limitedSenders));
return Map.of(LIST,list.email(),MODERATOR,userIsMod,"posts",Post.find(list,month,limitedSenders).stream().map(Post::safeMap).toList());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String error = handleGet(req, resp);
String error;
try {
error = handleGet(req, resp);
} catch (SQLException e) {
error = e.getMessage();
}
if (error != null) resp.sendError(400,error);
}
@ -118,6 +121,21 @@ public class Rest extends HttpServlet { @@ -118,6 +121,21 @@ public class Rest extends HttpServlet {
if (error != null) resp.sendError(400,error);
}
private Map dropMail(String messageId,User user){
try {
var message = Post.load(messageId);
if (message == null) return Map.of(ERROR,t("Cannot remove: unknown message id"));
var allowed = message.list().mayBeAlteredBy(user);
if (allowed){
message.remove();
return Map.of(SUCCESS,t("Message deleted"));
}
return Map.of(ERROR,t("You are not allowed to remove messages from this list!"));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private Map dropPermission(String userEmail, String permissions) {
if (userEmail == null || userEmail.isBlank()) return Map.of(ERROR,"missing user email address!");
try {
@ -131,7 +149,7 @@ public class Rest extends HttpServlet { @@ -131,7 +149,7 @@ public class Rest extends HttpServlet {
LOG.debug("Failed to load user for address {}",userEmail,e);
return Map.of(ERROR,t("Failed to load user for address {}",userEmail));
}
return Map.of(SUCCESS,"Updated user permissions");
return Map.of(SUCCESS,t("Updated user permissions"));
}
private Map enableList(MailingList list, User user, boolean enable) {
@ -146,7 +164,7 @@ public class Rest extends HttpServlet { @@ -146,7 +164,7 @@ public class Rest extends HttpServlet {
}
}
public String handleGet(HttpServletRequest req, HttpServletResponse resp){
public String handleGet(HttpServletRequest req, HttpServletResponse resp) throws SQLException {
var user = Util.getUser(req);
var path = Util.getPath(req);
@ -156,7 +174,7 @@ public class Rest extends HttpServlet { @@ -156,7 +174,7 @@ public class Rest extends HttpServlet {
json.put(USER,user.safeMap());
switch (path) {
case LIST_ARCHIVE:
json.put("archive",archive(req));
json.put("archive",archive(req,user));
break;
case USER_LIST:
try {
@ -179,7 +197,7 @@ public class Rest extends HttpServlet { @@ -179,7 +197,7 @@ public class Rest extends HttpServlet {
} else {
switch (path) {
case LIST_ARCHIVE:
json.put("archive",archive(req));
json.put("archive",archive(req,null));
break;
case LIST_SUBSCRIBABLE:
json.put("lists", MailingList.subscribable().stream().map(MailingList::minimalMap).toList());
@ -245,6 +263,10 @@ public class Rest extends HttpServlet { @@ -245,6 +263,10 @@ public class Rest extends HttpServlet {
case LIST_TEST:
json.putAll(testList(list,user));
break;
case MAIL_DROP:
var messageId = req.getParameter(MESSAGE_ID);
json.putAll(dropMail(messageId,user));
break;
case USER_ADD_PERMISSION:
if (user.hashPermission(User.PERMISSION_ADMIN)){
json.putAll(addPermission(userEmail,permissions));
@ -307,7 +329,7 @@ public class Rest extends HttpServlet { @@ -307,7 +329,7 @@ public class Rest extends HttpServlet {
if (list.hasState(MailingList.STATE_REPLY_TO_LIST)) map.put(KEY_REPLY_TO_LIST,true);
if (list.isOpenForGuests()) map.put(KEY_OPEN_FOR_GUESTS,true);
if (list.isOpenForSubscribers()) map.put(KEY_OPEN_FOR_SUBSCRIBERS,true);
if (list.hasState(MailingList.STATE_PUBLIC_ARCHIVE)) map.put(KEY_ARCHIVE,true);
if (list.hasPublicArchive()) map.put(KEY_ARCHIVE,true);
if (list.hasState(STATE_MODS_CAN_EDIT_MODS)) map.put(KEY_MODS_CAN_EDIT_MODS,true);
if (list.holdTime() != null) map.put(KEY_DELETE_MESSAGES,list.holdTime());
return map;

16
src/main/java/de/srsoftware/widerhall/web/Web.java

@ -133,13 +133,20 @@ public class Web extends TemplateServlet { @@ -133,13 +133,20 @@ public class Web extends TemplateServlet {
}
}
private String archive(HttpServletRequest req, HttpServletResponse resp) {
private String archive(MailingList list, User user, HttpServletRequest req, HttpServletResponse resp) {
if (list == null) return t("The mailing list you are trying to view does not exist!");
var allowed = list.hasPublicArchive() || list.mayBeAlteredBy(user);
if (!allowed) return t("You are not allowed to access the archive of this list");
var map = new HashMap<String,Object>();
var list = Util.getMailingList(req);
map.put(LIST,list.email());
var month = req.getParameter(MONTH);
if (month != null && !month.isBlank())map.put(MONTH,month);
if (month != null && !month.isBlank()){
map.put(MONTH,month);
map.put(MODERATOR,list.mayBeAlteredBy(user));
}
return loadTemplate(ARCHIVE,map,resp);
}
@ -278,10 +285,9 @@ public class Web extends TemplateServlet { @@ -278,10 +285,9 @@ public class Web extends TemplateServlet {
if (user != null) data.put(USER,user.safeMap());
if (list != null) data.put(LIST,list.minimalMap());
String notes = null;
switch (path){
case ARCHIVE:
return archive(req,resp);
return archive(list,user,req,resp);
case CONFIRM:
return confirm(req,resp);
case POST:

3
static/templates/archive.st

@ -18,6 +18,9 @@ @@ -18,6 +18,9 @@
<th>Date</th>
<th>From</th>
<th>Subject</th>
«if (data.moderator)»
<th>Actions</th>
«endif»
</tr>
</table>
<script type="text/javascript">

9
static/templates/js.st

@ -16,6 +16,10 @@ function dropList(listEmail){ @@ -16,6 +16,10 @@ function dropList(listEmail){
$.post('/api/list/drop',{list:listEmail},showListResult,'json');
}
function dropMail(mailId){
$.post('/api/mail/drop',{message_id:mailId},showListResult,'json');
}
function dropMember(userEmail,listEmail){
$.post('/api/list/drop_member',{list:listEmail,email:userEmail},reload,'json');
}
@ -78,6 +82,8 @@ function showList(listEmail){ @@ -78,6 +82,8 @@ function showList(listEmail){
function showListArchive(data){
console.log(data);
let moderator = data.archive.moderator;
console.log('moderator: ',moderator);
let posts = data.archive.posts;
for (let time in posts){
let post = posts[time];
@ -86,6 +92,9 @@ function showListArchive(data){ @@ -86,6 +92,9 @@ function showListArchive(data){
$('<td/>').html('<a href="'+url+'">'+post.date+'</a>').appendTo(row);
$('<td/>').html('<a href="'+url+'">'+post.from_name+'</a>').appendTo(row);
$('<td/>').html('<a href="'+url+'">'+post.subject+'</a>').appendTo(row);
if (moderator){
$('<button/>',{onclick:"dropMail('"+post.id+"');"}).text('Delete').appendTo($('<td/>')).appendTo(row);
}
row.appendTo($('#archive table'));
}
}

Loading…
Cancel
Save