001: /****************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one *
003: * or more contributor license agreements. See the NOTICE file *
004: * distributed with this work for additional information *
005: * regarding copyright ownership. The ASF licenses this file *
006: * to you under the Apache License, Version 2.0 (the *
007: * "License"); you may not use this file except in compliance *
008: * with the License. You may obtain a copy of the License at *
009: * *
010: * http://www.apache.org/licenses/LICENSE-2.0 *
011: * *
012: * Unless required by applicable law or agreed to in writing, *
013: * software distributed under the License is distributed on an *
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015: * KIND, either express or implied. See the License for the *
016: * specific language governing permissions and limitations *
017: * under the License. *
018: ****************************************************************/package org.apache.james.transport.mailets;
019:
020: import org.apache.avalon.cornerstone.services.store.Store;
021: import org.apache.avalon.framework.configuration.DefaultConfiguration;
022: import org.apache.avalon.framework.container.ContainerUtil;
023: import org.apache.avalon.framework.service.ServiceException;
024: import org.apache.avalon.framework.service.ServiceManager;
025: import org.apache.james.Constants;
026: import org.apache.james.James;
027: import org.apache.james.core.MailImpl;
028: import org.apache.james.services.MailRepository;
029: import org.apache.james.services.MailServer;
030: import org.apache.mailet.RFC2822Headers;
031:
032: import org.apache.mailet.GenericMailet;
033: import org.apache.mailet.Mail;
034: import org.apache.mailet.MailAddress;
035:
036: import javax.mail.Header;
037: import javax.mail.MessagingException;
038: import javax.mail.internet.MimeMessage;
039: import javax.mail.internet.InternetHeaders;
040:
041: import java.util.Collection;
042: import java.util.Enumeration;
043: import java.util.HashSet;
044: import java.util.Iterator;
045: import java.util.Vector;
046:
047: /**
048: * Receives a Mail from JamesSpoolManager and takes care of delivery of the
049: * message to local inboxes or a specific repository.
050: *
051: * Differently from LocalDelivery this does not lookup the UserRepository This
052: * simply store the message in a repository named like the local part of the
053: * recipient address.
054: *
055: * If no repository is specified then this fallback to MailServer.getUserInbox.
056: * Otherwise you can add your own configuration for the repository
057: *
058: * e.g: <repositoryUrl>file://var/spool/userspools/</repositoryUrl>
059: * <repositoryType>SPOOL</repositoryType>
060: *
061: * <repositoryUrl>file://var/mail/inboxes/</repositoryUrl> <repositoryType>MAIL</repositoryType>
062: *
063: * Header "Delivered-To" can be added to every message adding the
064: * <addDeliveryHeader>Delivered-To</addDeliveryHeader>
065: *
066: */
067: public class ToMultiRepository extends GenericMailet {
068: /**
069: * The number of mails generated. Access needs to be synchronized for thread
070: * safety and to ensure that all threads see the latest value.
071: */
072: private static long count;
073:
074: /**
075: * The mailserver reference
076: */
077: private MailServer mailServer;
078:
079: /**
080: * The mailstore
081: */
082: private Store store;
083:
084: /**
085: * The optional repositoryUrl
086: */
087: private String repositoryUrl;
088:
089: /**
090: * The optional repositoryType
091: */
092: private String repositoryType;
093:
094: /**
095: * The delivery header
096: */
097: private String deliveryHeader;
098:
099: /**
100: * resetReturnPath
101: */
102: private boolean resetReturnPath;
103:
104: /**
105: * Delivers a mail to a local mailbox.
106: *
107: * @param mail
108: * the mail being processed
109: *
110: * @throws MessagingException
111: * if an error occurs while storing the mail
112: */
113: public void service(Mail mail) throws MessagingException {
114: Collection recipients = mail.getRecipients();
115: Collection errors = new Vector();
116:
117: MimeMessage message = null;
118: if (deliveryHeader != null || resetReturnPath) {
119: message = mail.getMessage();
120: }
121:
122: if (resetReturnPath) {
123: // Set Return-Path and remove all other Return-Path headers from the
124: // message
125: // This only works because there is a placeholder inserted by
126: // MimeMessageWrapper
127: message.setHeader(RFC2822Headers.RETURN_PATH, (mail
128: .getSender() == null ? "<>" : "<"
129: + mail.getSender() + ">"));
130: }
131:
132: Enumeration headers;
133: InternetHeaders deliveredTo = new InternetHeaders();
134: if (deliveryHeader != null) {
135: // Copy any Delivered-To headers from the message
136: headers = message
137: .getMatchingHeaders(new String[] { deliveryHeader });
138: while (headers.hasMoreElements()) {
139: Header header = (Header) headers.nextElement();
140: deliveredTo.addHeader(header.getName(), header
141: .getValue());
142: }
143: }
144:
145: for (Iterator i = recipients.iterator(); i.hasNext();) {
146: MailAddress recipient = (MailAddress) i.next();
147: try {
148: if (deliveryHeader != null) {
149: // Add qmail's de facto standard Delivered-To header
150: message.addHeader(deliveryHeader, recipient
151: .toString());
152: }
153:
154: storeMail(mail.getSender(), recipient, message);
155:
156: if (deliveryHeader != null) {
157: if (i.hasNext()) {
158: // Remove headers but leave all placeholders
159: message.removeHeader(deliveryHeader);
160: headers = deliveredTo.getAllHeaders();
161: // And restore any original Delivered-To headers
162: while (headers.hasMoreElements()) {
163: Header header = (Header) headers
164: .nextElement();
165: message.addHeader(header.getName(), header
166: .getValue());
167: }
168: }
169: }
170: } catch (Exception ex) {
171: getMailetContext().log("Error while storing mail.", ex);
172: errors.add(recipient);
173: }
174: }
175:
176: if (!errors.isEmpty()) {
177: // If there were errors, we redirect the email to the ERROR
178: // processor.
179: // In order for this server to meet the requirements of the SMTP
180: // specification, mails on the ERROR processor must be returned to
181: // the sender. Note that this email doesn't include any details
182: // regarding the details of the failure(s).
183: // In the future we may wish to address this.
184: getMailetContext().sendMail(mail.getSender(), errors,
185: mail.getMessage(), Mail.ERROR);
186: }
187: // We always consume this message
188: mail.setState(Mail.GHOST);
189: }
190:
191: /**
192: * Return a string describing this mailet.
193: *
194: * @return a string describing this mailet
195: */
196: public String getMailetInfo() {
197: return "ToMultiRepository Mailet";
198: }
199:
200: /**
201: *
202: * @param sender
203: * @param recipient
204: * @param message
205: * @throws MessagingException
206: */
207: public void storeMail(MailAddress sender, MailAddress recipient,
208: MimeMessage message) throws MessagingException {
209: String username;
210: if (recipient == null) {
211: throw new IllegalArgumentException(
212: "Recipient for mail to be spooled cannot be null.");
213: }
214: if (message == null) {
215: throw new IllegalArgumentException(
216: "Mail message to be spooled cannot be null.");
217: }
218: username = recipient.getUser();
219:
220: Collection recipients = new HashSet();
221: recipients.add(recipient);
222: MailImpl mail = new MailImpl(getId(), sender, recipients,
223: message);
224: try {
225: MailRepository userInbox = getRepository(username);
226: if (userInbox == null) {
227: StringBuffer errorBuffer = new StringBuffer(128)
228: .append("The repository for user ").append(
229: username).append(
230: " was not found on this server.");
231: throw new MessagingException(errorBuffer.toString());
232: }
233: userInbox.store(mail);
234: } finally {
235: mail.dispose();
236: }
237: }
238:
239: /**
240: * Return a new mail id.
241: *
242: * @return a new mail id
243: */
244: public String getId() {
245: long localCount = -1;
246: synchronized (James.class) {
247: localCount = count++;
248: }
249: StringBuffer idBuffer = new StringBuffer(64).append("Mail")
250: .append(System.currentTimeMillis()).append("-").append(
251: localCount);
252: return idBuffer.toString();
253: }
254:
255: /**
256: * @see org.apache.mailet.GenericMailet#init()
257: */
258: public void init() throws MessagingException {
259: super .init();
260: ServiceManager compMgr = (ServiceManager) getMailetContext()
261: .getAttribute(Constants.AVALON_COMPONENT_MANAGER);
262:
263: try {
264: // Instantiate the a MailRepository for outgoing mails
265: mailServer = (MailServer) compMgr.lookup(MailServer.ROLE);
266: } catch (ServiceException cnfe) {
267: log("Failed to retrieve MailServer component:"
268: + cnfe.getMessage());
269: } catch (Exception e) {
270: log("Failed to retrieve MailServer component:"
271: + e.getMessage());
272: }
273:
274: try {
275: // Instantiate the a MailRepository for outgoing mails
276: store = (Store) compMgr.lookup(Store.ROLE);
277: } catch (ServiceException cnfe) {
278: log("Failed to retrieve Store component:"
279: + cnfe.getMessage());
280: } catch (Exception e) {
281: log("Failed to retrieve Store component:" + e.getMessage());
282: }
283:
284: repositoryUrl = getInitParameter("repositoryUrl");
285: if (repositoryUrl != null) {
286: repositoryType = getInitParameter("repositoryType");
287: if (repositoryType == null)
288: repositoryType = "MAIL";
289: }
290:
291: deliveryHeader = getInitParameter("addDeliveryHeader");
292: String resetReturnPathString = getInitParameter("resetReturnPath");
293: resetReturnPath = "true"
294: .equalsIgnoreCase(resetReturnPathString);
295: }
296:
297: /**
298: * Get the user inbox: if the repositoryUrl is null then get the userinbox
299: * from the mailserver, otherwise lookup the store with the given
300: * repositoryurl/type
301: *
302: * @param userName
303: * @return
304: */
305: private MailRepository getRepository(String userName) {
306: MailRepository userInbox;
307: if (repositoryUrl == null) {
308: userInbox = mailServer.getUserInbox(userName);
309: } else {
310: StringBuffer destinationBuffer = new StringBuffer(192)
311: .append(repositoryUrl).append(userName).append("/");
312: String destination = destinationBuffer.toString();
313: DefaultConfiguration mboxConf = new DefaultConfiguration(
314: "repository",
315: "generated:ToMultiRepository.getUserInbox()");
316: mboxConf.setAttribute("destinationURL", destination);
317: mboxConf.setAttribute("type", repositoryType);
318: try {
319: userInbox = (MailRepository) store.select(mboxConf);
320: } catch (Exception e) {
321: log("Cannot open repository " + e);
322: userInbox = null;
323: }
324: }
325: return userInbox;
326: }
327:
328: }
|