AbstractVirtualUserTablepublic abstract class AbstractVirtualUserTable extends org.apache.mailet.GenericMailet Provides an abstraction of common functionality needed for implementing
a Virtual User Table. Override the mapRecipients method to
map virtual recipients to real recipients. |
Fields Summary |
---|
private static final String | MARKER | private static final Random | random |
Methods Summary |
---|
private java.lang.String | getSeparator(java.lang.String targetString)Returns the character used to delineate multiple addresses.
return (targetString.indexOf(',") > -1 ? "," : (targetString.indexOf(';") > -1 ? ";" : (targetString.indexOf("regex:") > -1? "" : ":" )));
| protected abstract void | mapRecipients(java.util.Map recipientsMap)Override to map virtual recipients to real recipients, both local and non-local.
Each key in the provided map corresponds to a potential virtual recipient, stored as
a MailAddress object.
Translate virtual recipients to real recipients by mapping a string containing the
address of the real recipient as a value to a key. Leave the value null
if no mapping should be performed. Multiple recipients may be specified by delineating
the mapped string with commas, semi-colons or colons.
| private java.lang.String | newName(org.apache.mailet.Mail mail)Create a unique new primary key name. // Used to generate new mail names
String oldName = mail.getName();
// Checking if the original mail name is too long, perhaps because of a
// loop caused by a configuration error.
// it could cause a "null pointer exception" in AvalonMailRepository much
// harder to understand.
if (oldName.length() > 76) {
int count = 0;
int index = 0;
while ((index = oldName.indexOf('!", index + 1)) >= 0) {
count++;
}
// It looks like a configuration loop. It's better to stop.
if (count > 7) {
throw new MessagingException("Unable to create a new message name: too long. Possible loop in config.xml.");
}
else {
oldName = oldName.substring(0, 76);
}
}
StringBuffer nameBuffer =
new StringBuffer(64)
.append(oldName)
.append("-!")
.append(random.nextInt(1048576));
return nameBuffer.toString();
| private void | processDSN(org.apache.mailet.Mail mail, org.apache.mailet.MailAddress address, java.lang.String error)Sends the message for DSN processing
// parse "error:<code> <msg>"
int msgPos = error.indexOf(' ");
try {
Integer code = Integer.valueOf(error.substring("error:".length(),msgPos));
} catch (NumberFormatException e) {
log("Cannot send DSN. Exception parsing DSN code from: " + error, e);
return;
}
String msg = error.substring(msgPos + 1);
// process bounce for "source" address
try {
getMailetContext().bounce(mail, error);
}
catch (MessagingException me) {
log("Cannot send DSN. Exception during DSN processing: ", me);
}
| private java.lang.String | regexMap(org.apache.mailet.Mail mail, org.apache.mailet.MailAddress address, java.lang.String targetString)Processes regex virtual user mapping
If a mapped target string begins with the prefix regex:, it must be
formatted as regex::,
e.g., regex:(.*)@(.*):${1}@tld
String result = null;
try {
int msgPos = targetString.indexOf(':", "regex:".length() + 1);
// log("regex: targetString = " + targetString);
// log("regex: msgPos = " + msgPos);
// log("regex: compile " + targetString.substring("regex:".length(), msgPos));
// log("regex: address = " + address.toString());
// log("regex: replace = " + targetString.substring(msgPos + 1));
Pattern pattern = new Perl5Compiler().compile(targetString.substring("regex:".length(), msgPos));
Perl5Matcher matcher = new Perl5Matcher();
if (matcher.matches(address.toString(), pattern)) {
MatchResult match = matcher.getMatch();
Map parameters = new HashMap(match.groups());
for (int i = 1; i < match.groups(); i++) {
parameters.put(Integer.toString(i), match.group(i));
}
result = XMLResources.replaceParameters(targetString.substring(msgPos + 1), parameters);
}
}
catch (Exception e) {
log("Exception during regexMap processing: ", e);
}
// log("regex: result = " + result);
return result;
| public void | service(org.apache.mailet.Mail mail)Checks the recipient list of the email for user mappings. Maps recipients as
appropriate, modifying the recipient list of the mail and sends mail to any new
non-local recipients.
if (mail.getAttribute(MARKER) != null) {
mail.removeAttribute(MARKER);
return;
}
Collection recipientsToRemove = new HashSet();
Collection recipientsToAddLocal = new ArrayList();
Collection recipientsToAddForward = new ArrayList();
Collection recipients = mail.getRecipients();
Map recipientsMap = new HashMap(recipients.size());
for (Iterator iter = recipients.iterator(); iter.hasNext(); ) {
MailAddress address = (MailAddress)iter.next();
// Assume all addresses are non-virtual at start
recipientsMap.put(address, null);
}
mapRecipients(recipientsMap);
for (Iterator iter = recipientsMap.keySet().iterator(); iter.hasNext(); ) {
MailAddress source = (MailAddress)iter.next();
String targetString = (String)recipientsMap.get(source);
// Only non-null mappings are translated
if(targetString != null) {
if (targetString.startsWith("error:")) {
//Mark this source address as an address to remove from the recipient list
recipientsToRemove.add(source);
processDSN(mail, source, targetString);
} else {
StringTokenizer tokenizer = new StringTokenizer(targetString, getSeparator(targetString));
while (tokenizer.hasMoreTokens()) {
String targetAddress = tokenizer.nextToken().trim();
// log("Attempting to map from " + source + " to " + targetAddress);
if (targetAddress.startsWith("regex:")) {
targetAddress = regexMap(mail, source, targetAddress);
if (targetAddress == null) continue;
}
try {
MailAddress target = (targetAddress.indexOf('@") < 0) ? new MailAddress(targetAddress, "localhost")
: new MailAddress(targetAddress);
//Mark this source address as an address to remove from the recipient list
recipientsToRemove.add(source);
// We need to separate local and remote
// recipients. This is explained below.
if (getMailetContext().isLocalServer(target.getHost())) {
recipientsToAddLocal.add(target);
} else {
recipientsToAddForward.add(target);
}
StringBuffer buf = new StringBuffer().append("Translating virtual user ")
.append(source)
.append(" to ")
.append(target);
log(buf.toString());
} catch (ParseException pe) {
//Don't map this address... there's an invalid address mapping here
StringBuffer exceptionBuffer =
new StringBuffer(128)
.append("There is an invalid map from ")
.append(source)
.append(" to ")
.append(targetAddress);
log(exceptionBuffer.toString());
continue;
}
}
}
}
}
// Remove mapped recipients
recipients.removeAll(recipientsToRemove);
// Add mapped recipients that are local
recipients.addAll(recipientsToAddLocal);
// We consider an address that we map to be, by definition, a
// local address. Therefore if we mapped to a remote address,
// then we want to make sure that the mail can be relayed.
// However, the original e-mail would typically be subjected to
// relay testing. By posting a new mail back through the
// system, we have a locally generated mail, which will not be
// subjected to relay testing.
// Forward to mapped recipients that are remote
if (recipientsToAddForward.size() != 0) {
// Can't use this ... some mappings could lead to an infinite loop
// getMailetContext().sendMail(mail.getSender(), recipientsToAddForward, mail.getMessage());
// duplicates the Mail object, to be able to modify the new mail keeping the original untouched
MailImpl newMail = new MailImpl(mail,newName(mail));
try {
try {
newMail.setRemoteAddr(java.net.InetAddress.getLocalHost().getHostAddress());
newMail.setRemoteHost(java.net.InetAddress.getLocalHost().getHostName());
} catch (java.net.UnknownHostException _) {
newMail.setRemoteAddr("127.0.0.1");
newMail.setRemoteHost("localhost");
}
newMail.setRecipients(recipientsToAddForward);
newMail.setAttribute(MARKER, Boolean.TRUE);
getMailetContext().sendMail(newMail);
} finally {
newMail.dispose();
}
}
// If there are no recipients left, Ghost the message
if (recipients.size() == 0) {
mail.setState(Mail.GHOST);
}
|
|