Browse Source

working on dropping old mails

drop_old_mail
Stephan Richter 3 years ago
parent
commit
f37fabe21b
  1. 90
      doc/data structure.dia
  2. 2
      pom.xml
  3. 22
      src/main/java/de/srsoftware/widerhall/data/Database.java
  4. 36
      src/main/java/de/srsoftware/widerhall/data/MailingList.java
  5. 26
      src/main/java/de/srsoftware/widerhall/mail/ImapClient.java
  6. 1
      src/main/java/de/srsoftware/widerhall/web/Rest.java
  7. 3
      src/main/java/de/srsoftware/widerhall/web/Web.java
  8. 21
      static/templates/inspect.st
  9. 2
      static/templates/js.st

90
doc/data structure.dia

@ -1740,5 +1740,95 @@
</dia:composite> </dia:composite>
</dia:attribute> </dia:attribute>
</dia:object> </dia:object>
<dia:object type="Flowchart - Box" version="0" id="O38">
<dia:attribute name="obj_pos">
<dia:point val="12,33"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="11.95,32.95;17.05,35.05"/>
</dia:attribute>
<dia:attribute name="elem_corner">
<dia:point val="12,33"/>
</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>#hold_time#</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="14.5,34.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:object type="Flowchart - Box" version="0" id="O39">
<dia:attribute name="obj_pos">
<dia:point val="12,31"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="11.95,30.95;17.05,33.05"/>
</dia:attribute>
<dia:attribute name="elem_corner">
<dia:point val="12,31"/>
</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>#last_error#</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="14.5,32.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:layer>
</dia:diagram> </dia:diagram>

2
pom.xml

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

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

@ -11,6 +11,7 @@ import java.sql.SQLException;
import java.util.*; import java.util.*;
import static de.srsoftware.widerhall.Util.t; import static de.srsoftware.widerhall.Util.t;
import static de.srsoftware.widerhall.data.MailingList.HOLD_TIME;
/** /**
* @author Stephan Richter, 2022 * @author Stephan Richter, 2022
@ -280,9 +281,30 @@ public class Database {
if (!tableExists(MailingList.TABLE_NAME)) MailingList.createTable(); if (!tableExists(MailingList.TABLE_NAME)) MailingList.createTable();
if (!tableExists(ListMember.TABLE_NAME)) ListMember.createTable(); if (!tableExists(ListMember.TABLE_NAME)) ListMember.createTable();
if (!tableExists(Post.TABLE_NAME)) Post.createTable(); if (!tableExists(Post.TABLE_NAME)) Post.createTable();
if (!columnExists(MailingList.TABLE_NAME,HOLD_TIME)) MailingList.createHoldTimeColumn();
return this; return this;
} }
private boolean columnExists(String tableName, String columnName) throws SQLException {
var rs = Database.open().select("pragma_table_info('"+tableName+"')","COUNT(*) AS num").where("name",columnName).compile().exec();
try {
if (rs.next()) return rs.getInt("num") > 0;
} finally {
rs.close();
}
return false;
}
public void createColumn(String tableName, String colName, String...typeArgs) throws SQLException {
var sql = new StringBuilder("ALTER TABLE ")
.append(tableName)
.append(" ADD COLUMN ")
.append(colName)
.append(" ")
.append(String.join(" ",typeArgs));
new Request(sql).compile().run();
}
/** /**
* prepare a deletion statement * prepare a deletion statement
* @param tableName * @param tableName

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

@ -12,6 +12,7 @@ import org.stringtemplate.v4.ST;
import javax.mail.*; import javax.mail.*;
import javax.mail.internet.AddressException; import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress; import javax.mail.internet.InternetAddress;
import javax.xml.crypto.Data;
import java.io.*; import java.io.*;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
@ -27,14 +28,16 @@ import static de.srsoftware.widerhall.data.User.PERMISSION_ADMIN;
* this class encapsulates a MailingList db object * this class encapsulates a MailingList db object
*/ */
public class MailingList implements MessageHandler, ProblemListener { public class MailingList implements MessageHandler, ProblemListener {
public static final String KEY_FORWARD_FROM = "forward_from"; public static final String KEY_ARCHIVE = "archive";
public static final String KEY_DELETE_MESSAGES = "delete_messages";
public static final String KEY_FORWARD_ATTACHED = "forward_attached"; public static final String KEY_FORWARD_ATTACHED = "forward_attached";
public static final String KEY_FORWARD_FROM = "forward_from";
public static final String KEY_HIDE_RECEIVERS = "hide_receivers"; public static final String KEY_HIDE_RECEIVERS = "hide_receivers";
public static final String KEY_REPLY_TO_LIST = "reply_to_list"; public static final String KEY_MODS_CAN_EDIT_MODS = "edit_mods";
public static final String KEY_OPEN_FOR_GUESTS = "open_for_guests"; public static final String KEY_OPEN_FOR_GUESTS = "open_for_guests";
public static final String KEY_OPEN_FOR_SUBSCRIBERS = "open_for_subscribers"; public static final String KEY_OPEN_FOR_SUBSCRIBERS = "open_for_subscribers";
public static final String KEY_ARCHIVE = "archive"; public static final String KEY_REPLY_TO_LIST = "reply_to_list";
public static final String KEY_MODS_CAN_EDIT_MODS = "edit_mods"; public static final String HOLD_TIME = "hold_time";
private static final Logger LOG = LoggerFactory.getLogger(MailingList.class); private static final Logger LOG = LoggerFactory.getLogger(MailingList.class);
private static final String IMAP_HOST = "imap_host"; private static final String IMAP_HOST = "imap_host";
private static final String IMAP_PORT = "imap_port"; private static final String IMAP_PORT = "imap_port";
@ -68,6 +71,7 @@ public class MailingList implements MessageHandler, ProblemListener {
private ImapClient imap; private ImapClient imap;
private static final HashMap<String,MailingList> cache = new HashMap<>(); private static final HashMap<String,MailingList> cache = new HashMap<>();
private Integer holdTime = null;
/** /**
* create a new ML object * create a new ML object
@ -123,6 +127,10 @@ public class MailingList implements MessageHandler, ProblemListener {
return new MailingList(email, name, imapHost, imapPort, imapUser, imapPass, inbox, smtpHost, smtpPort, smtpUser, smtpPass, DEFAULT_STATE).save(); return new MailingList(email, name, imapHost, imapPort, imapUser, imapPass, inbox, smtpHost, smtpPort, smtpUser, smtpPass, DEFAULT_STATE).save();
} }
public static void createHoldTimeColumn() throws SQLException {
Database.open().createColumn(TABLE_NAME,HOLD_TIME,INT);
}
/** /**
* create underlying db table * create underlying db table
* @throws SQLException * @throws SQLException
@ -148,6 +156,20 @@ public class MailingList implements MessageHandler, ProblemListener {
Database.open().query(sql).compile().run(); Database.open().query(sql).compile().run();
} }
public void deleteMessages(boolean enable, String daysString) {
try {
holdTime = enable ? Integer.parseInt(daysString) : null;
Database.open().update(TABLE_NAME).set(HOLD_TIME, holdTime).run();
} catch (SQLException throwables) {
LOG.warn("Failed to update {} setting!",HOLD_TIME);
}
}
private void dropOldMails() throws MessagingException {
if (holdTime == null) return;
imap.dropMailsOlderThan(holdTime);
}
public String email() { public String email() {
return email; return email;
} }
@ -226,6 +248,10 @@ public class MailingList implements MessageHandler, ProblemListener {
return setFlag(STATE_HIDE_RECEIVERS,hide); return setFlag(STATE_HIDE_RECEIVERS,hide);
} }
public Integer holdTime() {
return holdTime;
}
/** /**
* test, whether the current ML is subscribable by a given user * test, whether the current ML is subscribable by a given user
* @param user * @param user
@ -435,7 +461,7 @@ public class MailingList implements MessageHandler, ProblemListener {
} catch (SQLException e){ } catch (SQLException e){
LOG.warn("Failed to process message '{}'",subject,e); LOG.warn("Failed to process message '{}'",subject,e);
} }
dropOldMails();
} }
public MailingList openForGuests(boolean open) throws SQLException { public MailingList openForGuests(boolean open) throws SQLException {

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

@ -6,6 +6,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.mail.*; import javax.mail.*;
import java.time.Duration;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Properties; import java.util.Properties;
@ -73,9 +75,7 @@ public class ImapClient {
private void handleMessages() throws MessagingException { private void handleMessages() throws MessagingException {
LOG.debug("Reading email of {}:",username); LOG.debug("Reading email of {}:",username);
if (!inbox.isOpen()){ if (!inbox.isOpen()) inbox.open(IMAPFolder.READ_WRITE);
inbox.open(IMAPFolder.READ_WRITE);
}
for (Message message : inbox.getMessages()){ for (Message message : inbox.getMessages()){
if (message.isSet(Flags.Flag.SEEN)) continue; if (message.isSet(Flags.Flag.SEEN)) continue;
handle(message); handle(message);
@ -145,6 +145,26 @@ public class ImapClient {
return this; return this;
} }
public void dropMailsOlderThan(Integer holdTime) throws MessagingException {
var now = new Date();
if (holdTime == null) return;
LOG.debug("Removing mails older than {} days:",holdTime);
if (!inbox.isOpen()) inbox.open(IMAPFolder.READ_WRITE);
for (Message message : inbox.getMessages()){
Date receivedDate = message.getReceivedDate();
Duration duration = Duration.between(receivedDate.toInstant(),now.toInstant());
var days = duration.toDays();
LOG.info("Message {} is {} days old!");
if (days > holdTime){
Folder folder = message.getFolder();
if (!folder.isOpen()) folder.open(Folder.READ_WRITE);
message.setFlag(Flags.Flag.DELETED, true);
}
}
inbox.expunge();
}
public String host(){ public String host(){
return host; return host;
} }

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

@ -309,6 +309,7 @@ public class Rest extends HttpServlet {
if (list.isOpenForSubscribers()) map.put(KEY_OPEN_FOR_SUBSCRIBERS,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.hasState(MailingList.STATE_PUBLIC_ARCHIVE)) map.put(KEY_ARCHIVE,true);
if (list.hasState(STATE_MODS_CAN_EDIT_MODS)) map.put(KEY_MODS_CAN_EDIT_MODS,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; return map;
} }

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

@ -408,7 +408,8 @@ public class Web extends TemplateServlet {
.modsMayNominateMods(Util.getCheckbox(req, KEY_MODS_CAN_EDIT_MODS)) .modsMayNominateMods(Util.getCheckbox(req, KEY_MODS_CAN_EDIT_MODS))
.openForGuests(Util.getCheckbox(req,KEY_OPEN_FOR_GUESTS)) .openForGuests(Util.getCheckbox(req,KEY_OPEN_FOR_GUESTS))
.openForSubscribers(Util.getCheckbox(req,KEY_OPEN_FOR_SUBSCRIBERS)) .openForSubscribers(Util.getCheckbox(req,KEY_OPEN_FOR_SUBSCRIBERS))
.archive(Util.getCheckbox(req,KEY_ARCHIVE)); .archive(Util.getCheckbox(req,KEY_ARCHIVE))
.deleteMessages(Util.getCheckbox(req,KEY_DELETE_MESSAGES),req.getParameter(HOLD_TIME));
data.put(NOTES,t("Sucessfully updated MailingList!")); data.put(NOTES,t("Sucessfully updated MailingList!"));
} catch (SQLException e){ } catch (SQLException e){
LOG.warn("Failed to update MailingList:",e); LOG.warn("Failed to update MailingList:",e);

21
static/templates/inspect.st

@ -17,43 +17,48 @@
</p> </p>
Oweners and Moderators Oweners and Moderators
<label> <label>
<input type="checkbox" name="open_for_subscribers"> <input type="checkbox" name="open_for_subscribers" />
All subscribers All subscribers
</label> </label>
<label> <label>
<input type="checkbox" name="open_for_guests"> <input type="checkbox" name="open_for_guests" />
Everyone (i.e. non members, DANGER!) Everyone (i.e. non members, DANGER!)
</label> </label>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Forward options</legend> <legend>Forward options</legend>
<label> <label>
<input type="checkbox" name="forward_from"> <input type="checkbox" name="forward_from" />
Forward using original sender Forward using original sender
</label> </label>
<label> <label>
<input type="checkbox" name="reply_to_list"> <input type="checkbox" name="reply_to_list" />
Set list adddress in "ReplyTo" header Set list adddress in "ReplyTo" header
</label> </label>
<label> <label>
<input type="checkbox" name="forward_attached"> <input type="checkbox" name="forward_attached" />
Append original message as attachment Append original message as attachment
</label> </label>
<label> <label>
<input type="checkbox" name="hide_receivers"> <input type="checkbox" name="hide_receivers" />
Hide receivers (using BCC) Hide receivers (using BCC)
</label> </label>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Other options</legend> <legend>Other options</legend>
<label> <label>
<input type="checkbox" name="archive"> <input type="checkbox" name="archive" />
Collect messages in public archive Collect messages in public archive
</label> </label>
<label> <label>
<input type="checkbox" name="edit_mods"> <input type="checkbox" name="edit_mods" />
Moderators may edit list of moderators Moderators may edit list of moderators
</label> </label>
<label>
<input type="checkbox" name="delete_messages" />
Delete messages after
<input type="number" name="hold_time" /> days.
</label>
</fieldset> </fieldset>
<button type="submit">Save</button> <button type="submit">Save</button>
</fieldset> </fieldset>

2
static/templates/js.st

@ -103,7 +103,7 @@ function showListArchiveSummary(data){
} }
function showListDetail(data){ function showListDetail(data){
var options = ['forward_from','forward_attached','hide_receivers','reply_to_list','open_for_guests','open_for_subscribers','archive','edit_mods']; var options = ['forward_from','forward_attached','hide_receivers','reply_to_list','open_for_guests','open_for_subscribers','archive','edit_mods','delete_messages'];
options.forEach(function(option,index,array){ options.forEach(function(option,index,array){
console.log(option,'→',data[option]); console.log(option,'→',data[option]);
if (data[option]) $('input[name="'+option+'"]').prop('checked',true); if (data[option]) $('input[name="'+option+'"]').prop('checked',true);

Loading…
Cancel
Save