WhiteListManagerpublic class WhiteListManager extends GenericMailet Manages for each local user a "white list" of remote addresses whose messages
should never be blocked as spam.
The normal behaviour is to check, for a local sender, if a remote recipient
is already in the list: if not, it will be automatically inserted.
This is under the interpretation that if a local sender X sends a message to a
remote recipient Y, then later on if a message is sent by Y to X it should be
considered always valid and never blocked; hence Y should be in the white list
of X.
Another mode of operations is when a local sender sends a message to whitelistManagerAddress
with one of three specific values in the subject, to
(i) send back a message displaying a list of the addresses in his own list;
(ii) insert some new addresses in his own list;
(iii) remove some addresses from his own list.
In all this cases the message will be ghosted and the postmaster will reply
to the sender.
The sender name is always converted to its primary name (handling aliases).
Sample configuration:
<mailet match="SMTPAuthSuccessful" class="WhiteListManager">
<repositoryPath> db://maildb </repositoryPath>
<!--
If true automatically inserts the local sender to remote recipients entries in the whitelist (default is false).
-->
<automaticInsert>true</automaticInsert>
<!--
Set this to an email address of the "whitelist manager" to send commands to (default is null).
-->
<whitelistManagerAddress>whitelist.manager@xxx.yyy</whitelistManagerAddress>
<!--
Set this to a unique text that you can use (by sending a message to the "whitelist manager" above)
to tell the mailet to send back the contents of the white list (default is null).
-->
<displayFlag>display whitelist</displayFlag>
<!--
Set this to a unique text that you can use (by sending a message to the "whitelist manager" above)
to tell the mailet to insert some new remote recipients to the white list (default is null).
-->
<insertFlag>insert whitelist</insertFlag>
<!--
Set this to a unique text that you can use (by sending a message to the "whitelist manager" above)
to tell the mailet to remove some remote recipients from the white list (default is null).
-->
<removeFlag>remove whitelist</removeFlag>
</mailet>
|
Fields Summary |
---|
private boolean | automaticInsert | private String | displayFlag | private String | insertFlag | private String | removeFlag | private MailAddress | whitelistManagerAddress | private String | selectByPK | private String | selectBySender | private String | insert | private String | deleteByPK | private org.apache.mailet.dates.RFC822DateFormat | rfc822DateFormatThe date format object used to generate RFC 822 compliant date headers. | private DataSourceComponent | datasource | private UsersStore | usersStoreThe store containing the local user repository. | private UsersRepository | localusersThe user repository for this mail server. Contains all the users with inboxes
on this server. | private final JDBCUtil | theJDBCUtilThe JDBCUtil helper class | private SqlResources | sqlQueriesContains all of the sql strings for this component. | private File | sqlFileHolds value of property sqlFile. | private Map | sqlParametersHolds value of property sqlParameters. |
Methods Summary |
---|
private void | checkAndInsert(MailAddress senderMailAddress, java.util.Collection recipients)Loops through each address in the recipient list, checks if in the senders
list and inserts in it otherwise.
String senderUser = senderMailAddress.getUser().toLowerCase(Locale.US);
String senderHost = senderMailAddress.getHost().toLowerCase(Locale.US);
senderUser = getPrimaryName(senderUser);
Connection conn = null;
PreparedStatement selectStmt = null;
PreparedStatement insertStmt = null;
boolean dbUpdated = false;
try {
for (Iterator i = recipients.iterator(); i.hasNext(); ) {
ResultSet selectRS = null;
try {
MailAddress recipientMailAddress = (MailAddress)i.next();
String recipientUser = recipientMailAddress.getUser().toLowerCase(Locale.US);
String recipientHost = recipientMailAddress.getHost().toLowerCase(Locale.US);
if (getMailetContext().isLocalServer(recipientHost)) {
// not a remote recipient, so skip
continue;
}
if (conn == null) {
conn = datasource.getConnection();
}
if (selectStmt == null) {
selectStmt = conn.prepareStatement(selectByPK);
}
selectStmt.setString(1, senderUser);
selectStmt.setString(2, senderHost);
selectStmt.setString(3, recipientUser);
selectStmt.setString(4, recipientHost);
selectRS = selectStmt.executeQuery();
if (selectRS.next()) {
//This address was already in the list
continue;
}
if (insertStmt == null) {
insertStmt = conn.prepareStatement(insert);
}
insertStmt.setString(1, senderUser);
insertStmt.setString(2, senderHost);
insertStmt.setString(3, recipientUser);
insertStmt.setString(4, recipientHost);
insertStmt.executeUpdate();
dbUpdated = true;
} finally {
theJDBCUtil.closeJDBCResultSet(selectRS);
}
//Commit our changes if necessary.
if (conn != null && dbUpdated && !conn.getAutoCommit()) {
conn.commit();
dbUpdated = false;
}
}
} catch (SQLException sqle) {
log("Error accessing database", sqle);
throw new MessagingException("Exception thrown", sqle);
} finally {
theJDBCUtil.closeJDBCStatement(selectStmt);
theJDBCUtil.closeJDBCStatement(insertStmt);
//Rollback our changes if necessary.
try {
if (conn != null && dbUpdated && !conn.getAutoCommit()) {
conn.rollback();
dbUpdated = false;
}
}
catch (Exception e) {}
theJDBCUtil.closeJDBCConnection(conn);
}
| private void | checkTables(java.sql.Connection conn)
DatabaseMetaData dbMetaData = conn.getMetaData();
// Need to ask in the case that identifiers are stored, ask the DatabaseMetaInfo.
// Try UPPER, lower, and MixedCase, to see if the table is there.
boolean dbUpdated = false;
dbUpdated = createTable(conn, "whiteListTableName", "createWhiteListTable");
//Commit our changes if necessary.
if (conn != null && dbUpdated && !conn.getAutoCommit()) {
conn.commit();
dbUpdated = false;
}
| private boolean | createTable(java.sql.Connection conn, java.lang.String tableNameSqlStringName, java.lang.String createSqlStringName)
String tableName = sqlQueries.getSqlString(tableNameSqlStringName, true);
DatabaseMetaData dbMetaData = conn.getMetaData();
// Try UPPER, lower, and MixedCase, to see if the table is there.
if (theJDBCUtil.tableExists(dbMetaData, tableName)) {
return false;
}
PreparedStatement createStatement = null;
try {
createStatement =
conn.prepareStatement(sqlQueries.getSqlString(createSqlStringName, true));
createStatement.execute();
StringBuffer logBuffer = null;
logBuffer =
new StringBuffer(64)
.append("Created table '")
.append(tableName)
.append("' using sqlResources string '")
.append(createSqlStringName)
.append("'.");
log(logBuffer.toString());
} finally {
theJDBCUtil.closeJDBCStatement(createStatement);
}
return true;
| public java.lang.String | getMailetInfo()Returns a string describing this mailet.
return "White List Manager mailet";
| private java.lang.String | getPrimaryName(java.lang.String originalUsername)Gets the main name of a local customer, handling alias
String username;
try {
username = localusers.getRealName(originalUsername);
JamesUser user = (JamesUser) localusers.getUserByName(username);
if (user.getAliasing()) {
username = user.getAlias();
}
}
catch (Exception e) {
username = originalUsername;
}
return username;
| private java.util.Map | getSqlParameters()Getter for property sqlParameters.
return this.sqlParameters;
| public void | init()Initializes the mailet.
automaticInsert = new Boolean(getInitParameter("automaticInsert")).booleanValue();
log("automaticInsert: " + automaticInsert);
displayFlag = getInitParameter("displayFlag");
insertFlag = getInitParameter("insertFlag");
removeFlag = getInitParameter("removeFlag");
String whitelistManagerAddressString = getInitParameter("whitelistManagerAddress");
if (whitelistManagerAddressString != null) {
whitelistManagerAddressString = whitelistManagerAddressString.trim();
log("whitelistManagerAddress: " + whitelistManagerAddressString);
try {
whitelistManagerAddress = new MailAddress(whitelistManagerAddressString);
}
catch (javax.mail.internet.ParseException pe) {
throw new MessagingException("Bad whitelistManagerAddress", pe);
}
if (displayFlag != null) {
displayFlag = displayFlag.trim();
log("displayFlag: " + displayFlag);
}
else {
log("displayFlag is null");
}
if (insertFlag != null) {
insertFlag = insertFlag.trim();
log("insertFlag: " + insertFlag);
}
else {
log("insertFlag is null");
}
if (removeFlag != null) {
removeFlag = removeFlag.trim();
log("removeFlag: " + removeFlag);
}
else {
log("removeFlag is null");
}
}
else {
log("whitelistManagerAddress is null; will ignore commands");
}
String repositoryPath = getInitParameter("repositoryPath");
if (repositoryPath != null) {
log("repositoryPath: " + repositoryPath);
}
else {
throw new MessagingException("repositoryPath is null");
}
ServiceManager serviceManager = (ServiceManager) getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
try {
// Get the DataSourceSelector block
DataSourceSelector datasources = (DataSourceSelector) serviceManager.lookup(DataSourceSelector.ROLE);
// Get the data-source required.
int stindex = repositoryPath.indexOf("://") + 3;
String datasourceName = repositoryPath.substring(stindex);
datasource = (DataSourceComponent) datasources.select(datasourceName);
} catch (Exception e) {
throw new MessagingException("Can't get datasource", e);
}
try {
// Get the UsersRepository
usersStore = (UsersStore)serviceManager.lookup(UsersStore.ROLE);
localusers = (UsersRepository)usersStore.getRepository("LocalUsers");
} catch (Exception e) {
throw new MessagingException("Can't get the local users repository", e);
}
try {
initSqlQueries(datasource.getConnection(), getMailetContext());
} catch (Exception e) {
throw new MessagingException("Exception initializing queries", e);
}
selectByPK = sqlQueries.getSqlString("selectByPK", true);
selectBySender = sqlQueries.getSqlString("selectBySender", true);
insert = sqlQueries.getSqlString("insert", true);
deleteByPK = sqlQueries.getSqlString("deleteByPK", true);
| public void | initSqlQueries(java.sql.Connection conn, org.apache.mailet.MailetContext mailetContext)Initializes the sql query environment from the SqlResources file.
Will look for conf/sqlResources.xml.
try {
if (conn.getAutoCommit()) {
conn.setAutoCommit(false);
}
this.sqlFile = new File((String) mailetContext.getAttribute("confDir"), "sqlResources.xml").getCanonicalFile();
sqlQueries.init(this.sqlFile, "WhiteList" , conn, getSqlParameters());
checkTables(conn);
} finally {
theJDBCUtil.closeJDBCConnection(conn);
}
| private void | manageDisplayRequest(Mail mail)Manages a display request.
MailAddress senderMailAddress = mail.getSender();
String senderUser = senderMailAddress.getUser().toLowerCase(Locale.US);
String senderHost = senderMailAddress.getHost().toLowerCase(Locale.US);
senderUser = getPrimaryName(senderUser);
Connection conn = null;
PreparedStatement selectStmt = null;
ResultSet selectRS = null;
StringWriter sout = new StringWriter();
PrintWriter out = new PrintWriter(sout, true);
try {
out.println("Answering on behalf of: " + whitelistManagerAddress);
out.println("Displaying white list of " + (new MailAddress(senderUser, senderHost)) + ":");
out.println();
conn = datasource.getConnection();
selectStmt = conn.prepareStatement(selectBySender);
selectStmt.setString(1, senderUser);
selectStmt.setString(2, senderHost);
selectRS = selectStmt.executeQuery();
while (selectRS.next()) {
MailAddress mailAddress =
new MailAddress(selectRS.getString(1), selectRS.getString(2));
out.println(mailAddress.toInternetAddress().toString());
}
out.println();
out.println("Finished");
sendReplyFromPostmaster(mail, sout.toString());
} catch (SQLException sqle) {
out.println("Error accessing the database");
sendReplyFromPostmaster(mail, sout.toString());
throw new MessagingException("Error accessing database", sqle);
} finally {
theJDBCUtil.closeJDBCResultSet(selectRS);
theJDBCUtil.closeJDBCStatement(selectStmt);
theJDBCUtil.closeJDBCConnection(conn);
}
| private void | manageInsertRequest(Mail mail)Manages an insert request.
MailAddress senderMailAddress = mail.getSender();
String senderUser = senderMailAddress.getUser().toLowerCase(Locale.US);
String senderHost = senderMailAddress.getHost().toLowerCase(Locale.US);
senderUser = getPrimaryName(senderUser);
Connection conn = null;
PreparedStatement selectStmt = null;
PreparedStatement insertStmt = null;
boolean dbUpdated = false;
StringWriter sout = new StringWriter();
PrintWriter out = new PrintWriter(sout, true);
try {
out.println("Answering on behalf of: " + whitelistManagerAddress);
out.println("Inserting in the white list of " + (new MailAddress(senderUser, senderHost)) + " ...");
out.println();
MimeMessage message = mail.getMessage() ;
Object content= message.getContent();
if (message.getContentType().startsWith("text/plain")
&& content instanceof String) {
StringTokenizer st = new StringTokenizer((String) content, " \t\n\r\f,;:<>");
while (st.hasMoreTokens()) {
ResultSet selectRS = null;
try {
MailAddress recipientMailAddress;
try {
recipientMailAddress = new MailAddress(st.nextToken());
}
catch (javax.mail.internet.ParseException pe) {
continue;
}
String recipientUser = recipientMailAddress.getUser().toLowerCase(Locale.US);
String recipientHost = recipientMailAddress.getHost().toLowerCase(Locale.US);
if (getMailetContext().isLocalServer(recipientHost)) {
// not a remote recipient, so skip
continue;
}
if (conn == null) {
conn = datasource.getConnection();
}
if (selectStmt == null) {
selectStmt = conn.prepareStatement(selectByPK);
}
selectStmt.setString(1, senderUser);
selectStmt.setString(2, senderHost);
selectStmt.setString(3, recipientUser);
selectStmt.setString(4, recipientHost);
selectRS = selectStmt.executeQuery();
if (selectRS.next()) {
//This address was already in the list
out.println("Skipped: " + recipientMailAddress);
continue;
}
if (insertStmt == null) {
insertStmt = conn.prepareStatement(insert);
}
insertStmt.setString(1, senderUser);
insertStmt.setString(2, senderHost);
insertStmt.setString(3, recipientUser);
insertStmt.setString(4, recipientHost);
insertStmt.executeUpdate();
dbUpdated = true;
out.println("Inserted: " + recipientMailAddress);
} finally {
theJDBCUtil.closeJDBCResultSet(selectRS);
}
}
if (dbUpdated) {
log("Insertion request issued by " + senderMailAddress);
}
//Commit our changes if necessary.
if (conn != null && dbUpdated && !conn.getAutoCommit()) {
conn.commit() ;
dbUpdated = false;
}
}
else {
out.println("The message must be plain - no action");
}
out.println();
out.println("Finished");
sendReplyFromPostmaster(mail, sout.toString());
} catch (SQLException sqle) {
out.println("Error accessing the database");
sendReplyFromPostmaster(mail, sout.toString());
throw new MessagingException("Error accessing the database", sqle);
} catch (IOException ioe) {
out.println("Error getting message content");
sendReplyFromPostmaster(mail, sout.toString());
throw new MessagingException("Error getting message content", ioe);
} finally {
theJDBCUtil.closeJDBCStatement(selectStmt);
theJDBCUtil.closeJDBCStatement(insertStmt);
//Rollback our changes if necessary.
try {
if (conn != null && dbUpdated && !conn.getAutoCommit()) {
conn.rollback() ;
dbUpdated = false;
}
}
catch (Exception e) {}
theJDBCUtil.closeJDBCConnection(conn);
}
| private void | manageRemoveRequest(Mail mail)Manages a remove request.
MailAddress senderMailAddress = mail.getSender();
String senderUser = senderMailAddress.getUser().toLowerCase(Locale.US);
String senderHost = senderMailAddress.getHost().toLowerCase(Locale.US);
senderUser = getPrimaryName(senderUser);
Connection conn = null;
PreparedStatement selectStmt = null;
PreparedStatement deleteStmt = null;
boolean dbUpdated = false;
StringWriter sout = new StringWriter();
PrintWriter out = new PrintWriter(sout, true);
try {
out.println("Answering on behalf of: " + whitelistManagerAddress);
out.println("Removing from the white list of " + (new MailAddress(senderUser, senderHost)) + " ...");
out.println();
MimeMessage message = mail.getMessage() ;
Object content= message.getContent();
if (message.getContentType().startsWith("text/plain")
&& content instanceof String) {
StringTokenizer st = new StringTokenizer((String) content, " \t\n\r\f,;:<>");
while (st.hasMoreTokens()) {
ResultSet selectRS = null;
try {
MailAddress recipientMailAddress;
try {
recipientMailAddress = new MailAddress(st.nextToken());
}
catch (javax.mail.internet.ParseException pe) {
continue;
}
String recipientUser = recipientMailAddress.getUser().toLowerCase(Locale.US);
String recipientHost = recipientMailAddress.getHost().toLowerCase(Locale.US);
if (getMailetContext().isLocalServer(recipientHost)) {
// not a remote recipient, so skip
continue;
}
if (conn == null) {
conn = datasource.getConnection();
}
if (selectStmt == null) {
selectStmt = conn.prepareStatement(selectByPK);
}
selectStmt.setString(1, senderUser);
selectStmt.setString(2, senderHost);
selectStmt.setString(3, recipientUser);
selectStmt.setString(4, recipientHost);
selectRS = selectStmt.executeQuery();
if (!selectRS.next()) {
//This address was not in the list
out.println("Skipped: " + recipientMailAddress);
continue;
}
if (deleteStmt == null) {
deleteStmt = conn.prepareStatement(deleteByPK);
}
deleteStmt.setString(1, senderUser);
deleteStmt.setString(2, senderHost);
deleteStmt.setString(3, recipientUser);
deleteStmt.setString(4, recipientHost);
deleteStmt.executeUpdate();
dbUpdated = true;
out.println("Removed: " + recipientMailAddress);
} finally {
theJDBCUtil.closeJDBCResultSet(selectRS);
}
}
if (dbUpdated) {
log("Removal request issued by " + senderMailAddress);
}
//Commit our changes if necessary.
if (conn != null && dbUpdated && !conn.getAutoCommit()) {
conn.commit() ;
dbUpdated = false;
}
}
else {
out.println("The message must be plain - no action");
}
out.println();
out.println("Finished");
sendReplyFromPostmaster(mail, sout.toString());
} catch (SQLException sqle) {
out.println("Error accessing the database");
sendReplyFromPostmaster(mail, sout.toString());
throw new MessagingException("Error accessing the database", sqle);
} catch (IOException ioe) {
out.println("Error getting message content");
sendReplyFromPostmaster(mail, sout.toString());
throw new MessagingException("Error getting message content", ioe);
} finally {
theJDBCUtil.closeJDBCStatement(selectStmt);
theJDBCUtil.closeJDBCStatement(deleteStmt);
//Rollback our changes if necessary.
try {
if (conn != null && dbUpdated && !conn.getAutoCommit()) {
conn.rollback() ;
dbUpdated = false;
}
}
catch (Exception e) {}
theJDBCUtil.closeJDBCConnection(conn);
}
| private void | sendReplyFromPostmaster(Mail mail, java.lang.String stringContent)
try {
MailAddress notifier = getMailetContext().getPostmaster();
MailAddress senderMailAddress = mail.getSender();
MimeMessage message = mail.getMessage();
//Create the reply message
MimeMessage reply = new MimeMessage(Session.getDefaultInstance(System.getProperties(), null));
//Create the list of recipients in the Address[] format
InternetAddress[] rcptAddr = new InternetAddress[1];
rcptAddr[0] = senderMailAddress.toInternetAddress();
reply.setRecipients(Message.RecipientType.TO, rcptAddr);
//Set the sender...
reply.setFrom(notifier.toInternetAddress());
//Create the message body
MimeMultipart multipart = new MimeMultipart();
//Add message as the first mime body part
MimeBodyPart part = new MimeBodyPart();
part.setContent(stringContent, "text/plain");
part.setHeader(RFC2822Headers.CONTENT_TYPE, "text/plain");
multipart.addBodyPart(part);
reply.setContent(multipart);
reply.setHeader(RFC2822Headers.CONTENT_TYPE, multipart.getContentType());
//Create the list of recipients in our MailAddress format
Set recipients = new HashSet();
recipients.add(senderMailAddress);
//Set additional headers
if (reply.getHeader(RFC2822Headers.DATE)==null){
reply.setHeader(RFC2822Headers.DATE, rfc822DateFormat.format(new java.util.Date()));
}
String subject = message.getSubject();
if (subject == null) {
subject = "";
}
if (subject.indexOf("Re:") == 0){
reply.setSubject(subject);
} else {
reply.setSubject("Re:" + subject);
}
reply.setHeader(RFC2822Headers.IN_REPLY_TO, message.getMessageID());
//Send it off...
getMailetContext().sendMail(notifier, recipients, reply);
}
catch (Exception e) {
log("Exception found sending reply", e);
}
| public void | service(Mail mail)Services the mailet.
// check if it's a local sender
MailAddress senderMailAddress = mail.getSender();
if (senderMailAddress == null) {
return;
}
String senderUser = senderMailAddress.getUser();
String senderHost = senderMailAddress.getHost();
if ( !getMailetContext().isLocalServer(senderHost)
|| !getMailetContext().isLocalUser(senderUser)) {
// not a local sender, so return
return;
}
Collection recipients = mail.getRecipients();
if (recipients.size() == 1
&& whitelistManagerAddress != null
&& whitelistManagerAddress.equals(recipients.toArray()[0])) {
mail.setState(Mail.GHOST);
String subject = mail.getMessage().getSubject();
if (displayFlag != null && displayFlag.equals(subject)) {
manageDisplayRequest(mail);
}
else if (insertFlag != null && insertFlag.equals(subject)) {
manageInsertRequest(mail);
}
else if (removeFlag != null && removeFlag.equals(subject)) {
manageRemoveRequest(mail);
}
else {
StringWriter sout = new StringWriter();
PrintWriter out = new PrintWriter(sout, true);
out.println("Answering on behalf of: " + whitelistManagerAddress);
out.println("ERROR: Unknown command in the subject line: " + subject);
sendReplyFromPostmaster(mail, sout.toString());
}
return;
}
if (automaticInsert) {
checkAndInsert(senderMailAddress, recipients);
}
| private void | setSqlParameters(java.util.Map sqlParameters)Setter for property sqlParameters.
this.sqlParameters = sqlParameters;
|
|