1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.james;
23
24 import org.apache.avalon.cornerstone.services.store.Store;
25 import org.apache.avalon.framework.activity.Initializable;
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.container.ContainerUtil;
31 import org.apache.avalon.framework.logger.AbstractLogEnabled;
32 import org.apache.avalon.framework.logger.Logger;
33 import org.apache.avalon.framework.service.DefaultServiceManager;
34 import org.apache.avalon.framework.service.ServiceException;
35 import org.apache.avalon.framework.service.ServiceManager;
36 import org.apache.avalon.framework.service.Serviceable;
37 import org.apache.commons.collections.map.ReferenceMap;
38
39 import org.apache.james.api.dnsservice.DNSService;
40 import org.apache.james.api.dnsservice.TemporaryResolutionException;
41 import org.apache.james.api.domainlist.DomainList;
42 import org.apache.james.api.domainlist.ManageableDomainList;
43 import org.apache.james.api.user.UsersRepository;
44 import org.apache.james.api.user.UsersStore;
45 import org.apache.james.core.MailHeaders;
46 import org.apache.james.core.MailImpl;
47 import org.apache.james.impl.jamesuser.JamesUsersRepository;
48 import org.apache.james.services.FileSystem;
49 import org.apache.james.services.MailRepository;
50 import org.apache.james.services.MailServer;
51 import org.apache.james.services.SpoolRepository;
52 import org.apache.james.transport.MailetConfigImpl;
53 import org.apache.james.transport.mailets.LocalDelivery;
54 import org.apache.mailet.Mail;
55 import org.apache.mailet.MailAddress;
56 import org.apache.mailet.Mailet;
57 import org.apache.mailet.MailetContext;
58 import org.apache.mailet.base.RFC2822Headers;
59
60 import javax.mail.Address;
61 import javax.mail.Message;
62 import javax.mail.MessagingException;
63 import javax.mail.internet.InternetAddress;
64 import javax.mail.internet.MimeMessage;
65 import javax.mail.internet.ParseException;
66
67 import java.io.ByteArrayInputStream;
68 import java.io.InputStream;
69 import java.io.SequenceInputStream;
70 import java.net.UnknownHostException;
71 import java.util.ArrayList;
72 import java.util.Collection;
73 import java.util.Collections;
74 import java.util.Date;
75 import java.util.Enumeration;
76 import java.util.HashSet;
77 import java.util.Hashtable;
78 import java.util.Iterator;
79 import java.util.List;
80 import java.util.Locale;
81 import java.util.Map;
82 import java.util.Vector;
83
84
85
86
87
88
89
90
91
92
93
94
95 public class James
96 extends AbstractLogEnabled
97 implements Serviceable, Configurable, Initializable, MailServer, MailetContext {
98
99
100
101
102 private final static String SOFTWARE_NAME_VERSION = Constants.SOFTWARE_NAME + " " + Constants.SOFTWARE_VERSION;
103
104
105
106
107 private DefaultServiceManager compMgr;
108
109
110
111
112 private Configuration conf = null;
113
114
115
116
117 private Logger mailetLogger = null;
118
119
120
121
122 private Store store;
123
124
125
126
127 private SpoolRepository spool;
128
129
130
131
132 private String inboxRootURL;
133
134
135
136
137
138 private UsersRepository localusers;
139
140
141
142
143
144 private Collection serverNames;
145
146
147
148
149
150 private static int count = 0;
151 private static final Object countLock = new Object();
152
153
154
155
156 private MailAddress postmaster;
157
158
159
160
161
162 private Map mailboxes;
163
164
165
166
167
168 private Hashtable attributes = new Hashtable();
169
170
171
172
173
174 protected Mailet localDeliveryMailet;
175
176 private FileSystem fileSystem;
177
178 private DomainList domains;
179
180 private boolean virtualHosting = false;
181
182 private String defaultDomain = null;
183
184 private String helloName = null;
185
186
187
188
189
190 public void service(ServiceManager comp) throws ServiceException {
191 compMgr = new DefaultServiceManager(comp);
192 mailboxes = new ReferenceMap();
193 setFileSystem((FileSystem) comp.lookup(FileSystem.ROLE));
194 domains = (DomainList) comp.lookup(DomainList.ROLE);
195 }
196
197
198
199
200
201
202 private void setFileSystem(FileSystem system) {
203 this.fileSystem = system;
204 }
205
206
207
208
209 public void configure(Configuration conf) {
210 this.conf = conf;
211 }
212
213
214
215
216 public void initialize() throws Exception {
217
218 final Logger logger = getLogger();
219 logger.info("JAMES init...");
220
221 initializeServices();
222
223 Configuration userNamesConf = conf.getChild("usernames");
224 if (userNamesConf != null) {
225 if (localusers instanceof JamesUsersRepository) {
226 logger.warn("<usernames> parameter in James block is deprecated. Please configure this data in UsersRepository block: configuration injected for backward compatibility");
227 ((JamesUsersRepository) localusers).setIgnoreCase(userNamesConf.getAttributeAsBoolean("ignoreCase", false));
228 ((JamesUsersRepository) localusers).setEnableAliases(userNamesConf.getAttributeAsBoolean("enableAliases", false));
229 ((JamesUsersRepository) localusers).setEnableForwarding(userNamesConf.getAttributeAsBoolean("enableForwarding", false));
230 } else {
231 logger.error("<usernames> parameter is no more supported. Backward compatibility is provided when using an AbstractUsersRepository but this repository is a "+localusers.getClass().toString());
232 }
233 }
234
235 Configuration serverConf = conf.getChild("servernames");
236 if (serverConf != null) {
237 if (domains instanceof ManageableDomainList) {
238 logger.warn("<servernames> parameter in James block is deprecated. Please configure this data in domainlist block: configuration injected for backward compatibility");
239 ManageableDomainList dom = (ManageableDomainList) domains;
240 dom.setAutoDetect(serverConf.getAttributeAsBoolean("autodetect",true));
241 dom.setAutoDetectIP(serverConf.getAttributeAsBoolean("autodetectIP", true));
242
243 Configuration[] serverNameConfs = serverConf.getChildren( "servername" );
244 for ( int i = 0; i < serverNameConfs.length; i++ ) {
245 dom.addDomain( serverNameConfs[i].getValue().toLowerCase(Locale.US));
246 }
247 } else {
248 logger.error("<servernames> parameter is no more supported. Backward compatibility is provided when using an XMLDomainList");
249 }
250 }
251
252 initializeServernamesAndPostmaster();
253
254
255
256
257
258
259 inboxRootURL = conf.getChild("inboxRepository").getChild("repository").getAttribute("destinationURL");
260
261 logger.info("Private Repository LocalInbox opened");
262
263 Configuration virtualHostingConfig = conf.getChild("enableVirtualHosting");
264 if (virtualHostingConfig != null ) {
265 virtualHosting = virtualHostingConfig.getValueAsBoolean(false);
266 }
267
268 logger.info("VirtualHosting supported: " + virtualHosting);
269
270 Configuration defaultDomainConfig = conf.getChild("defaultDomain");
271 if (defaultDomainConfig != null ) {
272 defaultDomain = defaultDomainConfig.getValue(null);
273 } else if (virtualHosting) {
274 throw new ConfigurationException("Please configure a defaultDomain if using VirtualHosting");
275 }
276
277 logger.info("Defaultdomain: " + defaultDomain);
278
279 Configuration helloNameConfig = conf.getChild("helloName");
280 if (helloNameConfig != null) {
281 boolean autodetect = helloNameConfig.getAttributeAsBoolean("autodetect", true);
282 if (autodetect) {
283 try {
284 helloName = lookupDNSServer().getHostName(lookupDNSServer().getLocalHost());
285 } catch (UnknownHostException e) {
286 helloName = "localhost";
287 }
288 } else {
289
290 helloName = helloNameConfig.getValue(defaultDomain);
291 }
292 attributes.put(Constants.HELLO_NAME, helloName);
293 }
294
295
296 compMgr.put( MailServer.ROLE, this);
297
298
299
300
301
302 attributes.put(Constants.AVALON_COMPONENT_MANAGER, compMgr);
303
304
305 String confDir;
306 try {
307 confDir = conf.getChild("configuration-directory").getValue();
308 } catch (ConfigurationException e) {
309 if (logger.isInfoEnabled()) {
310 logger.info("Failed to read configuration directory configuration. Will continue using default.");
311 }
312 logger.debug("Failed to read configuration directory configuration", e);
313 confDir = null;
314 }
315
316 if (confDir == null) confDir = "file://conf/";
317 java.io.File configDir = fileSystem.getFile(confDir);
318 attributes.put("confDir", configDir.getCanonicalPath());
319
320 try {
321 attributes.put(Constants.HOSTADDRESS, lookupDNSServer().getLocalHost().getHostAddress());
322 attributes.put(Constants.HOSTNAME, lookupDNSServer().getLocalHost().getHostName());
323 } catch (java.net.UnknownHostException _) {
324 attributes.put(Constants.HOSTADDRESS, "127.0.0.1");
325 attributes.put(Constants.HOSTNAME, "localhost");
326 }
327
328 initializeLocalDeliveryMailet();
329
330 System.out.println(SOFTWARE_NAME_VERSION);
331 logger.info("JAMES ...init end");
332 }
333
334 private void initializeServices() throws Exception {
335
336
337 try {
338 Store store = (Store) compMgr.lookup( Store.ROLE );
339 setStore(store);
340 if (getLogger().isDebugEnabled()) {
341 getLogger().debug("Using Store: " + store.toString());
342 }
343 } catch (Exception e) {
344 if (getLogger().isWarnEnabled()) {
345 getLogger().warn("Can't get Store: " + e);
346 }
347 }
348
349 try {
350 SpoolRepository spool = (SpoolRepository) compMgr.lookup(SpoolRepository.ROLE);
351 setSpool(spool);
352 if (getLogger().isDebugEnabled()) {
353 getLogger().debug("Using SpoolRepository: " + spool.toString());
354 }
355 } catch (Exception e) {
356 if (getLogger().isWarnEnabled()) {
357 getLogger().warn("Can't get spoolRepository: " + e);
358 }
359 }
360
361 try {
362
363
364
365 UsersStore usersStore = (UsersStore) compMgr.lookup( UsersStore.ROLE );
366 if (getLogger().isDebugEnabled()) {
367 getLogger().debug("Using UsersStore: " + usersStore.toString());
368 }
369 } catch (Exception e) {
370 if (getLogger().isWarnEnabled()) {
371 getLogger().warn("Can't get Store: " + e);
372 }
373 }
374
375 try {
376 UsersRepository localusers = (UsersRepository) compMgr.lookup(UsersRepository.ROLE);
377 setLocalusers(localusers);
378 if (getLogger().isDebugEnabled()) {
379 getLogger().debug("Using LocalUsersRepository: " + localusers.toString());
380 }
381 } catch (Exception e) {
382 getLogger().error("Cannot open private UserRepository");
383 throw e;
384 }
385 }
386
387 private void initializeServernamesAndPostmaster() throws ConfigurationException, ParseException {
388 String defaultDomain = getDefaultDomain();
389 if (domains.containsDomain(defaultDomain) == false) {
390 if (domains instanceof ManageableDomainList) {
391 if(((ManageableDomainList) domains).addDomain(defaultDomain) != false) {
392 throw new ConfigurationException("Configured defaultdomain could not get added to DomainList");
393 }
394 } else {
395 throw new ConfigurationException("Configured defaultDomain not exist in DomainList");
396 }
397 }
398 serverNames = domains.getDomains();
399
400 if (serverNames == null || serverNames.size() == 0) throw new ConfigurationException("No domainnames configured");
401
402
403 attributes.put(Constants.DEFAULT_DOMAIN, defaultDomain);
404
405
406 String postMasterAddress = conf.getChild("postmaster").getValue("postmaster").toLowerCase(Locale.US);
407
408
409
410 if (postMasterAddress.indexOf('@') < 0) {
411 String domainName = null;
412
413 Iterator i = serverNames.iterator();
414 while (i.hasNext()) {
415 String serverName = i.next().toString().toLowerCase(Locale.US);
416 if (!("localhost".equals(serverName))) {
417 domainName = serverName;
418 continue;
419 }
420 }
421
422 postMasterAddress = postMasterAddress + "@" + (domainName != null ? domainName : defaultDomain);
423 }
424 this.postmaster = new MailAddress( postMasterAddress );
425
426 if (!isLocalServer(postmaster.getHost())) {
427 StringBuffer warnBuffer
428 = new StringBuffer(320)
429 .append("The specified postmaster address ( ")
430 .append(postmaster)
431 .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.");
432 getLogger().warn(warnBuffer.toString());
433 }
434 }
435
436 private void initializeLocalDeliveryMailet() throws MessagingException {
437
438
439 DefaultConfiguration conf = new DefaultConfiguration("mailet", "generated:James.initialize()");
440 MailetConfigImpl configImpl = new MailetConfigImpl();
441 configImpl.setMailetName("LocalDelivery");
442 configImpl.setConfiguration(conf);
443 configImpl.setMailetContext(this);
444 localDeliveryMailet = new LocalDelivery();
445 localDeliveryMailet.init(configImpl);
446 }
447
448
449
450
451
452
453 public void setStore(Store store) {
454 this.store = store;
455 }
456
457
458
459
460
461
462 public void setSpool(SpoolRepository spool) {
463 this.spool = spool;
464 }
465
466
467
468
469
470
471 public void setLocalusers(UsersRepository localusers) {
472 this.localusers = localusers;
473 }
474
475
476
477
478
479
480
481
482
483 public void sendMail(MimeMessage message) throws MessagingException {
484 MailAddress sender = new MailAddress((InternetAddress)message.getFrom()[0]);
485 Collection recipients = new HashSet();
486 Address addresses[] = message.getAllRecipients();
487 if (addresses != null) {
488 for (int i = 0; i < addresses.length; i++) {
489
490
491 if ( addresses[i] instanceof InternetAddress ) {
492 recipients.add(new MailAddress((InternetAddress)addresses[i]));
493 }
494 }
495 }
496 sendMail(sender, recipients, message);
497 }
498
499
500
501
502 public void sendMail(MailAddress sender, Collection recipients, MimeMessage message)
503 throws MessagingException {
504 sendMail(sender, recipients, message, Mail.DEFAULT);
505 }
506
507
508
509
510 public void sendMail(MailAddress sender, Collection recipients, MimeMessage message, String state)
511 throws MessagingException {
512 MailImpl mail = new MailImpl(getId(), sender, recipients, message);
513 try {
514 mail.setState(state);
515 sendMail(mail);
516 } finally {
517 ContainerUtil.dispose(mail);
518 }
519 }
520
521
522
523
524 public void sendMail(MailAddress sender, Collection recipients, InputStream msg)
525 throws MessagingException {
526
527 MailHeaders headers = new MailHeaders(msg);
528
529
530 if (!headers.isValid()) {
531 throw new MessagingException("Some REQURED header field is missing. Invalid Message");
532 }
533 ByteArrayInputStream headersIn = new ByteArrayInputStream(headers.toByteArray());
534 sendMail(new MailImpl(getId(), sender, recipients, new SequenceInputStream(headersIn, msg)));
535 }
536
537
538
539
540 public void sendMail(Mail mail) throws MessagingException {
541 try {
542 spool.store(mail);
543 } catch (Exception e) {
544 getLogger().error("Error storing message: " + e.getMessage(),e);
545 try {
546 spool.remove(mail);
547 } catch (Exception ignored) {
548 getLogger().error("Error removing message after an error storing it: " + e.getMessage(),e);
549 }
550 throw new MessagingException("Exception spooling message: " + e.getMessage(), e);
551 }
552 if (getLogger().isDebugEnabled()) {
553 StringBuffer logBuffer =
554 new StringBuffer(64)
555 .append("Mail ")
556 .append(mail.getName())
557 .append(" pushed in spool");
558 getLogger().debug(logBuffer.toString());
559 }
560 }
561
562
563
564
565 public synchronized MailRepository getUserInbox(String userName) {
566 MailRepository userInbox = null;
567
568 if (virtualHosting == false && (userName.indexOf("@") < 0) == false) {
569 userName = userName.split("@")[0];
570 }
571
572 userInbox = (MailRepository) mailboxes.get(userName);
573
574 if (userInbox != null) {
575 return userInbox;
576
577
578
579
580
581
582
583
584
585 } else {
586
587 if (getLogger().isDebugEnabled()) {
588 getLogger().debug("Retrieving and caching inbox for " + userName );
589 }
590
591 StringBuffer destinationBuffer = new StringBuffer(192);
592
593 if (virtualHosting == true && inboxRootURL.startsWith("file://") && !(userName.indexOf("@") < 0)) {
594 String userArgs[] = userName.split("@");
595
596
597 destinationBuffer.append(inboxRootURL).append(userArgs[1]).append("/").append(userArgs[0]).append("/");
598 } else {
599 destinationBuffer.append(inboxRootURL).append(userName).append("/");
600 }
601
602 String destination = destinationBuffer.toString();
603 try {
604
605 DefaultConfiguration mboxConf = new DefaultConfiguration(conf
606 .getChild("inboxRepository").getChild("repository"));
607 mboxConf.setAttribute("destinationURL", destination);
608
609 userInbox = (MailRepository) store.select(mboxConf);
610 if (userInbox!=null) {
611 mailboxes.put(userName, userInbox);
612 }
613 } catch (Exception e) {
614 if (getLogger().isErrorEnabled()) {
615 getLogger().error("Cannot open user Mailbox",e);
616 }
617 throw new RuntimeException("Error in getUserInbox.",e);
618 }
619 return userInbox;
620 }
621 }
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649 public String getId() {
650
651 final long localCount;
652 synchronized (countLock) {
653 localCount = count++;
654 }
655 StringBuffer idBuffer =
656 new StringBuffer(64)
657 .append("Mail")
658 .append(System.currentTimeMillis())
659 .append("-")
660 .append(localCount);
661 return idBuffer.toString();
662 }
663
664
665
666
667
668
669
670 public static void main(String[] args) {
671 System.out.println("ERROR!");
672 System.out.println("Cannot execute James as a stand alone application.");
673 System.out.println("To run James, you need to have the Avalon framework installed.");
674 System.out.println("Please refer to the Readme file to know how to run James.");
675 }
676
677
678
679
680
681
682 public Collection getMailServers(String host) {
683 try {
684 return lookupDNSServer().findMXRecords(host);
685 } catch (TemporaryResolutionException e) {
686
687 return Collections.unmodifiableCollection(new ArrayList(0));
688 }
689 }
690
691
692
693
694 public Object getAttribute(String key) {
695 return attributes.get(key);
696 }
697
698
699
700
701 public void setAttribute(String key, Object object) {
702 attributes.put(key, object);
703 }
704
705
706
707
708 public void removeAttribute(String key) {
709 attributes.remove(key);
710 }
711
712
713
714
715 public Iterator getAttributeNames() {
716 Vector names = new Vector();
717 for (Enumeration e = attributes.keys(); e.hasMoreElements(); ) {
718 names.add(e.nextElement());
719 }
720 return names.iterator();
721 }
722
723
724
725
726
727
728
729
730
731
732 public void bounce(Mail mail, String message) throws MessagingException {
733 bounce(mail, message, getPostmaster());
734 }
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761 public void bounce(Mail mail, String message, MailAddress bouncer) throws MessagingException {
762 if (mail.getSender() == null) {
763 if (getLogger().isInfoEnabled())
764 getLogger().info("Mail to be bounced contains a null (<>) reverse path. No bounce will be sent.");
765 return;
766 } else {
767
768 if (getLogger().isInfoEnabled())
769 getLogger().info("Processing a bounce request for a message with a reverse path of " + mail.getSender().toString());
770 }
771
772 MailImpl reply = rawBounce(mail,message);
773
774 reply.getMessage().setFrom(bouncer.toInternetAddress());
775 reply.getMessage().saveChanges();
776
777 reply.setSender(null);
778 sendMail(reply);
779 ContainerUtil.dispose(reply);
780 }
781
782
783
784
785
786
787
788
789
790
791 private MailImpl rawBounce(Mail mail, String bounceText) throws MessagingException {
792
793 MimeMessage original = mail.getMessage();
794 MimeMessage reply = (MimeMessage) original.reply(false);
795 reply.setSubject("Re: " + original.getSubject());
796 reply.setSentDate(new Date());
797 Collection recipients = new HashSet();
798 recipients.add(mail.getSender());
799 InternetAddress addr[] = { new InternetAddress(mail.getSender().toString())};
800 reply.setRecipients(Message.RecipientType.TO, addr);
801 reply.setFrom(new InternetAddress(mail.getRecipients().iterator().next().toString()));
802 reply.setText(bounceText);
803 reply.setHeader(RFC2822Headers.MESSAGE_ID, "replyTo-" + mail.getName());
804 return new MailImpl(
805 "replyTo-" + mail.getName(),
806 new MailAddress(mail.getRecipients().iterator().next().toString()),
807 recipients,
808 reply);
809 }
810
811
812
813
814 public boolean isLocalUser(String name) {
815 if (name == null) {
816 return false;
817 }
818 try {
819 if (name.indexOf("@") == -1) {
820 return isLocalEmail(new MailAddress(name,"localhost"));
821 } else {
822 return isLocalEmail(new MailAddress(name));
823 }
824 } catch (ParseException e) {
825 log("Error checking isLocalUser for user "+name);
826 return false;
827 }
828 }
829
830
831
832
833 public boolean isLocalEmail(MailAddress mailAddress) {
834 String userName = mailAddress.toString();
835 if (!isLocalServer(mailAddress.getHost())) {
836 return false;
837 }
838 if (virtualHosting == false) {
839 userName = mailAddress.getUser();
840 }
841 return localusers.contains(userName);
842 }
843
844
845
846
847 public MailAddress getPostmaster() {
848 return postmaster;
849 }
850
851
852
853
854 public int getMajorVersion() {
855 return 2;
856 }
857
858
859
860
861 public int getMinorVersion() {
862 return 4;
863 }
864
865
866
867
868 public boolean isLocalServer( final String serverName ) {
869 String lowercase = serverName.toLowerCase(Locale.US);
870
871
872
873 if ("localhost".equals(serverName) || domains.containsDomain(lowercase)){
874 return true;
875 } else {
876 return false;
877 }
878 }
879
880
881
882
883 public String getServerInfo() {
884 return "Apache JAMES";
885 }
886
887
888
889
890
891
892 private Logger getMailetLogger() {
893 if (mailetLogger == null) {
894 mailetLogger = getLogger().getChildLogger("Mailet");
895 }
896 return mailetLogger;
897 }
898
899
900
901
902 public void log(String message) {
903 getMailetLogger().info(message);
904 }
905
906
907
908
909 public void log(String message, Throwable t) {
910 getMailetLogger().info(message,t);
911 }
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926 public boolean addUser(String userName, String password) {
927 return localusers.addUser(userName, password);
928 }
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946 public Iterator getSMTPHostAddresses(String domainName) {
947 try {
948 return lookupDNSServer().getSMTPHostAddresses(domainName);
949 } catch (TemporaryResolutionException e) {
950
951 return Collections.unmodifiableCollection(new ArrayList(0)).iterator();
952 }
953 }
954
955 protected DNSService lookupDNSServer() {
956 DNSService dnsServer;
957 try {
958 dnsServer = (DNSService) compMgr.lookup( DNSService.ROLE );
959 } catch ( final ServiceException cme ) {
960 getLogger().error("Fatal configuration error - DNS Servers lost!", cme );
961 throw new RuntimeException("Fatal configuration error - DNS Servers lost!");
962 }
963 return dnsServer;
964 }
965
966
967
968
969
970
971
972
973
974
975 public void storeMail(MailAddress sender, MailAddress recipient, MimeMessage msg) throws MessagingException {
976 if (recipient == null) {
977 throw new IllegalArgumentException("Recipient for mail to be spooled cannot be null.");
978 }
979 if (msg == null) {
980 throw new IllegalArgumentException("Mail message to be spooled cannot be null.");
981 }
982 Collection recipients = new HashSet();
983 recipients.add(recipient);
984 MailImpl m = new MailImpl(getId(),sender,recipients,msg);
985 localDeliveryMailet.service(m);
986 ContainerUtil.dispose(m);
987 }
988
989
990
991
992 public boolean supportVirtualHosting() {
993 return virtualHosting;
994 }
995
996
997
998
999 public String getDefaultDomain() {
1000 if (defaultDomain == null) {
1001 List domainList = domains.getDomains();
1002 if (domainList == null || domainList.isEmpty()) {
1003 return "localhost";
1004 } else {
1005 return (String) domainList.get(0);
1006 }
1007 } else {
1008 return defaultDomain;
1009 }
1010 }
1011
1012
1013
1014
1015 public String getHelloName() {
1016 if (helloName != null) {
1017 return helloName;
1018 } else {
1019 String hello = (String) getAttribute(Constants.HELLO_NAME);
1020 if (hello == null) {
1021 return defaultDomain;
1022 } else {
1023 return hello;
1024 }
1025 }
1026 }
1027 }