Work in progress ...
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Objects;
|
||||
* This class provides methods to set or get the account details and overrides methods for string
|
||||
* representation, equality checks, and hashing.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ImapAccount {
|
||||
|
||||
/**
|
||||
@@ -15,15 +16,6 @@ public class ImapAccount {
|
||||
* of the mail server that handles IMAP communication.
|
||||
*/
|
||||
protected String server;
|
||||
/**
|
||||
* Retrieves the server address associated with this IMAP*/
|
||||
public String getServer() { return server; }
|
||||
/**
|
||||
* Sets the server address for the IMAP account.
|
||||
*
|
||||
* @param server the server address to be configured for the IMAP account
|
||||
*/
|
||||
public void setServer(String server) { this.server = server; }
|
||||
|
||||
/**
|
||||
* Represents the username or identifier used to authenticate the IMAP account.
|
||||
@@ -31,66 +23,94 @@ public class ImapAccount {
|
||||
* to the IMAP server.
|
||||
*/
|
||||
protected String user;
|
||||
/**
|
||||
* Retrieves the username associated with this IMAP account.
|
||||
*
|
||||
* @return the username of the IMAP account as a String.
|
||||
*/
|
||||
public String getUser() { return user; }
|
||||
/**
|
||||
* Sets the username associated with the IMAP account.
|
||||
*
|
||||
* @param user The username to be set for this IMAP account.
|
||||
*/
|
||||
public void setUser(String user) { this.user = user; }
|
||||
|
||||
/**
|
||||
* The password associated with the IMAP account.
|
||||
* This is used to authenticate the user when connecting to the IMAP server.
|
||||
*/
|
||||
protected String password;
|
||||
/**
|
||||
* Retrieves the password associated with this IMAP account.
|
||||
*
|
||||
* @return the password of the IMAP account.
|
||||
*/
|
||||
public String getPassword() { return password; }
|
||||
/**
|
||||
* Sets the password for the IMAP account.
|
||||
*
|
||||
* @param password The password to be set for the account.
|
||||
*/
|
||||
public void setPassword(String password) { this.password = password; }
|
||||
|
||||
/**
|
||||
* Specifies the protocol used for the IMAP account, such as "IMAP" or "IMAPS".
|
||||
* This field determines the communication protocol for interacting with the server.
|
||||
*/
|
||||
protected String protocol;
|
||||
|
||||
/**
|
||||
* Default constructor only
|
||||
*/
|
||||
public ImapAccount() {
|
||||
// default constructor only
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the server address associated with this IMAP
|
||||
*/
|
||||
public String getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server address for the IMAP account.
|
||||
*
|
||||
* @param server the server address to be configured for the IMAP account
|
||||
*/
|
||||
public void setServer(String server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the username associated with this IMAP account.
|
||||
*
|
||||
* @return the username of the IMAP account as a String.
|
||||
*/
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the username associated with the IMAP account.
|
||||
*
|
||||
* @param user The username to be set for this IMAP account.
|
||||
*/
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the password associated with this IMAP account.
|
||||
*
|
||||
* @return the password of the IMAP account.
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password for the IMAP account.
|
||||
*
|
||||
* @param password The password to be set for the account.
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the protocol used by this IMAP account.
|
||||
*
|
||||
* @return the protocol as a String, which defines the communication method for the IMAP account.
|
||||
*/
|
||||
public String getProtocol() { return protocol; }
|
||||
/**
|
||||
* Sets the protocol used by the IMAP account.
|
||||
* @param protocol the protocol to be used, e.g., "IMAP" or "IMAPS".
|
||||
*/
|
||||
public void setProtocol(String protocol) { this.protocol = protocol; }
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a string representation of the ImapAccount object including its server, user,
|
||||
* a masked password, and protocol information. The password is represented as '########' if it is not empty.
|
||||
* Sets the protocol used by the IMAP account.
|
||||
*
|
||||
* @return A formatted string that describes the ImapAccount object with its server, user, masked password, and protocol.
|
||||
* @param protocol the protocol to be used, e.g., "IMAP" or "IMAPS".
|
||||
*/
|
||||
@Override public String toString() {
|
||||
return String.format("ImapAccount(%s, %s, %s, %s",
|
||||
server,
|
||||
user,
|
||||
password.length()==0 ? "''" : "########" ,
|
||||
protocol);
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +120,8 @@ public class ImapAccount {
|
||||
*
|
||||
* @return An integer hash code representing this ImapAccount instance.
|
||||
*/
|
||||
@Override public int hashCode() {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(server, user, password, protocol);
|
||||
}
|
||||
|
||||
@@ -111,7 +132,8 @@ public class ImapAccount {
|
||||
* @param obj the object to compare with this instance
|
||||
* @return true if the specified object is equal to this ImapAccount instance; false otherwise
|
||||
*/
|
||||
@Override public boolean equals(Object obj) {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// Check type of argument, includes check of null.
|
||||
if (!(obj instanceof ImapAccount)) return false;
|
||||
|
||||
@@ -120,9 +142,24 @@ public class ImapAccount {
|
||||
|
||||
// Check the comparison of all field
|
||||
ImapAccount other = (ImapAccount) obj;
|
||||
return (server == other.server || (server != null && server.equals(other.server))) &&
|
||||
(user == other.user || (user != null && user.equals(other.user))) &&
|
||||
(password == other.password || (password != null && password.equals(other.password))) &&
|
||||
(protocol == other.protocol || (protocol != null && protocol.equals(other.protocol)));
|
||||
return Objects.equals(server, other.server) &&
|
||||
Objects.equals(user, other.user) &&
|
||||
Objects.equals(password, other.password) &&
|
||||
Objects.equals(protocol, other.protocol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a string representation of the ImapAccount object including its server, user,
|
||||
* a masked password, and protocol information. The password is represented as '########' if it is not empty.
|
||||
*
|
||||
* @return A formatted string that describes the ImapAccount object with its server, user, masked password, and protocol.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("ImapAccount(%s, %s, %s, %s",
|
||||
server,
|
||||
user,
|
||||
(password == null || password.isEmpty()) ? "''" : "########",
|
||||
protocol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,18 @@ package de.neitzel.net.imap;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.mail.*;
|
||||
import java.util.*;
|
||||
import javax.mail.Flags;
|
||||
import javax.mail.Folder;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.NoSuchProviderException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Store;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* The ImapAccountMonitor class is responsible for monitoring an IMAP email account
|
||||
@@ -44,13 +54,20 @@ public class ImapAccountMonitor {
|
||||
*/
|
||||
protected Map<String, Folder> folders = new HashMap<String, Folder>();
|
||||
|
||||
/**
|
||||
* A collection of listeners that respond to IMAP-related events.
|
||||
* Each listener in this collection can perform specific actions when triggered
|
||||
* by events such as receiving new email notifications.
|
||||
*/
|
||||
private Collection<ImapListener> imapListeners = new ArrayList<ImapListener>();
|
||||
|
||||
/**
|
||||
* Monitors an IMAP account by establishing a connection to the specified mail server
|
||||
* using the provided account credentials and protocol.
|
||||
*
|
||||
* @param account the IMAP account containing details such as server, user, password, and protocol
|
||||
* @throws NoSuchProviderException if the specified mail store protocol is not available
|
||||
* @throws MessagingException if the connection to the mail server fails
|
||||
* @throws MessagingException if the connection to the mail server fails
|
||||
*/
|
||||
public ImapAccountMonitor(ImapAccount account) throws NoSuchProviderException, MessagingException {
|
||||
log.trace("Constructor ({})", account.toString());
|
||||
@@ -77,19 +94,19 @@ public class ImapAccountMonitor {
|
||||
* Closes the resources associated with the current instance, including
|
||||
* IMAP folders and the store. The method ensures that all folders are
|
||||
* closed properly, followed by the closure of the store.
|
||||
*
|
||||
* <p>
|
||||
* Any exceptions encountered during the closure of folders or the store
|
||||
* are caught and logged without interrupting the overall closing process.
|
||||
* This is to ensure that all resources are attempted to be closed even if
|
||||
* an error occurs with one of them.
|
||||
*
|
||||
* <p>
|
||||
* The method logs the start and end of the closing process for traceability.
|
||||
*/
|
||||
public void close() {
|
||||
log.trace("close() called.");
|
||||
|
||||
// Close the folders
|
||||
for (Folder folder: folders.values()) {
|
||||
for (Folder folder : folders.values()) {
|
||||
try {
|
||||
folder.close(false);
|
||||
} catch (MessagingException ex) {
|
||||
@@ -152,28 +169,28 @@ public class ImapAccountMonitor {
|
||||
* Checks all monitored folders for new email messages, processes unseen and undeleted messages,
|
||||
* and raises a NewMailEvent for each new email discovered. The method ensures processed messages
|
||||
* are marked as seen.
|
||||
*
|
||||
* <p>
|
||||
* The method iterates through all folders currently being monitored, fetching their messages and
|
||||
* evaluating the state of each. If a message is neither marked as seen nor deleted, it is marked
|
||||
* as seen and a NewMailEvent is raised for it.
|
||||
*
|
||||
* <p>
|
||||
* Proper error handling is implemented to log any MessagingException that occurs while processing
|
||||
* the messages of a folder without interrupting the processing of other folders.
|
||||
*
|
||||
* <p>
|
||||
* Note: This method relies on external logging (via `log`) and appropriate event handling
|
||||
* via `raiseNewEmailEvent`. The `folders` collection holds the monitored folders required.
|
||||
*
|
||||
* <p>
|
||||
* The folder processing stops only in case of irrecoverable exceptions outside this method's scope.
|
||||
*/
|
||||
public void check() {
|
||||
log.trace("check() called.");
|
||||
|
||||
// Loop through all folders that are monitored
|
||||
for (Folder folder: folders.values()) {
|
||||
for (Folder folder : folders.values()) {
|
||||
try {
|
||||
// Loop through all messages.
|
||||
Message[] messages = folder.getMessages();
|
||||
for (Message message: messages) {
|
||||
for (Message message : messages) {
|
||||
// Check if message wasn't seen and wasn't deleted
|
||||
if (!message.isSet(Flags.Flag.SEEN) && !message.isSet(Flags.Flag.DELETED)) {
|
||||
// Mark message as seen.
|
||||
@@ -193,11 +210,35 @@ public class ImapAccountMonitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of listeners that respond to IMAP-related events.
|
||||
* Each listener in this collection can perform specific actions when triggered
|
||||
* by events such as receiving new email notifications.
|
||||
* Raises a new email event and notifies all registered listeners.
|
||||
* The method loops through the listeners and invokes their {@code NewEmailReceived}
|
||||
* method with a newly created {@link NewEmailEvent}.
|
||||
* If a listener marks the event as handled, the remaining listeners will
|
||||
* not be notified.
|
||||
*
|
||||
* @param message The email message that triggered the event. This message
|
||||
* is included in the {@link NewEmailEvent} passed to the listeners.
|
||||
*/
|
||||
private Collection<ImapListener> imapListeners = new ArrayList<ImapListener>();
|
||||
protected void raiseNewEmailEvent(Message message) {
|
||||
log.trace("raiseNewEmailEvent() called!");
|
||||
|
||||
// Create the event.
|
||||
NewEmailEvent event = new NewEmailEvent(this, message);
|
||||
|
||||
// Call all listeners.
|
||||
for (ImapListener listener : imapListeners) {
|
||||
log.trace("Calling listener in {} ....", listener.getClass().getName());
|
||||
listener.newEmailReceived(event);
|
||||
|
||||
// Check if the event was handled so no further ImaListeners will be called.
|
||||
if (event.isHandled()) {
|
||||
log.trace("raiseNewEmailEvent(): Breaking out of listener loop because event was handled.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log.trace("raiseNewEmailEvent() call ended!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an IMAP listener to receive notifications about IMAP-related events such as new email arrivals.
|
||||
@@ -222,35 +263,4 @@ public class ImapAccountMonitor {
|
||||
imapListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises a new email event and notifies all registered listeners.
|
||||
* The method loops through the listeners and invokes their {@code NewEmailReceived}
|
||||
* method with a newly created {@link NewEmailEvent}.
|
||||
* If a listener marks the event as handled, the remaining listeners will
|
||||
* not be notified.
|
||||
*
|
||||
* @param message The email message that triggered the event. This message
|
||||
* is included in the {@link NewEmailEvent} passed to the listeners.
|
||||
*/
|
||||
protected void raiseNewEmailEvent(Message message) {
|
||||
log.trace("raiseNewEmailEvent() called!");
|
||||
|
||||
// Create the event.
|
||||
NewEmailEvent event = new NewEmailEvent(this, message);
|
||||
|
||||
// Call all listeners.
|
||||
for (ImapListener listener: imapListeners) {
|
||||
log.trace("Calling listener in {} ....", listener.getClass().getName());
|
||||
listener.newEmailReceived(event);
|
||||
|
||||
// Check if the event was handled so no further ImaListeners will be called.
|
||||
if (event.isHandled()) {
|
||||
log.trace("raiseNewEmailEvent(): Breaking out of listener loop because event was handled.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log.trace("raiseNewEmailEvent() call ended!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ public class NewEmailEvent extends EventObject {
|
||||
* This event encapsulates information about a newly received email,
|
||||
* including its associated message and the source of the event.
|
||||
*
|
||||
* @param source the object on which the event initially occurred; typically represents
|
||||
* the source of the email event, such as an email client or server.
|
||||
* @param source the object on which the event initially occurred; typically represents
|
||||
* the source of the email event, such as an email client or server.
|
||||
* @param message the Message object representing the email associated with this event.
|
||||
*/
|
||||
public NewEmailEvent(Object source, Message message) {
|
||||
|
||||
@@ -8,15 +8,49 @@ import javax.mail.internet.MimeMultipart;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Utility functions to use with javax.mail.Message instances.
|
||||
* Utility methods for extracting textual content from {@link javax.mail.Message} instances.
|
||||
*
|
||||
* <p>This class provides helpers to obtain a best-effort plain-text representation of an email's
|
||||
* content. It supports the common MIME types:
|
||||
* <ul>
|
||||
* <li>{@code text/plain} — returned as-is</li>
|
||||
* <li>{@code text/html} — the HTML body is returned as a string (not converted to plain text)</li>
|
||||
* <li>{@code multipart/*} — parts are inspected recursively; for {@code multipart/alternative}
|
||||
* the last (most faithful) alternative is preferred; for other multipart types the textual
|
||||
* content of all parts is concatenated in order.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>All methods return an empty string when no textual content can be found. The methods may throw
|
||||
* {@link IOException} or {@link MessagingException} when the underlying message parts cannot be read.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class MessageUtils {
|
||||
|
||||
/**
|
||||
* Gets the text of the email message.
|
||||
* @param message Message to get the text from.
|
||||
* @return Text of the email message.
|
||||
* @throws IOException
|
||||
* @throws MessagingException
|
||||
* Private constructor for utility class.
|
||||
* Prevents instantiation of this utility class.
|
||||
*/
|
||||
private MessageUtils() {
|
||||
// Utility class - private constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a best-effort textual representation from the provided {@link Message}.
|
||||
*
|
||||
* <p>Behavior:
|
||||
* <ul>
|
||||
* <li>If the message mime-type is {@code text/plain} the plain text content is returned.</li>
|
||||
* <li>If the message mime-type is {@code text/html} the HTML content is returned as a string.
|
||||
* (This method does not perform HTML-to-plain-text conversion.)</li>
|
||||
* <li>If the message mime-type is {@code multipart/*} the method inspects all parts recursively.
|
||||
* For {@code multipart/alternative} the most faithful alternative (the last part) is chosen.
|
||||
* For other multipart types the textual contents of all parts are concatenated in order.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param message the email message from which to extract text; must not be {@code null}
|
||||
* @return a <strong>non-null</strong> string containing the extracted text (possibly empty)
|
||||
* @throws IOException if an I/O error occurs while reading message content
|
||||
* @throws MessagingException if the message content cannot be processed due to MIME/format errors
|
||||
*/
|
||||
public static String getTextFromMessage(Message message) throws IOException, MessagingException {
|
||||
String result = "";
|
||||
@@ -30,11 +64,23 @@ public class MessageUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text of a MimeMultipart.
|
||||
* @param mimeMultipart MimeMultipart to get the text from.
|
||||
* @return Text of the MimeMultipart instance.
|
||||
* @throws IOException
|
||||
* @throws MessagingException
|
||||
* Extracts the textual content from a {@link MimeMultipart} instance.
|
||||
*
|
||||
* <p>Algorithm:
|
||||
* <ul>
|
||||
* <li>If the multipart has zero parts a {@link MessagingException} is thrown.</li>
|
||||
* <li>If the multipart's content type matches {@code multipart/alternative} the last part
|
||||
* (the most faithful alternative) is returned.</li>
|
||||
* <li>Otherwise the method concatenates the result of {@link #getTextFromBodyPart(BodyPart)}
|
||||
* for each part in document order and returns the combined text.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>The method handles nested multiparts by recursive calls.
|
||||
*
|
||||
* @param mimeMultipart the multipart object to inspect
|
||||
* @return a non-null string containing the concatenated textual content of the multipart
|
||||
* @throws IOException if an I/O error occurs while reading parts
|
||||
* @throws MessagingException if the multipart is malformed or cannot be processed
|
||||
*/
|
||||
private static String getTextFromMimeMultipart(
|
||||
MimeMultipart mimeMultipart) throws IOException, MessagingException {
|
||||
@@ -47,20 +93,29 @@ public class MessageUtils {
|
||||
// alternatives appear in an order of increasing
|
||||
// faithfulness to the original content. Customize as req'd.
|
||||
return getTextFromBodyPart(mimeMultipart.getBodyPart(count - 1));
|
||||
String result = "";
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < count; i++) {
|
||||
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
|
||||
result += getTextFromBodyPart(bodyPart);
|
||||
result.append(getTextFromBodyPart(bodyPart));
|
||||
}
|
||||
return result;
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text of a BodyPart
|
||||
* @param bodyPart BodyPart to get text from.
|
||||
* @return Text of body part or empty string if not a known type.
|
||||
* @throws IOException
|
||||
* @throws MessagingException
|
||||
* Extracts textual content from a single {@link BodyPart}.
|
||||
*
|
||||
* <p>Behavior:
|
||||
* <ul>
|
||||
* <li>{@code text/plain}: returns the plain text content.</li>
|
||||
* <li>{@code text/html}: returns the HTML content as a string (no conversion performed).</li>
|
||||
* <li>If the body part itself contains a {@link MimeMultipart} this method recurses into it.</li>
|
||||
* <li>For unknown or non-textual content types an empty string is returned.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param bodyPart the body part to extract text from
|
||||
* @return the textual content of the body part, or an empty string if none is available
|
||||
* @throws IOException if an I/O error occurs while reading the body part content
|
||||
* @throws MessagingException if the body part cannot be processed
|
||||
*/
|
||||
private static String getTextFromBodyPart(
|
||||
BodyPart bodyPart) throws IOException, MessagingException {
|
||||
@@ -73,8 +128,8 @@ public class MessageUtils {
|
||||
//String html = (String) bodyPart.getContent();
|
||||
// Not parsing the html right now!
|
||||
// result = org.jsoup.Jsoup.parse(html).text();
|
||||
} else if (bodyPart.getContent() instanceof MimeMultipart){
|
||||
result = getTextFromMimeMultipart((MimeMultipart)bodyPart.getContent());
|
||||
} else if (bodyPart.getContent() instanceof MimeMultipart) {
|
||||
result = getTextFromMimeMultipart((MimeMultipart) bodyPart.getContent());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user