PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/james-2.2.0/proposals/imap/java/org/apache/james/James.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 910 lines | 520 code | 83 blank | 307 comment | 70 complexity | d8996938e02f24c553609a8d70e877df MD5 | raw file
  1. /***********************************************************************
  2. * Copyright (c) 2000-2004 The Apache Software Foundation. *
  3. * All rights reserved. *
  4. * ------------------------------------------------------------------- *
  5. * Licensed under the Apache License, Version 2.0 (the "License"); you *
  6. * may not use this file except in compliance with the License. You *
  7. * may obtain a copy of the License at: *
  8. * *
  9. * http://www.apache.org/licenses/LICENSE-2.0 *
  10. * *
  11. * Unless required by applicable law or agreed to in writing, software *
  12. * distributed under the License is distributed on an "AS IS" BASIS, *
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
  14. * implied. See the License for the specific language governing *
  15. * permissions and limitations under the License. *
  16. ***********************************************************************/
  17. package org.apache.james;
  18. import org.apache.avalon.framework.activity.Initializable;
  19. import org.apache.avalon.framework.component.*;
  20. import org.apache.avalon.framework.configuration.Configurable;
  21. import org.apache.avalon.framework.configuration.Configuration;
  22. import org.apache.avalon.framework.configuration.ConfigurationException;
  23. import org.apache.avalon.framework.configuration.DefaultConfiguration;
  24. import org.apache.avalon.framework.context.Context;
  25. import org.apache.avalon.framework.context.Contextualizable;
  26. import org.apache.avalon.framework.context.DefaultContext;
  27. import org.apache.avalon.framework.logger.AbstractLogEnabled;
  28. import org.apache.avalon.framework.logger.Logger;
  29. import org.apache.james.core.MailHeaders;
  30. import org.apache.james.core.MailImpl;
  31. import org.apache.james.imapserver.ACLMailbox;
  32. import org.apache.james.imapserver.Host;
  33. import org.apache.james.services.*;
  34. import org.apache.james.userrepository.DefaultJamesUser;
  35. import org.apache.james.util.RFC2822Headers;
  36. import org.apache.james.util.RFC822DateFormat;
  37. import org.apache.mailet.Mail;
  38. import org.apache.mailet.MailAddress;
  39. import org.apache.mailet.MailetContext;
  40. import javax.mail.Address;
  41. import javax.mail.MessagingException;
  42. import javax.mail.internet.InternetAddress;
  43. import javax.mail.internet.MimeBodyPart;
  44. import javax.mail.internet.MimeMessage;
  45. import javax.mail.internet.MimeMultipart;
  46. import java.io.ByteArrayInputStream;
  47. import java.io.IOException;
  48. import java.io.InputStream;
  49. import java.io.SequenceInputStream;
  50. import java.net.InetAddress;
  51. import java.net.UnknownHostException;
  52. import java.util.*;
  53. /**
  54. * Core class for JAMES. Provides three primary services:
  55. * <br> 1) Instantiates resources, such as user repository, and protocol
  56. * handlers
  57. * <br> 2) Handles interactions between components
  58. * <br> 3) Provides container services for Mailets
  59. *
  60. *
  61. * @version This is $Revision: 1.6.2.3 $
  62. */
  63. public class James
  64. extends AbstractLogEnabled
  65. implements Contextualizable, Composable, Configurable, JamesMBean,
  66. Initializable, MailServer, MailetContext, Component {
  67. /**
  68. * The software name and version
  69. */
  70. private final static String SOFTWARE_NAME_VERSION = Constants.SOFTWARE_NAME + " " + Constants.SOFTWARE_VERSION;
  71. /**
  72. * The component manager used both internally by James and by Mailets.
  73. */
  74. private DefaultComponentManager compMgr; //Components shared
  75. /**
  76. * TODO: Investigate what this is supposed to do. Looks like it
  77. * was supposed to be the Mailet context.
  78. */
  79. private DefaultContext context;
  80. /**
  81. * The top level configuration object for this server.
  82. */
  83. private Configuration conf;
  84. /**
  85. * The logger used by the Mailet API.
  86. */
  87. private Logger mailetLogger = null;
  88. /**
  89. * The mail store containing the inbox repository and the spool.
  90. */
  91. private MailStore mailstore;
  92. /**
  93. * The store containing the local user repository.
  94. */
  95. private UsersStore usersStore;
  96. /**
  97. * The spool used for processing mail handled by this server.
  98. */
  99. private SpoolRepository spool;
  100. /**
  101. * The repository that stores the user inboxes.
  102. */
  103. private MailRepository localInbox;
  104. /**
  105. * The root URL used to get mailboxes from the repository
  106. */
  107. private String inboxRootURL;
  108. /**
  109. * The user repository for this mail server. Contains all the users with inboxes
  110. * on this server.
  111. */
  112. private UsersRepository localusers;
  113. /**
  114. * The collection of domain/server names for which this instance of James
  115. * will receive and process mail.
  116. */
  117. private Collection serverNames;
  118. /**
  119. * Whether to ignore case when looking up user names on this server
  120. */
  121. private boolean ignoreCase;
  122. /**
  123. * Whether to enable aliasing for users on this server
  124. */
  125. private boolean enableAliases;
  126. /**
  127. * Whether to enable forwarding for users on this server
  128. */
  129. private boolean enableForwarding;
  130. /**
  131. * The number of mails generated. Access needs to be synchronized for
  132. * thread safety and to ensure that all threads see the latest value.
  133. */
  134. private static long count;
  135. /**
  136. * The address of the postmaster for this server
  137. */
  138. private MailAddress postmaster;
  139. /**
  140. * A map used to store mailboxes and reduce the cost of lookup of individual
  141. * mailboxes.
  142. */
  143. private Map mailboxes; //Not to be shared!
  144. /**
  145. * A hash table of server attributes
  146. * These are the MailetContext attributes
  147. */
  148. private Hashtable attributes = new Hashtable();
  149. /**
  150. * The Avalon context used by the instance
  151. */
  152. protected Context myContext;
  153. /**
  154. * An RFC822 date formatter used to format dates in mail headers
  155. */
  156. private RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
  157. /**
  158. * Whether James should use IMAP storage
  159. */
  160. private boolean useIMAPstorage = false;
  161. /**
  162. * The host to be used for IMAP storage
  163. */
  164. private Host imapHost;
  165. /**
  166. * @see org.apache.avalon.framework.context.Contextualizable#contextualize(Context)
  167. */
  168. public void contextualize(final Context context) {
  169. this.myContext = context;
  170. }
  171. /**
  172. * @see org.apache.avalon.framework.component.Composable#compose(ComponentManager)
  173. */
  174. public void compose(ComponentManager comp) {
  175. compMgr = new DefaultComponentManager(comp);
  176. mailboxes = new HashMap(31);
  177. }
  178. /**
  179. * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
  180. */
  181. public void configure(Configuration conf) {
  182. this.conf = conf;
  183. }
  184. /**
  185. * @see org.apache.avalon.framework.activity.Initializable#initialize()
  186. */
  187. public void initialize() throws Exception {
  188. getLogger().info("JAMES init...");
  189. // TODO: This should retrieve a more specific named thread pool from
  190. // Context that is set up in server.xml
  191. try {
  192. mailstore = (MailStore) compMgr.lookup( MailStore.ROLE );
  193. } catch (Exception e) {
  194. if (getLogger().isWarnEnabled()) {
  195. getLogger().warn("Can't get Store: " + e);
  196. }
  197. }
  198. if (getLogger().isDebugEnabled()) {
  199. getLogger().debug("Using MailStore: " + mailstore.toString());
  200. }
  201. try {
  202. usersStore = (UsersStore) compMgr.lookup( UsersStore.ROLE );
  203. } catch (Exception e) {
  204. if (getLogger().isWarnEnabled()) {
  205. getLogger().warn("Can't get Store: " + e);
  206. }
  207. }
  208. if (getLogger().isDebugEnabled()) {
  209. getLogger().debug("Using UsersStore: " + usersStore.toString());
  210. }
  211. String hostName = null;
  212. try {
  213. hostName = InetAddress.getLocalHost().getHostName();
  214. } catch (UnknownHostException ue) {
  215. hostName = "localhost";
  216. }
  217. context = new DefaultContext();
  218. context.put("HostName", hostName);
  219. getLogger().info("Local host is: " + hostName);
  220. // Get the domains and hosts served by this instance
  221. serverNames = new HashSet();
  222. Configuration serverConf = conf.getChild("servernames");
  223. if (serverConf.getAttributeAsBoolean("autodetect") && (!hostName.equals("localhost"))) {
  224. serverNames.add(hostName.toLowerCase(Locale.US));
  225. }
  226. final Configuration[] serverNameConfs =
  227. conf.getChild( "servernames" ).getChildren( "servername" );
  228. for ( int i = 0; i < serverNameConfs.length; i++ ) {
  229. serverNames.add( serverNameConfs[i].getValue().toLowerCase(Locale.US));
  230. if (serverConf.getAttributeAsBoolean("autodetectIP", true)) {
  231. try {
  232. /* This adds the IP address(es) for each host to support
  233. * support <user@address-literal> - RFC 2821, sec 4.1.3.
  234. * It might be proper to use the actual IP addresses
  235. * available on this server, but we can't do that
  236. * without NetworkInterface from JDK 1.4. Because of
  237. * Virtual Hosting considerations, we may need to modify
  238. * this to keep hostname and IP associated, rather than
  239. * just both in the set.
  240. */
  241. InetAddress[] addrs = InetAddress.getAllByName(serverNameConfs[i].getValue());
  242. for (int j = 0; j < addrs.length ; j++) {
  243. serverNames.add(addrs[j].getHostAddress());
  244. }
  245. }
  246. catch(Exception genericException) {
  247. getLogger().error("Cannot get IP address(es) for " + serverNameConfs[i].getValue());
  248. }
  249. }
  250. }
  251. if (serverNames.isEmpty()) {
  252. throw new ConfigurationException( "Fatal configuration error: no servernames specified!");
  253. }
  254. if (getLogger().isInfoEnabled()) {
  255. for (Iterator i = serverNames.iterator(); i.hasNext(); ) {
  256. getLogger().info("Handling mail for: " + i.next());
  257. }
  258. }
  259. context.put(Constants.SERVER_NAMES, this.serverNames);
  260. attributes.put(Constants.SERVER_NAMES, this.serverNames);
  261. // Get postmaster
  262. String postMasterAddress = conf.getChild("postmaster").getValue("postmaster");
  263. // if there is no @domain part, then add the first one from the
  264. // list of supported domains that isn't localhost. If that
  265. // doesn't work, use the hostname, even if it is localhost.
  266. if (postMasterAddress.indexOf('@') < 0) {
  267. String domainName = null; // the domain to use
  268. // loop through candidate domains until we find one or exhaust the list
  269. for ( int i = 0; domainName == null && i < serverNameConfs.length ; i++ ) {
  270. String serverName = serverNameConfs[i].getValue().toLowerCase(Locale.US);
  271. if (!("localhost".equals(serverName))) {
  272. domainName = serverName; // ok, not localhost, so use it
  273. }
  274. }
  275. // if we found a suitable domain, use it. Otherwise fallback to the host name.
  276. postMasterAddress = postMasterAddress + "@" + (domainName != null ? domainName : hostName);
  277. }
  278. this.postmaster = new MailAddress( postMasterAddress );
  279. context.put( Constants.POSTMASTER, postmaster );
  280. if (!isLocalServer(postmaster.getHost())) {
  281. StringBuffer warnBuffer
  282. = new StringBuffer(320)
  283. .append("The specified postmaster address ( ")
  284. .append(postmaster)
  285. .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.");
  286. getLogger().warn(warnBuffer.toString());
  287. }
  288. Configuration userNamesConf = conf.getChild("usernames");
  289. ignoreCase = userNamesConf.getAttributeAsBoolean("ignoreCase", false);
  290. enableAliases = userNamesConf.getAttributeAsBoolean("enableAliases", false);
  291. enableForwarding = userNamesConf.getAttributeAsBoolean("enableForwarding", false);
  292. //Get localusers
  293. try {
  294. localusers = (UsersRepository) usersStore.getRepository("LocalUsers");
  295. } catch (Exception e) {
  296. getLogger().error("Cannot open private UserRepository");
  297. throw e;
  298. }
  299. //}
  300. compMgr.put( UsersRepository.ROLE, (Component)localusers);
  301. getLogger().info("Local users repository opened");
  302. try {
  303. // Get storage config param
  304. if (conf.getChild("storage").getValue().equals("IMAP")) {
  305. useIMAPstorage = true;
  306. getLogger().info("Using IMAP Store-System");
  307. }
  308. }catch(Exception e) {
  309. // No storage entry found in config file
  310. }
  311. // Get the LocalInbox repository
  312. if (useIMAPstorage) {
  313. try {
  314. // We will need to use a no-args constructor for flexibility
  315. imapHost = (Host) compMgr.lookup( Host.ROLE );
  316. } catch (Exception e) {
  317. getLogger().error("Exception in IMAP Storage init: " + e.getMessage());
  318. throw e;
  319. }
  320. } else {
  321. Configuration inboxConf = conf.getChild("inboxRepository");
  322. Configuration inboxRepConf = inboxConf.getChild("repository");
  323. try {
  324. localInbox = (MailRepository) mailstore.select(inboxRepConf);
  325. } catch (Exception e) {
  326. getLogger().error("Cannot open private MailRepository");
  327. throw e;
  328. }
  329. inboxRootURL = inboxRepConf.getAttribute("destinationURL");
  330. }
  331. getLogger().info("Private Repository LocalInbox opened");
  332. // Add this to comp
  333. compMgr.put( MailServer.ROLE, this);
  334. spool = mailstore.getInboundSpool();
  335. if (getLogger().isDebugEnabled()) {
  336. getLogger().debug("Got spool");
  337. }
  338. // For mailet engine provide MailetContext
  339. //compMgr.put("org.apache.mailet.MailetContext", this);
  340. // For AVALON aware mailets and matchers, we put the Component object as
  341. // an attribute
  342. attributes.put(Constants.AVALON_COMPONENT_MANAGER, compMgr);
  343. System.out.println(SOFTWARE_NAME_VERSION);
  344. getLogger().info("JAMES ...init end");
  345. }
  346. /**
  347. * Place a mail on the spool for processing
  348. *
  349. * @param message the message to send
  350. *
  351. * @throws MessagingException if an exception is caught while placing the mail
  352. * on the spool
  353. */
  354. public void sendMail(MimeMessage message) throws MessagingException {
  355. MailAddress sender = new MailAddress((InternetAddress)message.getFrom()[0]);
  356. Collection recipients = new HashSet();
  357. Address addresses[] = message.getAllRecipients();
  358. for (int i = 0; i < addresses.length; i++) {
  359. recipients.add(new MailAddress((InternetAddress)addresses[i]));
  360. }
  361. sendMail(sender, recipients, message);
  362. }
  363. /**
  364. * Place a mail on the spool for processing
  365. *
  366. * @param sender the sender of the mail
  367. * @param recipients the recipients of the mail
  368. * @param message the message to send
  369. *
  370. * @throws MessagingException if an exception is caught while placing the mail
  371. * on the spool
  372. */
  373. public void sendMail(MailAddress sender, Collection recipients, MimeMessage message)
  374. throws MessagingException {
  375. sendMail(sender, recipients, message, Mail.DEFAULT);
  376. }
  377. /**
  378. * Place a mail on the spool for processing
  379. *
  380. * @param sender the sender of the mail
  381. * @param recipients the recipients of the mail
  382. * @param message the message to send
  383. * @param state the state of the message
  384. *
  385. * @throws MessagingException if an exception is caught while placing the mail
  386. * on the spool
  387. */
  388. public void sendMail(MailAddress sender, Collection recipients, MimeMessage message, String state)
  389. throws MessagingException {
  390. MailImpl mail = new MailImpl(getId(), sender, recipients, message);
  391. mail.setState(state);
  392. sendMail(mail);
  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 msg an <code>InputStream</code> containing the message
  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, InputStream msg)
  405. throws MessagingException {
  406. // parse headers
  407. MailHeaders headers = new MailHeaders(msg);
  408. // if headers do not contains minimum REQUIRED headers fields throw Exception
  409. if (!headers.isValid()) {
  410. throw new MessagingException("Some REQURED header field is missing. Invalid Message");
  411. }
  412. ByteArrayInputStream headersIn = new ByteArrayInputStream(headers.toByteArray());
  413. sendMail(new MailImpl(getId(), sender, recipients, new SequenceInputStream(headersIn, msg)));
  414. }
  415. /**
  416. * Place a mail on the spool for processing
  417. *
  418. * @param mail the mail to place on the spool
  419. *
  420. * @throws MessagingException if an exception is caught while placing the mail
  421. * on the spool
  422. */
  423. public void sendMail(Mail mail) throws MessagingException {
  424. MailImpl mailimpl = (MailImpl)mail;
  425. try {
  426. spool.store(mailimpl);
  427. } catch (Exception e) {
  428. try {
  429. spool.remove(mailimpl);
  430. } catch (Exception ignored) {
  431. }
  432. throw new MessagingException("Exception spooling message: " + e.getMessage(), e);
  433. }
  434. if (getLogger().isDebugEnabled()) {
  435. StringBuffer logBuffer =
  436. new StringBuffer(64)
  437. .append("Mail ")
  438. .append(mailimpl.getName())
  439. .append(" pushed in spool");
  440. getLogger().debug(logBuffer.toString());
  441. }
  442. }
  443. /**
  444. * <p>Retrieve the mail repository for a user</p>
  445. *
  446. * <p>For POP3 server only - at the moment.</p>
  447. *
  448. * @param userName the name of the user whose inbox is to be retrieved
  449. *
  450. * @return the POP3 inbox for the user
  451. */
  452. public synchronized MailRepository getUserInbox(String userName) {
  453. MailRepository userInbox = (MailRepository) null;
  454. userInbox = (MailRepository) mailboxes.get(userName);
  455. if (userInbox != null) {
  456. return userInbox;
  457. } else if (mailboxes.containsKey(userName)) {
  458. // we have a problem
  459. getLogger().error("Null mailbox for non-null key");
  460. throw new RuntimeException("Error in getUserInbox.");
  461. } else {
  462. // need mailbox object
  463. if (getLogger().isDebugEnabled()) {
  464. getLogger().debug("Retrieving and caching inbox for " + userName );
  465. }
  466. StringBuffer destinationBuffer =
  467. new StringBuffer(192)
  468. .append(inboxRootURL)
  469. .append(userName)
  470. .append("/");
  471. String destination = destinationBuffer.toString();
  472. DefaultConfiguration mboxConf
  473. = new DefaultConfiguration("repository", "generated:AvalonFileRepository.compose()");
  474. mboxConf.setAttribute("destinationURL", destination);
  475. mboxConf.setAttribute("type", "MAIL");
  476. try {
  477. userInbox = (MailRepository) mailstore.select(mboxConf);
  478. mailboxes.put(userName, userInbox);
  479. } catch (Exception e) {
  480. e.printStackTrace();
  481. if (getLogger().isErrorEnabled())
  482. {
  483. getLogger().error("Cannot open user Mailbox" + e);
  484. }
  485. throw new RuntimeException("Error in getUserInbox." + e);
  486. }
  487. return userInbox;
  488. }
  489. }
  490. /**
  491. * Return a new mail id.
  492. *
  493. * @return a new mail id
  494. */
  495. public String getId() {
  496. long localCount = -1;
  497. synchronized (James.class) {
  498. localCount = count++;
  499. }
  500. StringBuffer idBuffer =
  501. new StringBuffer(64)
  502. .append("Mail")
  503. .append(System.currentTimeMillis())
  504. .append("-")
  505. .append(count++);
  506. return idBuffer.toString();
  507. }
  508. /**
  509. * The main method. Should never be invoked, as James must be called
  510. * from within an Avalon framework container.
  511. *
  512. * @param args the command line arguments
  513. */
  514. public static void main(String[] args) {
  515. System.out.println("ERROR!");
  516. System.out.println("Cannot execute James as a stand alone application.");
  517. System.out.println("To run James, you need to have the Avalon framework installed.");
  518. System.out.println("Please refer to the Readme file to know how to run James.");
  519. }
  520. //Methods for MailetContext
  521. /**
  522. * <p>Get the prioritized list of mail servers for a given host.</p>
  523. *
  524. * <p>TODO: This needs to be made a more specific ordered subtype of Collection.</p>
  525. *
  526. * @param host
  527. */
  528. public Collection getMailServers(String host) {
  529. DNSServer dnsServer = null;
  530. try {
  531. dnsServer = (DNSServer) compMgr.lookup( DNSServer.ROLE );
  532. } catch ( final ComponentException cme ) {
  533. getLogger().error("Fatal configuration error - DNS Servers lost!", cme );
  534. throw new RuntimeException("Fatal configuration error - DNS Servers lost!");
  535. }
  536. return dnsServer.findMXRecords(host);
  537. }
  538. public Object getAttribute(String key) {
  539. return attributes.get(key);
  540. }
  541. public void setAttribute(String key, Object object) {
  542. attributes.put(key, object);
  543. }
  544. public void removeAttribute(String key) {
  545. attributes.remove(key);
  546. }
  547. public Iterator getAttributeNames() {
  548. Vector names = new Vector();
  549. for (Enumeration e = attributes.keys(); e.hasMoreElements(); ) {
  550. names.add(e.nextElement());
  551. }
  552. return names.iterator();
  553. }
  554. /**
  555. * This generates a response to the Return-Path address, or the address of
  556. * the message's sender if the Return-Path is not available. Note that
  557. * this is different than a mail-client's reply, which would use the
  558. * Reply-To or From header. This will send the bounce with the server's
  559. * postmaster as the sender.
  560. */
  561. public void bounce(Mail mail, String message) throws MessagingException {
  562. bounce(mail, message, getPostmaster());
  563. }
  564. /**
  565. * This generates a response to the Return-Path address, or the address of
  566. * the message's sender if the Return-Path is not available. Note that
  567. * this is different than a mail-client's reply, which would use the
  568. * Reply-To or From header.
  569. */
  570. public void bounce(Mail mail, String message, MailAddress bouncer) throws MessagingException {
  571. MimeMessage orig = mail.getMessage();
  572. //Create the reply message
  573. MimeMessage reply = (MimeMessage) orig.reply(false);
  574. //If there is a Return-Path header,
  575. if (orig.getHeader(RFC2822Headers.RETURN_PATH) != null) {
  576. //Return the message to that address, not to the Reply-To address
  577. reply.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(orig.getHeader(RFC2822Headers.RETURN_PATH)[0]));
  578. }
  579. //Create the list of recipients in our MailAddress format
  580. Collection recipients = new HashSet();
  581. Address addresses[] = reply.getAllRecipients();
  582. for (int i = 0; i < addresses.length; i++) {
  583. recipients.add(new MailAddress((InternetAddress)addresses[i]));
  584. }
  585. //Change the sender...
  586. reply.setFrom(bouncer.toInternetAddress());
  587. try {
  588. //Create the message body
  589. MimeMultipart multipart = new MimeMultipart();
  590. //Add message as the first mime body part
  591. MimeBodyPart part = new MimeBodyPart();
  592. part.setContent(message, "text/plain");
  593. part.setHeader(RFC2822Headers.CONTENT_TYPE, "text/plain");
  594. multipart.addBodyPart(part);
  595. //Add the original message as the second mime body part
  596. part = new MimeBodyPart();
  597. part.setContent(orig.getContent(), orig.getContentType());
  598. part.setHeader(RFC2822Headers.CONTENT_TYPE, orig.getContentType());
  599. multipart.addBodyPart(part);
  600. reply.setHeader(RFC2822Headers.DATE, rfc822DateFormat.format(new Date()));
  601. reply.setContent(multipart);
  602. reply.setHeader(RFC2822Headers.CONTENT_TYPE, multipart.getContentType());
  603. } catch (IOException ioe) {
  604. throw new MessagingException("Unable to create multipart body", ioe);
  605. }
  606. //Send it off...
  607. sendMail(bouncer, recipients, reply);
  608. }
  609. /**
  610. * Returns whether that account has a local inbox on this server
  611. *
  612. * @param name the name to be checked
  613. *
  614. * @return whether the account has a local inbox
  615. */
  616. public boolean isLocalUser(String name) {
  617. if (ignoreCase) {
  618. return localusers.containsCaseInsensitive(name);
  619. } else {
  620. return localusers.contains(name);
  621. }
  622. }
  623. /**
  624. * Returns the address of the postmaster for this server.
  625. *
  626. * @return the <code>MailAddress</code> for the postmaster
  627. */
  628. public MailAddress getPostmaster() {
  629. return postmaster;
  630. }
  631. public void storeMail(MailAddress sender, MailAddress recipient, MimeMessage message)
  632. throws MessagingException {
  633. String username;
  634. if (recipient == null) {
  635. throw new IllegalArgumentException("Recipient for mail to be spooled cannot be null.");
  636. }
  637. if (message == null) {
  638. throw new IllegalArgumentException("Mail message to be spooled cannot be null.");
  639. }
  640. if (ignoreCase) {
  641. String originalUsername = recipient.getUser();
  642. username = localusers.getRealName(originalUsername);
  643. if (username == null) {
  644. StringBuffer errorBuffer =
  645. new StringBuffer(128)
  646. .append("The inbox for user ")
  647. .append(originalUsername)
  648. .append(" was not found on this server.");
  649. throw new MessagingException(errorBuffer.toString());
  650. }
  651. } else {
  652. username = recipient.getUser();
  653. }
  654. JamesUser user;
  655. if (enableAliases || enableForwarding) {
  656. user = (JamesUser) localusers.getUserByName(username);
  657. if (enableAliases && user.getAliasing()) {
  658. username = user.getAlias();
  659. }
  660. // Forwarding takes precedence over local aliases
  661. if (enableForwarding && user.getForwarding()) {
  662. MailAddress forwardTo = user.getForwardingDestination();
  663. if (forwardTo == null) {
  664. StringBuffer errorBuffer =
  665. new StringBuffer(128)
  666. .append("Forwarding was enabled for ")
  667. .append(username)
  668. .append(" but no forwarding address was set for this account.");
  669. throw new MessagingException(errorBuffer.toString());
  670. }
  671. Collection recipients = new HashSet();
  672. recipients.add(forwardTo);
  673. try {
  674. sendMail(sender, recipients, message);
  675. if (getLogger().isInfoEnabled()) {
  676. StringBuffer logBuffer =
  677. new StringBuffer(128)
  678. .append("Mail for ")
  679. .append(username)
  680. .append(" forwarded to ")
  681. .append(forwardTo.toString());
  682. getLogger().info(logBuffer.toString());
  683. }
  684. return;
  685. } catch (MessagingException me) {
  686. if (getLogger().isErrorEnabled()) {
  687. StringBuffer logBuffer =
  688. new StringBuffer(128)
  689. .append("Error forwarding mail to ")
  690. .append(forwardTo.toString())
  691. .append("attempting local delivery");
  692. getLogger().error(logBuffer.toString());
  693. }
  694. throw me;
  695. }
  696. }
  697. }
  698. if (useIMAPstorage) {
  699. ACLMailbox mbox = null;
  700. try {
  701. String folderName = "#user." + username + ".INBOX";
  702. getLogger().debug("Want to store to: " + folderName);
  703. mbox = imapHost.getMailbox(MailServer.MDA, folderName);
  704. if(mbox.store(message,MailServer.MDA)) {
  705. getLogger().info("Message " + message.getMessageID() +" stored in " + folderName);
  706. } else {
  707. throw new RuntimeException("Failed to store mail: ");
  708. }
  709. imapHost.releaseMailbox(MailServer.MDA, mbox);
  710. mbox = null;
  711. } catch (Exception e) {
  712. getLogger().error("Exception storing mail: " + e);
  713. e.printStackTrace();
  714. if (mbox != null) {
  715. imapHost.releaseMailbox(MailServer.MDA, mbox);
  716. mbox = null;
  717. }
  718. throw new RuntimeException("Exception storing mail: " + e);
  719. }
  720. } else {
  721. Collection recipients = new HashSet();
  722. recipients.add(recipient);
  723. MailImpl mailImpl = new MailImpl(getId(), sender, recipients, message);
  724. MailRepository userInbox = getUserInbox(username);
  725. if (userInbox == null) {
  726. StringBuffer errorBuffer =
  727. new StringBuffer(128)
  728. .append("The inbox for user ")
  729. .append(username)
  730. .append(" was not found on this server.");
  731. throw new MessagingException(errorBuffer.toString());
  732. }
  733. userInbox.store(mailImpl);
  734. }
  735. }
  736. /**
  737. * Return the major version number for the server
  738. *
  739. * @return the major vesion number for the server
  740. */
  741. public int getMajorVersion() {
  742. return 2;
  743. }
  744. /**
  745. * Return the minor version number for the server
  746. *
  747. * @return the minor vesion number for the server
  748. */
  749. public int getMinorVersion() {
  750. return 1;
  751. }
  752. /**
  753. * Check whether the mail domain in question is to be
  754. * handled by this server.
  755. *
  756. * @param serverName the name of the server to check
  757. * @return whether the server is local
  758. */
  759. public boolean isLocalServer( final String serverName ) {
  760. return serverNames.contains(serverName.toLowerCase(Locale.US));
  761. }
  762. /**
  763. * Return the type of the server
  764. *
  765. * @return the type of the server
  766. */
  767. public String getServerInfo() {
  768. return "Apache Jakarta JAMES";
  769. }
  770. /**
  771. * Return the logger for the Mailet API
  772. *
  773. * @return the logger for the Mailet API
  774. */
  775. private Logger getMailetLogger() {
  776. if (mailetLogger == null) {
  777. mailetLogger = getLogger().getChildLogger("Mailet");
  778. }
  779. return mailetLogger;
  780. }
  781. /**
  782. * Log a message to the Mailet logger
  783. *
  784. * @param message the message to pass to the Mailet logger
  785. */
  786. public void log(String message) {
  787. getMailetLogger().info(message);
  788. }
  789. /**
  790. * Log a message and a Throwable to the Mailet logger
  791. *
  792. * @param message the message to pass to the Mailet logger
  793. * @param t the <code>Throwable</code> to be logged
  794. */
  795. public void log(String message, Throwable t) {
  796. getMailetLogger().info(message,t);
  797. }
  798. /**
  799. * Adds a user to this mail server. Currently just adds user to a
  800. * UsersRepository.
  801. * <p> As we move to IMAP support this will also create mailboxes and
  802. * access control lists.
  803. *
  804. * @param userName String representing user name, that is the portion of
  805. * an email address before the '@<domain>'.
  806. * @param password String plaintext password
  807. * @return boolean true if user added succesfully, else false.
  808. */
  809. public boolean addUser(String userName, String password) {
  810. boolean success;
  811. DefaultJamesUser user = new DefaultJamesUser(userName, "SHA");
  812. user.setPassword(password);
  813. user.initialize();
  814. success = localusers.addUser(user);
  815. if (useIMAPstorage && success) {
  816. if ( imapHost.createPrivateMailAccount(userName) ) {
  817. getLogger().info("New MailAccount created for" + userName);
  818. }
  819. }
  820. return success;
  821. }
  822. public Iterator getSMTPHostAddresses(String domainName) {
  823. // TODO Auto-generated method stub
  824. return null;
  825. }
  826. }