228 lines
7.3 KiB
Java
228 lines
7.3 KiB
Java
package de.srsoftware.widerhall.mail;
|
|
|
|
import com.sun.mail.imap.IMAPFolder;
|
|
import de.srsoftware.widerhall.Constants;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import javax.mail.*;
|
|
import java.time.Duration;
|
|
import java.util.Date;
|
|
import java.util.HashSet;
|
|
import java.util.Properties;
|
|
|
|
public class ImapClient {
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(ImapClient.class);
|
|
private final int port;
|
|
private final String host, username, password, folderName;
|
|
private final ProblemListener problemListener;
|
|
private IMAPFolder inbox;
|
|
private ListeningThread listeningThread;
|
|
private Heartbeat heartbeat;
|
|
|
|
private class ListeningThread extends Thread {
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(ListeningThread.class);
|
|
private HashSet<MessageHandler> listeners = new HashSet<>();
|
|
private boolean stopped = false;
|
|
private Session session;
|
|
|
|
public ListeningThread addListener(MessageHandler messageHandler) {
|
|
listeners.add(messageHandler);
|
|
return this;
|
|
}
|
|
|
|
public ListeningThread dropListeners() {
|
|
listeners.clear();
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
while (!stopped) {
|
|
try {
|
|
sleep(5000);
|
|
} catch (InterruptedException interruptedException) {
|
|
interruptedException.printStackTrace();
|
|
}
|
|
try {
|
|
openInbox();
|
|
} catch (MessagingException e){
|
|
LOG.warn("Connection problem:",e);
|
|
problemListener.onImapException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void openInbox() throws MessagingException {
|
|
LOG.debug("Connecting and logging in…");
|
|
Properties props = imapProps();
|
|
session = Session.getInstance(props);
|
|
Store store = session.getStore(Constants.IMAPS);
|
|
store.connect(host,username,password);
|
|
LOG.debug("Connected, opening {}:",folderName);
|
|
inbox = (IMAPFolder)store.getFolder(folderName);
|
|
inbox.open(IMAPFolder.READ_WRITE);
|
|
|
|
while (!stopped){
|
|
handleMessages();
|
|
problemListener.clearProblems();
|
|
LOG.debug("Idling.");
|
|
inbox.idle(true);
|
|
}
|
|
}
|
|
|
|
private void handleMessages() throws MessagingException {
|
|
LOG.debug("Reading email of {}:",username);
|
|
if (!inbox.isOpen()) inbox.open(IMAPFolder.READ_WRITE);
|
|
for (Message message : inbox.getMessages()){
|
|
if (message.isSet(Flags.Flag.SEEN)) continue;
|
|
handle(message);
|
|
Folder folder = message.getFolder();
|
|
if (!folder.isOpen()) folder.open(Folder.READ_WRITE);
|
|
message.setFlag(Flags.Flag.SEEN,true);
|
|
}
|
|
}
|
|
|
|
private void handle(Message message) throws MessagingException {
|
|
LOG.debug("Handling {}",message.getSubject());
|
|
for (MessageHandler listener : listeners) listener.onMessageReceived(message);
|
|
}
|
|
|
|
private Properties imapProps() {
|
|
Properties props = new Properties();
|
|
props.put(Constants.PROTOCOL,Constants.IMAPS);
|
|
return props;
|
|
}
|
|
|
|
public ListeningThread doStop() {
|
|
stopped = true;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
private class Heartbeat extends Thread{
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(Heartbeat.class);
|
|
private boolean stopped = false;
|
|
|
|
@Override
|
|
public void run() {
|
|
while (!stopped){
|
|
try {
|
|
sleep(300034);
|
|
if (inbox != null) continue;
|
|
LOG.debug("sending NOOP");
|
|
inbox.doCommand(protocol -> {
|
|
protocol.simpleCommand("NOOP",null);
|
|
return null;
|
|
});
|
|
} catch (InterruptedException | MessagingException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public Heartbeat doStop() {
|
|
stopped = true;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
public ImapClient(String host, int port, String username, String password, String folderName,ProblemListener listener) {
|
|
this.host = host;
|
|
this.port = port;
|
|
this.username = username;
|
|
this.password = password;
|
|
this.folderName = folderName;
|
|
this.problemListener = listener;
|
|
}
|
|
|
|
public ImapClient addListener(MessageHandler messageHandler) {
|
|
if (listeningThread == null) listeningThread = new ListeningThread();
|
|
listeningThread.addListener(messageHandler);
|
|
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(){
|
|
return host;
|
|
}
|
|
|
|
public String username(){
|
|
return username;
|
|
}
|
|
|
|
public String password(){
|
|
return password;
|
|
}
|
|
|
|
public int port(){
|
|
return port;
|
|
}
|
|
|
|
public String folderName(){
|
|
return folderName;
|
|
}
|
|
|
|
public ImapClient move(Message message, String destinationFolder) throws MessagingException {
|
|
if (listeningThread == null || listeningThread.stopped) throw new IllegalStateException("IMAP client not connected!");
|
|
var source = message.getFolder();
|
|
if (!source.isOpen()) source.open(Folder.READ_WRITE);
|
|
var messages = new Message[]{message};
|
|
var store = source.getStore();
|
|
var dest = store.getFolder(new URLName(destinationFolder));
|
|
if (!dest.exists()) dest.create(Folder.HOLDS_MESSAGES);
|
|
source.copyMessages(messages,dest);
|
|
source.setFlags(messages, new Flags(Flags.Flag.DELETED), true);
|
|
source.close(true);
|
|
return this;
|
|
}
|
|
|
|
|
|
public ImapClient start() {
|
|
stop();
|
|
|
|
LOG.info("Creating ListeningThread for {}…",username);
|
|
(listeningThread = new ListeningThread()).start();
|
|
|
|
LOG.info("Creating Heartbeat for {}…",username);
|
|
(heartbeat = new Heartbeat()).start();
|
|
|
|
return this;
|
|
}
|
|
|
|
public ImapClient stop(){
|
|
if (listeningThread != null) {
|
|
LOG.info("Stopping old ListeningThread for {}…",username);
|
|
listeningThread.dropListeners().doStop();
|
|
listeningThread = null;
|
|
}
|
|
if (heartbeat != null){
|
|
heartbeat.doStop();
|
|
heartbeat = null;
|
|
}
|
|
return this;
|
|
}
|
|
}
|