KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > James


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;
19
20 import org.apache.avalon.framework.activity.Initializable;
21 import org.apache.avalon.framework.component.Component;
22 import org.apache.avalon.framework.component.Composable;
23 import org.apache.avalon.framework.component.DefaultComponentManager;
24 import org.apache.avalon.framework.component.ComponentManager;
25 import org.apache.avalon.framework.component.ComponentException;
26 import org.apache.avalon.framework.configuration.Configurable;
27 import org.apache.avalon.framework.configuration.Configuration;
28 import org.apache.avalon.framework.configuration.ConfigurationException;
29 import org.apache.avalon.framework.configuration.DefaultConfiguration;
30 import org.apache.avalon.framework.context.Context;
31 import org.apache.avalon.framework.context.Contextualizable;
32 import org.apache.avalon.framework.context.DefaultContext;
33 import org.apache.avalon.framework.logger.AbstractLogEnabled;
34 import org.apache.avalon.framework.logger.Logger;
35 import org.apache.james.core.MailHeaders;
36 import org.apache.james.core.MailImpl;
37 import org.apache.james.services.*;
38 import org.apache.james.userrepository.DefaultJamesUser;
39 import org.apache.james.util.RFC2822Headers;
40 import org.apache.james.util.RFC822DateFormat;
41 import org.apache.mailet.Mail;
42 import org.apache.mailet.MailAddress;
43 import org.apache.mailet.MailetContext;
44
45 import javax.mail.Address JavaDoc;
46 import javax.mail.MessagingException JavaDoc;
47 import javax.mail.internet.InternetAddress JavaDoc;
48 import javax.mail.internet.MimeBodyPart JavaDoc;
49 import javax.mail.internet.MimeMessage JavaDoc;
50 import javax.mail.internet.MimeMultipart JavaDoc;
51 import java.io.ByteArrayInputStream JavaDoc;
52 import java.io.IOException JavaDoc;
53 import java.io.InputStream JavaDoc;
54 import java.io.SequenceInputStream JavaDoc;
55 import java.net.InetAddress JavaDoc;
56 import java.net.UnknownHostException JavaDoc;
57 import java.util.*;
58
59 /**
60  * Core class for JAMES. Provides three primary services:
61  * <br> 1) Instantiates resources, such as user repository, and protocol
62  * handlers
63  * <br> 2) Handles interactions between components
64  * <br> 3) Provides container services for Mailets
65  *
66  *
67  * @version This is $Revision: 1.35.4.16 $
68
69  */

70 public class James
71     extends AbstractLogEnabled
72     implements Contextualizable, Composable, Configurable, JamesMBean,
73                Initializable, MailServer, MailetContext, Component {
74
75     /**
76      * The software name and version
77      */

78     private final static String JavaDoc SOFTWARE_NAME_VERSION = Constants.SOFTWARE_NAME + " " + Constants.SOFTWARE_VERSION;
79
80     /**
81      * The component manager used both internally by James and by Mailets.
82      */

83     private DefaultComponentManager compMgr; //Components shared
84

85     /**
86      * TODO: Investigate what this is supposed to do. Looks like it
87      * was supposed to be the Mailet context.
88      */

89     private DefaultContext context;
90
91     /**
92      * The top level configuration object for this server.
93      */

94     private Configuration conf;
95
96     /**
97      * The logger used by the Mailet API.
98      */

99     private Logger mailetLogger = null;
100
101     /**
102      * The mail store containing the inbox repository and the spool.
103      */

104     private MailStore mailstore;
105
106     /**
107      * The store containing the local user repository.
108      */

109     private UsersStore usersStore;
110
111     /**
112      * The spool used for processing mail handled by this server.
113      */

114     private SpoolRepository spool;
115
116     /**
117      * The repository that stores the user inboxes.
118      */

119     private MailRepository localInbox;
120
121     /**
122      * The root URL used to get mailboxes from the repository
123      */

124     private String JavaDoc inboxRootURL;
125
126     /**
127      * The user repository for this mail server. Contains all the users with inboxes
128      * on this server.
129      */

130     private UsersRepository localusers;
131
132     /**
133      * The collection of domain/server names for which this instance of James
134      * will receive and process mail.
135      */

136     private Collection serverNames;
137
138     /**
139      * Whether to ignore case when looking up user names on this server
140      */

141     private boolean ignoreCase;
142
143     /**
144      * Whether to enable aliasing for users on this server
145      */

146     private boolean enableAliases;
147
148     /**
149      * Whether to enable forwarding for users on this server
150      */

151     private boolean enableForwarding;
152
153     /**
154      * The number of mails generated. Access needs to be synchronized for
155      * thread safety and to ensure that all threads see the latest value.
156      */

157     private static long count;
158
159     /**
160      * The address of the postmaster for this server
161      */

162     private MailAddress postmaster;
163
164     /**
165      * A map used to store mailboxes and reduce the cost of lookup of individual
166      * mailboxes.
167      */

168     private Map mailboxes; //Not to be shared!
169

170     /**
171      * A hash table of server attributes
172      * These are the MailetContext attributes
173      */

174     private Hashtable attributes = new Hashtable();
175
176     /**
177      * The Avalon context used by the instance
178      */

179     protected Context myContext;
180
181     /**
182      * An RFC822 date formatter used to format dates in mail headers
183      */

184     private RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
185
186     /**
187      * @see org.apache.avalon.framework.context.Contextualizable#contextualize(Context)
188      */

189     public void contextualize(final Context context) {
190         this.myContext = context;
191     }
192
193     /**
194      * @see org.apache.avalon.framework.component.Composable#compose(ComponentManager)
195      */

196     public void compose(ComponentManager comp) {
197         compMgr = new DefaultComponentManager(comp);
198         mailboxes = new HashMap(31);
199     }
200
201     /**
202      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
203      */

204     public void configure(Configuration conf) {
205         this.conf = conf;
206     }
207
208     /**
209      * @see org.apache.avalon.framework.activity.Initializable#initialize()
210      */

211     public void initialize() throws Exception JavaDoc {
212
213         getLogger().info("JAMES init...");
214
215         // TODO: This should retrieve a more specific named thread pool from
216
// Context that is set up in server.xml
217
try {
218             mailstore = (MailStore) compMgr.lookup( MailStore.ROLE );
219         } catch (Exception JavaDoc e) {
220             if (getLogger().isWarnEnabled()) {
221                 getLogger().warn("Can't get Store: " + e);
222             }
223         }
224         if (getLogger().isDebugEnabled()) {
225             getLogger().debug("Using MailStore: " + mailstore.toString());
226         }
227         try {
228             usersStore = (UsersStore) compMgr.lookup( UsersStore.ROLE );
229         } catch (Exception JavaDoc e) {
230             if (getLogger().isWarnEnabled()) {
231                 getLogger().warn("Can't get Store: " + e);
232             }
233         }
234         if (getLogger().isDebugEnabled()) {
235             getLogger().debug("Using UsersStore: " + usersStore.toString());
236         }
237
238         String JavaDoc hostName = null;
239         try {
240             hostName = InetAddress.getLocalHost().getHostName();
241         } catch (UnknownHostException JavaDoc ue) {
242             hostName = "localhost";
243         }
244
245         context = new DefaultContext();
246         context.put("HostName", hostName);
247         getLogger().info("Local host is: " + hostName);
248
249         // Get the domains and hosts served by this instance
250
serverNames = new HashSet();
251         Configuration serverConf = conf.getChild("servernames");
252         if (serverConf.getAttributeAsBoolean("autodetect") && (!hostName.equals("localhost"))) {
253             serverNames.add(hostName.toLowerCase(Locale.US));
254         }
255
256         final Configuration[] serverNameConfs =
257             conf.getChild( "servernames" ).getChildren( "servername" );
258         for ( int i = 0; i < serverNameConfs.length; i++ ) {
259             serverNames.add( serverNameConfs[i].getValue().toLowerCase(Locale.US));
260
261             if (serverConf.getAttributeAsBoolean("autodetectIP", true)) {
262                 try {
263                     /* This adds the IP address(es) for each host to support
264                      * support <user@address-literal> - RFC 2821, sec 4.1.3.
265                      * It might be proper to use the actual IP addresses
266                      * available on this server, but we can't do that
267                      * without NetworkInterface from JDK 1.4. Because of
268                      * Virtual Hosting considerations, we may need to modify
269                      * this to keep hostname and IP associated, rather than
270                      * just both in the set.
271                      */

272                     InetAddress JavaDoc[] addrs = InetAddress.getAllByName(serverNameConfs[i].getValue());
273                     for (int j = 0; j < addrs.length ; j++) {
274                         serverNames.add(addrs[j].getHostAddress());
275                     }
276                 }
277                 catch(Exception JavaDoc genericException) {
278                     getLogger().error("Cannot get IP address(es) for " + serverNameConfs[i].getValue());
279                 }
280             }
281         }
282         if (serverNames.isEmpty()) {
283             throw new ConfigurationException( "Fatal configuration error: no servernames specified!");
284         }
285
286         if (getLogger().isInfoEnabled()) {
287             for (Iterator i = serverNames.iterator(); i.hasNext(); ) {
288                 getLogger().info("Handling mail for: " + i.next());
289             }
290         }
291         context.put(Constants.SERVER_NAMES, this.serverNames);
292         attributes.put(Constants.SERVER_NAMES, this.serverNames);
293
294
295         // Get postmaster
296
String JavaDoc postMasterAddress = conf.getChild("postmaster").getValue("postmaster").toLowerCase(Locale.US);
297         // if there is no @domain part, then add the first one from the
298
// list of supported domains that isn't localhost. If that
299
// doesn't work, use the hostname, even if it is localhost.
300
if (postMasterAddress.indexOf('@') < 0) {
301             String JavaDoc domainName = null; // the domain to use
302
// loop through candidate domains until we find one or exhaust the list
303
for ( int i = 0; domainName == null && i < serverNameConfs.length ; i++ ) {
304                 String JavaDoc serverName = serverNameConfs[i].getValue().toLowerCase(Locale.US);
305                 if (!("localhost".equals(serverName))) {
306                     domainName = serverName; // ok, not localhost, so use it
307
}
308             }
309             // if we found a suitable domain, use it. Otherwise fallback to the host name.
310
postMasterAddress = postMasterAddress + "@" + (domainName != null ? domainName : hostName);
311         }
312         this.postmaster = new MailAddress( postMasterAddress );
313         context.put( Constants.POSTMASTER, postmaster );
314
315         if (!isLocalServer(postmaster.getHost())) {
316             StringBuffer JavaDoc warnBuffer
317                 = new StringBuffer JavaDoc(320)
318                         .append("The specified postmaster address ( ")
319                         .append(postmaster)
320                         .append(" ) is not a local address. This is not necessarily a problem, but it does mean that emails addressed to the postmaster will be routed to another server. For some configurations this may cause problems.");
321             getLogger().warn(warnBuffer.toString());
322         }
323
324         Configuration userNamesConf = conf.getChild("usernames");
325         ignoreCase = userNamesConf.getAttributeAsBoolean("ignoreCase", false);
326         enableAliases = userNamesConf.getAttributeAsBoolean("enableAliases", false);
327         enableForwarding = userNamesConf.getAttributeAsBoolean("enableForwarding", false);
328
329         //Get localusers
330
try {
331             localusers = (UsersRepository) usersStore.getRepository("LocalUsers");
332         } catch (Exception JavaDoc e) {
333             getLogger().error("Cannot open private UserRepository");
334             throw e;
335         }
336         //}
337
compMgr.put( UsersRepository.ROLE, (Component)localusers);
338         getLogger().info("Local users repository opened");
339
340         Configuration inboxConf = conf.getChild("inboxRepository");
341         Configuration inboxRepConf = inboxConf.getChild("repository");
342         try {
343             localInbox = (MailRepository) mailstore.select(inboxRepConf);
344         } catch (Exception JavaDoc e) {
345             getLogger().error("Cannot open private MailRepository");
346             throw e;
347         }
348         inboxRootURL = inboxRepConf.getAttribute("destinationURL");
349
350         getLogger().info("Private Repository LocalInbox opened");
351
352         // Add this to comp
353
compMgr.put( MailServer.ROLE, this);
354
355         spool = mailstore.getInboundSpool();
356         if (getLogger().isDebugEnabled()) {
357             getLogger().debug("Got spool");
358         }
359
360         // For mailet engine provide MailetContext
361
//compMgr.put("org.apache.mailet.MailetContext", this);
362
// For AVALON aware mailets and matchers, we put the Component object as
363
// an attribute
364
attributes.put(Constants.AVALON_COMPONENT_MANAGER, compMgr);
365
366         System.out.println(SOFTWARE_NAME_VERSION);
367         getLogger().info("JAMES ...init end");
368     }
369
370     /**
371      * Place a mail on the spool for processing
372      *
373      * @param message the message to send
374      *
375      * @throws MessagingException if an exception is caught while placing the mail
376      * on the spool
377      */

378     public void sendMail(MimeMessage JavaDoc message) throws MessagingException JavaDoc {
379         MailAddress sender = new MailAddress((InternetAddress JavaDoc)message.getFrom()[0]);
380         Collection recipients = new HashSet();
381         Address addresses[] = message.getAllRecipients();
382         if (addresses != null) {
383             for (int i = 0; i < addresses.length; i++) {
384                 // Javamail treats the "newsgroups:" header field as a
385
// recipient, so we want to filter those out.
386
if ( addresses[i] instanceof InternetAddress JavaDoc ) {
387                     recipients.add(new MailAddress((InternetAddress JavaDoc)addresses[i]));
388                 }
389             }
390         }
391         sendMail(sender, recipients, message);
392     }
393
394     /**
395      * Place a mail on the spool for processing
396      *
397      * @param sender the sender of the mail
398      * @param recipients the recipients of the mail
399      * @param message the message to send
400      *
401      * @throws MessagingException if an exception is caught while placing the mail
402      * on the spool
403      */

404     public void sendMail(MailAddress sender, Collection recipients, MimeMessage JavaDoc message)
405             throws MessagingException JavaDoc {
406         sendMail(sender, recipients, message, Mail.DEFAULT);
407     }
408
409     /**
410      * Place a mail on the spool for processing
411      *
412      * @param sender the sender of the mail
413      * @param recipients the recipients of the mail
414      * @param message the message to send
415      * @param state the state of the message
416      *
417      * @throws MessagingException if an exception is caught while placing the mail
418      * on the spool
419      */

420     public void sendMail(MailAddress sender, Collection recipients, MimeMessage JavaDoc message, String JavaDoc state)
421             throws MessagingException JavaDoc {
422         MailImpl mail = new MailImpl(getId(), sender, recipients, message);
423         mail.setState(state);
424         sendMail(mail);
425     }
426
427     /**
428      * Place a mail on the spool for processing
429      *
430      * @param sender the sender of the mail
431      * @param recipients the recipients of the mail
432      * @param msg an <code>InputStream</code> containing the message
433      *
434      * @throws MessagingException if an exception is caught while placing the mail
435      * on the spool
436      */

437     public void sendMail(MailAddress sender, Collection recipients, InputStream JavaDoc msg)
438             throws MessagingException JavaDoc {
439         // parse headers
440
MailHeaders headers = new MailHeaders(msg);
441
442         // if headers do not contains minimum REQUIRED headers fields throw Exception
443
if (!headers.isValid()) {
444             throw new MessagingException JavaDoc("Some REQURED header field is missing. Invalid Message");
445         }
446         ByteArrayInputStream JavaDoc headersIn = new ByteArrayInputStream JavaDoc(headers.toByteArray());
447         sendMail(new MailImpl(getId(), sender, recipients, new SequenceInputStream JavaDoc(headersIn, msg)));
448     }
449
450     /**
451      * Place a mail on the spool for processing
452      *
453      * @param mail the mail to place on the spool
454      *
455      * @throws MessagingException if an exception is caught while placing the mail
456      * on the spool
457      */

458     public void sendMail(Mail mail) throws MessagingException JavaDoc {
459         MailImpl mailimpl = (MailImpl)mail;
460         try {
461             spool.store(mailimpl);
462         } catch (Exception JavaDoc e) {
463             try {
464                 spool.remove(mailimpl);
465             } catch (Exception JavaDoc ignored) {
466             }
467             throw new MessagingException JavaDoc("Exception spooling message: " + e.getMessage(), e);
468         }
469         if (getLogger().isDebugEnabled()) {
470             StringBuffer JavaDoc logBuffer =
471                 new StringBuffer JavaDoc(64)
472                         .append("Mail ")
473                         .append(mailimpl.getName())
474                         .append(" pushed in spool");
475             getLogger().debug(logBuffer.toString());
476         }
477     }
478
479     /**
480      * <p>Retrieve the mail repository for a user</p>
481      *
482      * <p>For POP3 server only - at the moment.</p>
483      *
484      * @param userName the name of the user whose inbox is to be retrieved
485      *
486      * @return the POP3 inbox for the user
487      */

488     public synchronized MailRepository getUserInbox(String JavaDoc userName) {
489         MailRepository userInbox = (MailRepository) null;
490
491         userInbox = (MailRepository) mailboxes.get(userName);
492
493         if (userInbox != null) {
494             return userInbox;
495         } else if (mailboxes.containsKey(userName)) {
496             // we have a problem
497
getLogger().error("Null mailbox for non-null key");
498             throw new RuntimeException JavaDoc("Error in getUserInbox.");
499         } else {
500             // need mailbox object
501
if (getLogger().isDebugEnabled()) {
502                 getLogger().debug("Retrieving and caching inbox for " + userName );
503             }
504             StringBuffer JavaDoc destinationBuffer =
505                 new StringBuffer JavaDoc(192)
506                         .append(inboxRootURL)
507                         .append(userName)
508                         .append("/");
509             String JavaDoc destination = destinationBuffer.toString();
510             DefaultConfiguration mboxConf
511                 = new DefaultConfiguration("repository", "generated:AvalonFileRepository.compose()");
512             mboxConf.setAttribute("destinationURL", destination);
513             mboxConf.setAttribute("type", "MAIL");
514             try {
515                 userInbox = (MailRepository) mailstore.select(mboxConf);
516                 mailboxes.put(userName, userInbox);
517             } catch (Exception JavaDoc e) {
518                 if (getLogger().isErrorEnabled())
519                 {
520                     getLogger().error("Cannot open user Mailbox" + e);
521                 }
522                 throw new RuntimeException JavaDoc("Error in getUserInbox." + e);
523             }
524             return userInbox;
525         }
526     }
527
528     /**
529      * Return a new mail id.
530      *
531      * @return a new mail id
532      */

533     public String JavaDoc getId() {
534         long localCount = -1;
535         synchronized (James.class) {
536             localCount = count++;
537         }
538         StringBuffer JavaDoc idBuffer =
539             new StringBuffer JavaDoc(64)
540                     .append("Mail")
541                     .append(System.currentTimeMillis())
542                     .append("-")
543                     .append(localCount);
544         return idBuffer.toString();
545     }
546
547     /**
548      * The main method. Should never be invoked, as James must be called
549      * from within an Avalon framework container.
550      *
551      * @param args the command line arguments
552      */

553     public static void main(String JavaDoc[] args) {
554         System.out.println("ERROR!");
555         System.out.println("Cannot execute James as a stand alone application.");
556         System.out.println("To run James, you need to have the Avalon framework installed.");
557         System.out.println("Please refer to the Readme file to know how to run James.");
558     }
559
560     //Methods for MailetContext
561

562     /**
563      * <p>Get the prioritized list of mail servers for a given host.</p>
564      *
565      * <p>TODO: This needs to be made a more specific ordered subtype of Collection.</p>
566      *
567      * @param host
568      */

569     public Collection getMailServers(String JavaDoc host) {
570         DNSServer dnsServer = null;
571         try {
572             dnsServer = (DNSServer) compMgr.lookup( DNSServer.ROLE );
573         } catch ( final ComponentException cme ) {
574             getLogger().error("Fatal configuration error - DNS Servers lost!", cme );
575             throw new RuntimeException JavaDoc("Fatal configuration error - DNS Servers lost!");
576         }
577         return dnsServer.findMXRecords(host);
578     }
579
580     public Object JavaDoc getAttribute(String JavaDoc key) {
581         return attributes.get(key);
582     }
583
584     public void setAttribute(String JavaDoc key, Object JavaDoc object) {
585         attributes.put(key, object);
586     }
587
588     public void removeAttribute(String JavaDoc key) {
589         attributes.remove(key);
590     }
591
592     public Iterator getAttributeNames() {
593         Vector names = new Vector();
594         for (Enumeration e = attributes.keys(); e.hasMoreElements(); ) {
595             names.add(e.nextElement());
596         }
597         return names.iterator();
598     }
599
600     /**
601      * This generates a response to the Return-Path address, or the address of
602      * the message's sender if the Return-Path is not available. Note that
603      * this is different than a mail-client's reply, which would use the
604      * Reply-To or From header. This will send the bounce with the server's
605      * postmaster as the sender.
606      */

607     public void bounce(Mail mail, String JavaDoc message) throws MessagingException JavaDoc {
608         bounce(mail, message, getPostmaster());
609     }
610
611     /**
612      * This generates a response to the Return-Path address, or the
613      * address of the message's sender if the Return-Path is not
614      * available. Note that this is different than a mail-client's
615      * reply, which would use the Reply-To or From header.
616      *
617      * Bounced messages are attached in their entirety (headers and
618      * content) and the resulting MIME part type is "message/rfc822".
619      *
620      * The attachment to the subject of the original message (or "No
621      * Subject" if there is no subject in the original message)
622      *
623      * There are outstanding issues with this implementation revolving
624      * around handling of the return-path header.
625      *
626      * MIME layout of the bounce message:
627      *
628      * multipart (mixed)/
629      * contentPartRoot (body) = mpContent (alternative)/
630      * part (body) = message
631      * part (body) = original
632      *
633      */

634
635     public void bounce(Mail mail, String JavaDoc message, MailAddress bouncer) throws MessagingException JavaDoc {
636         MimeMessage JavaDoc orig = mail.getMessage();
637
638         //Create the reply message
639
MimeMessage JavaDoc reply = (MimeMessage JavaDoc) orig.reply(false);
640
641         //If there is a Return-Path header,
642
String JavaDoc[] returnPathHeaders = orig.getHeader(RFC2822Headers.RETURN_PATH);
643         String JavaDoc returnPathHeader = null;
644         if (returnPathHeaders != null) {
645             // TODO: Take a look at the JavaMail spec to see if the originating header
646
// is guaranteed to be at position 0
647
returnPathHeader = returnPathHeaders[0];
648             if (returnPathHeader != null) {
649                 returnPathHeader = returnPathHeader.trim();
650                 if (returnPathHeader.equals("<>")) {
651                     if (getLogger().isInfoEnabled())
652                         getLogger().info("Processing a bounce request for a message with an empty return path. No bounce will be sent.");
653                     return;
654                 } else {
655                     if (getLogger().isInfoEnabled())
656                         getLogger().info("Processing a bounce request for a message with a return path header. The bounce will be sent to " + returnPathHeader);
657                     //Return the message to that address, not to the Reply-To address
658
reply.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress JavaDoc(returnPathHeader));
659                 }
660             }
661         } else {
662             getLogger().warn("Mail to be bounced does not contain a Return-Path header.");
663         }
664
665         reply.setSentDate(new Date());
666         reply.setHeader(RFC2822Headers.RETURN_PATH,"<>");
667         //Create the list of recipients in our MailAddress format
668
Collection recipients = new HashSet();
669         Address addresses[] = reply.getAllRecipients();
670         if (addresses != null) {
671             for (int i = 0; i < addresses.length; i++) {
672                 // Javamail treats the "newsgroups:" header field as a
673
// recipient, so we want to filter those out.
674
if ( addresses[i] instanceof InternetAddress JavaDoc ) {
675                     recipients.add(new MailAddress((InternetAddress JavaDoc)addresses[i]));
676                 }
677             }
678         }
679         //Change the sender...
680
reply.setFrom(bouncer.toInternetAddress());
681         try {
682             //Create the message body
683
MimeMultipart JavaDoc multipart = new MimeMultipart JavaDoc("mixed");
684
685             // Create the message
686
MimeMultipart JavaDoc mpContent = new MimeMultipart JavaDoc("alternative");
687             MimeBodyPart JavaDoc contentPartRoot = new MimeBodyPart JavaDoc();
688             contentPartRoot.setContent(mpContent);
689
690             multipart.addBodyPart(contentPartRoot);
691
692             MimeBodyPart JavaDoc part = new MimeBodyPart JavaDoc();
693             part.setText(message);
694             mpContent.addBodyPart(part);
695
696             //Add the original message as the second mime body part
697
part = new MimeBodyPart JavaDoc();
698             part.setContent(orig, "message/rfc822");
699             if ((orig.getSubject() != null) && (orig.getSubject().trim().length() > 0)) {
700                 part.setFileName(orig.getSubject().trim());
701             } else {
702                 part.setFileName("No Subject");
703             }
704             part.setDisposition(javax.mail.Part.ATTACHMENT);
705             multipart.addBodyPart(part);
706             reply.setContent(multipart);
707         } catch (Exception JavaDoc ioe) {
708             throw new MessagingException JavaDoc("Unable to create multipart body", ioe);
709         }
710         reply.saveChanges();
711         //Send it off ... with null reverse-path
712
sendMail(null, recipients, reply);
713     }
714
715     /**
716      * Returns whether that account has a local inbox on this server
717      *
718      * @param name the name to be checked
719      *
720      * @return whether the account has a local inbox
721      */

722     public boolean isLocalUser(String JavaDoc name) {
723         if (ignoreCase) {
724             return localusers.containsCaseInsensitive(name);
725         } else {
726             return localusers.contains(name);
727         }
728     }
729
730     /**
731      * Returns the address of the postmaster for this server.
732      *
733      * @return the <code>MailAddress</code> for the postmaster
734      */

735     public MailAddress getPostmaster() {
736         return postmaster;
737     }
738
739     public void storeMail(MailAddress sender, MailAddress recipient, MimeMessage JavaDoc message)
740         throws MessagingException JavaDoc {
741         String JavaDoc username;
742         if (recipient == null) {
743             throw new IllegalArgumentException JavaDoc("Recipient for mail to be spooled cannot be null.");
744         }
745         if (message == null) {
746             throw new IllegalArgumentException JavaDoc("Mail message to be spooled cannot be null.");
747         }
748         if (ignoreCase) {
749             String JavaDoc originalUsername = recipient.getUser();
750             username = localusers.getRealName(originalUsername);
751             if (username == null) {
752                 StringBuffer JavaDoc errorBuffer =
753                     new StringBuffer JavaDoc(128)
754                         .append("The inbox for user ")
755                         .append(originalUsername)
756                         .append(" was not found on this server.");
757                 throw new MessagingException JavaDoc(errorBuffer.toString());
758             }
759         } else {
760             username = recipient.getUser();
761         }
762         JamesUser user;
763         if (enableAliases || enableForwarding) {
764             user = (JamesUser) localusers.getUserByName(username);
765             if (enableAliases && user.getAliasing()) {
766                 username = user.getAlias();
767             }
768             // Forwarding takes precedence over local aliases
769
if (enableForwarding && user.getForwarding()) {
770                 MailAddress forwardTo = user.getForwardingDestination();
771                 if (forwardTo == null) {
772                     StringBuffer JavaDoc errorBuffer =
773                         new StringBuffer JavaDoc(128)
774                             .append("Forwarding was enabled for ")
775                             .append(username)
776                             .append(" but no forwarding address was set for this account.");
777                     throw new MessagingException JavaDoc(errorBuffer.toString());
778                 }
779                 Collection recipients = new HashSet();
780                 recipients.add(forwardTo);
781                 try {
782                     sendMail(sender, recipients, message);
783                     if (getLogger().isInfoEnabled()) {
784                         StringBuffer JavaDoc logBuffer =
785                             new StringBuffer JavaDoc(128)
786                                     .append("Mail for ")
787                                     .append(username)
788                                     .append(" forwarded to ")
789                                     .append(forwardTo.toString());
790                         getLogger().info(logBuffer.toString());
791                     }
792                     return;
793                 } catch (MessagingException JavaDoc me) {
794                     if (getLogger().isErrorEnabled()) {
795                         StringBuffer JavaDoc logBuffer =
796                             new StringBuffer JavaDoc(128)
797                                     .append("Error forwarding mail to ")
798                                     .append(forwardTo.toString())
799                                     .append("attempting local delivery");
800                         getLogger().error(logBuffer.toString());
801                     }
802                     throw me;
803                 }
804             }
805         }
806
807         Collection recipients = new HashSet();
808         recipients.add(recipient);
809         MailImpl mailImpl = new MailImpl(getId(), sender, recipients, message);
810         MailRepository userInbox = getUserInbox(username);
811         if (userInbox == null) {
812             StringBuffer JavaDoc errorBuffer =
813                 new StringBuffer JavaDoc(128)
814                     .append("The inbox for user ")
815                     .append(username)
816                     .append(" was not found on this server.");
817             throw new MessagingException JavaDoc(errorBuffer.toString());
818         }
819         userInbox.store(mailImpl);
820     }
821
822     /**
823      * Return the major version number for the server
824      *
825      * @return the major vesion number for the server
826      */

827     public int getMajorVersion() {
828         return 2;
829     }
830
831     /**
832      * Return the minor version number for the server
833      *
834      * @return the minor vesion number for the server
835      */

836     public int getMinorVersion() {
837         return 1;
838     }
839
840     /**
841      * Check whether the mail domain in question is to be
842      * handled by this server.
843      *
844      * @param serverName the name of the server to check
845      * @return whether the server is local
846      */

847     public boolean isLocalServer( final String JavaDoc serverName ) {
848         return serverNames.contains(serverName.toLowerCase(Locale.US));
849     }
850
851     /**
852      * Return the type of the server
853      *
854      * @return the type of the server
855      */

856     public String JavaDoc getServerInfo() {
857         return "Apache JAMES";
858     }
859
860     /**
861      * Return the logger for the Mailet API
862      *
863      * @return the logger for the Mailet API
864      */

865     private Logger getMailetLogger() {
866         if (mailetLogger == null) {
867             mailetLogger = getLogger().getChildLogger("Mailet");
868         }
869         return mailetLogger;
870     }
871
872     /**
873      * Log a message to the Mailet logger
874      *
875      * @param message the message to pass to the Mailet logger
876      */

877     public void log(String JavaDoc message) {
878         getMailetLogger().info(message);
879     }
880
881     /**
882      * Log a message and a Throwable to the Mailet logger
883      *
884      * @param message the message to pass to the Mailet logger
885      * @param t the <code>Throwable</code> to be logged
886      */

887     public void log(String JavaDoc message, Throwable JavaDoc t) {
888         getMailetLogger().info(message,t);
889     }
890
891     /**
892      * Adds a user to this mail server. Currently just adds user to a
893      * UsersRepository.
894      *
895      * @param userName String representing user name, that is the portion of
896      * an email address before the '@<domain>'.
897      * @param password String plaintext password
898      * @return boolean true if user added succesfully, else false.
899      */

900     public boolean addUser(String JavaDoc userName, String JavaDoc password) {
901         boolean success;
902         DefaultJamesUser user = new DefaultJamesUser(userName, "SHA");
903         user.setPassword(password);
904         user.initialize();
905         success = localusers.addUser(user);
906         return success;
907     }
908
909     /**
910      * Performs DNS lookups as needed to find servers which should or might
911      * support SMTP.
912      * Returns an Iterator over HostAddress, a specialized subclass of
913      * javax.mail.URLName, which provides location information for
914      * servers that are specified as mail handlers for the given
915      * hostname. This is done using MX records, and the HostAddress
916      * instances are returned sorted by MX priority. If no host is
917      * found for domainName, the Iterator returned will be empty and the
918      * first call to hasNext() will return false.
919      *
920      * @see org.apache.james.DNSServer#getSMTPHostAddresses(String)
921      * @since Mailet API v2.2.0a16-unstable
922      * @param domainName - the domain for which to find mail servers
923      * @return an Iterator over HostAddress instances, sorted by priority
924      */

925     public Iterator getSMTPHostAddresses(String JavaDoc domainName) {
926         DNSServer dnsServer = null;
927         try {
928             dnsServer = (DNSServer) compMgr.lookup( DNSServer.ROLE );
929         } catch ( final ComponentException cme ) {
930             getLogger().error("Fatal configuration error - DNS Servers lost!", cme );
931             throw new RuntimeException JavaDoc("Fatal configuration error - DNS Servers lost!");
932         }
933         return dnsServer.getSMTPHostAddresses(domainName);
934     }
935 }
936
Popular Tags