You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by sb...@apache.org on 2004/02/11 19:02:07 UTC
cvs commit: james-server/src/xdocs fetchmail_configuration_2_2.xml documentation_2_1.xml fetchpop_configuration_2_1.xml
sbrewin 2004/02/11 10:02:07
Modified: src/java/org/apache/james/fetchmail FetchMail.java
FetchScheduler.java FetchScheduler.xinfo
ReaderInputStream.java
src/conf james-config.xml
src/xdocs documentation_2_1.xml
fetchpop_configuration_2_1.xml
Added: src/java/org/apache/james/fetchmail StoreProcessor.java
Account.java DynamicAccount.java
MessageProcessor.java ProcessorAbstract.java
FolderProcessor.java ParsedConfiguration.java
src/conf/samples/fetchmail maxMessageSize.xml
oneAccountManyUsers.xml
oneAccountPerUserDynamic.xml oneAccountPerUser.xml
oneAccountManyUsersDynamic.xml
src/conf james-fetchmail.xml
src/xdocs fetchmail_configuration_2_2.xml
Log:
Synchronized with the (previously) much more recent version of fetchmail in branch 2_1_fcs.
Set copyright notices to 2003-2004.
Revision Changes Path
1.11 +858 -327 james-server/src/java/org/apache/james/fetchmail/FetchMail.java
Index: FetchMail.java
===================================================================
RCS file: /home/cvs/james-server/src/java/org/apache/james/fetchmail/FetchMail.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- FetchMail.java 30 Jan 2004 02:22:08 -0000 1.10
+++ FetchMail.java 11 Feb 2004 18:02:06 -0000 1.11
@@ -1,5 +1,5 @@
/***********************************************************************
- * Copyright (c) 2000-2004 The Apache Software Foundation. *
+ * Copyright (c) 2003-2004 The Apache Software Foundation. *
* All rights reserved. *
* ------------------------------------------------------------------- *
* Licensed under the Apache License, Version 2.0 (the "License"); you *
@@ -18,20 +18,16 @@
package org.apache.james.fetchmail;
import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Properties;
-import javax.mail.Address;
-import javax.mail.Flags;
-import javax.mail.Folder;
-import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
-import javax.mail.Store;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
-import javax.mail.internet.ParseException;
import org.apache.avalon.cornerstone.services.scheduler.Target;
import org.apache.avalon.framework.configuration.Configurable;
@@ -40,398 +36,933 @@
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
-import org.apache.james.core.MailImpl;
import org.apache.james.services.MailServer;
-import org.apache.mailet.Mail;
-import org.apache.mailet.MailAddress;
+import org.apache.james.services.UsersStore;
+import org.apache.mailet.UsersRepository;
/**
- *
- * A class which fetches mail from a single account and inserts it
- * into the incoming spool
- *
- * $Id$
- *
+ * <p>Class <code>FetchMail</code> is an Avalon task that is periodically
+ * triggered to fetch mail from a JavaMail Message Store.</p>
+ *
+ * <p>The lifecycle of an instance of <code>FetchMail</code> is managed by
+ * Avalon. The <code>configure(Configuration)</code> method is invoked to parse
+ * and validate Configuration properties. The targetTriggered(String) method is
+ * invoked to execute the task.</p>
+ *
+ * <p>When triggered, a sorted list of Message Store Accounts to be processed is
+ * built. Each Message Store Account is processed by delegating to
+ * <code>StoreProcessor</code>.</p>
+ *
+ * <p>There are two kinds of Message Store Accounts, static and dynamic. Static
+ * accounts are expliciltly declared in the Configuration. Dynamic accounts are
+ * built each time the task is executed, one per each user defined to James,
+ * using the James user name with a configurable prefix and suffix to define
+ * the host user identity and recipient identity for each Account. Dynamic
+ * accounts allow <code>FetchMail</code> to fetch mail for all James users
+ * without modifying the Configuration parameters or restarting the Avalon
+ * server.</p>
+ *
+ * <p>To fully understand the operations supported by this task, read the Class
+ * documention for each Class in the delegation chain starting with this
+ * class' delegate, <code>StoreProcessor</code>. </p>
+ *
+ * <p>Creation Date: 24-May-03</p>
+ *
*/
-public class FetchMail extends AbstractLogEnabled implements Configurable, Target {
+public class FetchMail extends AbstractLogEnabled implements Configurable, Target
+{
/**
- * The MailServer service
- */
- private MailServer server;
- /**
- * The user to send the fetched mail to
+ * Key fields for DynamicAccounts.
*/
- private MailAddress recipient;
+ private class DynamicAccountKey
+ {
+ /**
+ * The base user name without prfix or suffix
+ */
+ private String fieldUserName;
+
+ /**
+ * The sequence number of the parameters used to construct the Account
+ */
+ private int fieldSequenceNumber;
+
+ /**
+ * Constructor for DynamicAccountKey.
+ */
+ private DynamicAccountKey()
+ {
+ super();
+ }
+
+ /**
+ * Constructor for DynamicAccountKey.
+ */
+ public DynamicAccountKey(String userName, int sequenceNumber)
+ {
+ this();
+ setUserName(userName);
+ setSequenceNumber(sequenceNumber);
+ }
+
+ /**
+ * @see java.lang.Object#equals(Object)
+ */
+ public boolean equals(Object obj)
+ {
+ if (null == obj)
+ return false;
+ if (!(obj.getClass() == getClass()))
+ return false;
+ return (
+ getUserName().equals(((DynamicAccountKey) obj).getUserName())
+ && getSequenceNumber()
+ == ((DynamicAccountKey) obj).getSequenceNumber());
+ }
- /**
- * Don't parse header looking for recipient
- */
- private boolean ignoreOriginalRecipient;
+ /**
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return getUserName().hashCode() ^ getSequenceNumber();
+ }
+
+ /**
+ * Returns the sequenceNumber.
+ * @return int
+ */
+ public int getSequenceNumber()
+ {
+ return fieldSequenceNumber;
+ }
+ /**
+ * Returns the userName.
+ * @return String
+ */
+ public String getUserName()
+ {
+ return fieldUserName;
+ }
+
+ /**
+ * Sets the sequenceNumber.
+ * @param sequenceNumber The sequenceNumber to set
+ */
+ protected void setSequenceNumber(int sequenceNumber)
+ {
+ fieldSequenceNumber = sequenceNumber;
+ }
+
+ /**
+ * Sets the userName.
+ * @param userName The userName to set
+ */
+ protected void setUserName(String userName)
+ {
+ fieldUserName = userName;
+ }
+
+ }
/**
- * The unique, identifying name for this task
+ * Creation Date: 06-Jun-03
*/
- private String fetchTaskName;
+ private class ParsedDynamicAccountParameters
+ {
+ private String fieldUserPrefix;
+ private String fieldUserSuffix;
+
+ private String fieldPassword;
+
+ private int fieldSequenceNumber;
+
+ private boolean fieldIgnoreRecipientHeader;
+ private String fieldRecipientPrefix;
+ private String fieldRecipientSuffix;
+
+ /**
+ * Constructor for ParsedDynamicAccountParameters.
+ */
+ private ParsedDynamicAccountParameters()
+ {
+ super();
+ }
+
+ /**
+ * Constructor for ParsedDynamicAccountParameters.
+ */
+ public ParsedDynamicAccountParameters(
+ int sequenceNumber,
+ Configuration configuration)
+ throws ConfigurationException
+ {
+ this();
+ setSequenceNumber(sequenceNumber);
+ setUserPrefix(configuration.getAttribute("userprefix", ""));
+ setUserSuffix(configuration.getAttribute("usersuffix", ""));
+ setRecipientPrefix(configuration.getAttribute("recipientprefix", ""));
+ setRecipientSuffix(configuration.getAttribute("recipientsuffix", ""));
+ setPassword(configuration.getAttribute("password"));
+ setIgnoreRecipientHeader(
+ configuration.getAttributeAsBoolean("ignorercpt-header"));
+ }
+
+ /**
+ * Returns the recipientprefix.
+ * @return String
+ */
+ public String getRecipientPrefix()
+ {
+ return fieldRecipientPrefix;
+ }
+
+ /**
+ * Returns the recipientsuffix.
+ * @return String
+ */
+ public String getRecipientSuffix()
+ {
+ return fieldRecipientSuffix;
+ }
+
+ /**
+ * Returns the userprefix.
+ * @return String
+ */
+ public String getUserPrefix()
+ {
+ return fieldUserPrefix;
+ }
+
+ /**
+ * Returns the userSuffix.
+ * @return String
+ */
+ public String getUserSuffix()
+ {
+ return fieldUserSuffix;
+ }
+
+ /**
+ * Sets the recipientprefix.
+ * @param recipientprefix The recipientprefix to set
+ */
+ protected void setRecipientPrefix(String recipientprefix)
+ {
+ fieldRecipientPrefix = recipientprefix;
+ }
+
+ /**
+ * Sets the recipientsuffix.
+ * @param recipientsuffix The recipientsuffix to set
+ */
+ protected void setRecipientSuffix(String recipientsuffix)
+ {
+ fieldRecipientSuffix = recipientsuffix;
+ }
+
+ /**
+ * Sets the userprefix.
+ * @param userprefix The userprefix to set
+ */
+ protected void setUserPrefix(String userprefix)
+ {
+ fieldUserPrefix = userprefix;
+ }
+
+ /**
+ * Sets the userSuffix.
+ * @param userSuffix The userSuffix to set
+ */
+ protected void setUserSuffix(String userSuffix)
+ {
+ fieldUserSuffix = userSuffix;
+ }
+
+ /**
+ * Returns the password.
+ * @return String
+ */
+ public String getPassword()
+ {
+ return fieldPassword;
+ }
+
+ /**
+ * Sets the ignoreRecipientHeader.
+ * @param ignoreRecipientHeader The ignoreRecipientHeader to set
+ */
+ protected void setIgnoreRecipientHeader(boolean ignoreRecipientHeader)
+ {
+ fieldIgnoreRecipientHeader = ignoreRecipientHeader;
+ }
+
+ /**
+ * Sets the password.
+ * @param password The password to set
+ */
+ protected void setPassword(String password)
+ {
+ fieldPassword = password;
+ }
+
+ /**
+ * Returns the ignoreRecipientHeader.
+ * @return boolean
+ */
+ public boolean isIgnoreRecipientHeader()
+ {
+ return fieldIgnoreRecipientHeader;
+ }
+
+ /**
+ * Returns the sequenceNumber.
+ * @return int
+ */
+ public int getSequenceNumber()
+ {
+ return fieldSequenceNumber;
+ }
+
+ /**
+ * Sets the sequenceNumber.
+ * @param sequenceNumber The sequenceNumber to set
+ */
+ protected void setSequenceNumber(int sequenceNumber)
+ {
+ fieldSequenceNumber = sequenceNumber;
+ }
+ }
/**
- * The server host name for this fetch task
+ * @see org.apache.avalon.cornerstone.services.scheduler.Target#targetTriggered(String)
*/
- private String sHost;
+ private boolean fieldFetching = false;
+
/**
- * The user name for this fetch task
+ * The Configuration for this task
*/
- private String sUser;
-
+ private ParsedConfiguration fieldConfiguration;
+
/**
- * The user password for this fetch task
+ * A List of ParsedDynamicAccountParameters, one for every <alllocal> entry
+ * in the configuration.
*/
- private String sPass;
-
+ private List fieldParsedDynamicAccountParameters;
+
/**
- * Keep retrieved messages on the remote mailserver. Normally, messages
- * are deleted from the folder on the mailserver after they have been retrieved
+ * The Static Accounts for this task.
+ * These are setup when the task is configured.
*/
- private boolean bKeep = false;
-
+ private List fieldStaticAccounts;
+
/**
- * Retrieve both old (seen) and new messages from the mailserver. The default
- * is to fetch only messages the server has not marked as seen.
- */
- private boolean bAll = false;
+ * The JavaMail Session for this fetch task.
+ */
+ private Session fieldSession;
+
/**
- * Recurse folders if available?
+ * The Dynamic Accounts for this task.
+ * These are setup each time the fetchtask is run.
+ */
+ private Map fieldDynamicAccounts;
+
+ /**
+ * The MailServer service
+ */
+ private MailServer fieldServer;
+
+ /**
+ * The Local Users repository
*/
- private boolean bRecurse = false;
+ private UsersRepository fieldLocalUsers;
/**
- * The name of the javamail provider we want to user (pop3,imap,nntp,etc...)
- *
+ * Constructor for POP3mail.
*/
- private String javaMailProviderName = "pop3";
+ public FetchMail()
+ {
+ super();
+ }
/**
- * The name of the folder to fetch from the javamail provider
- *
+ * Method configure parses and validates the Configuration data and creates
+ * a new <code>ParsedConfiguration</code>, an <code>Account</code> for each
+ * configured static account and a <code>ParsedDynamicAccountParameters</code>
+ * for each dynamic account.
+ *
+ * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
*/
- private String javaMailFolderName = "INBOX";
+ public void configure(Configuration configuration)
+ throws ConfigurationException
+ {
+ // Set any Session parameters passed in the Configuration
+ setSessionParameters(configuration);
+
+ // Create the ParsedConfiguration used in the delegation chain
+ ParsedConfiguration parsedConfiguration =
+ new ParsedConfiguration(
+ configuration,
+ getLogger(),
+ getServer(),
+ getLocalUsers());
+ setConfiguration(parsedConfiguration);
+
+ // Setup the Accounts
+ Configuration[] allAccounts = configuration.getChildren("accounts");
+ if (allAccounts.length < 1)
+ throw new ConfigurationException("Missing <accounts> section.");
+ if (allAccounts.length > 1)
+ throw new ConfigurationException("Too many <accounts> sections, there must be exactly one");
+ Configuration accounts = allAccounts[0];
+
+ // Create an Account for every configured account
+ Configuration[] accountsChildren = accounts.getChildren();
+ if (accountsChildren.length < 1)
+ throw new ConfigurationException("Missing <account> section.");
+
+ for (int i = 0; i < accountsChildren.length; i++)
+ {
+ Configuration accountsChild = accountsChildren[i];
+
+ if (accountsChild.getName() == "alllocal")
+ {
+ // <allLocal> is dynamic, save the parameters for accounts to
+ // be created when the task is triggered
+ getParsedDynamicAccountParameters().add(
+ new ParsedDynamicAccountParameters(i, accountsChild));
+ continue;
+ }
+ if (accountsChild.getName() == "account")
+ {
+ // Create an Account for the named user and
+ // add it to the list of static accounts
+ getStaticAccounts().add(
+ new Account(
+ i,
+ parsedConfiguration,
+ accountsChild.getAttribute("user"),
+ accountsChild.getAttribute("password"),
+ accountsChild.getAttribute("recipient"),
+ accountsChild.getAttributeAsBoolean(
+ "ignorercpt-header"),
+ getSession()));
+ continue;
+ }
+
+ throw new ConfigurationException(
+ "Illegal token: <"
+ + accountsChild.getName()
+ + "> in <accounts>");
+ }
+ }
/**
+ * Method target triggered fetches mail for each configured account.
+ *
* @see org.apache.avalon.cornerstone.services.scheduler.Target#targetTriggered(String)
*/
- private boolean fetching = false;
-
-
- public boolean processMessage(Session session, MimeMessage message, MimeMessage received) {
-
- Collection recipients = new ArrayList(1);
- try {
-
+ public void targetTriggered(String arg0)
+ {
+ // if we are already fetching then just return
+ if (isFetching())
+ {
+ getLogger().info(
+ "Triggered fetch cancelled. A fetch is already in progress.");
+ return;
+ }
- if (!ignoreOriginalRecipient) {
- String er = getEnvelopeRecipient(message);
- if (er != null) {
- recipients.add(new MailAddress(er));
- getLogger().info("Using original envelope recipient as new envelope recipient");
- } else {
- Address[] to = message.getAllRecipients();
- if (to.length == 1) {
- recipients.add(new
- MailAddress((InternetAddress) to[0]));
- getLogger().info("Using To: header address as new envelope recipient");
- } else {
- getLogger().info("Using configured recipient as new envelope recipient");
- recipients.add(recipient);
+ // Enter Fetching State
+ try
+ {
+ setFetching(true);
+ getLogger().info("Fetcher starting fetches");
+
+ // if debugging, list the JavaMail property key/value pairs
+ // for this Session
+ if (getLogger().isDebugEnabled())
+ {
+ getLogger().debug("Session properties:");
+ Properties properties = getSession().getProperties();
+ Enumeration e = properties.keys();
+ while (e.hasMoreElements())
+ {
+ String key = (String) e.nextElement();
+ String val = (String) properties.get(key);
+ if (val.length() > 40)
+ {
+ val = val.substring(0, 37) + "...";
}
- }
- } else {
- getLogger().info("Using configured recipient as new envelope recipient");
- recipients.add(recipient);
- }
-
-
-
-
- //
- // set the X-fetched-from header
- received.addHeader("X-fetched-from", fetchTaskName);
-
- Mail mail = new MailImpl(server.getId(), new
- MailAddress((InternetAddress) received.getFrom()[0]), recipients, received);
-
-
- // Lets see if this mail has been bouncing by counting
- // the X-fetched-from headers
- // if it is then move it to the ERROR repository
- Enumeration enum = message.getMatchingHeaderLines(new String[]{"X-fetched-from"});
- int count = 1;
- while (enum.hasMoreElements()) {
- Object o = enum.nextElement();
- count++;
- }
- if (count > 3) {
- mail.setState(Mail.ERROR);
- mail.setErrorMessage("This mail from FetchMail task " + fetchTaskName + " seems to be bounceing!");
- getLogger().error("A message from FetchMail task " + fetchTaskName + " seems to be bounceing! Moved to Error repository");
- return false;
- }
+ getLogger().debug(key + "=" + val);
- server.sendMail(mail);
- getLogger().debug("Spooled message to " +
- recipients.toString());
-
- //
- // logging if needed
- getLogger().debug("Sent message " + message.toString());
- return true;
- } catch (ParseException pe) {
- recipients.add(recipient);
- } catch (MessagingException innerE) {
- getLogger().error("can't insert message " + message.toString());
- }
- return false;
- }
-
- public boolean processFolder(Session session, Folder folder) {
-
- boolean ret = false;
-
- try {
-
- //
- // try to open read/write and if that fails try read-only
- try {
- folder.open(Folder.READ_WRITE);
- } catch (MessagingException ex) {
- try {
- folder.open(Folder.READ_ONLY);
- } catch (MessagingException ex2) {
- getLogger().debug(fetchTaskName + " Failed to open folder!");
}
}
-// int totalMessages = folder.getMessageCount();
-// if (totalMessages == 0) {
-// getLogger().debug(fetchTaskName + " Empty folder");
-// folder.close(false);
-// fetching = false;
-// return false;
-// }
-
- Message[] msgs = folder.getMessages();
- MimeMessage[] received = new MimeMessage[folder.getMessageCount()];
-
- int j = 0;
- for (int i = 0; i < msgs.length; i++, j++) {
- Flags flags = msgs[i].getFlags();
- MimeMessage message = (MimeMessage) msgs[i];
-
- //
- // saved recieved messages for further processing...
- received[j] = new MimeMessage(/*session,*/ message);
-
- received[j].addHeader("X-fetched-folder", folder.getFullName());
-
- if (bAll) {
- ret = processMessage(session, message, received[j]);
- } else if (message.isSet(Flags.Flag.SEEN)) {
- ret = processMessage(session, message, received[j]);
- }
-
-
- if (ret) {
- //
- // need to get the flags again just in case processMessage
- // has changed the flags....
- Flags f = received[j].getFlags();
-
- if (!bKeep) {
-
- message.setFlag(Flags.Flag.DELETED, true);
- } else {
- f.add(Flags.Flag.SEEN);
-
- received[j].setFlags(f, true);
+ // Update the dynamic accounts,
+ // merge with the static accounts and
+ // sort the accounts so they are in the order
+ // they were entered in config.xml
+ updateDynamicAccounts();
+ ArrayList mergedAccounts =
+ new ArrayList(
+ getDynamicAccounts().size() + getStaticAccounts().size());
+ mergedAccounts.addAll(getDynamicAccounts().values());
+ mergedAccounts.addAll(getStaticAccounts());
+ Collections.sort(mergedAccounts);
+
+ StringBuffer logMessage = new StringBuffer(64);
+ logMessage.append("Processing ");
+ logMessage.append(getStaticAccounts().size());
+ logMessage.append(" static accounts and ");
+ logMessage.append(getDynamicAccounts().size());
+ logMessage.append(" dynamic accounts.");
+ getLogger().info(logMessage.toString());
+
+ // Fetch each account
+ Iterator accounts = mergedAccounts.iterator();
+ while (accounts.hasNext())
+ {
+ try
+ {
+ new StoreProcessor((Account) accounts.next()).process();
}
+ catch (MessagingException ex)
+ {
+ getLogger().error(
+ "A MessagingException has terminated processing of this Account",
+ ex);
}
}
- folder.close(true);
+ }
+ catch (Exception ex)
+ {
+ getLogger().error("An Exception has terminated this fetch.", ex);
+ }
+ finally
+ {
+ getLogger().info("Fetcher completed fetches");
- //
- // see if this folder contains subfolders and recurse is true
- if (bRecurse) {
- if ((folder.getType() & Folder.HOLDS_FOLDERS) != 0) {
- //
- // folder contains subfolders...
- Folder folders[] = folder.list();
+ // Exit Fetching State
+ setFetching(false);
+ }
+ }
- for (int k = 0; k < folders.length; k++) {
- processFolder(session, folders[k]);
- }
+ /**
+ * Returns the fetching.
+ * @return boolean
+ */
+ protected boolean isFetching()
+ {
+ return fieldFetching;
+ }
- }
- }
- return true;
- } catch (MessagingException mex) {
- getLogger().debug(mex.toString());
+ /**
+ * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
+ */
+ public void service(final ServiceManager manager) throws ServiceException
+ {
+ try
+ {
+ setServer((MailServer) manager.lookup(MailServer.ROLE));
+ }
+ catch (ClassCastException cce)
+ {
+ StringBuffer errorBuffer =
+ new StringBuffer(128).append("Component ").append(
+ MailServer.ROLE).append(
+ "does not implement the required interface.");
+ throw new ServiceException("", errorBuffer.toString());
+ }
- } /*catch (IOException ioex) {
- getLogger().debug(ioex.toString());
- } */
- fetching = false;
- return false;
+ UsersStore usersStore =
+ (UsersStore) manager.lookup("org.apache.james.services.UsersStore");
+ setLocalUsers(usersStore.getRepository("LocalUsers"));
+ if (getLocalUsers() == null)
+ throw new ServiceException(
+ "",
+ "The user repository could not be found.");
}
- public void targetTriggered(String arg0) {
- Store store = null;
- Session session = null;
- Folder folder = null;
- // Get a Properties object
- Properties props = System.getProperties();
+
- //
- // if we are already fetching then just return
- if (fetching) return;
- fetching = true;
+ /**
+ * Sets the fetching.
+ * @param fetching The fetching to set
+ */
+ protected void setFetching(boolean fetching)
+ {
+ fieldFetching = fetching;
+ }
+ /**
+ * Returns the server.
+ * @return MailServer
+ */
+ protected MailServer getServer()
+ {
+ return fieldServer;
+ }
- if (getLogger().isDebugEnabled()) {
- getLogger().debug(fetchTaskName + " fetcher starting fetch");
- }
+ /**
+ * Returns the configuration.
+ * @return ParsedConfiguration
+ */
+ protected ParsedConfiguration getConfiguration()
+ {
+ return fieldConfiguration;
+ }
+ /**
+ * Sets the configuration.
+ * @param configuration The configuration to set
+ */
+ protected void setConfiguration(ParsedConfiguration configuration)
+ {
+ fieldConfiguration = configuration;
+ }
- // Get a Session object
- session = Session.getDefaultInstance(props, null);
- // session.setDebug(debug);
+/**
+ * Sets the server.
+ * @param server The server to set
+ */
+protected void setServer(MailServer server)
+{
+ fieldServer = server;
+}
- // Get a Store object
- try {
- store = session.getStore(javaMailProviderName);
+/**
+ * Returns the localUsers.
+ * @return UsersRepository
+ */
+protected UsersRepository getLocalUsers()
+{
+ return fieldLocalUsers;
+}
- // Connect
- if (sHost != null || sUser != null || sPass != null)
- store.connect(sHost, sUser, sPass);
- else
- store.connect();
+/**
+ * Sets the localUsers.
+ * @param localUsers The localUsers to set
+ */
+protected void setLocalUsers(UsersRepository localUsers)
+{
+ fieldLocalUsers = localUsers;
+}
+
+ /**
+ * Returns the accounts. Initializes if required.
+ * @return List
+ */
+ protected List getStaticAccounts()
+ {
+ List accounts = null;
+ if (null == (accounts = getStaticAccountsBasic()))
+ {
+ updateStaticAccounts();
+ return getStaticAccounts();
+ }
+ return fieldStaticAccounts;
+ }
+
+ /**
+ * Returns the staticAccounts.
+ * @return List
+ */
+ private List getStaticAccountsBasic()
+ {
+ return fieldStaticAccounts;
+ }
- // Open the Folder
- folder = store.getFolder(javaMailFolderName);
- if (folder == null) {
- getLogger().debug(fetchTaskName + " No default folder");
+ /**
+ * Sets the accounts.
+ * @param accounts The accounts to set
+ */
+ protected void setStaticAccounts(List accounts)
+ {
+ fieldStaticAccounts = accounts;
+ }
+
+ /**
+ * Updates the staticAccounts.
+ */
+ protected void updateStaticAccounts()
+ {
+ setStaticAccounts(computeStaticAccounts());
+ }
+
+ /**
+ * Updates the ParsedDynamicAccountParameters.
+ */
+ protected void updateParsedDynamicAccountParameters()
+ {
+ setParsedDynamicAccountParameters(computeParsedDynamicAccountParameters());
+ }
+
+ /**
+ * Updates the dynamicAccounts.
+ */
+ protected void updateDynamicAccounts() throws ConfigurationException
+ {
+ setDynamicAccounts(computeDynamicAccounts());
+ }
+
+ /**
+ * Computes the staticAccounts.
+ */
+ protected List computeStaticAccounts()
+ {
+ return new ArrayList();
+ }
+
+ /**
+ * Computes the ParsedDynamicAccountParameters.
+ */
+ protected List computeParsedDynamicAccountParameters()
+ {
+ return new ArrayList();
+ }
+
+ /**
+ * Computes the dynamicAccounts.
+ */
+ protected Map computeDynamicAccounts() throws ConfigurationException
+ {
+ Map newAccounts =
+ new HashMap(
+ getLocalUsers().countUsers()
+ * getParsedDynamicAccountParameters().size());
+ Map oldAccounts = getDynamicAccountsBasic();
+ if (null == oldAccounts)
+ oldAccounts = new HashMap(0);
+
+ Iterator parameterIterator =
+ getParsedDynamicAccountParameters().iterator();
+
+ // Process each ParsedDynamicParameters
+ while (parameterIterator.hasNext())
+ {
+ Map accounts =
+ computeDynamicAccounts(
+ oldAccounts,
+ (ParsedDynamicAccountParameters) parameterIterator.next());
+ // Remove accounts from oldAccounts.
+ // This avoids an average 2*N increase in heapspace used as the
+ // newAccounts are created.
+ Iterator oldAccountsIterator = oldAccounts.keySet().iterator();
+ while (oldAccountsIterator.hasNext())
+ {
+ if (accounts.containsKey(oldAccountsIterator.next()))
+ oldAccountsIterator.remove();
}
-
-
- processFolder(session, folder);
-
- store.close();
- } catch (MessagingException ex) {
- getLogger().debug(fetchTaskName + ex.getMessage());
+ // Add this parameter's accounts to newAccounts
+ newAccounts.putAll(accounts);
}
- fetching = false;
+ return newAccounts;
}
+
+ /**
+ * Returns the dynamicAccounts. Initializes if required.
+ * @return Map
+ */
+ protected Map getDynamicAccounts() throws ConfigurationException
+ {
+ Map accounts = null;
+ if (null == (accounts = getDynamicAccountsBasic()))
+ {
+ updateDynamicAccounts();
+ return getDynamicAccounts();
+ }
+ return fieldDynamicAccounts;
+ }
+
+ /**
+ * Returns the dynamicAccounts.
+ * @return Map
+ */
+ private Map getDynamicAccountsBasic()
+ {
+ return fieldDynamicAccounts;
+ }
/**
- * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
+ * Sets the dynamicAccounts.
+ * @param dynamicAccounts The dynamicAccounts to set
*/
- public void service(final ServiceManager manager ) throws ServiceException {
- try {
- server = (MailServer) manager.lookup(MailServer.ROLE);
- } catch (ClassCastException cce) {
- StringBuffer errorBuffer =
- new StringBuffer(128).append("Component ").append(MailServer.ROLE).append(
- "does not implement the required interface.");
- throw new ServiceException("",errorBuffer.toString());
+ protected void setDynamicAccounts(Map dynamicAccounts)
+ {
+ fieldDynamicAccounts = dynamicAccounts;
+ }
+
+ /**
+ * Compute the dynamicAccounts for the passed parameters.
+ * Accounts for existing users are copied and accounts for new users are
+ * created.
+ * @param oldAccounts
+ * @param parameters
+ * @return Map - The current Accounts
+ * @throws ConfigurationException
+ */
+ protected Map computeDynamicAccounts(
+ Map oldAccounts,
+ ParsedDynamicAccountParameters parameters)
+ throws ConfigurationException
+ {
+ Map accounts = new HashMap(getLocalUsers().countUsers());
+ Iterator usersIterator = getLocalUsers().list();
+ while (usersIterator.hasNext())
+ {
+ String userName = (String) usersIterator.next();
+ DynamicAccountKey key =
+ new DynamicAccountKey(userName, parameters.getSequenceNumber());
+ Account account = (Account) oldAccounts.get(key);
+ if (null == account)
+ {
+ // Create a new DynamicAccount
+ account =
+ new DynamicAccount(
+ parameters.getSequenceNumber(),
+ getConfiguration(),
+ userName,
+ parameters.getUserPrefix(),
+ parameters.getUserSuffix(),
+ parameters.getPassword(),
+ parameters.getRecipientPrefix(),
+ parameters.getRecipientSuffix(),
+ parameters.isIgnoreRecipientHeader(),
+ getSession());
+ }
+ accounts.put(key, account);
}
+ return accounts;
}
+ /**
+ * Resets the dynamicAccounts.
+ */
+ protected void resetDynamicAccounts()
+ {
+ setDynamicAccounts(null);
+ }
+
+ /**
+ * Returns the ParsedDynamicAccountParameters.
+ * @return List
+ */
+ protected List getParsedDynamicAccountParameters()
+ {
+ List accounts = null;
+ if (null == (accounts = getParsedDynamicAccountParametersBasic()))
+ {
+ updateParsedDynamicAccountParameters();
+ return getParsedDynamicAccountParameters();
+ }
+ return fieldParsedDynamicAccountParameters;
+ }
+
+ /**
+ * Returns the ParsedDynamicAccountParameters.
+ * @return List
+ */
+ private List getParsedDynamicAccountParametersBasic()
+ {
+ return fieldParsedDynamicAccountParameters;
+ }
+ /**
+ * Sets the ParsedDynamicAccountParameters.
+ * @param ParsedDynamicAccountParameters The ParsedDynamicAccountParametersto set
+ */
+ protected void setParsedDynamicAccountParameters(List parsedDynamicAccountParameters)
+ {
+ fieldParsedDynamicAccountParameters = parsedDynamicAccountParameters;
+ }
/**
- * Try and parse the "for" parameter from a Received header
- * Maybe not the most accurate parsing in the world but it should do
- * I opted not to use ORO (maybe I should have)
- */
- private String getEnvelopeRecipient(MimeMessage msg) {
- try {
- Enumeration enum = msg.getMatchingHeaderLines(new String[]{"Received"});
- while (enum.hasMoreElements()) {
- String received = (String) enum.nextElement();
-
- int nextSearchAt = 0;
- int i = 0;
- int start = 0;
- int end = 0;
- boolean hasBracket = false;
- boolean usableAddress = false;
- while (!usableAddress && (i != -1)) {
- hasBracket = false;
- i = received.indexOf("for ", nextSearchAt);
- if (i > 0) {
- start = i + 4;
- end = 0;
- nextSearchAt = start;
- for (int c = start; c < received.length(); c++) {
- char ch = received.charAt(c);
- switch (ch) {
- case '<':
- hasBracket = true;
- continue;
- case '@':
- usableAddress = true;
- continue;
- case ' ':
- end = c;
- break;
- case ';':
- end = c;
- break;
- }
- if (end > 0)
- break;
- }
- }
- }
- if (usableAddress) {
- // lets try and grab the email address
- String mailFor = received.substring(start, end);
-
- // strip the <> around the address if there are any
- if (mailFor.startsWith("<") && mailFor.endsWith(">"))
- mailFor = mailFor.substring(1, (mailFor.length() - 1));
+ * Returns the session, lazily initialized if required.
+ * @return Session
+ */
+ protected Session getSession()
+ {
+ Session session = null;
+ if (null == (session = getSessionBasic()))
+ {
+ updateSession();
+ return getSession();
+ }
+ return session;
+ }
+
+ /**
+ * Returns the session.
+ * @return Session
+ */
+ private Session getSessionBasic()
+ {
+ return fieldSession;
+ }
- return mailFor;
- }
- }
- } catch (MessagingException me) {
- getLogger().info("No Recieved headers found");
- }
- return null;
+ /**
+ * Answers a new Session.
+ * @return Session
+ */
+ protected Session computeSession()
+ {
+ return Session.getInstance(System.getProperties());
}
+
+ /**
+ * Updates the current Session.
+ */
+ protected void updateSession()
+ {
+ setSession(computeSession());
+ }
/**
- * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
+ * Sets the session.
+ * @param session The session to set
*/
- public void configure(Configuration conf) throws ConfigurationException {
- this.sHost = conf.getChild("host").getValue();
- this.sUser = conf.getChild("user").getValue();
- this.sPass = conf.getChild("password").getValue();
- this.fetchTaskName = conf.getAttribute("name");
- this.javaMailProviderName = conf.getChild("javaMailProviderName").getValue();
- this.javaMailFolderName = conf.getChild("javaMailFolderName").getValue();
- try {
- this.recipient = new MailAddress(conf.getChild("recipient").getValue());
- } catch (ParseException pe) {
- throw new ConfigurationException("Invalid recipient address specified");
- }
- this.ignoreOriginalRecipient = conf.getChild("recipient").getAttributeAsBoolean("ignorercpt-header");
- this.bAll = conf.getChild("fetchall").getValueAsBoolean();
- this.bKeep = conf.getChild("leaveonserver").getValueAsBoolean();
- this.bRecurse = conf.getChild("recursesubfolders").getValueAsBoolean();
- if (getLogger().isDebugEnabled()) {
- getLogger().info("Configured FetchMail fetch task " + fetchTaskName);
- }
+ protected void setSession(Session session)
+ {
+ fieldSession = session;
}
+
+
+ /**
+ * Propogate any Session parameters in the configuration to the Session.
+ * @param configuration The configuration containing the parameters
+ * @throws ConfigurationException
+ */
+ protected void setSessionParameters(Configuration configuration)
+ throws ConfigurationException
+ {
+ Configuration javaMailProperties =
+ configuration.getChild("javaMailProperties", false);
+ if (null != javaMailProperties)
+ {
+ Properties properties = getSession().getProperties();
+ Configuration[] allProperties =
+ javaMailProperties.getChildren("property");
+ for (int i = 0; i < allProperties.length; i++)
+ {
+ properties.setProperty(
+ allProperties[i].getAttribute("name"),
+ allProperties[i].getAttribute("value"));
+ if (getLogger().isDebugEnabled())
+ {
+ StringBuffer messageBuffer =
+ new StringBuffer("Set property name: ");
+ messageBuffer.append(allProperties[i].getAttribute("name"));
+ messageBuffer.append(" to: ");
+ messageBuffer.append(
+ allProperties[i].getAttribute("value"));
+ getLogger().debug(messageBuffer.toString());
+ }
+ }
+ }
+ }
+
}
1.10 +40 -28 james-server/src/java/org/apache/james/fetchmail/FetchScheduler.java
Index: FetchScheduler.java
===================================================================
RCS file: /home/cvs/james-server/src/java/org/apache/james/fetchmail/FetchScheduler.java,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- FetchScheduler.java 30 Jan 2004 02:22:08 -0000 1.9
+++ FetchScheduler.java 11 Feb 2004 18:02:06 -0000 1.10
@@ -1,5 +1,5 @@
/***********************************************************************
- * Copyright (c) 2000-2004 The Apache Software Foundation. *
+ * Copyright (c) 2003-2004 The Apache Software Foundation. *
* All rights reserved. *
* ------------------------------------------------------------------- *
* Licensed under the Apache License, Version 2.0 (the "License"); you *
@@ -14,7 +14,7 @@
* implied. See the License for the specific language governing *
* permissions and limitations under the License. *
***********************************************************************/
-
+
package org.apache.james.fetchmail;
import java.util.ArrayList;
@@ -37,7 +37,7 @@
*
* $Id$
*
- * @see org.apache.james.fetchmail.FetchMail#configure(Configuration) FetchMail
+ * @see org.apache.james.fetchmail.FetchMailOriginal#configure(Configuration) FetchMailOriginal
*
*/
public class FetchScheduler
@@ -69,61 +69,73 @@
/**
* @see org.apache.avalon.framework.service.Serviceable#service( ServiceManager )
*/
- public void service(ServiceManager comp) throws ServiceException {
+ public void service(ServiceManager comp) throws ServiceException
+ {
m_manager = comp;
}
/**
* @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
*/
- public void configure(Configuration conf) throws ConfigurationException {
+ public void configure(Configuration conf) throws ConfigurationException
+ {
this.conf = conf;
}
/**
* @see org.apache.avalon.framework.activity.Initializable#initialize()
*/
- public void initialize() throws Exception {
+ public void initialize() throws Exception
+ {
enabled = conf.getAttributeAsBoolean("enabled", false);
- if (enabled) {
+ if (enabled)
+ {
scheduler = (TimeScheduler) m_manager.lookup(TimeScheduler.ROLE);
Configuration[] fetchConfs = conf.getChildren("fetch");
- for (int i = 0; i < fetchConfs.length; i++) {
+ for (int i = 0; i < fetchConfs.length; i++)
+ {
FetchMail fetcher = new FetchMail();
Configuration fetchConf = fetchConfs[i];
String fetchTaskName = fetchConf.getAttribute("name");
- fetcher.enableLogging(getLogger().getChildLogger(fetchTaskName));
- fetcher.service( m_manager );
+ fetcher.enableLogging(
+ getLogger().getChildLogger(fetchTaskName));
+ fetcher.service(m_manager);
fetcher.configure(fetchConf);
- Integer interval = new Integer(fetchConf.getChild("interval").getValue());
- PeriodicTimeTrigger fetchTrigger = new PeriodicTimeTrigger(0, interval.intValue());
+ Integer interval =
+ new Integer(fetchConf.getChild("interval").getValue());
+ PeriodicTimeTrigger fetchTrigger =
+ new PeriodicTimeTrigger(0, interval.intValue());
- scheduler.addTrigger(fetchTaskName, fetchTrigger, fetcher );
+ scheduler.addTrigger(fetchTaskName, fetchTrigger, fetcher);
theFetchTaskNames.add(fetchTaskName);
}
- if( getLogger().isInfoEnabled() )
- {
+
+ if (getLogger().isInfoEnabled())
getLogger().info("FetchMail Started");
- }
- } else {
- if( getLogger().isInfoEnabled() )
- {
+ System.out.println("FetchMail Started");
+ }
+ else
+ {
+ if (getLogger().isInfoEnabled())
getLogger().info("FetchMail Disabled");
- }
+ System.out.println("FetchMail Disabled");
}
}
/**
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
- public void dispose() {
- if (enabled) {
- getLogger().info( "FetchMail dispose..." );
+ public void dispose()
+ {
+ if (enabled)
+ {
+ getLogger().info("FetchMail dispose...");
Iterator nameIterator = theFetchTaskNames.iterator();
- while (nameIterator.hasNext()) {
- scheduler.removeTrigger((String)nameIterator.next());
+ while (nameIterator.hasNext())
+ {
+ scheduler.removeTrigger((String) nameIterator.next());
}
- getLogger().info( "FetchMail ...dispose end" );
+ getLogger().info("FetchMail ...dispose end");
}
- }
+ }
}
1.4 +3 -0 james-server/src/java/org/apache/james/fetchmail/FetchScheduler.xinfo
Index: FetchScheduler.xinfo
===================================================================
RCS file: /home/cvs/james-server/src/java/org/apache/james/fetchmail/FetchScheduler.xinfo,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- FetchScheduler.xinfo 8 Feb 2003 04:12:25 -0000 1.3
+++ FetchScheduler.xinfo 11 Feb 2004 18:02:06 -0000 1.4
@@ -14,6 +14,9 @@
<dependency>
<service name="org.apache.james.services.MailServer" version="1.0"/>
</dependency>
+ <dependency>
+ <service name="org.apache.james.services.UsersStore" version="1.0"/>
+ </dependency>
<dependency>
<service name="org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" version="1.0"/>
</dependency>
1.6 +2 -12 james-server/src/java/org/apache/james/fetchmail/ReaderInputStream.java
Index: ReaderInputStream.java
===================================================================
RCS file: /home/cvs/james-server/src/java/org/apache/james/fetchmail/ReaderInputStream.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- ReaderInputStream.java 30 Jan 2004 02:22:08 -0000 1.5
+++ ReaderInputStream.java 11 Feb 2004 18:02:06 -0000 1.6
@@ -1,5 +1,5 @@
/***********************************************************************
- * Copyright (c) 2000-2004 The Apache Software Foundation. *
+ * Copyright (c) 2003-2004 The Apache Software Foundation. *
* All rights reserved. *
* ------------------------------------------------------------------- *
* Licensed under the Apache License, Version 2.0 (the "License"); you *
@@ -15,16 +15,6 @@
* permissions and limitations under the License. *
***********************************************************************/
-/**
- * ReaderInputStream.java
- *
- * Copyright (C) 24-Sep-2002 The Apache Software Foundation. All rights reserved.
- *
- * This software is published under the terms of the Apache Software License
- * version 1.1, a copy of which has been included with this distribution in
- * the LICENSE file.
- *
- */
package org.apache.james.fetchmail;
import java.io.IOException;
import java.io.InputStream;
1.2 +117 -0 james-server/src/java/org/apache/james/fetchmail/StoreProcessor.java
1.2 +337 -0 james-server/src/java/org/apache/james/fetchmail/Account.java
1.2 +104 -0 james-server/src/java/org/apache/james/fetchmail/DynamicAccount.java
1.2 +1476 -0 james-server/src/java/org/apache/james/fetchmail/MessageProcessor.java
1.2 +541 -0 james-server/src/java/org/apache/james/fetchmail/ProcessorAbstract.java
1.2 +301 -0 james-server/src/java/org/apache/james/fetchmail/FolderProcessor.java
1.2 +1071 -0 james-server/src/java/org/apache/james/fetchmail/ParsedConfiguration.java
1.2 +105 -0 james-server/src/conf/samples/fetchmail/maxMessageSize.xml
1.2 +57 -0 james-server/src/conf/samples/fetchmail/oneAccountManyUsers.xml
1.2 +59 -0 james-server/src/conf/samples/fetchmail/oneAccountPerUserDynamic.xml
1.2 +67 -0 james-server/src/conf/samples/fetchmail/oneAccountPerUser.xml
1.2 +65 -0 james-server/src/conf/samples/fetchmail/oneAccountManyUsersDynamic.xml
1.56 +13 -49 james-server/src/conf/james-config.xml
Index: james-config.xml
===================================================================
RCS file: /home/cvs/james-server/src/conf/james-config.xml,v
retrieving revision 1.55
retrieving revision 1.56
diff -u -r1.55 -r1.56
--- james-config.xml 12 Sep 2003 13:42:40 -0000 1.55
+++ james-config.xml 11 Feb 2004 18:02:06 -0000 1.56
@@ -1,4 +1,7 @@
<?xml version="1.0"?>
+<!DOCTYPE config [
+<!ENTITY fetchmailConfig SYSTEM "file:../apps/james/conf/james-fetchmail.xml">
+]>
<!-- Configuration file for the Apache Jakarta James server -->
<!-- This file contains important settings that control the behaviour -->
@@ -101,54 +104,15 @@
</fetch>
</fetchpop>
- <!-- Fetch mail block, fetches mail from message servers and inserts it into the incoming spool -->
- <!-- Warning: It is important to prevent mail from looping by setting the -->
- <!-- fetched domains in the <servernames> section of the <James> block -->
- <!-- above. This block is disabled by default. -->
- <fetchmail enabled="false">
- <!-- You can have as many fetch tasks as you want, but each must have a -->
- <!-- unique name by which it identified -->
- <fetch name="mydomain.com">
- <!-- Host name or IP address -->
- <host>mail.mydomain.com</host>
- <!-- Account login username -->
- <user>username</user>
- <!-- Account login password -->
- <password>pass</password>
- <!-- How frequently this account is checked - in milliseconds. 600000 is every ten minutes -->
- <interval>600000</interval>
- <!-- Fetchmail will leave the fetched message unchanged and will add a new envelope -->
- <!-- recipient before spooling the message in James. -->
- <!-- Fetchmail will try and determine the original recipient of the message -->
- <!-- in the following ways: -->
- <!-- First it tries to parse the Received: headers and look for the "for" parameter -->
- <!-- If the "for" parameter is not found it will check the To: header and if it only -->
- <!-- contains one address it will be used as the new envelope recipient. -->
- <!-- If no recipient can be determined, the value defined in the <recipient> parameter -->
- <!-- will be used. Setting ignorercpt-header to true will force the defined recipient -->
- <!-- to ALWAYS be used and the parsing of headers will be ignored -->
- <recipient ignorercpt-header="false">user@mydomain.com</recipient>
-
- <!-- name of the javamail provider you wish to use -->
- <!-- (pop3, imap, etc. -->
- <javaMailProviderName>pop3</javaMailProviderName>
-
- <!-- name of the folder to open -->
- <javaMailFolderName>INBOX</javaMailFolderName>
-
- <!-- If "true" will leave the messages on the server -->
- <!-- and mark them as SEEN if "false" will delete the messages -->
- <leaveonserver>false</leaveonserver>
-
- <!-- Retrieve both old (seen) and new messages from mailserver. The default -->
- <!-- is to fetch only messages the server has not marked as seen -->
- <fetchall>false</fetchall>
-
- <!-- If the folder javaMailFolderName contains subfolders do you -->
- <!-- want to recurse into the subfolders as well? true = yes, false = no -->
- <recursesubfolders>false</recursesubfolders>
- </fetch>
- </fetchmail>
+ <!-- This is an example configuration for FetchMail, a JavaMail based gateway -->
+ <!-- service that pulls messages from other sources, and inserts them into the -->
+ <!-- spool. They are then processed normally, although FetchMail generally -->
+ <!-- has to fabricate some of the envelope information. FetchMail should be -->
+ <!-- considered a mail gateway, rather than a relay, in RFC terms. -->
+ <!-- Fetchmail is a functionally richer replacement for FetchPOP. -->
+ <!-- CHECKME: FetchMail is disabled by default, and must be configured to use. -->
+ <!-- Edit the file referred to by fetchmailConfig to enable and configure. -->
+ &fetchmailConfig;
<!-- The James Spool Manager block -->
<!-- -->
1.2 +232 -0 james-server/src/conf/james-fetchmail.xml
1.10 +2 -1 james-server/src/xdocs/documentation_2_1.xml
Index: documentation_2_1.xml
===================================================================
RCS file: /home/cvs/james-server/src/xdocs/documentation_2_1.xml,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- documentation_2_1.xml 4 Feb 2004 20:11:56 -0000 1.9
+++ documentation_2_1.xml 11 Feb 2004 18:02:06 -0000 1.10
@@ -53,7 +53,8 @@
<li><a href="pop3_configuration_2_1.html">POP3 Server Configuration</a></li>
<li><a href="smtp_configuration_2_1.html">SMTP Server Configuration</a></li>
<li><a href="nntp_configuration_2_1.html">NNTP Server Configuration</a></li>
-<li><a href="fetchpop_configuration_2_1.html">FetchPOP Configuration</a></li>
+<li><a href="fetchpop_configuration_2_1.html">FetchPOP Configuration <i>(deprecated)</i></a></li>
+<li><a href="fetchmail_configuration_2_2.html">fetchMail Configuration</a></li>
<li><a href="remotemanager_configuration_2_1.html">RemoteManager Configuration</a></li>
<li><a href="repositories_2_1.html">Repository Configuration</a></li>
<li><a href="spoolmanager_configuration_2_1.html">SpoolManager Configuration</a></li>
1.4 +6 -0 james-server/src/xdocs/fetchpop_configuration_2_1.xml
Index: fetchpop_configuration_2_1.xml
===================================================================
RCS file: /home/cvs/james-server/src/xdocs/fetchpop_configuration_2_1.xml,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- fetchpop_configuration_2_1.xml 7 Dec 2002 20:38:20 -0000 1.3
+++ fetchpop_configuration_2_1.xml 11 Feb 2004 18:02:06 -0000 1.4
@@ -7,6 +7,12 @@
</properties>
<body>
+<section name="FetchPOP Is Being Deprecated">
+<p>FetchPOP is being deprecated.
+<a href="fetchmail_configuration_2_2.html">fetchMail</a> provides a superset of
+FetchPOP's functionality and is the preferred solution.</p>
+</section>
+
<section name="FetchPOP Configuration">
<p>FetchPOP is controlled by a configuration block in the config.xml.
The fetchpop tag defines the boundaries of the configuration block. It encloses
1.2 +918 -0 james-server/src/xdocs/fetchmail_configuration_2_2.xml
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org