FileDocCategorySizeDatePackage
AbstractVirtualUserTable.javaAPI DocApache James 2.3.113528Fri Jan 12 12:56:28 GMT 2007org.apache.james.transport.mailets

AbstractVirtualUserTable

public 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
Constructors Summary
Methods Summary
private java.lang.StringgetSeparator(java.lang.String targetString)
Returns the character used to delineate multiple addresses.

param
targetString the string to parse
return
the character to tokenize on

      return (targetString.indexOf(',") > -1 ? "," : (targetString.indexOf(';") > -1 ? ";" : (targetString.indexOf("regex:") > -1? "" : ":" )));
  
protected abstract voidmapRecipients(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.

param
recipientsMap the mapping of virtual to real recipients, as MailAddresses to Strings.

private java.lang.StringnewName(org.apache.mailet.Mail mail)
Create a unique new primary key name.

param
mail the mail to use as the basis for the new mail name
return
a new 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 voidprocessDSN(org.apache.mailet.Mail mail, org.apache.mailet.MailAddress address, java.lang.String error)
Sends the message for DSN processing

param
mail the Mail instance being processed
param
address the MailAddress causing the DSN
param
error a String in the form "error: "

        // 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.StringregexMap(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

param
mail the Mail instance being processed
param
address the MailAddress to be mapped
param
targetString a String specifying the mapping

      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 voidservice(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.

param
mail the mail to process


                                             
         
    
        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);
        }