1 /************************************************************************
2 * Copyright (c) 2000-2006 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.cornerstone.services.store.Store;
21 import org.apache.avalon.framework.activity.Initializable;
22 import org.apache.avalon.framework.configuration.Configurable;
23 import org.apache.avalon.framework.configuration.Configuration;
24 import org.apache.avalon.framework.configuration.ConfigurationException;
25 import org.apache.avalon.framework.configuration.DefaultConfiguration;
26 import org.apache.avalon.framework.container.ContainerUtil;
27 import org.apache.avalon.framework.context.Context;
28 import org.apache.avalon.framework.context.Contextualizable;
29 import org.apache.avalon.framework.context.DefaultContext;
30 import org.apache.avalon.framework.logger.AbstractLogEnabled;
31 import org.apache.avalon.framework.logger.Logger;
32 import org.apache.avalon.framework.service.DefaultServiceManager;
33 import org.apache.avalon.framework.service.ServiceException;
34 import org.apache.avalon.framework.service.ServiceManager;
35 import org.apache.avalon.framework.service.Serviceable;
36 import org.apache.commons.collections.ReferenceMap;
37
38 import org.apache.james.context.AvalonContextUtilities;
39 import org.apache.james.core.MailHeaders;
40 import org.apache.james.core.MailImpl;
41 import org.apache.james.core.MailetConfigImpl;
42 import org.apache.james.services.DNSServer;
43 import org.apache.james.services.MailRepository;
44 import org.apache.james.services.MailServer;
45 import org.apache.james.services.SpoolRepository;
46 import org.apache.james.services.UsersRepository;
47 import org.apache.james.services.UsersStore;
48 import org.apache.james.transport.mailets.LocalDelivery;
49 import org.apache.james.userrepository.DefaultJamesUser;
50 import org.apache.mailet.Mail;
51 import org.apache.mailet.MailAddress;
52 import org.apache.mailet.Mailet;
53 import org.apache.mailet.MailetContext;
54 import org.apache.mailet.RFC2822Headers;
55
56 import javax.mail.Address;
57 import javax.mail.Message;
58 import javax.mail.MessagingException;
59 import javax.mail.internet.InternetAddress;
60 import javax.mail.internet.MimeMessage;
61 import java.io.ByteArrayInputStream;
62 import java.io.InputStream;
63 import java.io.SequenceInputStream;
64 import java.net.InetAddress;
65 import java.net.UnknownHostException;
66 import java.util.Collection;
67 import java.util.Date;
68 import java.util.Enumeration;
69 import java.util.HashSet;
70 import java.util.Hashtable;
71 import java.util.Iterator;
72 import java.util.Locale;
73 import java.util.Map;
74 import java.util.Vector;
75
76 /***
77 * Core class for JAMES. Provides three primary services:
78 * <br> 1) Instantiates resources, such as user repository, and protocol
79 * handlers
80 * <br> 2) Handles interactions between components
81 * <br> 3) Provides container services for Mailets
82 *
83 *
84 * @version This is $Revision: 442572 $
85
86 */
87 public class James
88 extends AbstractLogEnabled
89 implements Contextualizable, Serviceable, Configurable, JamesMBean, Initializable, MailServer, MailetContext {
90
91 /***
92 * The software name and version
93 */
94 private final static String SOFTWARE_NAME_VERSION = Constants.SOFTWARE_NAME + " " + Constants.SOFTWARE_VERSION;
95
96 /***
97 * The component manager used both internally by James and by Mailets.
98 */
99 private DefaultServiceManager compMgr;
100
101 /***
102 * TODO: Investigate what this is supposed to do. Looks like it
103 * was supposed to be the Mailet context.
104 */
105 private DefaultContext context;
106
107 /***
108 * The top level configuration object for this server.
109 */
110 private Configuration conf;
111
112 /***
113 * The logger used by the Mailet API.
114 */
115 private Logger mailetLogger = null;
116
117 /***
118 * The mail store containing the inbox repository and the spool.
119 */
120 private Store store;
121
122 /***
123 * The store containing the local user repository.
124 */
125 private UsersStore usersStore;
126
127 /***
128 * The spool used for processing mail handled by this server.
129 */
130 private SpoolRepository spool;
131
132 /***
133 * The root URL used to get mailboxes from the repository
134 */
135 private String inboxRootURL;
136
137 /***
138 * The user repository for this mail server. Contains all the users with inboxes
139 * on this server.
140 */
141 private UsersRepository localusers;
142
143 /***
144 * The collection of domain/server names for which this instance of James
145 * will receive and process mail.
146 */
147 private Collection serverNames;
148
149 /***
150 * Whether to ignore case when looking up user names on this server
151 */
152 private boolean ignoreCase;
153
154 /***
155 * The number of mails generated. Access needs to be synchronized for
156 * thread safety and to ensure that all threads see the latest value.
157 */
158 private static long count;
159
160 /***
161 * The address of the postmaster for this server
162 */
163 private MailAddress postmaster;
164
165 /***
166 * A map used to store mailboxes and reduce the cost of lookup of individual
167 * mailboxes.
168 */
169 private Map mailboxes;
170
171 /***
172 * A hash table of server attributes
173 * These are the MailetContext attributes
174 */
175 private Hashtable attributes = new Hashtable();
176
177 /***
178 * The Avalon context used by the instance
179 */
180 protected Context myContext;
181
182 /***
183 * Currently used by storeMail to avoid code duplication (we moved store logic to that mailet).
184 * TODO We should remove this and its initialization when we remove storeMail method.
185 */
186 protected Mailet localDeliveryMailet;
187
188 /***
189 * @see org.apache.avalon.framework.context.Contextualizable#contextualize(Context)
190 */
191 public void contextualize(final Context context) {
192 this.myContext = context;
193 }
194
195 /***
196 * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
197 */
198 public void service(ServiceManager comp) {
199 compMgr = new DefaultServiceManager(comp);
200 mailboxes = new ReferenceMap();
201 }
202
203 /***
204 * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
205 */
206 public void configure(Configuration conf) {
207 this.conf = conf;
208 }
209
210 /***
211 * @see org.apache.avalon.framework.activity.Initializable#initialize()
212 */
213 public void initialize() throws Exception {
214
215 getLogger().info("JAMES init...");
216
217
218
219 try {
220 store = (Store) compMgr.lookup( Store.ROLE );
221 } catch (Exception e) {
222 if (getLogger().isWarnEnabled()) {
223 getLogger().warn("Can't get Store: " + e);
224 }
225 }
226 if (getLogger().isDebugEnabled()) {
227 getLogger().debug("Using Store: " + store.toString());
228 }
229 try {
230 spool = (SpoolRepository) compMgr.lookup( SpoolRepository.ROLE );
231 } catch (Exception e) {
232 if (getLogger().isWarnEnabled()) {
233 getLogger().warn("Can't get spoolRepository: " + e);
234 }
235 }
236 if (getLogger().isDebugEnabled()) {
237 getLogger().debug("Using SpoolRepository: " + spool.toString());
238 }
239 try {
240 usersStore = (UsersStore) compMgr.lookup( UsersStore.ROLE );
241 } catch (Exception e) {
242 if (getLogger().isWarnEnabled()) {
243 getLogger().warn("Can't get Store: " + e);
244 }
245 }
246 if (getLogger().isDebugEnabled()) {
247 getLogger().debug("Using UsersStore: " + usersStore.toString());
248 }
249
250 String hostName = null;
251 try {
252 hostName = InetAddress.getLocalHost().getHostName();
253 } catch (UnknownHostException ue) {
254 hostName = "localhost";
255 }
256
257 context = new DefaultContext();
258 context.put("HostName", hostName);
259 getLogger().info("Local host is: " + hostName);
260
261
262 serverNames = new HashSet();
263 Configuration serverConf = conf.getChild("servernames");
264 if (serverConf.getAttributeAsBoolean("autodetect") && (!hostName.equals("localhost"))) {
265 serverNames.add(hostName.toLowerCase(Locale.US));
266 }
267
268 final Configuration[] serverNameConfs =
269 conf.getChild( "servernames" ).getChildren( "servername" );
270 for ( int i = 0; i < serverNameConfs.length; i++ ) {
271 serverNames.add( serverNameConfs[i].getValue().toLowerCase(Locale.US));
272
273 if (serverConf.getAttributeAsBoolean("autodetectIP", true)) {
274 try {
275
276
277
278
279
280
281
282
283
284 InetAddress[] addrs = InetAddress.getAllByName(serverNameConfs[i].getValue());
285 for (int j = 0; j < addrs.length ; j++) {
286 serverNames.add(addrs[j].getHostAddress());
287 }
288 }
289 catch(Exception genericException) {
290 getLogger().error("Cannot get IP address(es) for " + serverNameConfs[i].getValue());
291 }
292 }
293 }
294 if (serverNames.isEmpty()) {
295 throw new ConfigurationException( "Fatal configuration error: no servernames specified!");
296 }
297
298 if (getLogger().isInfoEnabled()) {
299 for (Iterator i = serverNames.iterator(); i.hasNext(); ) {
300 getLogger().info("Handling mail for: " + i.next());
301 }
302 }
303
304 String defaultDomain = (String) serverNames.iterator().next();
305 context.put(Constants.DEFAULT_DOMAIN, defaultDomain);
306 attributes.put(Constants.DEFAULT_DOMAIN, defaultDomain);
307
308
309 String postMasterAddress = conf.getChild("postmaster").getValue("postmaster").toLowerCase(Locale.US);
310
311
312
313 if (postMasterAddress.indexOf('@') < 0) {
314 String domainName = null;
315
316 for ( int i = 0; domainName == null && i < serverNameConfs.length ; i++ ) {
317 String serverName = serverNameConfs[i].getValue().toLowerCase(Locale.US);
318 if (!("localhost".equals(serverName))) {
319 domainName = serverName;
320 }
321 }
322
323 postMasterAddress = postMasterAddress + "@" + (domainName != null ? domainName : hostName);
324 }
325 this.postmaster = new MailAddress( postMasterAddress );
326 context.put( Constants.POSTMASTER, postmaster );
327
328 if (!isLocalServer(postmaster.getHost())) {
329 StringBuffer warnBuffer
330 = new StringBuffer(320)
331 .append("The specified postmaster address ( ")
332 .append(postmaster)
333 .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.");
334 getLogger().warn(warnBuffer.toString());
335 }
336
337 Configuration userNamesConf = conf.getChild("usernames");
338 ignoreCase = userNamesConf.getAttributeAsBoolean("ignoreCase", false);
339 boolean enableAliases = userNamesConf.getAttributeAsBoolean("enableAliases", false);
340 boolean enableForwarding = userNamesConf.getAttributeAsBoolean("enableForwarding", false);
341 attributes.put(Constants.DEFAULT_ENABLE_ALIASES,new Boolean(enableAliases));
342 attributes.put(Constants.DEFAULT_ENABLE_FORWARDING,new Boolean(enableForwarding));
343 attributes.put(Constants.DEFAULT_IGNORE_USERNAME_CASE,new Boolean(ignoreCase));
344
345
346 try {
347 localusers = (UsersRepository) compMgr.lookup(UsersRepository.ROLE);
348 } catch (Exception e) {
349 getLogger().error("Cannot open private UserRepository");
350 throw e;
351 }
352
353 compMgr.put( UsersRepository.ROLE, localusers);
354 getLogger().info("Local users repository opened");
355
356 Configuration inboxConf = conf.getChild("inboxRepository");
357 Configuration inboxRepConf = inboxConf.getChild("repository");
358
359
360 try {
361 store.select(inboxRepConf);
362 } catch (Exception e) {
363 getLogger().error("Cannot open private MailRepository");
364 throw e;
365 }
366 inboxRootURL = inboxRepConf.getAttribute("destinationURL");
367
368 getLogger().info("Private Repository LocalInbox opened");
369
370
371 compMgr.put( MailServer.ROLE, this);
372
373
374
375
376
377 attributes.put(Constants.AVALON_COMPONENT_MANAGER, compMgr);
378
379
380 java.io.File configDir = AvalonContextUtilities.getFile(myContext, "file://conf/");
381 attributes.put("confDir", configDir.getCanonicalPath());
382
383
384
385 DefaultConfiguration conf = new DefaultConfiguration("mailet", "generated:James.initialize()");
386 MailetConfigImpl configImpl = new MailetConfigImpl();
387 configImpl.setMailetName("LocalDelivery");
388 configImpl.setConfiguration(conf);
389 configImpl.setMailetContext(this);
390 localDeliveryMailet = new LocalDelivery();
391 localDeliveryMailet.init(configImpl);
392
393 System.out.println(SOFTWARE_NAME_VERSION);
394 getLogger().info("JAMES ...init end");
395 }
396
397 /***
398 * Place a mail on the spool for processing
399 *
400 * @param message the message to send
401 *
402 * @throws MessagingException if an exception is caught while placing the mail
403 * on the spool
404 */
405 public void sendMail(MimeMessage message) throws MessagingException {
406 MailAddress sender = new MailAddress((InternetAddress)message.getFrom()[0]);
407 Collection recipients = new HashSet();
408 Address addresses[] = message.getAllRecipients();
409 if (addresses != null) {
410 for (int i = 0; i < addresses.length; i++) {
411
412
413 if ( addresses[i] instanceof InternetAddress ) {
414 recipients.add(new MailAddress((InternetAddress)addresses[i]));
415 }
416 }
417 }
418 sendMail(sender, recipients, message);
419 }
420
421 /***
422 * Place a mail on the spool for processing
423 *
424 * @param sender the sender of the mail
425 * @param recipients the recipients of the mail
426 * @param message the message to send
427 *
428 * @throws MessagingException if an exception is caught while placing the mail
429 * on the spool
430 */
431 public void sendMail(MailAddress sender, Collection recipients, MimeMessage message)
432 throws MessagingException {
433 sendMail(sender, recipients, message, Mail.DEFAULT);
434 }
435
436 /***
437 * Place a mail on the spool for processing
438 *
439 * @param sender the sender of the mail
440 * @param recipients the recipients of the mail
441 * @param message the message to send
442 * @param state the state of the message
443 *
444 * @throws MessagingException if an exception is caught while placing the mail
445 * on the spool
446 */
447 public void sendMail(MailAddress sender, Collection recipients, MimeMessage message, String state)
448 throws MessagingException {
449 MailImpl mail = new MailImpl(getId(), sender, recipients, message);
450 try {
451 mail.setState(state);
452 sendMail(mail);
453 } finally {
454 ContainerUtil.dispose(mail);
455 }
456 }
457
458 /***
459 * Place a mail on the spool for processing
460 *
461 * @param sender the sender of the mail
462 * @param recipients the recipients of the mail
463 * @param msg an <code>InputStream</code> containing the message
464 *
465 * @throws MessagingException if an exception is caught while placing the mail
466 * on the spool
467 */
468 public void sendMail(MailAddress sender, Collection recipients, InputStream msg)
469 throws MessagingException {
470
471 MailHeaders headers = new MailHeaders(msg);
472
473
474 if (!headers.isValid()) {
475 throw new MessagingException("Some REQURED header field is missing. Invalid Message");
476 }
477 ByteArrayInputStream headersIn = new ByteArrayInputStream(headers.toByteArray());
478 sendMail(new MailImpl(getId(), sender, recipients, new SequenceInputStream(headersIn, msg)));
479 }
480
481 /***
482 * Place a mail on the spool for processing
483 *
484 * @param mail the mail to place on the spool
485 *
486 * @throws MessagingException if an exception is caught while placing the mail
487 * on the spool
488 */
489 public void sendMail(Mail mail) throws MessagingException {
490 try {
491 spool.store(mail);
492 } catch (Exception e) {
493 getLogger().error("Error storing message: " + e.getMessage(),e);
494 try {
495 spool.remove(mail);
496 } catch (Exception ignored) {
497 getLogger().error("Error removing message after an error storing it: " + e.getMessage(),e);
498 }
499 throw new MessagingException("Exception spooling message: " + e.getMessage(), e);
500 }
501 if (getLogger().isDebugEnabled()) {
502 StringBuffer logBuffer =
503 new StringBuffer(64)
504 .append("Mail ")
505 .append(mail.getName())
506 .append(" pushed in spool");
507 getLogger().debug(logBuffer.toString());
508 }
509 }
510
511 /***
512 * <p>Retrieve the mail repository for a user</p>
513 *
514 * <p>For POP3 server only - at the moment.</p>
515 *
516 * @param userName the name of the user whose inbox is to be retrieved
517 *
518 * @return the POP3 inbox for the user
519 */
520 public synchronized MailRepository getUserInbox(String userName) {
521 MailRepository userInbox = null;
522
523 userInbox = (MailRepository) mailboxes.get(userName);
524
525 if (userInbox != null) {
526 return userInbox;
527 } else if (mailboxes.containsKey(userName)) {
528
529 getLogger().error("Null mailbox for non-null key");
530 throw new RuntimeException("Error in getUserInbox.");
531 } else {
532
533 if (getLogger().isDebugEnabled()) {
534 getLogger().debug("Retrieving and caching inbox for " + userName );
535 }
536 StringBuffer destinationBuffer =
537 new StringBuffer(192)
538 .append(inboxRootURL)
539 .append(userName)
540 .append("/");
541 String destination = destinationBuffer.toString();
542 DefaultConfiguration mboxConf
543 = new DefaultConfiguration("repository", "generated:AvalonFileRepository.compose()");
544 mboxConf.setAttribute("destinationURL", destination);
545 mboxConf.setAttribute("type", "MAIL");
546 try {
547 userInbox = (MailRepository) store.select(mboxConf);
548 if (userInbox!=null) {
549 mailboxes.put(userName, userInbox);
550 }
551 } catch (Exception e) {
552 if (getLogger().isErrorEnabled())
553 {
554 getLogger().error("Cannot open user Mailbox" + e);
555 }
556 throw new RuntimeException("Error in getUserInbox." + e);
557 }
558 return userInbox;
559 }
560 }
561
562 /***
563 * Return a new mail id.
564 *
565 * @return a new mail id
566 */
567 public String getId() {
568 long localCount = -1;
569 synchronized (James.class) {
570 localCount = count++;
571 }
572 StringBuffer idBuffer =
573 new StringBuffer(64)
574 .append("Mail")
575 .append(System.currentTimeMillis())
576 .append("-")
577 .append(localCount);
578 return idBuffer.toString();
579 }
580
581 /***
582 * The main method. Should never be invoked, as James must be called
583 * from within an Avalon framework container.
584 *
585 * @param args the command line arguments
586 */
587 public static void main(String[] args) {
588 System.out.println("ERROR!");
589 System.out.println("Cannot execute James as a stand alone application.");
590 System.out.println("To run James, you need to have the Avalon framework installed.");
591 System.out.println("Please refer to the Readme file to know how to run James.");
592 }
593
594
595
596 /***
597 * <p>Get the prioritized list of mail servers for a given host.</p>
598 *
599 * <p>TODO: This needs to be made a more specific ordered subtype of Collection.</p>
600 *
601 * @param host
602 */
603 public Collection getMailServers(String host) {
604 DNSServer dnsServer = null;
605 try {
606 dnsServer = (DNSServer) compMgr.lookup( DNSServer.ROLE );
607 } catch ( final ServiceException cme ) {
608 getLogger().error("Fatal configuration error - DNS Servers lost!", cme );
609 throw new RuntimeException("Fatal configuration error - DNS Servers lost!");
610 }
611 return dnsServer.findMXRecords(host);
612 }
613
614 public Object getAttribute(String key) {
615 return attributes.get(key);
616 }
617
618 public void setAttribute(String key, Object object) {
619 attributes.put(key, object);
620 }
621
622 public void removeAttribute(String key) {
623 attributes.remove(key);
624 }
625
626 public Iterator getAttributeNames() {
627 Vector names = new Vector();
628 for (Enumeration e = attributes.keys(); e.hasMoreElements(); ) {
629 names.add(e.nextElement());
630 }
631 return names.iterator();
632 }
633
634 /***
635 * This generates a response to the Return-Path address, or the address of
636 * the message's sender if the Return-Path is not available. Note that
637 * this is different than a mail-client's reply, which would use the
638 * Reply-To or From header. This will send the bounce with the server's
639 * postmaster as the sender.
640 */
641 public void bounce(Mail mail, String message) throws MessagingException {
642 bounce(mail, message, getPostmaster());
643 }
644
645 /***
646 * This generates a response to the Return-Path address, or the
647 * address of the message's sender if the Return-Path is not
648 * available. Note that this is different than a mail-client's
649 * reply, which would use the Reply-To or From header.
650 *
651 * Bounced messages are attached in their entirety (headers and
652 * content) and the resulting MIME part type is "message/rfc822".
653 *
654 * The attachment to the subject of the original message (or "No
655 * Subject" if there is no subject in the original message)
656 *
657 * There are outstanding issues with this implementation revolving
658 * around handling of the return-path header.
659 *
660 * MIME layout of the bounce message:
661 *
662 * multipart (mixed)/
663 * contentPartRoot (body) = mpContent (alternative)/
664 * part (body) = message
665 * part (body) = original
666 *
667 */
668
669 public void bounce(Mail mail, String message, MailAddress bouncer) throws MessagingException {
670 if (mail.getSender() == null) {
671 if (getLogger().isInfoEnabled())
672 getLogger().info("Mail to be bounced contains a null (<>) reverse path. No bounce will be sent.");
673 return;
674 } else {
675
676 if (getLogger().isInfoEnabled())
677 getLogger().info("Processing a bounce request for a message with a reverse path of " + mail.getSender().toString());
678 }
679
680 MailImpl reply = rawBounce(mail,message);
681
682 reply.getMessage().setFrom(bouncer.toInternetAddress());
683 reply.getMessage().saveChanges();
684
685 reply.setSender(null);
686 sendMail(reply);
687 ContainerUtil.dispose(reply);
688 }
689
690 /***
691 * Generates a bounce mail that is a bounce of the original message.
692 *
693 * @param bounceText the text to be prepended to the message to describe the bounce condition
694 *
695 * @return the bounce mail
696 *
697 * @throws MessagingException if the bounce mail could not be created
698 */
699 private MailImpl rawBounce(Mail mail, String bounceText) throws MessagingException {
700
701 MimeMessage original = mail.getMessage();
702 MimeMessage reply = (MimeMessage) original.reply(false);
703 reply.setSubject("Re: " + original.getSubject());
704 reply.setSentDate(new Date());
705 Collection recipients = new HashSet();
706 recipients.add(mail.getSender());
707 InternetAddress addr[] = { new InternetAddress(mail.getSender().toString())};
708 reply.setRecipients(Message.RecipientType.TO, addr);
709 reply.setFrom(new InternetAddress(mail.getRecipients().iterator().next().toString()));
710 reply.setText(bounceText);
711 reply.setHeader(RFC2822Headers.MESSAGE_ID, "replyTo-" + mail.getName());
712 return new MailImpl(
713 "replyTo-" + mail.getName(),
714 new MailAddress(mail.getRecipients().iterator().next().toString()),
715 recipients,
716 reply);
717 }
718
719 /***
720 * Returns whether that account has a local inbox on this server
721 *
722 * @param name the name to be checked
723 *
724 * @return whether the account has a local inbox
725 */
726 public boolean isLocalUser(String name) {
727 if (ignoreCase) {
728 return localusers.containsCaseInsensitive(name);
729 } else {
730 return localusers.contains(name);
731 }
732 }
733
734 /***
735 * Returns the address of the postmaster for this server.
736 *
737 * @return the <code>MailAddress</code> for the postmaster
738 */
739 public MailAddress getPostmaster() {
740 return postmaster;
741 }
742
743 /***
744 * Return the major version number for the server
745 *
746 * @return the major vesion number for the server
747 */
748 public int getMajorVersion() {
749 return 2;
750 }
751
752 /***
753 * Return the minor version number for the server
754 *
755 * @return the minor vesion number for the server
756 */
757 public int getMinorVersion() {
758 return 3;
759 }
760
761 /***
762 * Check whether the mail domain in question is to be
763 * handled by this server.
764 *
765 * @param serverName the name of the server to check
766 * @return whether the server is local
767 */
768 public boolean isLocalServer( final String serverName ) {
769 return serverNames.contains(serverName.toLowerCase(Locale.US));
770 }
771
772 /***
773 * Return the type of the server
774 *
775 * @return the type of the server
776 */
777 public String getServerInfo() {
778 return "Apache JAMES";
779 }
780
781 /***
782 * Return the logger for the Mailet API
783 *
784 * @return the logger for the Mailet API
785 */
786 private Logger getMailetLogger() {
787 if (mailetLogger == null) {
788 mailetLogger = getLogger().getChildLogger("Mailet");
789 }
790 return mailetLogger;
791 }
792
793 /***
794 * Log a message to the Mailet logger
795 *
796 * @param message the message to pass to the Mailet logger
797 */
798 public void log(String message) {
799 getMailetLogger().info(message);
800 }
801
802 /***
803 * Log a message and a Throwable to the Mailet logger
804 *
805 * @param message the message to pass to the Mailet logger
806 * @param t the <code>Throwable</code> to be logged
807 */
808 public void log(String message, Throwable t) {
809 getMailetLogger().info(message,t);
810 }
811
812 /***
813 * Adds a user to this mail server. Currently just adds user to a
814 * UsersRepository.
815 *
816 * @param userName String representing user name, that is the portion of
817 * an email address before the '@<domain>'.
818 * @param password String plaintext password
819 * @return boolean true if user added succesfully, else false.
820 *
821 * @deprecated we deprecated this in the MailServer interface and this is an implementation
822 * this component depends already depends on a UsersRepository: clients could directly
823 * use the addUser of the usersRepository.
824 */
825 public boolean addUser(String userName, String password) {
826 boolean success;
827 DefaultJamesUser user = new DefaultJamesUser(userName, "SHA");
828 user.setPassword(password);
829 user.initialize();
830 success = localusers.addUser(user);
831 return success;
832 }
833
834 /***
835 * Performs DNS lookups as needed to find servers which should or might
836 * support SMTP.
837 * Returns an Iterator over HostAddress, a specialized subclass of
838 * javax.mail.URLName, which provides location information for
839 * servers that are specified as mail handlers for the given
840 * hostname. This is done using MX records, and the HostAddress
841 * instances are returned sorted by MX priority. If no host is
842 * found for domainName, the Iterator returned will be empty and the
843 * first call to hasNext() will return false.
844 *
845 * @see org.apache.james.DNSServer#getSMTPHostAddresses(String)
846 * @since Mailet API v2.2.0a16-unstable
847 * @param domainName - the domain for which to find mail servers
848 * @return an Iterator over HostAddress instances, sorted by priority
849 */
850 public Iterator getSMTPHostAddresses(String domainName) {
851 DNSServer dnsServer = null;
852 try {
853 dnsServer = (DNSServer) compMgr.lookup( DNSServer.ROLE );
854 } catch ( final ServiceException cme ) {
855 getLogger().error("Fatal configuration error - DNS Servers lost!", cme );
856 throw new RuntimeException("Fatal configuration error - DNS Servers lost!");
857 }
858 return dnsServer.getSMTPHostAddresses(domainName);
859 }
860
861 /***
862 * This method has been moved to LocalDelivery (the only client of the method).
863 * Now we can safely remove it from the Mailet API and from this implementation of MailetContext.
864 *
865 * The local field localDeliveryMailet will be removed when we remove the storeMail method.
866 *
867 * @deprecated since 2.2.0 look at the LocalDelivery code to find out how to do the local delivery.
868 * @see org.apache.mailet.MailetContext#storeMail(org.apache.mailet.MailAddress, org.apache.mailet.MailAddress, javax.mail.internet.MimeMessage)
869 */
870 public void storeMail(MailAddress sender, MailAddress recipient, MimeMessage msg) throws MessagingException {
871 if (recipient == null) {
872 throw new IllegalArgumentException("Recipient for mail to be spooled cannot be null.");
873 }
874 if (msg == null) {
875 throw new IllegalArgumentException("Mail message to be spooled cannot be null.");
876 }
877 Collection recipients = new HashSet();
878 recipients.add(recipient);
879 MailImpl m = new MailImpl(getId(),sender,recipients,msg);
880 localDeliveryMailet.service(m);
881 ContainerUtil.dispose(m);
882 }
883 }