package ariba.appcore.util;
import ariba.ui.meta.persistence.ObjectContext;
import ariba.ui.aribaweb.util.AWGenericException;
import ariba.ui.aribaweb.util.AWMimeParsedMessage;
import ariba.util.core.Assert;
import ariba.util.core.ProgressMonitor;
import ariba.util.core.Fmt;
import ariba.appcore.Global;
import ariba.appcore.User;
import com.sun.mail.imap.IMAPFolder;
import javax.mail.Store;
import javax.mail.Folder;
import javax.mail.Session;
import javax.mail.MessagingException;
import javax.mail.AuthenticationFailedException;
import javax.mail.Message;
import javax.mail.event.MessageCountAdapter;
import javax.mail.event.MessageCountEvent;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.InternetAddress;
import java.util.Properties;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class MailMonitor
{
public interface MessageHandler
{
void process (MimeMessage message);
}
Store _store;
Folder _inbox;
Properties _loginParams;
static final String ProtocolProperty = "mail.store.protocol";
static final int NewMessagePollIntervalMsec = 10*1000;
static Session session;
static void initialize (Properties loginParams)
{
if (session == null) {
session = Session.getDefaultInstance(loginParams, null);
}
}
static Session sharedSession ()
{
Assert.that(session != null, "sharedSession called before initialize");
return session;
}
public static MimeMessage mimeForData (byte[] mimeData)
{
ByteArrayInputStream is = new ByteArrayInputStream(mimeData);
try {
return new MimeMessage(sharedSession(), is);
} catch (MessagingException e) {
throw new AWGenericException(e);
}
}
public static byte[] bytesForMime (MimeMessage message)
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
message.writeTo(os);
} catch (IOException e) {
throw new AWGenericException(e);
} catch (MessagingException e) {
throw new AWGenericException(e);
}
return os.toByteArray();
}
public MailMonitor (Properties loginParams)
{
_loginParams = loginParams;
}
public static Store connect (Properties loginParams) throws AuthenticationFailedException
{
initialize(loginParams);
ProgressMonitor.instance().prepare("Connecting to mail server...", 0);
String protocol = (String)loginParams.get(ProtocolProperty);
Store store = null;
try {
store = session.getStore(protocol);
String host = (String)loginParams.get(Fmt.S("mail.%s.host", protocol));
String userName = (String)loginParams.get("mail.username");
String password = (String)loginParams.get("mail.password");
store.connect(host, userName, password);
} catch (MessagingException e) {
throw new AWGenericException(e);
}
return store;
}
void connect ()
{
try {
_store = connect(_loginParams);
_inbox = _store.getFolder("INBOX");
ProgressMonitor.instance().prepare("Fetching messages for folder ${inbox.name}...".toString(), 0);
_inbox.open(Folder.READ_ONLY);
} catch (AuthenticationFailedException e) {
throw new AWGenericException("Failed to login: ${e.getMessage()}".toString(), e);
} catch (MessagingException e) {
throw new AWGenericException("Failed to login: ${e.getMessage()}".toString(), e);
}
}
static final String _LastProcessedKey = MailMonitor.class.getName();
static String getMessageID (Message message)
{
try {
return ((MimeMessage)message).getMessageID();
} catch (MessagingException e) {
throw new AWGenericException(e);
}
}
// Todo: add support for getNewMessagesSince...
public void process (int atMost, MessageHandler handler)
{
ObjectContext.bindNewContext();
connect();
Message[] messages;
try {
messages = _inbox.getMessages();
} catch (MessagingException e) {
throw new AWGenericException(e);
}
int count = messages.length;
int start = Math.max(0, count-atMost);
// we persist the ID of the last message that we processed
Global proceededRecord = Global.findOrCreate(_LastProcessedKey);
String lastId = proceededRecord.getStringValue();
if (lastId != null) {
// backup until we hit our id (or the start
for (int i=count-1; i >=start; i--) {
if (getMessageID(messages[i]).equals(lastId)) {
start = i + 1;
break;
}
}
}
// process backlog
for (int i=start; i < count; i++) {
MimeMessage message = (MimeMessage)messages[i];
proceededRecord.setStringValue(getMessageID(message));
ObjectContext.get().save();
handler.process(message);
}
processIncomingMessages(_inbox, handler, proceededRecord);
}
void processIncomingMessages (Folder folder, final MessageHandler handler, final Global proceededRecord)
{
// event handler for new ones
MessageCountAdapter listener = new MessageCountAdapter() {
public void messagesAdded (MessageCountEvent ev)
{
Message[] messages = ev.getMessages();
System.out.printf("New messages: %s\n", messages.length);
ObjectContext.bindNewContext();
for (Message m : messages) {
MimeMessage message = (MimeMessage)m;
proceededRecord.setStringValue(getMessageID(message));
ObjectContext.get().save();
handler.process(message);
}
}
};
folder.addMessageCountListener(listener);
try {
while (true) {
if (folder instanceof IMAPFolder) {
System.out.println("Using IMAP IDLE...");
try {
((IMAPFolder)folder).idle();
} catch (Exception e) {
// Continue / reconnect
} finally {
folder.removeMessageCountListener(listener);
if (!folder.isOpen()) folder.open(Folder.READ_ONLY);
folder.addMessageCountListener(listener);
}
} else {
int count = _inbox.getMessageCount();
System.out.printf("Checked Inbox... count = %s", count);
Thread.currentThread().sleep(NewMessagePollIntervalMsec);
}
}
} catch (Exception e) {
throw new AWGenericException(e);
}
}
private static final Pattern _FromPattern = Pattern.compile(
"(?m)^From: (?:\"(.+?)\")?\\s?<(.+?)>$");
public static User findOrCreateSender (AWMimeParsedMessage parsedMessage)
{
User sender = null;
if (parsedMessage.getIsPureForward()) {
String text = parsedMessage.plainTextForPart(parsedMessage.getTopLevelParts().get(0));
Matcher m = _FromPattern.matcher(text);
if (m.find()) {
sender = User.findOrCreate(m.group(2), m.group(1));
}
}
if (sender == null) {
MimeMessage message = parsedMessage.getMessage();
InternetAddress from = null;
try {
from = (InternetAddress)message.getFrom()[0];
} catch (MessagingException e) {
throw new AWGenericException(e);
}
sender = User.findOrCreate(from.getAddress(), from.getPersonal());
}
return sender;
}
}