Commit 01c3728b authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Final touches for 1.0 release.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@9697 b35dd754-fafc-0310-a699-88a17e54d16e
parent f8ff8d60
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Email Listener Plugin Changelog</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY, TD, TH {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
}
H2 {
font-size : 10pt;
font-weight : bold;
padding-left : 1em;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new;
font-weight : bold;
color : #060;
}
PRE {
font-family : courier new;
font-size : 100%;
}
</style>
</head>
<body>
<h1>
Email Listener Plugin Changelog
</h1>
<p><b>1.0</b> -- Dec 27, 2007</p>
<ul>
<li>Initial release.</li>
</ul>
</body>
</html>
\ No newline at end of file
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Email Listener Plugin Readme</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY, TD, TH {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
}
H2 {
font-size : 10pt;
font-weight : bold;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new;
font-weight : bold;
color : #060;
}
PRE {
font-family : courier new;
font-size : 100%;
}
#datatable TH {
color : #fff;
background-color : #2A448C;
text-align : left;
}
#datatable TD {
background-color : #FAF6EF;
}
#datatable .name {
background-color : #DCE2F5;
}
</style>
</head>
<body>
<h1>
Email Listener Plugin Readme
</h1>
<h2>Overview</h2>
<p>
The email listener plugin is a service that connects to an email server using IMAP and
forwards new email messages to specified users.
</p>
<h2>Installation</h2>
<p>Copy emailListener.jar into the plugins directory of your Openfire installation. The
plugin will then be automatically deployed. To upgrade to a new version, copy the new
emailListener.jar file over the existing file or upgrade from the admin console.</p>
<h2>Configuration</h2>
The email listener plugin can be configured via the Openfire Admin Console.
Use the configuration page to specify the email server and user to use to
connect to the server. The list of users to notify of new email messages should be
comma delimited and the bare JID (e.g. john@server.com) should be used. Once the
service has been configued it will automatically connect and start monitoring for new
email messages.
<h2>Using the Plugin</h2>
Once Openfire is started the plugin will connect to the email server and start to monitor
for new email messages. New email message will be sent to specified users. Every time the
configuration of the email listener service is updated the service will be automatically
restarted.
</body>
</html>
......@@ -12,15 +12,20 @@
package org.jivesoftware.openfire.plugin.emailListener;
import com.sun.mail.imap.IMAPFolder;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.xmpp.packet.JID;
import javax.mail.*;
import javax.mail.event.MessageCountAdapter;
import javax.mail.event.MessageCountEvent;
import java.io.IOException;
import java.security.Security;
import java.util.Date;
import java.util.Properties;
import java.util.Collection;
import java.util.ArrayList;
/**
* Email listener service that will send an instant message to specified users
......@@ -48,6 +53,19 @@ public class EmailListener {
private EmailListener() {
}
/**
* Returns true if a connection to the IMAP server was successful.
*
* @return true if a connection to the IMAP server was successful.
*/
public static boolean testConnection(String host, int port, boolean isSSLEnabled, String user, String password,
String folderName) {
Folder folder = openFolder(host, port, isSSLEnabled, user, password, folderName);
boolean success = folder != null && folder.isOpen();
closeFolder(folder, null);
return success;
}
/**
* Opens a connection to the IMAP server and listen for new messages.
*/
......@@ -59,7 +77,7 @@ public class EmailListener {
Thread thread = new Thread("Email Listener Thread") {
public void run() {
// Open the email folder and keep it
folder = openFolder();
folder = openFolder(getHost(), getPort(), isSSLEnabled(), getUser(), getPassword(), getFolder());
if (folder != null) {
// Listen for new email messages until #stop is requested
listenMessages();
......@@ -81,40 +99,25 @@ public class EmailListener {
messageListener = null;
}
/**
* Returns true if a connection to the IMAP server was successful.
*
* @return true if a connection to the IMAP server was successful.
*/
public boolean testConnection() {
Folder folder = openFolder();
boolean success = folder != null && folder.isOpen();
closeFolder(folder, null);
return success;
}
private void listenMessages() {
try {
// Add messageCountListener to listen for new messages
messageListener = new MessageCountAdapter() {
public void messagesAdded(MessageCountEvent ev) {
Message[] msgs = ev.getMessages();
System.out.println("Got " + msgs.length + " new messages");
// Just dump out the new messages
for (int i = 0; i < msgs.length; i++) {
// Send new messages to specified users
for (Message msg : msgs) {
try {
System.out.println("-----");
System.out.println("Message " +
msgs[i].getMessageNumber() + ":");
msgs[i].writeTo(System.out);
} catch (IOException ioex) {
ioex.printStackTrace();
} catch (MessagingException mex) {
mex.printStackTrace();
sendMessage(msg);
}
catch (Exception e) {
Log.error("Error while sending new email message", e);
}
}
}
};
folder.addMessageCountListener(messageListener);
......@@ -127,16 +130,19 @@ public class EmailListener {
f.idle();
supportsIdle = true;
}
} catch (FolderClosedException fex) {
}
catch (FolderClosedException fex) {
throw fex;
} catch (MessagingException mex) {
}
catch (MessagingException mex) {
supportsIdle = false;
}
while (messageListener != null) {
if (supportsIdle && folder instanceof IMAPFolder) {
IMAPFolder f = (IMAPFolder) folder;
f.idle();
} else {
}
else {
Thread.sleep(freq); // sleep for freq milliseconds
// This is to force the IMAP server to send us
......@@ -147,26 +153,103 @@ public class EmailListener {
}
}
} catch (Exception ex) {
}
catch (Exception ex) {
Log.error("Error listening new email messages", ex);
}
}
private Folder openFolder(){
private void sendMessage(Message message) throws Exception {
StringBuilder sb = new StringBuilder();
sb.append("New email has been received\n");
// FROM
sb.append("From: ");
for (Address address : message.getFrom()) {
sb.append(address.toString()).append(" ");
}
sb.append("\n");
// DATE
Date date = message.getSentDate();
sb.append("Received: ").append(date != null ? date.toString() : "UNKNOWN").append("\n");
// SUBJECT
sb.append("Subject: ").append(message.getSubject()).append("\n");
// Apend body
appendMessagePart(message, sb);
// Send notifications to specified users
for (String user : getUsers()) {
// Create notification message
org.xmpp.packet.Message notification = new org.xmpp.packet.Message();
notification.setFrom(XMPPServer.getInstance().getServerInfo().getName());
notification.setTo(user);
notification.setSubject("New email has been received");
notification.setBody(sb.toString());
// Send notification message
XMPPServer.getInstance().getMessageRouter().route(notification);
}
}
private void appendMessagePart(Part part, StringBuilder sb) throws Exception {
/*
* Using isMimeType to determine the content type avoids
* fetching the actual content data until we need it.
*/
if (part.isMimeType("text/plain")) {
// This is plain text"
sb.append((String) part.getContent()).append("\n");
}
else if (part.isMimeType("multipart/*")) {
// This is a Multipart
Multipart mp = (Multipart) part.getContent();
int count = mp.getCount();
for (int i = 0; i < count; i++) {
appendMessagePart(mp.getBodyPart(i), sb);
}
}
else if (part.isMimeType("message/rfc822")) {
// This is a Nested Message
appendMessagePart((Part) part.getContent(), sb);
}
else {
/*
* If we actually want to see the data, and it's not a
* MIME type we know, fetch it and check its Java type.
*/
/*Object o = part.getContent();
if (o instanceof String) {
// This is a string
System.out.println((String) o);
}
else if (o instanceof InputStream) {
// This is just an input stream
InputStream is = (InputStream) o;
int c;
while ((c = is.read()) != -1) {
System.out.write(c);
}
}
else {
// This is an unknown type
System.out.println(o.toString());
}*/
}
}
private static Folder openFolder(String host, int port, boolean isSSLEnabled, String user, String password,
String folder) {
try {
Properties props = System.getProperties();
props.setProperty("mail.imap.host", getHost());
props.setProperty("mail.imap.port", String.valueOf(getPort()));
// props.setProperty("mail.imap.user", getUser());
props.setProperty("mail.imap.host", host);
props.setProperty("mail.imap.port", String.valueOf(port));
props.setProperty("mail.imap.connectiontimeout", String.valueOf(10 * 1000));
// Allow messages with a mix of valid and invalid recipients to still be sent.
props.setProperty("mail.debug", JiveGlobals.getProperty("plugin.email.listener.debug", "false"));
props.setProperty("mail.debug", JiveGlobals.getProperty("plugin.email.listener.debug", "false"));
// Methology from an article on www.javaworld.com (Java Tip 115)
// We will attempt to failback to an insecure connection
// if the secure one cannot be made
if (isSSLEnabled()) {
if (isSSLEnabled) {
// Register with security provider.
Security.setProperty("ssl.SocketFactory.provider", SSL_FACTORY);
......@@ -179,34 +262,36 @@ public class EmailListener {
Session session = Session.getInstance(props, null);
// Get a Store object
Store store = session.getStore(isSSLEnabled() ? "imaps" : "imap");
Store store = session.getStore(isSSLEnabled ? "imaps" : "imap");
// Connect
store.connect(getHost(), getUser(), getPassword());
store.connect(host, user, password);
// Open a Folder
Folder newFolder = store.getFolder(getFolder());
Folder newFolder = store.getFolder(folder);
if (newFolder == null || !newFolder.exists()) {
Log.error("Invalid email folder: " + getFolder());
Log.error("Invalid email folder: " + folder);
return null;
}
newFolder.open(Folder.READ_WRITE);
return newFolder;
} catch (MessagingException e) {
}
catch (Exception e) {
Log.error("Error while initializing email listener", e);
}
return null;
}
private void closeFolder(Folder folder, MessageCountAdapter messageListener) {
private static void closeFolder(Folder folder, MessageCountAdapter messageListener) {
if (folder != null) {
if (messageListener != null) {
folder.removeMessageCountListener(messageListener);
}
try {
folder.close(false);
} catch (MessagingException e) {
}
catch (MessagingException e) {
Log.error("Error closing folder", e);
}
}
......@@ -221,6 +306,15 @@ public class EmailListener {
return JiveGlobals.getProperty("plugin.email.listener.host");
}
/**
* Sets the host where the IMAP server is running or <tt>null</tt> if none was defined.
*
* @param host the host where the IMAP server is running or null if none was defined.
*/
public void setHost(String host) {
JiveGlobals.setProperty("plugin.email.listener.host", host);
}
/**
* Returns the port where the IMAP server is listening. By default unsecured connections
* use port 143 and secured ones use 993.
......@@ -235,7 +329,7 @@ public class EmailListener {
* Sets the port where the IMAP server is listening. By default unsecured connections
* use port 143 and secured ones use 993.
*
* @param port port where the IMAP server is listening.
* @param port port where the IMAP server is listening.
*/
public void setPort(int port) {
JiveGlobals.setProperty("plugin.email.listener.port", Integer.toString(port));
......@@ -251,6 +345,16 @@ public class EmailListener {
return JiveGlobals.getProperty("plugin.email.listener.user");
}
/**
* Sets the user to use to connect to the IMAP server. A null value means that
* this property needs to be configured to be used.
*
* @param user the user to use to connect to the IMAP server.
*/
public void setUser(String user) {
JiveGlobals.setProperty("plugin.email.listener.user", user);
}
/**
* Returns the password to use to connect to the IMAP server. A null value means that
* this property needs to be configured to be used.
......@@ -261,6 +365,16 @@ public class EmailListener {
return JiveGlobals.getProperty("plugin.email.listener.password");
}
/**
* Sets the password to use to connect to the IMAP server. A null value means that
* this property needs to be configured to be used.
*
* @param password the password to use to connect to the IMAP server.
*/
public void setPassword(String password) {
JiveGlobals.setProperty("plugin.email.listener.password", password);
}
/**
* Returns the name of the folder. In some Stores, name can be an absolute path if
* it starts with the hierarchy delimiter. Else it is interpreted relative to the
......@@ -272,6 +386,17 @@ public class EmailListener {
return JiveGlobals.getProperty("plugin.email.listener.folder");
}
/**
* Sets the name of the folder. In some Stores, name can be an absolute path if
* it starts with the hierarchy delimiter. Else it is interpreted relative to the
* 'root' of this namespace.
*
* @param folder the name of the folder.
*/
public void setFolder(String folder) {
JiveGlobals.setProperty("plugin.email.listener.folder", folder);
}
/**
* Returns the milliseconds to wait to check for new emails. This frequency
* is used if the IMAP server does not support idle.
......@@ -282,6 +407,15 @@ public class EmailListener {
return JiveGlobals.getIntProperty("plugin.email.listener.frequency", 5 * 60 * 1000);
}
/**
* Sets the milliseconds to wait to check for new emails. This frequency
* is used if the IMAP server does not support idle.
*
* @param frequency the milliseconds to wait to check for new emails.
*/
public void setFrequency(int frequency) {
JiveGlobals.setProperty("plugin.email.listener.frequency", Integer.toString(frequency));
}
/**
* Returns true if SSL is enabled to connect to the server.
*
......@@ -300,54 +434,19 @@ public class EmailListener {
JiveGlobals.setProperty("plugin.email.listener.ssl", Boolean.toString(enabled));
}
/**
* Sets the host where the IMAP server is running or <tt>null</tt> if none was defined.
*
* @param host the host where the IMAP server is running or null if none was defined.
*/
public void setHost(String host) {
JiveGlobals.setProperty("plugin.email.listener.host", host);
}
/**
* Sets the user to use to connect to the IMAP server. A null value means that
* this property needs to be configured to be used.
*
* @param user the user to use to connect to the IMAP server.
*/
public void setUser(String user) {
JiveGlobals.setProperty("plugin.email.listener.user", user);
}
/**
* Sets the password to use to connect to the IMAP server. A null value means that
* this property needs to be configured to be used.
*
* @param password the password to use to connect to the IMAP server.
*/
public void setPassword(String password) {
JiveGlobals.setProperty("plugin.email.listener.password", password);
}
/**
* Sets the name of the folder. In some Stores, name can be an absolute path if
* it starts with the hierarchy delimiter. Else it is interpreted relative to the
* 'root' of this namespace.
*
* @param folder the name of the folder.
*/
public void setFolder(String folder) {
JiveGlobals.setProperty("plugin.email.listener.folder", folder);
public Collection<String> getUsers() {
String users = JiveGlobals.getProperty("plugin.email.listener.users");
if (users == null || users.trim().length() == 0) {
Collection<String> admins = new ArrayList<String>();
for (JID jid : XMPPServer.getInstance().getAdmins()) {
admins.add(jid.toString());
}
return admins;
}
return StringUtils.stringToCollection(users);
}
/**
* Sets the milliseconds to wait to check for new emails. This frequency
* is used if the IMAP server does not support idle.
*
* @param frequency the milliseconds to wait to check for new emails.
*/
public void setFrequency(int frequency) {
JiveGlobals.setProperty("plugin.email.listener.frequency", Integer.toString(frequency));
public void setUsers(Collection<String> users) {
JiveGlobals.setProperty("plugin.email.listener.users", StringUtils.collectionToString(users));
}
}
......@@ -24,30 +24,25 @@
boolean ssl = ParamUtils.getBooleanParameter(request,"ssl");
String folder = ParamUtils.getParameter(request,"folder");
int frequency = ParamUtils.getIntParameter(request,"frequency",0);
String users = ParamUtils.getParameter(request,"users");
boolean save = request.getParameter("save") != null;
boolean test = request.getParameter("test") != null;
boolean success = ParamUtils.getBooleanParameter(request,"success");
boolean testSuccess = false;
// Handle a test request
EmailListener emailListener = EmailListener.getInstance();
if (test) {
testSuccess = emailListener.testConnection();
}
// Save the email settings if requested
Map errors = new HashMap();
if (save) {
if (host == null) {
Map<String, String> errors = new HashMap<String, String>();
if (test || save) {
if (host == null || host.trim().length() == 0) {
errors.put("host","");
}
if (username == null) {
if (username == null || username.trim().length() == 0) {
errors.put("username","");
}
if (password == null) {
if (password == null || password.trim().length() == 0) {
errors.put("password","");
}
if (folder == null) {
if (folder == null || folder.trim().length() == 0) {
errors.put("folder","");
}
if (frequency <= 0) {
......@@ -56,50 +51,69 @@
if (port <= 0) {
errors.put("port","");
}
if (errors.isEmpty()) {
if (users == null || users.trim().length() == 0) {
errors.put("users","");
}
// Get hash value of existing password
String existingHashPassword = "";
if (emailListener.getPassword() != null) {
existingHashPassword = StringUtils.hash(emailListener.getPassword());
}
// Get hash value of existing password
String existingHashPassword = "";
if (emailListener.getPassword() != null) {
existingHashPassword = StringUtils.hash(emailListener.getPassword());
}
// Check if the new password was changed. If it wasn't changed, then it is the original hashed password
// NOTE: if the new PLAIN password equals the previous HASHED password this fails, but is unlikely.
if (!existingHashPassword.equals(password)) {
// Hash the new password since it was changed
String newHashPassword = "";
if (password != null) {
newHashPassword = StringUtils.hash(password);
}
// Change password if hash values are different
if (!existingHashPassword.equals(newHashPassword)) {
emailListener.setPassword(password);
}
// Check if the new password was changed. If it wasn't changed, then it is the original hashed password
// NOTE: if the new PLAIN password equals the previous HASHED password this fails, but is unlikely.
if (!existingHashPassword.equals(password)) {
// Hash the new password since it was changed
String newHashPassword = "";
if (password != null) {
newHashPassword = StringUtils.hash(password);
}
// Change password if hash values are different
if (!existingHashPassword.equals(newHashPassword)) {
//password = password;
}
}
else {
password = emailListener.getPassword();
}
emailListener.setHost(host);
emailListener.setPort(port);
emailListener.setSSLEnabled(ssl);
emailListener.setUser(username);
emailListener.setFolder(folder);
emailListener.setFrequency(frequency);
// Restart the email listener service
emailListener.stop();
emailListener.start();
}
response.sendRedirect("email-listener.jsp?success=true");
}
// Handle a test request
if (test && errors.isEmpty()) {
testSuccess = EmailListener.testConnection(host, port, ssl, username, password, folder);
}
else {
// Save the email settings if requested
if (save) {
if (errors.isEmpty()) {
emailListener.setHost(host);
emailListener.setPort(port);
emailListener.setSSLEnabled(ssl);
emailListener.setUser(username);
emailListener.setPassword(password);
emailListener.setFolder(folder);
emailListener.setFrequency(frequency);
emailListener.setUsers(StringUtils.stringToCollection(users));
// Restart the email listener service
emailListener.stop();
emailListener.start();
response.sendRedirect("email-listener.jsp?success=true");
}
}
host = emailListener.getHost();
port = emailListener.getPort();
ssl = emailListener.isSSLEnabled();
username = emailListener.getUser();
password = emailListener.getPassword();
folder = emailListener.getFolder();
frequency = emailListener.getFrequency();
host = emailListener.getHost();
port = emailListener.getPort();
ssl = emailListener.isSSLEnabled();
username = emailListener.getUser();
password = emailListener.getPassword();
folder = emailListener.getFolder();
frequency = emailListener.getFrequency();
users = StringUtils.collectionToString(emailListener.getUsers());
}
%>
<html>
......@@ -111,7 +125,7 @@
<p>
Configure the email listener service with the following form. The email listener service
connects to an SMTP server and listens for new messages. Specified users are then alerted by
connects to an email server using IMAP and listens for new messages. Specified users are then alerted by
IM when new messages were detected. Messages are not deleted from the mail server.
</p>
......@@ -130,7 +144,7 @@ IM when new messages were detected. Messages are not deleted from the mail serve
<% } %>
<% if (test && testSuccess) { %>
<% if (test && testSuccess && errors.isEmpty()) { %>
<div class="jive-success">
<table cellpadding="0" cellspacing="0" border="0">
......@@ -143,7 +157,7 @@ IM when new messages were detected. Messages are not deleted from the mail serve
</table>
</div>
<% } else if (test && !testSuccess) { %>
<% } else if (test && !testSuccess && errors.isEmpty()) { %>
<div class="jive-error">
<table cellpadding="0" cellspacing="0" border="0">
......@@ -236,6 +250,19 @@ IM when new messages were detected. Messages are not deleted from the mail serve
</table>
</div>
<% } else if (errors.containsKey("users")) { %>
<div class="jive-error">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td class="jive-icon"><img src="/images/error-16x16.gif" width="16" height="16" border="0" alt=""></td>
<td class="jive-icon-label">Please specify one or more users that will get the notifications.</td>
</tr>
</tbody>
</table>
</div>
<% } %>
<p>
......@@ -293,16 +320,25 @@ IM when new messages were detected. Messages are not deleted from the mail serve
Folder:
</td>
<td nowrap>
<input type="text" name="folder" value="<%= (folder != null) ? folder : "" %>" size="40" maxlength="150">
<input type="text" name="folder" value="<%= (folder != null) ? folder : "Inbox" %>" size="40" maxlength="150">
</td>
</tr>
<tr>
<td nowrap>
Check Frequency:
Check Frequency (millis):
</td>
<td nowrap>
<input type="text" name="frequency" value="<%= (frequency > 0) ? String.valueOf(frequency) : "" %>" size="10" maxlength="15">
</td>
</tr>
<tr>
<td nowrap>
JID of users to notify:<br>
<i>(comma delimited)</i>
</td>
<td nowrap>
<textarea name="users" cols="40" rows="3" wrap="virtual"><%= users%></textarea>
</td>
</tr>
</table>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment