KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > transport > mailets > AbstractVirtualUserTable


1 /***********************************************************************
2  * Copyright (c) 2000-2004 The Apache Software Foundation. *
3  * All rights reserved. *
4  * ------------------------------------------------------------------- *
5  * Licensed under the Apache License, Version 2.0 (the "License"); you *
6  * may not use this file except in compliance with the License. You *
7  * may obtain a copy of the License at: *
8  * *
9  * http://www.apache.org/licenses/LICENSE-2.0 *
10  * *
11  * Unless required by applicable law or agreed to in writing, software *
12  * distributed under the License is distributed on an "AS IS" BASIS, *
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14  * implied. See the License for the specific language governing *
15  * permissions and limitations under the License. *
16  ***********************************************************************/

17
18 package org.apache.james.transport.mailets;
19
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.StringTokenizer JavaDoc;
27
28 import javax.mail.MessagingException JavaDoc;
29 import javax.mail.internet.ParseException JavaDoc;
30
31 import org.apache.james.core.MailImpl;
32 import org.apache.james.util.XMLResources;
33
34 import org.apache.mailet.GenericMailet;
35 import org.apache.mailet.Mail;
36 import org.apache.mailet.MailAddress;
37
38 import org.apache.oro.text.regex.MalformedPatternException;
39 import org.apache.oro.text.regex.MatchResult;
40 import org.apache.oro.text.regex.Pattern;
41 import org.apache.oro.text.regex.Perl5Compiler;
42 import org.apache.oro.text.regex.Perl5Matcher;
43
44 /**
45  * Provides an abstraction of common functionality needed for implementing
46  * a Virtual User Table. Override the <code>mapRecipients</code> method to
47  * map virtual recipients to real recipients.
48  */

49 public abstract class AbstractVirtualUserTable extends GenericMailet
50 {
51     static private final String JavaDoc MARKER = "org.apache.james.transport.mailets.AbstractVirtualUserTable.mapped";
52
53     /**
54      * Checks the recipient list of the email for user mappings. Maps recipients as
55      * appropriate, modifying the recipient list of the mail and sends mail to any new
56      * non-local recipients.
57      *
58      * @param mail the mail to process
59      */

60     public void service(Mail mail) throws MessagingException JavaDoc
61     {
62         if (mail.getAttribute(MARKER) != null) {
63             mail.removeAttribute(MARKER);
64             return;
65         }
66
67         Collection JavaDoc recipientsToRemove = new HashSet JavaDoc();
68         Collection JavaDoc recipientsToAddLocal = new ArrayList JavaDoc();
69         Collection JavaDoc recipientsToAddForward = new ArrayList JavaDoc();
70
71         Collection JavaDoc recipients = mail.getRecipients();
72         Map JavaDoc recipientsMap = new HashMap JavaDoc(recipients.size());
73
74         for (Iterator JavaDoc iter = recipients.iterator(); iter.hasNext(); ) {
75             MailAddress address = (MailAddress)iter.next();
76
77             // Assume all addresses are non-virtual at start
78
recipientsMap.put(address, null);
79         }
80
81         mapRecipients(recipientsMap);
82
83         for (Iterator JavaDoc iter = recipientsMap.keySet().iterator(); iter.hasNext(); ) {
84             MailAddress source = (MailAddress)iter.next();
85             String JavaDoc targetString = (String JavaDoc)recipientsMap.get(source);
86
87             // Only non-null mappings are translated
88
if(targetString != null) {
89                 if (targetString.startsWith("error:")) {
90                     //Mark this source address as an address to remove from the recipient list
91
recipientsToRemove.add(source);
92                     processDSN(mail, source, targetString);
93                 } else {
94                     StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(targetString, getSeparator(targetString));
95
96                     while (tokenizer.hasMoreTokens()) {
97                         String JavaDoc targetAddress = tokenizer.nextToken().trim();
98
99                         // log("Attempting to map from " + source + " to " + targetAddress);
100

101                         if (targetAddress.startsWith("regex:")) {
102                             targetAddress = regexMap(mail, source, targetAddress);
103                             if (targetAddress == null) continue;
104                         }
105
106                         try {
107                             MailAddress target = (targetAddress.indexOf('@') < 0) ? new MailAddress(targetAddress, "localhost")
108                                 : new MailAddress(targetAddress);
109
110                             //Mark this source address as an address to remove from the recipient list
111
recipientsToRemove.add(source);
112
113                             // We need to separate local and remote
114
// recipients. This is explained below.
115
if (getMailetContext().isLocalServer(target.getHost())) {
116                                 recipientsToAddLocal.add(target);
117                             } else {
118                                 recipientsToAddForward.add(target);
119                             }
120
121                             StringBuffer JavaDoc buf = new StringBuffer JavaDoc().append("Translating virtual user ")
122                                                                  .append(source)
123                                                                  .append(" to ")
124                                                                  .append(target);
125                             log(buf.toString());
126
127                         } catch (ParseException JavaDoc pe) {
128                             //Don't map this address... there's an invalid address mapping here
129
StringBuffer JavaDoc exceptionBuffer =
130                                 new StringBuffer JavaDoc(128)
131                                 .append("There is an invalid map from ")
132                                 .append(source)
133                                 .append(" to ")
134                                 .append(targetAddress);
135                             log(exceptionBuffer.toString());
136                             continue;
137                         }
138                     }
139                 }
140             }
141         }
142
143         // Remove mapped recipients
144
recipients.removeAll(recipientsToRemove);
145
146         // Add mapped recipients that are local
147
recipients.addAll(recipientsToAddLocal);
148
149         // We consider an address that we map to be, by definition, a
150
// local address. Therefore if we mapped to a remote address,
151
// then we want to make sure that the mail can be relayed.
152
// However, the original e-mail would typically be subjected to
153
// relay testing. By posting a new mail back through the
154
// system, we have a locally generated mail, which will not be
155
// subjected to relay testing.
156

157         // Forward to mapped recipients that are remote
158
if (recipientsToAddForward.size() != 0) {
159             // Can't use this ... some mappings could lead to an infinite loop
160
// getMailetContext().sendMail(mail.getSender(), recipientsToAddForward, mail.getMessage());
161

162             // duplicates the Mail object, to be able to modify the new mail keeping the original untouched
163
MailImpl newMail = (MailImpl) ((MailImpl) mail).duplicate(newName((MailImpl) mail));
164             try {
165                 newMail.setRemoteAddr(java.net.InetAddress.getLocalHost().getHostAddress());
166                 newMail.setRemoteHost(java.net.InetAddress.getLocalHost().getHostName());
167             } catch (java.net.UnknownHostException JavaDoc _) {
168                 newMail.setRemoteAddr("127.0.0.1");
169                 newMail.setRemoteHost("localhost");
170             }
171             newMail.setRecipients(recipientsToAddForward);
172             newMail.setAttribute(MARKER, Boolean.TRUE);
173             getMailetContext().sendMail(newMail);
174         }
175
176         // If there are no recipients left, Ghost the message
177
if (recipients.size() == 0) {
178             mail.setState(Mail.GHOST);
179         }
180     }
181
182     /**
183      * Override to map virtual recipients to real recipients, both local and non-local.
184      * Each key in the provided map corresponds to a potential virtual recipient, stored as
185      * a <code>MailAddress</code> object.
186      *
187      * Translate virtual recipients to real recipients by mapping a string containing the
188      * address of the real recipient as a value to a key. Leave the value <code>null<code>
189      * if no mapping should be performed. Multiple recipients may be specified by delineating
190      * the mapped string with commas, semi-colons or colons.
191      *
192      * @param recipientsMap the mapping of virtual to real recipients, as
193      * <code>MailAddress</code>es to <code>String</code>s.
194      */

195     protected abstract void mapRecipients(Map JavaDoc recipientsMap) throws MessagingException JavaDoc;
196   
197     /**
198      * Sends the message for DSN processing
199      *
200      * @param mail the Mail instance being processed
201      * @param address the MailAddress causing the DSN
202      * @param error a String in the form "error:<code> <msg>"
203      */

204     private void processDSN(Mail mail, MailAddress address, String JavaDoc error) {
205         // parse "error:<code> <msg>"
206
int msgPos = error.indexOf(' ');
207       try {
208           Integer JavaDoc code = Integer.valueOf(error.substring("error:".length(),msgPos));
209       } catch (NumberFormatException JavaDoc e) {
210           log("Cannot send DSN. Exception parsing DSN code from: " + error, e);
211           return;
212       }
213       String JavaDoc msg = error.substring(msgPos + 1);
214       // process bounce for "source" address
215
try {
216           getMailetContext().bounce(mail, error);
217       }
218       catch (MessagingException JavaDoc me) {
219           log("Cannot send DSN. Exception during DSN processing: ", me);
220       }
221   }
222
223   /**
224    * Processes regex virtual user mapping
225    *
226    * If a mapped target string begins with the prefix regex:, it must be
227    * formatted as regex:<regular-expression>:<parameterized-string>,
228    * e.g., regex:(.*)@(.*):${1}@tld
229    *
230    * @param mail the Mail instance being processed
231    * @param address the MailAddress to be mapped
232    * @param targetString a String specifying the mapping
233    */

234   private String JavaDoc regexMap(Mail mail, MailAddress address, String JavaDoc targetString) {
235       String JavaDoc result = null;
236
237       try {
238           int msgPos = targetString.indexOf(':', "regex:".length() + 1);
239
240           // log("regex: targetString = " + targetString);
241
// log("regex: msgPos = " + msgPos);
242
// log("regex: compile " + targetString.substring("regex:".length(), msgPos));
243
// log("regex: address = " + address.toString());
244
// log("regex: replace = " + targetString.substring(msgPos + 1));
245

246           Pattern pattern = new Perl5Compiler().compile(targetString.substring("regex:".length(), msgPos));
247           Perl5Matcher matcher = new Perl5Matcher();
248
249           if (matcher.matches(address.toString(), pattern)) {
250               MatchResult match = matcher.getMatch();
251               Map JavaDoc parameters = new HashMap JavaDoc(match.groups());
252               for (int i = 1; i < match.groups(); i++) {
253                   parameters.put(Integer.toString(i), match.group(i));
254               }
255               result = XMLResources.replaceParameters(targetString.substring(msgPos + 1), parameters);
256           }
257       }
258       catch (Exception JavaDoc e) {
259           log("Exception during regexMap processing: ", e);
260       }
261
262       // log("regex: result = " + result);
263
return result;
264   }
265
266   /**
267    * Returns the character used to delineate multiple addresses.
268    *
269    * @param targetString the string to parse
270    * @return the character to tokenize on
271    */

272   private String JavaDoc getSeparator(String JavaDoc targetString) {
273       return (targetString.indexOf(',') > -1 ? "," : (targetString.indexOf(';') > -1 ? ";" : (targetString.indexOf("regex:") > -1? "" : ":" )));
274   }
275
276   private static final java.util.Random JavaDoc random = new java.util.Random JavaDoc(); // Used to generate new mail names
277

278   /**
279    * Create a unique new primary key name.
280    *
281    * @param mail the mail to use as the basis for the new mail name
282    * @return a new name
283    */

284   private String JavaDoc newName(MailImpl mail) throws MessagingException JavaDoc {
285       String JavaDoc oldName = mail.getName();
286
287         // Checking if the original mail name is too long, perhaps because of a
288
// loop caused by a configuration error.
289
// it could cause a "null pointer exception" in AvalonMailRepository much
290
// harder to understand.
291
if (oldName.length() > 76) {
292           int count = 0;
293           int index = 0;
294           while ((index = oldName.indexOf('!', index + 1)) >= 0) {
295               count++;
296           }
297             // It looks like a configuration loop. It's better to stop.
298
if (count > 7) {
299               throw new MessagingException JavaDoc("Unable to create a new message name: too long. Possible loop in config.xml.");
300           }
301           else {
302               oldName = oldName.substring(0, 76);
303           }
304       }
305
306       StringBuffer JavaDoc nameBuffer =
307                                new StringBuffer JavaDoc(64)
308                                .append(oldName)
309                                .append("-!")
310                                .append(random.nextInt(1048576));
311       return nameBuffer.toString();
312   }
313 }
314
Popular Tags