PageRenderTime 31ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/james-2.2.0/src/java/org/apache/james/transport/mailets/AbstractVirtualUserTable.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 313 lines | 165 code | 42 blank | 106 comment | 24 complexity | a386e6d3bc44afd57e30e4363f7968a6 MD5 | raw file
  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. package org.apache.james.transport.mailets;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.Map;
  24. import java.util.StringTokenizer;
  25. import javax.mail.MessagingException;
  26. import javax.mail.internet.ParseException;
  27. import org.apache.james.core.MailImpl;
  28. import org.apache.james.util.XMLResources;
  29. import org.apache.mailet.GenericMailet;
  30. import org.apache.mailet.Mail;
  31. import org.apache.mailet.MailAddress;
  32. import org.apache.oro.text.regex.MalformedPatternException;
  33. import org.apache.oro.text.regex.MatchResult;
  34. import org.apache.oro.text.regex.Pattern;
  35. import org.apache.oro.text.regex.Perl5Compiler;
  36. import org.apache.oro.text.regex.Perl5Matcher;
  37. /**
  38. * Provides an abstraction of common functionality needed for implementing
  39. * a Virtual User Table. Override the <code>mapRecipients</code> method to
  40. * map virtual recipients to real recipients.
  41. */
  42. public abstract class AbstractVirtualUserTable extends GenericMailet
  43. {
  44. static private final String MARKER = "org.apache.james.transport.mailets.AbstractVirtualUserTable.mapped";
  45. /**
  46. * Checks the recipient list of the email for user mappings. Maps recipients as
  47. * appropriate, modifying the recipient list of the mail and sends mail to any new
  48. * non-local recipients.
  49. *
  50. * @param mail the mail to process
  51. */
  52. public void service(Mail mail) throws MessagingException
  53. {
  54. if (mail.getAttribute(MARKER) != null) {
  55. mail.removeAttribute(MARKER);
  56. return;
  57. }
  58. Collection recipientsToRemove = new HashSet();
  59. Collection recipientsToAddLocal = new ArrayList();
  60. Collection recipientsToAddForward = new ArrayList();
  61. Collection recipients = mail.getRecipients();
  62. Map recipientsMap = new HashMap(recipients.size());
  63. for (Iterator iter = recipients.iterator(); iter.hasNext(); ) {
  64. MailAddress address = (MailAddress)iter.next();
  65. // Assume all addresses are non-virtual at start
  66. recipientsMap.put(address, null);
  67. }
  68. mapRecipients(recipientsMap);
  69. for (Iterator iter = recipientsMap.keySet().iterator(); iter.hasNext(); ) {
  70. MailAddress source = (MailAddress)iter.next();
  71. String targetString = (String)recipientsMap.get(source);
  72. // Only non-null mappings are translated
  73. if(targetString != null) {
  74. if (targetString.startsWith("error:")) {
  75. //Mark this source address as an address to remove from the recipient list
  76. recipientsToRemove.add(source);
  77. processDSN(mail, source, targetString);
  78. } else {
  79. StringTokenizer tokenizer = new StringTokenizer(targetString, getSeparator(targetString));
  80. while (tokenizer.hasMoreTokens()) {
  81. String targetAddress = tokenizer.nextToken().trim();
  82. // log("Attempting to map from " + source + " to " + targetAddress);
  83. if (targetAddress.startsWith("regex:")) {
  84. targetAddress = regexMap(mail, source, targetAddress);
  85. if (targetAddress == null) continue;
  86. }
  87. try {
  88. MailAddress target = (targetAddress.indexOf('@') < 0) ? new MailAddress(targetAddress, "localhost")
  89. : new MailAddress(targetAddress);
  90. //Mark this source address as an address to remove from the recipient list
  91. recipientsToRemove.add(source);
  92. // We need to separate local and remote
  93. // recipients. This is explained below.
  94. if (getMailetContext().isLocalServer(target.getHost())) {
  95. recipientsToAddLocal.add(target);
  96. } else {
  97. recipientsToAddForward.add(target);
  98. }
  99. StringBuffer buf = new StringBuffer().append("Translating virtual user ")
  100. .append(source)
  101. .append(" to ")
  102. .append(target);
  103. log(buf.toString());
  104. } catch (ParseException pe) {
  105. //Don't map this address... there's an invalid address mapping here
  106. StringBuffer exceptionBuffer =
  107. new StringBuffer(128)
  108. .append("There is an invalid map from ")
  109. .append(source)
  110. .append(" to ")
  111. .append(targetAddress);
  112. log(exceptionBuffer.toString());
  113. continue;
  114. }
  115. }
  116. }
  117. }
  118. }
  119. // Remove mapped recipients
  120. recipients.removeAll(recipientsToRemove);
  121. // Add mapped recipients that are local
  122. recipients.addAll(recipientsToAddLocal);
  123. // We consider an address that we map to be, by definition, a
  124. // local address. Therefore if we mapped to a remote address,
  125. // then we want to make sure that the mail can be relayed.
  126. // However, the original e-mail would typically be subjected to
  127. // relay testing. By posting a new mail back through the
  128. // system, we have a locally generated mail, which will not be
  129. // subjected to relay testing.
  130. // Forward to mapped recipients that are remote
  131. if (recipientsToAddForward.size() != 0) {
  132. // Can't use this ... some mappings could lead to an infinite loop
  133. // getMailetContext().sendMail(mail.getSender(), recipientsToAddForward, mail.getMessage());
  134. // duplicates the Mail object, to be able to modify the new mail keeping the original untouched
  135. MailImpl newMail = (MailImpl) ((MailImpl) mail).duplicate(newName((MailImpl) mail));
  136. try {
  137. newMail.setRemoteAddr(java.net.InetAddress.getLocalHost().getHostAddress());
  138. newMail.setRemoteHost(java.net.InetAddress.getLocalHost().getHostName());
  139. } catch (java.net.UnknownHostException _) {
  140. newMail.setRemoteAddr("127.0.0.1");
  141. newMail.setRemoteHost("localhost");
  142. }
  143. newMail.setRecipients(recipientsToAddForward);
  144. newMail.setAttribute(MARKER, Boolean.TRUE);
  145. getMailetContext().sendMail(newMail);
  146. }
  147. // If there are no recipients left, Ghost the message
  148. if (recipients.size() == 0) {
  149. mail.setState(Mail.GHOST);
  150. }
  151. }
  152. /**
  153. * Override to map virtual recipients to real recipients, both local and non-local.
  154. * Each key in the provided map corresponds to a potential virtual recipient, stored as
  155. * a <code>MailAddress</code> object.
  156. *
  157. * Translate virtual recipients to real recipients by mapping a string containing the
  158. * address of the real recipient as a value to a key. Leave the value <code>null<code>
  159. * if no mapping should be performed. Multiple recipients may be specified by delineating
  160. * the mapped string with commas, semi-colons or colons.
  161. *
  162. * @param recipientsMap the mapping of virtual to real recipients, as
  163. * <code>MailAddress</code>es to <code>String</code>s.
  164. */
  165. protected abstract void mapRecipients(Map recipientsMap) throws MessagingException;
  166. /**
  167. * Sends the message for DSN processing
  168. *
  169. * @param mail the Mail instance being processed
  170. * @param address the MailAddress causing the DSN
  171. * @param error a String in the form "error:<code> <msg>"
  172. */
  173. private void processDSN(Mail mail, MailAddress address, String error) {
  174. // parse "error:<code> <msg>"
  175. int msgPos = error.indexOf(' ');
  176. try {
  177. Integer code = Integer.valueOf(error.substring("error:".length(),msgPos));
  178. } catch (NumberFormatException e) {
  179. log("Cannot send DSN. Exception parsing DSN code from: " + error, e);
  180. return;
  181. }
  182. String msg = error.substring(msgPos + 1);
  183. // process bounce for "source" address
  184. try {
  185. getMailetContext().bounce(mail, error);
  186. }
  187. catch (MessagingException me) {
  188. log("Cannot send DSN. Exception during DSN processing: ", me);
  189. }
  190. }
  191. /**
  192. * Processes regex virtual user mapping
  193. *
  194. * If a mapped target string begins with the prefix regex:, it must be
  195. * formatted as regex:<regular-expression>:<parameterized-string>,
  196. * e.g., regex:(.*)@(.*):${1}@tld
  197. *
  198. * @param mail the Mail instance being processed
  199. * @param address the MailAddress to be mapped
  200. * @param targetString a String specifying the mapping
  201. */
  202. private String regexMap(Mail mail, MailAddress address, String targetString) {
  203. String result = null;
  204. try {
  205. int msgPos = targetString.indexOf(':', "regex:".length() + 1);
  206. // log("regex: targetString = " + targetString);
  207. // log("regex: msgPos = " + msgPos);
  208. // log("regex: compile " + targetString.substring("regex:".length(), msgPos));
  209. // log("regex: address = " + address.toString());
  210. // log("regex: replace = " + targetString.substring(msgPos + 1));
  211. Pattern pattern = new Perl5Compiler().compile(targetString.substring("regex:".length(), msgPos));
  212. Perl5Matcher matcher = new Perl5Matcher();
  213. if (matcher.matches(address.toString(), pattern)) {
  214. MatchResult match = matcher.getMatch();
  215. Map parameters = new HashMap(match.groups());
  216. for (int i = 1; i < match.groups(); i++) {
  217. parameters.put(Integer.toString(i), match.group(i));
  218. }
  219. result = XMLResources.replaceParameters(targetString.substring(msgPos + 1), parameters);
  220. }
  221. }
  222. catch (Exception e) {
  223. log("Exception during regexMap processing: ", e);
  224. }
  225. // log("regex: result = " + result);
  226. return result;
  227. }
  228. /**
  229. * Returns the character used to delineate multiple addresses.
  230. *
  231. * @param targetString the string to parse
  232. * @return the character to tokenize on
  233. */
  234. private String getSeparator(String targetString) {
  235. return (targetString.indexOf(',') > -1 ? "," : (targetString.indexOf(';') > -1 ? ";" : (targetString.indexOf("regex:") > -1? "" : ":" )));
  236. }
  237. private static final java.util.Random random = new java.util.Random(); // Used to generate new mail names
  238. /**
  239. * Create a unique new primary key name.
  240. *
  241. * @param mail the mail to use as the basis for the new mail name
  242. * @return a new name
  243. */
  244. private String newName(MailImpl mail) throws MessagingException {
  245. String oldName = mail.getName();
  246. // Checking if the original mail name is too long, perhaps because of a
  247. // loop caused by a configuration error.
  248. // it could cause a "null pointer exception" in AvalonMailRepository much
  249. // harder to understand.
  250. if (oldName.length() > 76) {
  251. int count = 0;
  252. int index = 0;
  253. while ((index = oldName.indexOf('!', index + 1)) >= 0) {
  254. count++;
  255. }
  256. // It looks like a configuration loop. It's better to stop.
  257. if (count > 7) {
  258. throw new MessagingException("Unable to create a new message name: too long. Possible loop in config.xml.");
  259. }
  260. else {
  261. oldName = oldName.substring(0, 76);
  262. }
  263. }
  264. StringBuffer nameBuffer =
  265. new StringBuffer(64)
  266. .append(oldName)
  267. .append("-!")
  268. .append(random.nextInt(1048576));
  269. return nameBuffer.toString();
  270. }
  271. }