Pull up changes from old CONNECTORS-553 branch

git-svn-id: https://svn.apache.org/repos/asf/manifoldcf/branches/CONNECTORS-553-2@1552344 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.xml b/build.xml
index 646a533..3abeb6b 100644
--- a/build.xml
+++ b/build.xml
@@ -59,6 +59,7 @@
         <ant dir="connectors/alfresco" target="clean"/>
         <ant dir="connectors/cmis" target="clean"/>
         <ant dir="connectors/dropbox" target="clean"/>
+        <ant dir="connectors/email" target="clean"/>
         <ant dir="connectors/generic" target="clean"/>
         <ant dir="connectors/googledrive" target="clean"/>
         <ant dir="connectors/jira" target="clean"/>
@@ -116,6 +117,7 @@
         <ant dir="connectors/alfresco" target="clean"/>
         <ant dir="connectors/cmis" target="clean"/>
         <ant dir="connectors/dropbox" target="clean"/>
+        <ant dir="connectors/email" target="clean"/>
         <ant dir="connectors/generic" target="clean"/>
         <ant dir="connectors/googledrive" target="clean"/>
         <ant dir="connectors/jira" target="clean"/>
@@ -1127,6 +1129,41 @@
         <ant dir="connectors/wiki" target="run-tests-HSQLDB"/>
     </target>
 
+    <target name="setup-email-connector" depends="build-framework" if="downloaded"/>
+    
+    <target name="setup-email-connector-tests" depends="build-tests-framework" if="downloaded"/>
+
+    <target name="build-email-connector" depends="setup-email-connector" if="downloaded">
+        <ant dir="connectors/email" target="build"/>
+    </target>
+
+    <target name="doc-email-connector" depends="setup-email-connector" if="downloaded">
+        <ant dir="connectors/email" target="doc"/>
+    </target>
+
+    <target name="build-tests-email-connector" depends="setup-email-connector,setup-email-connector-tests" if="downloaded">
+        <ant dir="connectors/email" target="build-tests"/>
+    </target>
+
+    <target name="run-tests-email-connector" depends="setup-email-connector,setup-email-connector-tests" if="downloaded">
+        <ant dir="connectors/email" target="run-tests"/>
+    </target>
+
+    <target name="run-tests-derby-email-connector" depends="setup-email-connector,setup-email-connector-tests" if="downloaded">
+        <ant dir="connectors/email" target="run-tests-derby"/>
+    </target>
+
+    <target name="run-tests-postgresql-email-connector" depends="setup-email-connector,setup-email-connector-tests" if="downloaded">
+        <ant dir="connectors/email" target="run-tests-postgresql"/>
+    </target>
+
+    <target name="run-tests-mysql-email-connector" depends="setup-email-connector,setup-email-connector-tests" if="downloaded">
+        <ant dir="connectors/email" target="run-tests-mysql"/>
+    </target>
+
+    <target name="run-tests-HSQLDB-email-connector" depends="setup-email-connector,setup-email-connector-tests" if="downloaded">
+        <ant dir="connectors/email" target="run-tests-HSQLDB"/>
+    </target>
 
     <target name="deliver-site-doc" depends="presite-check" if="site-exists">
           <mkdir dir="dist/doc"/>
@@ -2518,6 +2555,43 @@
         </antcall>
     </target>
 
+    <target name="calculate-email-condition" depends="build-email-connector">
+        <available file="connectors/email/dist/lib" type="dir" property="email.exists"/>
+        <condition property="email.include">
+            <and>
+                <isset property="email.exists"/>
+                <isset property="downloaded"/>
+            </and>
+        </condition>
+    </target>
+
+    <target name="calculate-email-doc-condition" depends="doc-email-connector">
+        <available file="connectors/email/dist/doc" type="dir" property="email-doc.exists"/>
+        <condition property="email-doc.include">
+            <and>
+                <isset property="email-doc.exists"/>
+                <isset property="downloaded"/>
+            </and>
+        </condition>
+    </target>
+
+    <target name="deliver-email-connector" depends="calculate-email-condition" if="email.include">
+        <antcall target="general-connector-delivery">
+            <param name="connector-name" value="email"/>
+        </antcall>
+        <antcall target="general-add-repository-connector">
+            <param name="connector-name" value="email"/>
+            <param name="connector-label" value="EMail"/>
+            <param name="connector-class" value="org.apache.manifoldcf.crawler.connectors.email.EmailConnector"/>
+        </antcall>
+    </target>
+    
+    <target name="deliver-email-connector-doc" depends="calculate-email-doc-condition" if="email-doc.include">
+        <antcall target="general-connector-doc-delivery">
+            <param name="connector-name" value="email"/>
+        </antcall>
+    </target>
+
     <target name="calculate-filesystem-tests-condition" depends="calculate-filesystem-condition,calculate-nulloutput-condition">
       <condition property="filesystem-tests.include">
         <and>
@@ -2995,8 +3069,8 @@
 
     <target name="end-to-end-loadtests-HSQLDB" depends="run-filesystem-loadtests-HSQLDB,run-hdfs-loadtests-HSQLDB,run-rss-loadtests-HSQLDB,run-wiki-loadtests-HSQLDB,run-alfresco-loadtests-HSQLDB,run-cmis-loadtests-HSQLDB,run-sharepoint-loadtests-HSQLDB"/>
 
-    <target name="deliver-open-connectors" depends="deliver-generic-connector,deliver-jira-connector,deliver-googledrive-connector,deliver-dropbox-connector,deliver-nullauthority-connector,deliver-activedirectory-connector,deliver-ldap-connector,deliver-alfresco-connector,deliver-cmis-connector,deliver-filesystem-connector,deliver-hdfs-connector,deliver-rss-connector,deliver-webcrawler-connector,deliver-wiki-connector,deliver-jdbc-connector"/>
-    <target name="deliver-open-connectors-doc" depends="deliver-generic-connector-doc,deliver-jira-connector-doc,deliver-googledrive-connector-doc,deliver-dropbox-connector-doc,deliver-nullauthority-connector-doc,deliver-activedirectory-connector-doc,deliver-ldap-connector-doc,deliver-alfresco-connector-doc,deliver-cmis-connector-doc,deliver-filesystem-connector-doc,deliver-hdfs-connector-doc,deliver-rss-connector-doc,deliver-webcrawler-connector-doc,deliver-wiki-connector-doc,deliver-jdbc-connector-doc"/>
+    <target name="deliver-open-connectors" depends="deliver-email-connector,deliver-generic-connector,deliver-jira-connector,deliver-googledrive-connector,deliver-dropbox-connector,deliver-nullauthority-connector,deliver-activedirectory-connector,deliver-ldap-connector,deliver-alfresco-connector,deliver-cmis-connector,deliver-filesystem-connector,deliver-hdfs-connector,deliver-rss-connector,deliver-webcrawler-connector,deliver-wiki-connector,deliver-jdbc-connector"/>
+    <target name="deliver-open-connectors-doc" depends="deliver-email-connector-doc,deliver-generic-connector-doc,deliver-jira-connector-doc,deliver-googledrive-connector-doc,deliver-dropbox-connector-doc,deliver-nullauthority-connector-doc,deliver-activedirectory-connector-doc,deliver-ldap-connector-doc,deliver-alfresco-connector-doc,deliver-cmis-connector-doc,deliver-filesystem-connector-doc,deliver-hdfs-connector-doc,deliver-rss-connector-doc,deliver-webcrawler-connector-doc,deliver-wiki-connector-doc,deliver-jdbc-connector-doc"/>
 
     <target name="deliver-output-connectors" depends="deliver-gts-connector,deliver-solr-connector,deliver-nulloutput-connector,deliver-opensearchserver-connector,deliver-elasticsearch-connector"/>
     <target name="deliver-output-connectors-doc" depends="deliver-gts-connector-doc,deliver-solr-connector-doc,deliver-nulloutput-connector-doc,deliver-opensearchserver-connector-doc,deliver-elasticsearch-connector-doc"/>
@@ -4184,6 +4258,7 @@
         <ant dir="connectors/activedirectory" target="download-dependencies"/>
         <ant dir="connectors/ldap" target="download-dependencies"/>
         <ant dir="connectors/documentum" target="download-dependencies"/>
+        <ant dir="connectors/email" target="download-dependencies"/>
         <ant dir="connectors/filenet" target="download-dependencies"/>
         <ant dir="connectors/filesystem" target="download-dependencies"/>
         <ant dir="connectors/gts" target="download-dependencies"/>
diff --git a/connectors/alfresco/build.xml b/connectors/alfresco/build.xml
index 6b18ce8..82d45bd 100644
--- a/connectors/alfresco/build.xml
+++ b/connectors/alfresco/build.xml
@@ -50,7 +50,6 @@
             <include name="saaj*.jar"/>	
             <include name="commons-discovery*.jar"/>
             <include name="jaxrpc*.jar"/>
-            <include name="mail*.jar"/>
             <include name="opensaml*.jar"/>
             <include name="wsdl4j*.jar"/>
             <include name="wss4j*.jar"/>
@@ -73,7 +72,6 @@
                 <include name="saaj*.jar"/>	
                 <include name="commons-discovery*.jar"/>
                 <include name="jaxrpc*.jar"/>
-                <include name="mail*.jar"/>
                 <include name="opensaml*.jar"/>
                 <include name="wsdl4j*.jar"/>
                 <include name="wss4j*.jar"/>
diff --git a/connectors/email/build.xml b/connectors/email/build.xml
new file mode 100644
index 0000000..7e7e045
--- /dev/null
+++ b/connectors/email/build.xml
@@ -0,0 +1,22 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<project name="email" default="all">
+
+    <import file="../connector-build.xml"/>
+
+</project>
\ No newline at end of file
diff --git a/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/email/EmailConfig.java b/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/email/EmailConfig.java
new file mode 100644
index 0000000..a8da2dc
--- /dev/null
+++ b/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/email/EmailConfig.java
@@ -0,0 +1,99 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.manifoldcf.crawler.connectors.email;
+
+
+/**
+* Parameters data for the Email repository connector.
+*/
+public class EmailConfig {
+
+  /**
+  * Username
+  */
+  public static final String USERNAME_PARAM = "username";
+
+  /**
+  * Password
+  */
+  public static final String PASSWORD_PARAM = "password";
+
+  /**
+  * Protocol
+  */
+  public static final String PROTOCOL_PARAM = "protocol";
+
+  /**
+  * Server name
+  */
+  public static final String SERVER_PARAM = "server";
+
+  /**
+  * Port
+  */
+  public static final String PORT_PARAM = "port";
+
+  /**
+  * Properties
+  */
+  public static final String PROPERTIES_PARAM = "properties";
+
+  public static final String PROTOCOL_IMAP = "IMAP";
+  public static final String PROTOCOL_IMAP_PROVIDER = "imap";
+  public static final String PROTOCOL_IMAPS = "IMAP-SSL";
+  public static final String PROTOCOL_IMAPS_PROVIDER = "imaps";
+  public static final String PROTOCOL_POP3 = "POP3";
+  public static final String PROTOCOL_POP3_PROVIDER = "pop3";
+  public static final String PROTOCOL_POP3S = "POP3-SSL";
+  public static final String PROTOCOL_POP3S_PROVIDER = "pop3s";
+  
+  public static final String PROTOCOL_DEFAULT_VALUE = "IMAP";
+  public static final String PORT_DEFAULT_VALUE = "25";
+
+
+  public static final String NODE_PROPERTIES = "properties";
+  public static final String NODE_METADATA = "metadata";
+  public static final String NODE_FILTER = "filter";
+  public static final String SERVER_PROPERTY = "serverproperty";
+  public static final String VALUE = "p_value";
+  public static final String ATTRIBUTE_NAME = "name";
+
+  public static final String ATTRIBUTE_VALUE = "value";
+  public static final String[] BASIC_METADATA = {"To","From","Subject","Body","Date","Encoding of Attachment","MIME type of attachment"};
+  public static final String[] BASIC_SEARCHABLE_ATTRIBUTES = {"To","From","Subject","Body","Date","Folder"};
+  protected final static String ACTIVITY_FETCH = "fetch";
+
+
+  public static final String RELATIONSHIP_CHILD = "child";
+  public static final String FOLDER_INBOX = "INBOX";
+  public static final String EMAIL_SUBJECT = "subject";
+  public static final String EMAIL_FROM = "from";
+  public static final String EMAIL_TO = "to";
+  public static final String EMAIL_BODY = "body";
+  public static final String ATTRIBUTE_FOLDER = "Folder";
+  public static final String EMAIL_DATE = "date";
+  public static final String EMAIL_ATTACHMENT_ENCODING = "encoding of attachment";
+  public static final String EMAIL_ATTACHMENT_MIMETYPE = "mime type of attachment";
+  public static final String EMAIL_VERSION = "1.0";
+  public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
+  public static final String MIMETYPE_HTML = "text/html";
+  public static final String ENCODING_FIELD = "encoding";
+  public static final String MIMETYPE_FIELD = "mimetype";
+  protected final static long SESSION_EXPIRATION_MILLISECONDS = 300000L;
+  //public static final String TO = "To";
+}
\ No newline at end of file
diff --git a/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/email/EmailConnector.java b/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/email/EmailConnector.java
new file mode 100644
index 0000000..2c3d0e5
--- /dev/null
+++ b/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/email/EmailConnector.java
@@ -0,0 +1,1016 @@
+/* $Id$ */
+
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.manifoldcf.crawler.connectors.email;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.manifoldcf.agents.interfaces.RepositoryDocument;
+import org.apache.manifoldcf.agents.interfaces.ServiceInterruption;
+import org.apache.manifoldcf.core.interfaces.*;
+import org.apache.manifoldcf.crawler.interfaces.*;
+import org.apache.manifoldcf.crawler.system.Logging;
+
+import java.io.*;
+import java.util.*;
+import javax.mail.*;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.search.*;
+
+/**
+* This interface describes an instance of a connection between a repository and ManifoldCF's
+* standard "pull" ingestion agent.
+* <p/>
+* Each instance of this interface is used in only one thread at a time. Connection Pooling
+* on these kinds of objects is performed by the factory which instantiates repository connectors
+* from symbolic names and config parameters, and is pooled by these parameters. That is, a pooled connector
+* handle is used only if all the connection parameters for the handle match.
+* <p/>
+* Implementers of this interface should provide a default constructor which has this signature:
+* <p/>
+* xxx();
+* <p/>
+* Connectors are either configured or not. If configured, they will persist in a pool, and be
+* reused multiple times. Certain methods of a connector may be called before the connector is
+* configured. This includes basically all methods that permit inspection of the connector's
+* capabilities. The complete list is:
+* <p/>
+* <p/>
+* The purpose of the repository connector is to allow documents to be fetched from the repository.
+* <p/>
+* Each repository connector describes a set of documents that are known only to that connector.
+* It therefore establishes a space of document identifiers. Each connector will only ever be
+* asked to deal with identifiers that have in some way originated from the connector.
+* <p/>
+* Documents are fetched in three stages. First, the getDocuments() method is called in the connector
+* implementation. This returns a set of document identifiers. The document identifiers are used to
+* obtain the current document version strings in the second stage, using the getDocumentVersions() method.
+* The last stage is processDocuments(), which queues up any additional documents needed, and also ingests.
+* This method will not be called if the document version seems to indicate that no document change took
+* place.
+*/
+
+public class EmailConnector extends org.apache.manifoldcf.crawler.connectors.BaseRepositoryConnector {
+
+  // Local variables.
+  protected long sessionExpiration = -1L;
+  protected String server = null;
+  protected String port = null;
+  protected String username = null;
+  protected String password = null;
+  protected String protocol = null;
+  protected Map<String, String> properties = new HashMap<String,String>();
+  private String folderName = null;
+  private Folder folder;
+  private Store store;
+
+  private static Map<String,String> providerMap;
+  static
+  {
+    providerMap = new HashMap<String,String>();
+    providerMap.put(EmailConfig.PROTOCOL_POP3, EmailConfig.PROTOCOL_POP3_PROVIDER);
+    providerMap.put(EmailConfig.PROTOCOL_POP3S, EmailConfig.PROTOCOL_POP3S_PROVIDER);
+    providerMap.put(EmailConfig.PROTOCOL_IMAP, EmailConfig.PROTOCOL_IMAP_PROVIDER);
+    providerMap.put(EmailConfig.PROTOCOL_IMAPS, EmailConfig.PROTOCOL_IMAPS_PROVIDER);
+  }
+  //////////////////////////////////Start of Basic Connector Methods/////////////////////////
+
+  /**
+  * Connect.
+  *
+  * @param configParameters is the set of configuration parameters, which
+  * in this case describe the root directory.
+  */
+  @Override
+  public void connect(ConfigParams configParameters) {
+    super.connect(configParameters);
+    this.server = configParameters.getParameter(EmailConfig.SERVER_PARAM);
+    this.port = configParameters.getParameter(EmailConfig.PORT_PARAM);
+    this.protocol = configParameters.getParameter(EmailConfig.PROTOCOL_PARAM);
+    this.username = configParameters.getParameter(EmailConfig.USERNAME_PARAM);
+    this.password = configParameters.getParameter(EmailConfig.PASSWORD_PARAM);
+    int i = 0;
+    while (i < configParameters.getChildCount()) //In post property set is added as a configuration node
+    {
+      ConfigNode cn = configParameters.getChild(i++);
+      if (cn.getType().equals(EmailConfig.NODE_PROPERTIES)) {
+        String findParameterName = cn.getAttributeValue(EmailConfig.ATTRIBUTE_NAME);
+        String findParameterValue = cn.getAttributeValue(EmailConfig.ATTRIBUTE_VALUE);
+        this.properties.put(findParameterName, findParameterValue);
+      }
+    }
+  }
+
+  /**
+  * Close the connection. Call this before discarding this instance of the
+  * repository connector.
+  */
+  @Override
+  public void disconnect()
+    throws ManifoldCFException {
+    this.server = null;
+    this.port = null;
+    this.protocol = null;
+    this.username = null;
+    this.password = null;
+    this.properties = null;
+    finalizeConnection();
+    super.disconnect();
+  }
+
+  /**
+  * This method is periodically called for all connectors that are connected but not
+  * in active use.
+  */
+  @Override
+  public void poll() throws ManifoldCFException {
+    if (store != null)
+    {
+      if (System.currentTimeMillis() >= sessionExpiration)
+        finalizeConnection();
+    }
+  }
+
+  /**
+  * Test the connection. Returns a string describing the connection integrity.
+  *
+  * @return the connection's status as a displayable string.
+  */
+  public String check()
+      throws ManifoldCFException {
+    try {
+      checkConnection();
+      return super.check();
+    } catch (ServiceInterruption e) {
+      return "Connection temporarily failed: " + e.getMessage();
+    } catch (ManifoldCFException e) {
+      return "Connection failed: " + e.getMessage();
+    }
+  }
+
+  protected void checkConnection() throws ManifoldCFException, ServiceInterruption {
+    while (true) {
+      try {
+        store = getSession().getStore(providerMap.get(protocol));
+        store.connect(server, username, password);
+        Folder defaultFolder = store.getDefaultFolder();
+        if (defaultFolder == null) {
+          Logging.connectors.warn("Email: Error checking the connection: No default folder.");
+          throw new ManifoldCFException("Error checking the connection: No default folder.");
+        }
+
+      } catch (Exception e) {
+        Logging.connectors.warn(
+            "Email: Error checking the connection: "+e.getMessage(),e);
+        throw new ManifoldCFException("Error checking the connection: "+e.getMessage(),e);
+      }
+      store = null;
+      return;
+    }
+  }
+
+  ///////////////////////////////End of Basic Connector Methods////////////////////////////////////////
+
+  //////////////////////////////Start of Repository Connector Method///////////////////////////////////
+
+
+  public int getConnectorModel() {
+    return MODEL_ADD; //Change is not applicable in context of email
+  }
+
+  /**
+  * Return the list of activities that this connector supports (i.e. writes into the log).
+  *
+  * @return the list.
+  */
+  public String[] getActivitiesList() {
+    return new String[]{EmailConfig.ACTIVITY_FETCH};
+  }
+
+  /**
+  * Return the list of relationship types that this connector recognizes.
+  *
+  * @return the list.
+  */
+  public String[] getRelationshipTypes() {
+    String[] relationships = new String[1];
+    relationships[0] = EmailConfig.RELATIONSHIP_CHILD;
+    return relationships;
+  }
+
+  /**
+  * Get the bin name strings for a document identifier. The bin name describes the queue to which the
+  * document will be assigned for throttling purposes. Throttling controls the rate at which items in a
+  * given queue are fetched; it does not say anything about the overall fetch rate, which may operate on
+  * multiple queues or bins.
+  * For example, if you implement a web crawler, a good choice of bin name would be the server name, since
+  * that is likely to correspond to a real resource that will need real throttle protection.
+  *
+  * @param documentIdentifier is the document identifier.
+  * @return the set of bin names. If an empty array is returned, it is equivalent to there being no request
+  * rate throttling available for this identifier.
+  */
+  public String[] getBinNames(String documentIdentifier) {
+    return new String[]{server};
+  }
+
+  /**
+  * Get the maximum number of documents to amalgamate together into one batch, for this connector.
+  *
+  * @return the maximum number. 0 indicates "unlimited".
+  */
+  public int getMaxDocumentRequest() {
+    return 50;
+  }
+
+  /**
+  * Queue "seed" documents. Seed documents are the starting places for crawling activity. Documents
+  * are seeded when this method calls appropriate methods in the passed in ISeedingActivity object.
+  * <p/>
+  * This method can choose to find repository changes that happen only during the specified time interval.
+  * The seeds recorded by this method will be viewed by the framework based on what the
+  * getConnectorModel() method returns.
+  * <p/>
+  * It is not a big problem if the connector chooses to create more seeds than are
+  * strictly necessary; it is merely a question of overall work required.
+  * <p/>
+  * The times passed to this method may be interpreted for greatest efficiency. The time ranges
+  * any given job uses with this connector will not overlap, but will proceed starting at 0 and going
+  * to the "current time", each time the job is run. For continuous crawling jobs, this method will
+  * be called once, when the job starts, and at various periodic intervals as the job executes.
+  * <p/>
+  * When a job's specification is changed, the framework automatically resets the seeding start time to 0. The
+  * seeding start time may also be set to 0 on each job run, depending on the connector model returned by
+  * getConnectorModel().
+  * <p/>
+  * Note that it is always ok to send MORE documents rather than less to this method.
+  *
+  * @param activities is the interface this method should use to perform whatever framework actions are desired.
+  * @param spec is a document specification (that comes from the job).
+  * @param startTime is the beginning of the time range to consider, inclusive.
+  * @param endTime is the end of the time range to consider, exclusive.
+  * @param jobMode is an integer describing how the job is being run, whether continuous or once-only.
+  */
+  @Override
+  public void addSeedDocuments(ISeedingActivity activities,
+    DocumentSpecification spec, long startTime, long endTime, int jobMode)
+    throws ManifoldCFException, ServiceInterruption {
+    Session session = getSession();
+    int i = 0, j = 0;
+    Map findMap;
+    while (i < spec.getChildCount()) {
+      SpecificationNode sn = spec.getChild(i++);
+      if (sn.getType().equals(EmailConfig.NODE_FILTER) && sn.getAttributeValue(EmailConfig.ATTRIBUTE_NAME).equals(EmailConfig.ATTRIBUTE_FOLDER)) {
+        folderName = sn.getAttributeValue(EmailConfig.ATTRIBUTE_VALUE);
+      }
+    }
+    while (j < spec.getChildCount()) {
+      SpecificationNode sn = spec.getChild(j++);
+      if (sn.getType().equals(EmailConfig.NODE_FILTER)) {
+        String findParameterName, findParameterValue;
+        findParameterName = sn.getAttributeValue(EmailConfig.ATTRIBUTE_NAME);
+        findParameterValue = sn.getAttributeValue(EmailConfig.ATTRIBUTE_VALUE);
+        findMap = new HashMap();
+        findMap.put(findParameterName, findParameterValue);
+        try {
+          Message[] messages = findMessages(startTime, endTime, findMap);
+          for (Message message : messages) {
+            String emailID = ((MimeMessage) message).getMessageID();
+            activities.addSeedDocument(emailID);
+          }
+        } catch (MessagingException e) {
+          Logging.connectors.warn("Email: Error finding emails: " + e.getMessage(), e);
+          throw new ManifoldCFException(e.getMessage(), e);
+        } catch (Exception e) {
+          Logging.connectors.warn("Email: Error finding emails: " + e.getMessage(), e);
+          throw new ManifoldCFException(e.getMessage(), e);
+        }finally {
+          finalizeConnection();
+        }
+
+      }
+
+    }
+  }
+
+  /*
+  This method will return the list of messages which matches the given criteria
+  */
+  private Message[] findMessages(long startTime, long endTime, Map findMap) throws MessagingException {
+    Message[] result;
+    String findParameterName;
+    String findParameterValue;
+    initializeConnection();
+    if (findMap.size() > 0) {
+      result = null;
+      Iterator it = findMap.entrySet().iterator();
+      while (it.hasNext()) {
+        Map.Entry pair = (Map.Entry) it.next();
+        findParameterName = ((String) pair.getKey()).toLowerCase();
+        findParameterValue = (String) pair.getValue();
+        it.remove();
+        if (Logging.connectors.isDebugEnabled())
+          Logging.connectors.debug("Email: Finding emails where " + findParameterName +
+              "= '" + findParameterValue + "'");
+        if (findParameterName.equals(EmailConfig.EMAIL_SUBJECT)) {
+          SubjectTerm subjectTerm = new SubjectTerm(findParameterValue);
+          result = folder.search(subjectTerm);
+        } else if (findParameterName.equals(EmailConfig.EMAIL_FROM)) {
+          FromStringTerm fromTerm = new FromStringTerm(findParameterValue);
+          result = folder.search(fromTerm);
+        } else if (findParameterName.equals(EmailConfig.EMAIL_TO)) {
+          RecipientStringTerm recipientTerm = new RecipientStringTerm(Message.RecipientType.TO, findParameterValue);
+          result = folder.search(recipientTerm);
+        } else if (findParameterName.equals(EmailConfig.EMAIL_BODY)) {
+          BodyTerm bodyTerm = new BodyTerm(findParameterValue);
+          result = folder.search(bodyTerm);
+        }
+      }
+    } else {
+      result = folder.getMessages();
+    }
+
+    return result;
+  }
+
+  private Session getSession() {
+    // Create empty properties
+    Properties props = new Properties();
+    // Get session
+    Session session = Session.getDefaultInstance(props, null);
+    sessionExpiration = System.currentTimeMillis() + EmailConfig.SESSION_EXPIRATION_MILLISECONDS;
+    return session;
+  }
+
+  private void initializeConnection() throws MessagingException {
+    store = getSession().getStore(providerMap.get(protocol));
+    store.connect(server, username, password);
+
+    if (protocol.equals(EmailConfig.PROTOCOL_IMAP)) {
+      folder = store.getFolder(folderName);
+    } else {
+      folder = store.getFolder(EmailConfig.FOLDER_INBOX);
+    }
+    folder.open(Folder.READ_ONLY);
+  }
+
+  private void finalizeConnection() {
+    try {
+      if (folder != null)
+        folder.close(false);
+      if (store != null)
+        store.close();
+    } catch (MessagingException e) {
+      if (Logging.connectors.isDebugEnabled())
+        Logging.connectors.debug("Error while closing connection to server" + e.getMessage());
+    } finally {
+      folder = null;
+      store = null;
+    }
+  }
+
+  /**
+  * Get document versions given an array of document identifiers.
+  * This method is called for EVERY document that is considered. It is therefore important to perform
+  * as little work as possible here.
+  * The connector will be connected before this method can be called.
+  *
+  * @param documentIdentifiers is the array of local document identifiers, as understood by this connector.
+  * @param oldVersions is the corresponding array of version strings that have been saved for the document identifiers.
+  * A null value indicates that this is a first-time fetch, while an empty string indicates that the previous document
+  * had an empty version string.
+  * @param activities is the interface this method should use to perform whatever framework actions are desired.
+  * @param spec is the current document specification for the current job. If there is a dependency on this
+  * specification, then the version string should include the pertinent data, so that reingestion will occur
+  * when the specification changes. This is primarily useful for metadata.
+  * @param jobMode is an integer describing how the job is being run, whether continuous or once-only.
+  * @param usesDefaultAuthority will be true only if the authority in use for these documents is the default one.
+  * @return the corresponding version strings, with null in the places where the document no longer exists.
+  * Empty version strings indicate that there is no versioning ability for the corresponding document, and the document
+  * will always be processed.
+  */
+  @Override
+  public String[] getDocumentVersions(String[] documentIdentifiers, String[] oldVersions, IVersionActivity activities,
+    DocumentSpecification spec, int jobMode, boolean usesDefaultAuthority)
+    throws ManifoldCFException, ServiceInterruption {
+
+    String[] result = null;
+    if (documentIdentifiers.length > 0) {
+      result = new String[documentIdentifiers.length];
+      //Since visioning is not applicable in the current context.
+      if (result != null) {
+        for (int i=0;i<documentIdentifiers.length;i++) {
+          result[i]=EmailConfig.EMAIL_VERSION;
+        }
+      }
+      return result;
+
+    } else {
+      return new String[]{EmailConfig.EMAIL_VERSION};
+    }
+
+  }
+
+  /**
+  * Process a set of documents.
+  * This is the method that should cause each document to be fetched, processed, and the results either added
+  * to the queue of documents for the current job, and/or entered into the incremental ingestion manager.
+  * The document specification allows this class to filter what is done based on the job.
+  * The connector will be connected before this method can be called.
+  *
+  * @param documentIdentifiers is the set of document identifiers to process.
+  * @param versions is the corresponding document versions to process, as returned by getDocumentVersions() above.
+  * The implementation may choose to ignore this parameter and always process the current version.
+  * @param activities is the interface this method should use to queue up new document references
+  * and ingest documents.
+  * @param spec is the document specification.
+  * @param scanOnly is an array corresponding to the document identifiers. It is set to true to indicate when the processing
+  * should only find other references, and should not actually call the ingestion methods.
+  * @param jobMode is an integer describing how the job is being run, whether continuous or once-only.
+  */
+  @Override
+  public void processDocuments(String[] documentIdentifiers, String[] versions, IProcessActivity activities,
+    DocumentSpecification spec, boolean[] scanOnly, int jobMode)
+    throws ManifoldCFException, ServiceInterruption {
+    int i = 0, count=0;
+    List<String> requiredMetadata = new ArrayList<String>();
+    try {
+      initializeConnection();
+
+      while (i < spec.getChildCount()) {
+        SpecificationNode sn = spec.getChild(i++);
+        if (sn.getType().equals(EmailConfig.NODE_METADATA)) {
+          String metadataAttribute = sn.getAttributeValue(EmailConfig.ATTRIBUTE_NAME);
+          requiredMetadata.add(metadataAttribute);
+        }
+      }
+      for (String id : documentIdentifiers) {
+        long startTime = System.currentTimeMillis();
+        String msgId = documentIdentifiers[count];
+        InputStream is = null;
+        if (Logging.connectors.isDebugEnabled())
+          Logging.connectors.debug("Email: Processing document identifier '"
+              + msgId + "'");
+        MessageIDTerm messageIDTerm = new MessageIDTerm(id);
+        Message[] message = null;
+
+        message = folder.search(messageIDTerm);
+        for (Message msg : message) {
+          RepositoryDocument rd = new RepositoryDocument();
+          Date setDate = msg.getSentDate();
+          rd.setFileName(msg.getFileName());
+          is = msg.getInputStream();
+          rd.setBinary(is, msg.getSize());
+          String subject = StringUtils.EMPTY;
+          for (String metadata : requiredMetadata) {
+            if (metadata.toLowerCase().equals(EmailConfig.EMAIL_TO)) {
+              Address[] to = msg.getRecipients(Message.RecipientType.TO);
+              String[] toStr = new String[to.length];
+              int j = 0;
+              for (Address address : to) {
+                toStr[j] = address.toString();
+              }
+              rd.addField(EmailConfig.EMAIL_TO, toStr);
+            } else if (metadata.toLowerCase().equals(EmailConfig.EMAIL_FROM)) {
+              Address[] from = msg.getFrom();
+              String[] fromStr = new String[from.length];
+              int j = 0;
+              for (Address address : from) {
+                fromStr[j] = address.toString();
+              }
+              rd.addField(EmailConfig.EMAIL_TO, fromStr);
+
+            } else if (metadata.toLowerCase().equals(EmailConfig.EMAIL_SUBJECT)) {
+              subject = msg.getSubject();
+              rd.addField(EmailConfig.EMAIL_SUBJECT, subject);
+            } else if (metadata.toLowerCase().equals(EmailConfig.EMAIL_BODY)) {
+              Multipart mp = (Multipart) msg.getContent();
+              for (int j = 0, n = mp.getCount(); i < n; i++) {
+                Part part = mp.getBodyPart(i);
+                String disposition = part.getDisposition();
+                if ((disposition == null)) {
+                  MimeBodyPart mbp = (MimeBodyPart) part;
+                  if (mbp.isMimeType(EmailConfig.MIMETYPE_TEXT_PLAIN)) {
+                    rd.addField(EmailConfig.EMAIL_BODY, mbp.getContent().toString());
+                  } else if (mbp.isMimeType(EmailConfig.MIMETYPE_HTML)) {
+                    rd.addField(EmailConfig.EMAIL_BODY, mbp.getContent().toString()); //handle html accordingly. Returns content with html tags
+                  }
+                }
+              }
+            } else if (metadata.toLowerCase().equals(EmailConfig.EMAIL_DATE)) {
+              Date sentDate = msg.getSentDate();
+              rd.addField(EmailConfig.EMAIL_DATE, sentDate.toString());
+            } else if (metadata.toLowerCase().equals(EmailConfig.EMAIL_ATTACHMENT_ENCODING)) {
+              Multipart mp = (Multipart) msg.getContent();
+              if (mp != null) {
+                String[] encoding = new String[mp.getCount()];
+                for (int k = 0, n = mp.getCount(); i < n; i++) {
+                  Part part = mp.getBodyPart(i);
+                  String disposition = part.getDisposition();
+                  if ((disposition != null) &&
+                      ((disposition.equals(Part.ATTACHMENT) ||
+                          (disposition.equals(Part.INLINE))))) {
+                    encoding[k] = part.getFileName().split("\\?")[1];
+
+                  }
+                }
+                rd.addField(EmailConfig.ENCODING_FIELD, encoding);
+              }
+            } else if (metadata.toLowerCase().equals(EmailConfig.EMAIL_ATTACHMENT_MIMETYPE)) {
+              Multipart mp = (Multipart) msg.getContent();
+              String[] MIMEType = new String[mp.getCount()];
+              for (int k = 0, n = mp.getCount(); i < n; i++) {
+                Part part = mp.getBodyPart(i);
+                String disposition = part.getDisposition();
+                if ((disposition != null) &&
+                    ((disposition.equals(Part.ATTACHMENT) ||
+                        (disposition.equals(Part.INLINE))))) {
+                  MIMEType[k] = part.getContentType();
+
+                }
+              }
+              rd.addField(EmailConfig.MIMETYPE_FIELD, MIMEType);
+            }
+          }
+          String documentURI = subject + messageIDTerm;
+          String version = versions[count++];
+          activities.ingestDocument(id, version, documentURI, rd);
+
+        }
+      }
+
+    } catch (MessagingException e) {
+
+    } catch (IOException e) {
+      throw new ManifoldCFException(e.getMessage(), e,
+          ManifoldCFException.INTERRUPTED);
+    } finally {
+      finalizeConnection();
+    }
+
+  }
+
+  //////////////////////////////End of Repository Connector Methods///////////////////////////////////
+
+
+  ///////////////////////////////////////Start of Configuration UI/////////////////////////////////////
+
+  /**
+  * Output the configuration header section.
+  * This method is called in the head section of the connector's configuration page. Its purpose is to
+  * add the required tabs to the list, and to output any javascript methods that might be needed by
+  * the configuration editing HTML.
+  * The connector does not need to be connected for this method to be called.
+  *
+  * @param threadContext is the local thread context.
+  * @param out is the output to which any HTML should be sent.
+  * @param locale is the desired locale.
+  * @param parameters are the configuration parameters, as they currently exist, for this connection being configured.
+  * @param tabsArray is an array of tab names. Add to this array any tab names that are specific to the connector.
+  */
+  @Override
+  public void outputConfigurationHeader(IThreadContext threadContext, IHTTPOutput out,
+    Locale locale, ConfigParams parameters, List<String> tabsArray)
+    throws ManifoldCFException, IOException {
+    tabsArray.add(Messages.getString(locale, "EmailConnector.Server"));
+    // Map the parameters
+    Map<String, Object> paramMap = new HashMap<String, Object>();
+
+    // Fill in the parameters from each tab
+    fillInServerConfigurationMap(paramMap, parameters);
+
+    // Output the Javascript - only one Velocity template for all tabs
+    Messages.outputResourceWithVelocity(out, locale, "ConfigurationHeader.js", paramMap);
+  }
+
+  @Override
+  public void outputConfigurationBody(IThreadContext threadContext, IHTTPOutput out,
+    Locale locale, ConfigParams parameters, String tabName)
+    throws ManifoldCFException, IOException {
+    // Output the Server tab
+    Map<String, Object> paramMap = new HashMap<String, Object>();
+    // Set the tab name
+    paramMap.put("TabName", tabName);
+    // Fill in the parameters
+    fillInServerConfigurationMap(paramMap, parameters);
+    Messages.outputResourceWithVelocity(out, locale, "Configuration_Server.html", paramMap);
+  }
+
+  private void fillInServerConfigurationMap(Map<String, Object> paramMap, ConfigParams parameters) {
+    int i = 0;
+    String username = parameters.getParameter(EmailConfig.USERNAME_PARAM);
+    String password = parameters.getParameter(EmailConfig.PASSWORD_PARAM);
+    String protocol = parameters.getParameter(EmailConfig.PROTOCOL_PARAM);
+    String server = parameters.getParameter(EmailConfig.SERVER_PARAM);
+    String port = parameters.getParameter(EmailConfig.PORT_PARAM);
+    List<Map<String, String>> list = new ArrayList<Map<String, String>>();
+    while (i < parameters.getChildCount()) //In post property set is added as a configuration node
+    {
+      ConfigNode cn = parameters.getChild(i++);
+      if (cn.getType().equals(EmailConfig.NODE_PROPERTIES)) {
+        String findParameterName = cn.getAttributeValue(EmailConfig.ATTRIBUTE_NAME);
+        String findParameterValue = cn.getAttributeValue(EmailConfig.ATTRIBUTE_VALUE);
+        Map<String, String> row = new HashMap<String, String>();
+        row.put("name", findParameterName);
+        row.put("value", findParameterValue);
+        list.add(row);
+      }
+    }
+
+    if (username == null)
+      username = StringUtils.EMPTY;
+    if (password == null)
+      password = StringUtils.EMPTY;
+    if (protocol == null)
+      protocol = EmailConfig.PROTOCOL_DEFAULT_VALUE;
+    if (server == null)
+      server = StringUtils.EMPTY;
+    if (port == null)
+      port = EmailConfig.PORT_DEFAULT_VALUE;
+
+    paramMap.put("USERNAME", username);
+    paramMap.put("PASSWORD", password);
+    paramMap.put("PROTOCOL", protocol);
+    paramMap.put("SERVER", server);
+    paramMap.put("PORT", port);
+    paramMap.put("PROPERTIES", list);
+
+  }
+
+  /**
+  * Process a configuration post.
+  * This method is called at the start of the connector's configuration page, whenever there is a possibility
+  * that form data for a connection has been posted. Its purpose is to gather form information and modify
+  * the configuration parameters accordingly.
+  * The name of the posted form is always "editconnection".
+  * The connector does not need to be connected for this method to be called.
+  *
+  * @param threadContext is the local thread context.
+  * @param variableContext is the set of variables available from the post, including binary file post information.
+  * @param parameters are the configuration parameters, as they currently exist, for this connection being configured.
+  * @return null if all is well, or a string error message if there is an error that should prevent saving of the
+  * connection (and cause a redirection to an error page).
+  */
+  @Override
+  public String processConfigurationPost(IThreadContext threadContext, IPostParameters variableContext,
+    ConfigParams parameters) throws ManifoldCFException {
+
+    String userName = variableContext.getParameter("username");
+    if (userName != null)
+      parameters.setParameter(EmailConfig.USERNAME_PARAM, userName);
+
+    String password = variableContext.getParameter("password");
+    if (password != null)
+      parameters.setParameter(EmailConfig.PASSWORD_PARAM, password);
+
+    String protocol = variableContext.getParameter("protocol");
+    if (protocol != null)
+      parameters.setParameter(EmailConfig.PROTOCOL_PARAM, protocol);
+
+    String server = variableContext.getParameter("server");
+    if (server != null)
+      parameters.setParameter(EmailConfig.SERVER_PARAM, server);
+    String port = variableContext.getParameter("port");
+    if (port != null)
+      parameters.setParameter(EmailConfig.PORT_PARAM, port);
+    // Remove old find parameter document specification information
+    removeNodes(parameters, EmailConfig.NODE_PROPERTIES);
+
+    // Parse the number of records that were posted
+    String findCountString = variableContext.getParameter("findcount");
+    if (findCountString != null) {
+      int findCount = Integer.parseInt(findCountString);
+
+      // Loop throught them and add new server properties
+      int i = 0;
+      while (i < findCount) {
+        String suffix = "_" + Integer.toString(i++);
+        // Only add the name/value if the item was not deleted.
+        String findParameterOp = variableContext.getParameter("findop" + suffix);
+        if (findParameterOp == null || !findParameterOp.equals("Delete")) {
+          String findParameterName = variableContext.getParameter("findname" + suffix);
+          String findParameterValue = variableContext.getParameter("findvalue" + suffix);
+          addFindParameterNode(parameters, findParameterName, findParameterValue);
+        }
+      }
+    }
+
+    // Now, look for a global "Add" operation
+    String operation = variableContext.getParameter("findop");
+    if (operation != null && operation.equals("Add")) {
+      // Pick up the global parameter name and value
+      String findParameterName = variableContext.getParameter("findname");
+      String findParameterValue = variableContext.getParameter("findvalue");
+      addFindParameterNode(parameters, findParameterName, findParameterValue);
+    }
+
+    return null;
+  }
+
+  private void addFindParameterNode(ConfigParams parameters, String findParameterName, String findParameterValue) {
+    ConfigNode cn = new ConfigNode(EmailConfig.NODE_PROPERTIES);
+    cn.setAttribute(EmailConfig.ATTRIBUTE_NAME, findParameterName);
+    cn.setAttribute(EmailConfig.ATTRIBUTE_VALUE, findParameterValue);
+    // Add to the end
+    parameters.addChild(parameters.getChildCount(), cn);
+  }
+
+  protected static void removeNodes(ConfigParams parameters,
+                    String nodeTypeName) {
+    int i = 0;
+    while (i < parameters.getChildCount()) {
+      ConfigNode cn = parameters.getChild(i);
+      if (cn.getType().equals(nodeTypeName))
+        parameters.removeChild(i);
+      else
+        i++;
+    }
+  }
+
+  /**
+  * View configuration. This method is called in the body section of the
+  * connector's view configuration page. Its purpose is to present the
+  * connection information to the user. The coder can presume that the HTML that
+  * is output from this configuration will be within appropriate <html> and
+  * <body> tags.
+  *
+  * @param threadContext is the local thread context.
+  * @param out is the output to which any HTML should be sent.
+  * @param parameters are the configuration parameters, as they currently exist, for
+  * this connection being configured.
+  */
+  @Override
+  public void viewConfiguration(IThreadContext threadContext, IHTTPOutput out,
+    Locale locale, ConfigParams parameters) throws ManifoldCFException, IOException {
+    Map<String, Object> paramMap = new HashMap<String, Object>();
+
+    // Fill in map from each tab
+    fillInServerConfigurationMap(paramMap, parameters);
+
+    Messages.outputResourceWithVelocity(out, locale, "ConfigurationView.html", paramMap);
+  }
+
+
+  /////////////////////////////////End of configuration UI////////////////////////////////////////////////////
+
+
+  /////////////////////////////////Start of Specification UI//////////////////////////////////////////////////
+
+  /**
+  * Output the specification header section.
+  * This method is called in the head section of a job page which has selected a repository connection of the
+  * current type. Its purpose is to add the required tabs to the list, and to output any javascript methods
+  * that might be needed by the job editing HTML.
+  * The connector will be connected before this method can be called.
+  *
+  * @param out is the output to which any HTML should be sent.
+  * @param locale is the desired locale.
+  * @param ds is the current document specification for this job.
+  * @param tabsArray is an array of tab names. Add to this array any tab names that are specific to the connector.
+  */
+  @Override
+  public void outputSpecificationHeader(IHTTPOutput out, Locale locale,
+    DocumentSpecification ds, List<String> tabsArray)
+    throws ManifoldCFException, IOException {
+    // Add the tabs
+    tabsArray.add(Messages.getString(locale, "EmailConnector.Metadata"));
+    tabsArray.add(Messages.getString(locale, "EmailConnector.Filter"));
+    Messages.outputResourceWithVelocity(out, locale, "SpecificationHeader.js", null);
+  }
+
+  /**
+  * Output the specification body section.
+  * This method is called in the body section of a job page which has selected a repository connection of the
+  * current type. Its purpose is to present the required form elements for editing.
+  * The coder can presume that the HTML that is output from this configuration will be within appropriate
+  * <html>, <body>, and <form> tags. The name of the form is always "editjob".
+  * The connector will be connected before this method can be called.
+  *
+  * @param out is the output to which any HTML should be sent.
+  * @param locale is the desired locale.
+  * @param ds is the current document specification for this job.
+  * @param tabName is the current tab name.
+  */
+  @Override
+  public void outputSpecificationBody(IHTTPOutput out, Locale locale,
+    DocumentSpecification ds, String tabName)
+    throws ManifoldCFException, IOException {
+    outputFilterTab(out, locale, ds, tabName);
+    outputMetadataTab(out, locale, ds, tabName);
+  }
+
+  /**
+* Take care of "Metadata" tab.
+*/
+  protected void outputMetadataTab(IHTTPOutput out, Locale locale,
+                   DocumentSpecification ds, String tabName)
+      throws ManifoldCFException, IOException {
+    Map<String, Object> paramMap = new HashMap<String, Object>();
+    paramMap.put("TabName", tabName);
+    fillInMetadataTab(paramMap, ds);
+    fillInMetadataAttributes(paramMap);
+    Messages.outputResourceWithVelocity(out, locale, "Specification_Metadata.html", paramMap);
+  }
+
+  /**
+  * Fill in Velocity context for Metadata tab.
+  */
+  protected static void fillInMetadataTab(Map<String, Object> paramMap,
+    DocumentSpecification ds) {
+    Set<String> metadataSelections = new HashSet<String>();
+    int i = 0;
+    while (i < ds.getChildCount()) {
+      SpecificationNode sn = ds.getChild(i++);
+      if (sn.getType().equals(EmailConfig.NODE_METADATA)) {
+        String metadataName = sn.getAttributeValue(EmailConfig.ATTRIBUTE_NAME);
+        metadataSelections.add(metadataName);
+      }
+    }
+    paramMap.put("metadataselections", metadataSelections);
+  }
+
+  /**
+  * Fill in Velocity context with data to permit attribute selection.
+  */
+  protected void fillInMetadataAttributes(Map<String, Object> paramMap) {
+    String[] matchNames = EmailConfig.BASIC_METADATA;
+    paramMap.put("metadataattributes", matchNames);
+  }
+
+  protected void outputFilterTab(IHTTPOutput out, Locale locale,
+    DocumentSpecification ds, String tabName)
+    throws ManifoldCFException, IOException {
+    Map<String, Object> paramMap = new HashMap<String, Object>();
+    paramMap.put("TabName", tabName);
+    fillInFilterTab(paramMap, ds);
+    fillInSearchableAttributes(paramMap);
+    Messages.outputResourceWithVelocity(out, locale, "Specification_Filter.html", paramMap);
+  }
+
+  private void fillInSearchableAttributes(Map<String, Object> paramMap) {
+    String[] attributes = EmailConfig.BASIC_SEARCHABLE_ATTRIBUTES;
+    paramMap.put("SEARCHABLEATTRIBUTES", attributes);
+  }
+
+  protected static void fillInFilterTab(Map<String, Object> paramMap,
+    DocumentSpecification ds) {
+    List<Map<String, String>> list = new ArrayList<Map<String, String>>();
+    int i = 0;
+    while (i < ds.getChildCount()) {
+      SpecificationNode sn = ds.getChild(i++);
+      if (sn.getType().equals(EmailConfig.NODE_FILTER)) {
+
+        String findParameterName = sn.getAttributeValue(EmailConfig.ATTRIBUTE_NAME);
+        String findParameterValue = sn.getAttributeValue(EmailConfig.ATTRIBUTE_VALUE);
+        Map<String, String> row = new HashMap<String, String>();
+        row.put("name", findParameterName);
+        row.put("value", findParameterValue);
+        list.add(row);
+      }
+    }
+    paramMap.put("MATCHES", list);
+  }
+
+  /**
+  * Process a specification post.
+  * This method is called at the start of job's edit or view page, whenever there is a possibility that form
+  * data for a connection has been posted. Its purpose is to gather form information and modify the
+  * document specification accordingly. The name of the posted form is always "editjob".
+  * The connector will be connected before this method can be called.
+  *
+  * @param variableContext contains the post data, including binary file-upload information.
+  * @param ds is the current document specification for this job.
+  * @return null if all is well, or a string error message if there is an error that should prevent saving of
+  * the job (and cause a redirection to an error page).
+  */
+  @Override
+  public String processSpecificationPost(IPostParameters variableContext, DocumentSpecification ds)
+      throws ManifoldCFException {
+
+    String result = processFilterTab(variableContext, ds);
+    if (result != null)
+      return result;
+    result = processMetadataTab(variableContext, ds);
+    return result;
+  }
+
+
+  protected String processFilterTab(IPostParameters variableContext, DocumentSpecification ds)
+      throws ManifoldCFException {
+    // Remove old find parameter document specification information
+    removeNodes(ds, EmailConfig.NODE_FILTER);
+
+    String findCountString = variableContext.getParameter("findcount");
+    if (findCountString != null) {
+      int findCount = Integer.parseInt(findCountString);
+
+      int i = 0;
+      while (i < findCount) {
+        String suffix = "_" + Integer.toString(i++);
+        // Only add the name/value if the item was not deleted.
+        String findParameterOp = variableContext.getParameter("findop" + suffix);
+        if (findParameterOp == null || !findParameterOp.equals("Delete")) {
+          String findParameterName = variableContext.getParameter("findname" + suffix);
+          String findParameterValue = variableContext.getParameter("findvalue" + suffix);
+          addFindParameterNode(ds, findParameterName, findParameterValue);
+        }
+      }
+    }
+
+    String operation = variableContext.getParameter("findop");
+    if (operation != null && operation.equals("Add")) {
+      String findParameterName = variableContext.getParameter("findname");
+      String findParameterValue = variableContext.getParameter("findvalue");
+      addFindParameterNode(ds, findParameterName, findParameterValue);
+    }
+
+    return null;
+  }
+
+
+  protected String processMetadataTab(IPostParameters variableContext, DocumentSpecification ds)
+      throws ManifoldCFException {
+    // Remove old included metadata nodes
+    removeNodes(ds, EmailConfig.NODE_METADATA);
+
+    // Get the posted metadata values
+    String[] metadataNames = variableContext.getParameterValues("metadata");
+    if (metadataNames != null) {
+      // Add each metadata name as a node to the document specification
+      int i = 0;
+      while (i < metadataNames.length) {
+        String metadataName = metadataNames[i++];
+        addIncludedMetadataNode(ds, metadataName);
+      }
+    }
+
+    return null;
+  }
+
+  protected static void removeNodes(DocumentSpecification ds,
+                    String nodeTypeName) {
+    int i = 0;
+    while (i < ds.getChildCount()) {
+      SpecificationNode sn = ds.getChild(i);
+      if (sn.getType().equals(nodeTypeName))
+        ds.removeChild(i);
+      else
+        i++;
+    }
+  }
+
+  protected static void addIncludedMetadataNode(DocumentSpecification ds,
+                          String metadataName) {
+    // Build the proper node
+    SpecificationNode sn = new SpecificationNode(EmailConfig.NODE_METADATA);
+    sn.setAttribute(EmailConfig.ATTRIBUTE_NAME, metadataName);
+    // Add to the end
+    ds.addChild(ds.getChildCount(), sn);
+  }
+
+  private void addFindParameterNode(DocumentSpecification ds, String findParameterName, String findParameterValue) {
+    SpecificationNode sn = new SpecificationNode(EmailConfig.NODE_FILTER);
+    sn.setAttribute(EmailConfig.ATTRIBUTE_NAME, findParameterName);
+    sn.setAttribute(EmailConfig.ATTRIBUTE_VALUE, findParameterValue);
+    // Add to the end
+    ds.addChild(ds.getChildCount(), sn);
+  }
+
+  /**
+  * View specification.
+  * This method is called in the body section of a job's view page. Its purpose is to present the document
+  * specification information to the user. The coder can presume that the HTML that is output from
+  * this configuration will be within appropriate <html> and <body> tags.
+  * The connector will be connected before this method can be called.
+  *
+  * @param out is the output to which any HTML should be sent.
+  * @param locale is the desired locale.
+  * @param ds is the current document specification for this job.
+*/
+  @Override
+  public void viewSpecification(IHTTPOutput out, Locale locale, DocumentSpecification ds)
+      throws ManifoldCFException, IOException {
+    Map<String, Object> paramMap = new HashMap<String, Object>();
+    fillInFilterTab(paramMap, ds);
+    fillInMetadataTab(paramMap, ds);
+    Messages.outputResourceWithVelocity(out, locale, "SpecificationView.html", paramMap);
+  }
+
+  ///////////////////////////////////////End of specification UI///////////////////////////////////////////////
+}
\ No newline at end of file
diff --git a/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/email/Messages.java b/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/email/Messages.java
new file mode 100644
index 0000000..655fa6e
--- /dev/null
+++ b/connectors/email/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/email/Messages.java
@@ -0,0 +1,141 @@
+/* $Id$ */
+
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.manifoldcf.crawler.connectors.email;
+
+import java.util.Locale;
+import java.util.Map;
+import org.apache.manifoldcf.core.interfaces.ManifoldCFException;
+import org.apache.manifoldcf.core.interfaces.IHTTPOutput;
+
+public class Messages extends org.apache.manifoldcf.ui.i18n.Messages
+{
+  public static final String DEFAULT_BUNDLE_NAME="org.apache.manifoldcf.crawler.connectors.email.common";
+  public static final String DEFAULT_PATH_NAME="org.apache.manifoldcf.crawler.connectors.email";
+  
+  /** Constructor - do no instantiate
+  */
+  protected Messages()
+  {
+  }
+  
+  public static String getString(Locale locale, String messageKey)
+  {
+    return getString(DEFAULT_BUNDLE_NAME, locale, messageKey, null);
+  }
+
+  public static String getAttributeString(Locale locale, String messageKey)
+  {
+    return getAttributeString(DEFAULT_BUNDLE_NAME, locale, messageKey, null);
+  }
+
+  public static String getBodyString(Locale locale, String messageKey)
+  {
+    return getBodyString(DEFAULT_BUNDLE_NAME, locale, messageKey, null);
+  }
+
+  public static String getAttributeJavascriptString(Locale locale, String messageKey)
+  {
+    return getAttributeJavascriptString(DEFAULT_BUNDLE_NAME, locale, messageKey, null);
+  }
+
+  public static String getBodyJavascriptString(Locale locale, String messageKey)
+  {
+    return getBodyJavascriptString(DEFAULT_BUNDLE_NAME, locale, messageKey, null);
+  }
+
+  public static String getString(Locale locale, String messageKey, Object[] args)
+  {
+    return getString(DEFAULT_BUNDLE_NAME, locale, messageKey, args);
+  }
+
+  public static String getAttributeString(Locale locale, String messageKey, Object[] args)
+  {
+    return getAttributeString(DEFAULT_BUNDLE_NAME, locale, messageKey, args);
+  }
+  
+  public static String getBodyString(Locale locale, String messageKey, Object[] args)
+  {
+    return getBodyString(DEFAULT_BUNDLE_NAME, locale, messageKey, args);
+  }
+
+  public static String getAttributeJavascriptString(Locale locale, String messageKey, Object[] args)
+  {
+    return getAttributeJavascriptString(DEFAULT_BUNDLE_NAME, locale, messageKey, args);
+  }
+
+  public static String getBodyJavascriptString(Locale locale, String messageKey, Object[] args)
+  {
+    return getBodyJavascriptString(DEFAULT_BUNDLE_NAME, locale, messageKey, args);
+  }
+
+  // More general methods which allow bundlenames and class loaders to be specified.
+  
+  public static String getString(String bundleName, Locale locale, String messageKey, Object[] args)
+  {
+    return getString(Messages.class, bundleName, locale, messageKey, args);
+  }
+
+  public static String getAttributeString(String bundleName, Locale locale, String messageKey, Object[] args)
+  {
+    return getAttributeString(Messages.class, bundleName, locale, messageKey, args);
+  }
+
+  public static String getBodyString(String bundleName, Locale locale, String messageKey, Object[] args)
+  {
+    return getBodyString(Messages.class, bundleName, locale, messageKey, args);
+  }
+  
+  public static String getAttributeJavascriptString(String bundleName, Locale locale, String messageKey, Object[] args)
+  {
+    return getAttributeJavascriptString(Messages.class, bundleName, locale, messageKey, args);
+  }
+
+  public static String getBodyJavascriptString(String bundleName, Locale locale, String messageKey, Object[] args)
+  {
+    return getBodyJavascriptString(Messages.class, bundleName, locale, messageKey, args);
+  }
+
+  // Resource output
+  
+  public static void outputResource(IHTTPOutput output, Locale locale, String resourceKey,
+    Map<String,String> substitutionParameters, boolean mapToUpperCase)
+    throws ManifoldCFException
+  {
+    outputResource(output,Messages.class,DEFAULT_PATH_NAME,locale,resourceKey,
+      substitutionParameters,mapToUpperCase);
+  }
+  
+  public static void outputResourceWithVelocity(IHTTPOutput output, Locale locale, String resourceKey,
+    Map<String,String> substitutionParameters, boolean mapToUpperCase)
+    throws ManifoldCFException
+  {
+    outputResourceWithVelocity(output,Messages.class,DEFAULT_BUNDLE_NAME,DEFAULT_PATH_NAME,locale,resourceKey,
+      substitutionParameters,mapToUpperCase);
+  }
+
+  public static void outputResourceWithVelocity(IHTTPOutput output, Locale locale, String resourceKey,
+    Map<String,Object> contextObjects)
+    throws ManifoldCFException
+  {
+    outputResourceWithVelocity(output,Messages.class,DEFAULT_BUNDLE_NAME,DEFAULT_PATH_NAME,locale,resourceKey,
+      contextObjects);
+  }
+  
+}
+
diff --git a/connectors/email/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/connectors/email/common_en_US.properties b/connectors/email/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/connectors/email/common_en_US.properties
new file mode 100644
index 0000000..81358f3
--- /dev/null
+++ b/connectors/email/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/connectors/email/common_en_US.properties
@@ -0,0 +1,53 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+EmailConnector.Server=Server
+EmailConnector.Metadata=Metadata
+EmailConnector.Filter=Filter
+
+EmailConnector.EnterAMailServerHostName=Enter a mail server host name
+EmailConnector.PleaseSelectAConfigurationParameterName=Please select a configuration parameter name
+EmailConnector.PleaseSelectAMetadataName=Please select a metadata name
+EmailConnector.ValueCannotBeBlank=Value cannot be blank
+
+EmailConnector.ConfigurationPropertiesColon=Configuration properties:
+EmailConnector.ProtocolColon=Protocol:
+EmailConnector.HostNameColon=Host name:
+EmailConnector.PortColon=Port:
+EmailConnector.UserNameColon=User name:
+EmailConnector.PasswordColon=Password:
+EmailConnector.MatchesColon=Matches:
+EmailConnector.RecordFilterColon=Record filter:
+EmailConnector.ServerProperty=Server property
+EmailConnector.Value=Value
+EmailConnector.NoServerPropertiesSpecified=No server properties specified
+EmailConnector.AddNewMatch=Add new match
+EmailConnector.AddNewProperty=Add new property
+EmailConnector.Add=Add
+EmailConnector.DeleteMatchNumber=Delete match #
+EmailConnector.DeletePropertyNumber=Delete property #
+EmailConnector.Delete=Delete
+EmailConnector.MetadataName=Metadata name
+EmailConnector.NoMetadataSpecified=No metadata specified
+EmailConnector.SelectMetadataName=--Select metadata name --
+EmailConnector.IncludedMetadataColon=Included metadata:
+
+
+
+
+
+
+
+
diff --git a/connectors/email/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/connectors/email/common_ja_JP.properties b/connectors/email/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/connectors/email/common_ja_JP.properties
new file mode 100644
index 0000000..81358f3
--- /dev/null
+++ b/connectors/email/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/connectors/email/common_ja_JP.properties
@@ -0,0 +1,53 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+EmailConnector.Server=Server
+EmailConnector.Metadata=Metadata
+EmailConnector.Filter=Filter
+
+EmailConnector.EnterAMailServerHostName=Enter a mail server host name
+EmailConnector.PleaseSelectAConfigurationParameterName=Please select a configuration parameter name
+EmailConnector.PleaseSelectAMetadataName=Please select a metadata name
+EmailConnector.ValueCannotBeBlank=Value cannot be blank
+
+EmailConnector.ConfigurationPropertiesColon=Configuration properties:
+EmailConnector.ProtocolColon=Protocol:
+EmailConnector.HostNameColon=Host name:
+EmailConnector.PortColon=Port:
+EmailConnector.UserNameColon=User name:
+EmailConnector.PasswordColon=Password:
+EmailConnector.MatchesColon=Matches:
+EmailConnector.RecordFilterColon=Record filter:
+EmailConnector.ServerProperty=Server property
+EmailConnector.Value=Value
+EmailConnector.NoServerPropertiesSpecified=No server properties specified
+EmailConnector.AddNewMatch=Add new match
+EmailConnector.AddNewProperty=Add new property
+EmailConnector.Add=Add
+EmailConnector.DeleteMatchNumber=Delete match #
+EmailConnector.DeletePropertyNumber=Delete property #
+EmailConnector.Delete=Delete
+EmailConnector.MetadataName=Metadata name
+EmailConnector.NoMetadataSpecified=No metadata specified
+EmailConnector.SelectMetadataName=--Select metadata name --
+EmailConnector.IncludedMetadataColon=Included metadata:
+
+
+
+
+
+
+
+
diff --git a/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/ConfigurationHeader.js b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/ConfigurationHeader.js
new file mode 100644
index 0000000..3bcfefc
--- /dev/null
+++ b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/ConfigurationHeader.js
@@ -0,0 +1,66 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<script type="text/javascript">
+<!--
+function checkConfigForSave()
+{
+  if (editconnection.server.value == "")
+  {
+    alert("$Encoder.bodyJavascriptEscape($ResourceBundle.getString('EmailConnector.EnterAMailServerHostName'))");
+    SelectTab("$Encoder.bodyJavascriptEscape($ResourceBundle.getString('EmailConnector.Server'))");
+    editconnection.server.focus();
+    return false;
+  }
+  return true;
+}
+
+function addProperty()
+{
+  postFormSetAnchor("property"); //Repost the form and send the browser to property anchor
+}
+
+function SpecOp(n, opValue, anchorvalue)
+{
+  eval("editconnection."+n+".value = \""+opValue+"\"");
+  postFormSetAnchor(anchorvalue);
+}
+
+function FindDelete(n)
+{
+  SpecOp("findop_"+n, "Delete", "find_"+n);
+}
+
+function FindAdd(n)
+{
+  if (editconnection.findname.value == "")
+  {
+    alert("$Encoder.bodyJavascriptEscape($ResourceBundle.getString('EmailConnector.PleaseSelectAConfigurationParameterName'))");
+    editconnection.findname.focus();
+    return;
+  }
+  if (editconnection.findvalue.value == "")
+  {
+    alert("$Encoder.bodyJavascriptEscape($ResourceBundle.getString('EmailConnector.ValueCannotBeBlank'))");
+    editconnection.findvalue.focus();
+    return;
+  }
+  SpecOp("findop", "Add", "find_"+n);
+}
+
+//-->
+</script>
diff --git a/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/ConfigurationView.html b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/ConfigurationView.html
new file mode 100644
index 0000000..9666520
--- /dev/null
+++ b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/ConfigurationView.html
@@ -0,0 +1,85 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<table class="displaytable">
+  
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.ProtocolColon'))</nobr>
+    </td>
+    <td class="value">
+      <nobr>$Encoder.bodyEscape($PROTOCOL)</nobr>
+    </td>
+  </tr>
+  
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.HostNameColon'))</nobr>
+    </td>
+    <td class="value">
+      <nobr>$Encoder.bodyEscape($SERVER)</nobr>
+    </td>
+  </tr>
+  
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.PortColon'))</nobr>
+    </td>
+    <td class="value">
+      <nobr>$Encoder.bodyEscape($PORT)</nobr>
+    </td>
+  </tr>
+
+  <tr><td  class="separator" colspan="2"><hr/></td></tr>
+  
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.UserNameColon'))</nobr>
+    </td>
+    <td class="value">
+      <nobr>$Encoder.bodyEscape($USERNAME)</nobr>
+    </td>
+  </tr>
+  
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.PasswordColon'))</nobr>
+    </td>
+    <td class="value">
+      <nobr>********</nobr>
+    </td>
+  </tr>
+
+  <tr><td  class="separator" colspan="2"><hr/></td></tr>
+
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.ConfigurationPropertiesColon'))</nobr>
+    </td>
+    <td class="value">
+#foreach( $property in $PROPERTIES )
+        <nobr>
+            $Encoder.bodyEscape($property.get('name')) : $Encoder.bodyEscape($property.get('value'))
+        </nobr>
+        <br/>
+#end
+
+    </td>
+  </tr>
+  
+</table>
+
diff --git a/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/Configuration_Server.html b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/Configuration_Server.html
new file mode 100644
index 0000000..ee74393
--- /dev/null
+++ b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/Configuration_Server.html
@@ -0,0 +1,189 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+#if($TabName == $ResourceBundle.getString('EmailConnector.Server'))
+
+<table class="displaytable">
+  <tr><td class="separator" colspan="2"><hr/></td></tr>
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.ProtocolColon'))</nobr>
+    </td>
+    <td class="value">
+      <select id="protocol" name="protocol" size="2">
+  #if($PROTOCOL == 'IMAP')
+        <option value="IMAP" selected="selected">IMAP</option>
+  #else
+        <option value="IMAP">IMAP</option>
+  #end
+  #if($PROTOCOL == 'IMAP-SSL')
+        <option value="IMAP-SSL" selected="selected">IMAP-SSL</option>
+  #else
+        <option value="IMAP-SSL">IMAP-SSL</option>
+  #end
+  #if($PROTOCOL == 'POP3')
+        <option value="POP3" selected="selected">POP3</option>
+  #else
+        <option value="POP3">POP3</option>
+  #end
+  #if($PROTOCOL == 'POP3-SSL')
+        <option value="POP3-SSL" selected="selected">POP3-SSL</option>
+  #else
+        <option value="POP3-SSL">POP3-SSL</option>
+  #end
+      </select>
+    </td>
+  </tr>
+  
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.HostNameColon'))</nobr>
+    </td>
+    <td class="value">
+      <input id="server" name="server" type="text" size="32" value="$Encoder.attributeEscape($SERVER)"/>
+    </td>
+  </tr>
+  
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.PortColon'))</nobr>
+    </td>
+    <td class="value">
+      <input type="text" id="port" name="port" value="$Encoder.attributeEscape($PORT)"/>
+    </td>
+  </tr>
+
+  <tr><td class="separator" colspan="2"><hr/></td></tr>
+
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.UserNameColon'))</nobr>
+    </td>
+    <td class="value">
+      <input type="text" id="username" name="username" value="$Encoder.attributeEscape($USERNAME)"/>
+    </td>
+  </tr>
+
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.PasswordColon'))</nobr>
+    </td>
+    <td class="value">
+      <input type="password" id="password" name="password" value="$Encoder.attributeEscape($PASSWORD)"/>
+    </td>
+  </tr>
+
+  <tr><td class="separator" colspan="2"><hr/></td></tr>
+
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.ConfigurationPropertiesColon'))</nobr>
+    </td>
+    <td class="boxcell">
+      <table class="formtable">
+        <tr class="formheaderrow">
+          <td class="formcolumnheader"></td>
+          <td class="formcolumnheader">
+            <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.ServerProperty'))</nobr>
+          </td>
+          <td class="formcolumnheader">
+            <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.Value'))</nobr>
+          </td>
+        </tr>
+
+  #set($k = 0)
+  #foreach($property in $PROPERTIES)
+
+    #if(($k % 2) == 0)
+        <tr class="evenformrow">
+    #else
+        <tr class="oddformrow">
+    #end
+          <td class="formcolumncell">
+            <input type="hidden" name="findop_$k" value=""/>
+            <input type="hidden" name="findname_$k" value="$Encoder.attributeEscape($property.get('name'))"/>
+            <input type="hidden" name="findvalue_$k" value="$Encoder.attributeEscape($property.get('value'))"/>
+            <a name="find_$k">
+              <input type="button" value="$Encoder.attributeEscape($ResourceBundle.getString('EmailConnector.Delete'))" onClick='Javascript:FindDelete("$k")' alt="$Encoder.attributeEscape($ResourceBundle.getString('EmailConnector.DeletePropertyNumber'))$k"/>
+            </a>
+          </td>
+          <td class="formcolumncell">
+            <nobr>$Encoder.bodyEscape($property.get('name'))</nobr>
+          </td>
+          <td class="formcolumncell">
+            <nobr>$Encoder.bodyEscape($property.get('value'))</nobr>
+          </td>
+        </tr>
+
+    #set($k = $k + 1)
+  #end
+
+  #if($k == 0)
+        <tr class="formrow">
+          <td class="formcolumnmessage" colspan="3">$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.NoServerPropertiesSpecified'))</td>
+        </tr>
+  #end
+
+        <tr class="formrow"><td class="formseparator" colspan="3"><hr/></td></tr>
+
+  #set($nextk = $k + 1)
+
+        <tr class="formrow">
+          <td class="formcolumncell">
+            <nobr>
+              <a name="find_$k">
+                <input type="button" value="$Encoder.attributeEscape($ResourceBundle.getString('EmailConnector.Add'))" onClick='Javascript:FindAdd("$nextk")' alt="$Encoder.attributeEscape($ResourceBundle.getString('EmailConnector.AddNewProperty'))"/>
+                <input type="hidden" name="findcount" value="$k"/>
+                <input type="hidden" name="findop" value=""/>
+              </a>
+            </nobr>
+          </td>
+          <td class="formcolumncell">
+            <nobr><input type="text" size="32" name="findname" value=""/></nobr>
+          </td>
+          <td class="formcolumncell">
+            <nobr><input type="text" size="32" name="findvalue" value=""/></nobr>
+          </td>
+        </tr>
+
+
+      </table>
+    </td>
+  </tr>
+
+</table>
+
+#else
+
+<input type="hidden" name="username" value="$Encoder.attributeEscape($USERNAME)"/>
+<input type="hidden" name="password" value="$Encoder.attributeEscape($PASSWORD)"/>
+<input type="hidden" name="protocol" value="$Encoder.attributeEscape($PROTOCOL)"/>
+<input type="hidden" name="server" value="$Encoder.attributeEscape($SERVER)"/>
+<input type="hidden" name="port" value="$Encoder.attributeEscape($PORT)"/>
+
+  #set($k = 0)
+  #foreach($property in $PROPERTIES)
+
+<input type="hidden" name="findname_$k" value="$Encoder.attributeEscape($property.get('name'))"/>
+<input type="hidden" name="findvalue_$k" value="$Encoder.attributeEscape($property.get('value'))"/>
+
+    #set($k = $k + 1)
+  #end
+
+<input type="hidden" name="findcount" value="$k"/>
+
+#end
\ No newline at end of file
diff --git a/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/SpecificationHeader.js b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/SpecificationHeader.js
new file mode 100644
index 0000000..137acd2
--- /dev/null
+++ b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/SpecificationHeader.js
@@ -0,0 +1,69 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<script type="text/javascript">
+<!--
+
+function checkSpecification()
+{
+  if (checkDocumentsTab() == false)
+    return false;
+  if (checkMetadataTab() == false)
+    return false;
+  return true;
+}
+ 
+function SpecOp(n, opValue, anchorvalue)
+{
+  eval("editjob."+n+".value = \""+opValue+"\"");
+  postFormSetAnchor(anchorvalue);
+}
+
+function checkDocumentsTab()
+{
+  return true;
+}
+
+function checkMetadataTab()
+{
+  return true;
+}
+
+function FindDelete(n)
+{
+  SpecOp("findop_"+n, "Delete", "find_"+n);
+}
+
+function FindAdd(n)
+{
+  if (editjob.findname.value == "")
+  {
+    alert("$Encoder.bodyJavascriptEscape($ResourceBundle.getString('EmailConnector.PleaseSelectAMetadataName'))");
+    editjob.findname.focus();
+    return;
+  }
+  if (editjob.findvalue.value == "")
+  {
+    alert("$Encoder.bodyJavascriptEscape($ResourceBundle.getString('EmailConnector.ValueCannotBeBlank'))");
+    editjob.findvalue.focus();
+    return;
+  }
+  SpecOp("findop", "Add", "find_"+n);
+}
+
+//-->
+</script>
diff --git a/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/SpecificationView.html b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/SpecificationView.html
new file mode 100644
index 0000000..1913aab
--- /dev/null
+++ b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/SpecificationView.html
@@ -0,0 +1,64 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<table class="displaytable">
+  <tr>
+    <td class="description"><nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.RecordFilterColon'))</nobr></td>
+    <td class="boxcell">
+      <table class="formtable">
+        <tr class="formheaderrow">
+          <td class="formcolumnheader"><nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.MetadataName'))</nobr></td>
+          <td class="formcolumnheader"><nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.Value'))</nobr></td>
+        </tr>
+
+#set($k = 0)
+#foreach($match in $MATCHES)
+  #if(($k % 2) == 0)
+        <tr class="evenformrow">
+  #else
+        <tr class="oddformrow">
+  #end
+          <td class="formcolumncell">
+            <nobr>$Encoder.bodyEscape($match.get('name'))</nobr>
+          </td>
+          <td class="formcolumncell">
+            <nobr>$Encoder.bodyEscape($match.get('value'))</nobr>
+          </td>
+        </tr>
+  #set($k = $k + 1)
+#end
+    
+      </table>
+    </td>
+  </tr>
+
+  <tr>
+    <td class="description"><nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.IncludedMetadataColon'))</nobr></td>
+    <td class="value">
+
+#set($seendata = false)
+#foreach($metadataselection in $METADATASELECTIONS)
+  #if($seendata)
+      <br/>
+  #end
+  #set($seendata = true)
+      <nobr>$Encoder.bodyEscape($metadataselection)</nobr>
+#end
+
+    </td>
+  </tr>
+</table>
diff --git a/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/Specification_Filter.html b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/Specification_Filter.html
new file mode 100644
index 0000000..a31726a
--- /dev/null
+++ b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/Specification_Filter.html
@@ -0,0 +1,115 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+#if($TabName == $ResourceBundle.getString('EmailConnector.Filter'))
+
+<table class="displaytable">
+  <tr><td class="separator" colspan="2"><hr/></td></tr>
+  <tr>
+    <td class="description">
+      <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.RecordFilterColon'))</nobr>
+    </td>
+    <td class="boxcell">
+      <table class="formtable">
+        <tr class="formheaderrow">
+          <td class="formcolumnheader"></td>
+          <td class="formcolumnheader">
+            <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.MetadataName'))</nobr>
+          </td>
+          <td class="formcolumnheader">
+            <nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.Value'))</nobr>
+          </td>
+        </tr>
+
+  #set($k = 0)
+  #foreach($match in $MATCHES)
+
+    #if(($k % 2) == 0)
+        <tr class="evenformrow">
+    #else
+        <tr class="oddformrow">
+    #end
+          <td class="formcolumncell">
+            <input type="hidden" name="findop_$k" value=""/>
+            <input type="hidden" name="findname_$k" value="$Encoder.attributeEscape($match.get('name'))"/>
+            <input type="hidden" name="findvalue_$k" value="$Encoder.attributeEscape($match.get('value'))"/>
+            <a name="find_$k">
+              <input type="button" value="$Encoder.attributeEscape($ResourceBundle.getString('EmailConnector.Delete'))" onClick='Javascript:FindDelete("$k")' alt="$Encoder.attributeEscape($ResourceBundle.getString('EmailConnector.DeleteMatchNumber'))$k"/>
+            </a>
+          </td>
+          <td class="formcolumncell">
+            <nobr>$Encoder.bodyEscape($match.get('name'))</nobr>
+          </td>
+          <td class="formcolumncell">
+            <nobr>$Encoder.bodyEscape($match.get('value'))</nobr>
+          </td>
+        </tr>
+
+    #set($k = $k + 1)
+  #end
+
+  #if($k == 0)
+        <tr class="formrow">
+          <td class="formcolumnmessage" colspan="3">$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.NoMetadataSpecified'))</td>
+        </tr>
+  #end
+
+        <tr class="formrow"><td class="formseparator" colspan="3"><hr/></td></tr>
+  #set($nextk = $k + 1)
+
+        <tr class="formrow">
+          <td class="formcolumncell">
+            <nobr>
+              <a name="find_$k">
+                <input type="button" value="$Encoder.attributeEscape($ResourceBundle.getString('EmailConnector.Add'))" onClick='Javascript:FindAdd("$nextk")' alt="$Encoder.attributeEscape($ResourceBundle.getString('EmailConnector.AddNewMatch'))"/>
+                <input type="hidden" name="findcount" value="$k"/>
+                <input type="hidden" name="findop" value=""/>
+              </a>
+            </nobr>
+          </td>
+          <td class="formcolumncell">
+            <select name="findname">
+              <option value="" selected="true">$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.SelectMetadataName'))</option>
+  #foreach($name in $SEARCHABLEATTRIBUTES)
+              <option value="$Encoder.attributeEscape($name)">$Encoder.bodyEscape($name)</option>
+  #end
+            </select>
+          </td>
+          <td class="formcolumncell">
+            <nobr><input type="text" size="32" name="findvalue" value=""/></nobr>
+          </td>
+        </tr>
+
+      </table>
+    </td>
+  </tr>
+</table>
+
+#else
+
+  #set($k = 0)
+  #foreach($match in $MATCHES)
+
+<input type="hidden" name="findname_$k" value="$Encoder.attributeEscape($match.get('name'))"/>
+<input type="hidden" name="findvalue_$k" value="$Encoder.attributeEscape($match.get('value'))"/>
+
+    #set($k = $k + 1)
+  #end
+
+<input type="hidden" name="findcount" value="$k"/>
+
+#end
diff --git a/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/Specification_Metadata.html b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/Specification_Metadata.html
new file mode 100644
index 0000000..50da62f
--- /dev/null
+++ b/connectors/email/connector/src/main/resources/org/apache/manifoldcf/crawler/connectors/email/Specification_Metadata.html
@@ -0,0 +1,46 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+#if($TabName == $ResourceBundle.getString('EmailConnector.Metadata'))
+
+<table class="displaytable">
+  <tr><td class="separator" colspan="2"><hr/></td></tr>
+  <tr>
+    <td class="description"><nobr>$Encoder.bodyEscape($ResourceBundle.getString('EmailConnector.IncludedMetadataColon'))</nobr></td>
+    <td class="value">
+
+
+  #foreach($metadataattribute in $METADATAATTRIBUTES)
+    #if($METADATASELECTIONS.contains($metadataattribute))
+      <input type="checkbox" name="metadata" value="$Encoder.attributeEscape($metadataattribute)" checked="true"/>
+    #else
+      <input type="checkbox" name="metadata" value="$Encoder.attributeEscape($metadataattribute)"/>
+    #end
+      $Encoder.bodyEscape($metadataattribute)<br/>
+  #end
+    </td>
+
+  </tr>
+</table>
+
+#else
+
+  #foreach($metadataselection in $METADATASELECTIONS)
+<input type="hidden" name="metadata" value="$Encoder.attributeEscape($metadataselection)"/>
+  #end
+  
+#end
diff --git a/connectors/email/pom.xml b/connectors/email/pom.xml
new file mode 100644
index 0000000..da19ec3
--- /dev/null
+++ b/connectors/email/pom.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <groupId>org.apache.manifoldcf</groupId>
+    <artifactId>mcf-connectors</artifactId>
+    <version>1.5-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>mcf-email-connector</artifactId>
+  <name>ManifoldCF - Connectors - Email</name>
+
+  <build>
+    <sourceDirectory>${basedir}/connector/src/main/java</sourceDirectory>
+    <testSourceDirectory>${basedir}/connector/src/test/java</testSourceDirectory>
+    <resources>
+      <resource>
+        <directory>${basedir}/connector/src/main/resources</directory>
+        <includes>
+          <include>**/*.html</include>
+          <include>**/*.js</include>
+        </includes>
+      </resource>
+      <resource>
+        <directory>${basedir}/connector/src/main/native2ascii</directory>
+        <includes>
+          <include>**/*.properties</include>
+        </includes>
+      </resource>
+    </resources> 
+
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>native2ascii-maven-plugin</artifactId>
+        <version>1.0-beta-1</version>
+        <configuration>
+            <workDir>target/classes</workDir>
+        </configuration>
+        <executions>
+            <execution>
+                <id>native2ascii-utf8</id>
+                <goals>
+                    <goal>native2ascii</goal>
+                </goals>
+                <configuration>
+                    <encoding>UTF8</encoding>
+                    <includes>
+                      <include>**/*.properties</include>
+                    </includes>
+                </configuration>
+            </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>**/*Postgresql*.java</exclude>
+            <exclude>**/*MySQL*.java</exclude>
+          </excludes>
+          <forkMode>always</forkMode>
+          <workingDirectory>target/test-output</workingDirectory>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  
+  <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mcf-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mcf-agents</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mcf-pull-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mcf-ui-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${junit.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mcf-core</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mcf-agents</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mcf-pull-agent</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+      <version>${postgresql.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hsqldb</groupId>
+      <artifactId>hsqldb</artifactId>
+      <version>${hsqldb.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.derby</groupId>
+      <artifactId>derby</artifactId>
+      <version>${derby.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+      <version>${mysql.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.mail</groupId>
+      <artifactId>mail</artifactId>
+      <version>1.4</version>
+    </dependency> 
+  </dependencies>
+</project>
diff --git a/connectors/pom.xml b/connectors/pom.xml
index 076944a..44ecdfd 100644
--- a/connectors/pom.xml
+++ b/connectors/pom.xml
@@ -56,6 +56,7 @@
     <module>jira</module>
     <module>generic</module>
     <module>regexpmapper</module>
+    <module>email</module>
   </modules>
 
 </project>
diff --git a/framework/build.xml b/framework/build.xml
index 73379e2..262d99c 100644
--- a/framework/build.xml
+++ b/framework/build.xml
@@ -58,6 +58,7 @@
             <include name="xercesImpl*.jar"/>
             <include name="xml-apis*.jar"/>
             <include name="velocity*.jar"/>
+            <include name="mail*.jar"/>
         </fileset>
         <fileset dir="../lib">
             <include name="postgresql*.jar"/>
@@ -326,6 +327,7 @@
                 <include name="xercesImpl*.jar"/>
                 <include name="xml-apis*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -365,6 +367,7 @@
                 <include name="xercesImpl*.jar"/>
                 <include name="xml-apis*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -409,6 +412,7 @@
                 <include name="xercesImpl*.jar"/>
                 <include name="xml-apis*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -449,6 +453,7 @@
                 <include name="xercesImpl*.jar"/>
                 <include name="xml-apis*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -496,6 +501,7 @@
                 <include name="xml-apis*.jar"/>
                 <include name="velocity*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -544,6 +550,7 @@
                 <include name="xml-apis*.jar"/>
                 <include name="velocity*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -599,6 +606,7 @@
                 <include name="xml-apis*.jar"/>
                 <include name="velocity*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -649,6 +657,7 @@
                 <include name="xml-apis*.jar"/>
                 <include name="velocity*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -768,6 +777,7 @@
                 <include name="xml-apis*.jar"/>
                 <include name="velocity*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -809,6 +819,7 @@
                 <include name="xml-apis*.jar"/>
                 <include name="velocity*.jar"/>
                 <include name="slf4j*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -1159,6 +1170,7 @@
                 <include name="xercesImpl*.jar"/>
                 <include name="xml-apis*.jar"/>
                 <include name="velocity*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -1247,7 +1259,8 @@
         <property name="manifest-cp-59" value="${manifest-cp-58} lib/slf4j-simple.jar"/>
         <property name="manifest-cp-60" value="${manifest-cp-59} lib/httpcore.jar"/>
         <property name="manifest-cp-61" value="${manifest-cp-60} lib/httpclient.jar"/>
-        <property name="manifest-cp" value="${manifest-cp-61}"/>
+        <property name="manifest-cp-62" value="${manifest-cp-61} lib/mail.jar"/>
+        <property name="manifest-cp" value="${manifest-cp-62}"/>
         <mkdir dir="build/example"/>
         <manifest file="build/example/manifest">
             <attribute name="Main-Class" value="org.apache.manifoldcf.jettyrunner.ManifoldCFJettyRunner"/>
@@ -1287,6 +1300,7 @@
                 <include name="xercesImpl*.jar"/>
                 <include name="xml-apis*.jar"/>
                 <include name="velocity*.jar"/>
+                <include name="mail*.jar"/>
             </fileset>
             <fileset dir="../lib">
                 <include name="postgresql*.jar"/>
@@ -1383,7 +1397,8 @@
         <property name="manifest-cp-proprietary-61" value="${manifest-cp-proprietary-60} lib/slf4j-simple.jar"/>
         <property name="manifest-cp-proprietary-62" value="${manifest-cp-proprietary-61} lib/httpcore.jar"/>
         <property name="manifest-cp-proprietary-63" value="${manifest-cp-proprietary-62} lib/httpclient.jar"/>
-        <property name="manifest-cp-proprietary" value="${manifest-cp-proprietary-63}"/>
+        <property name="manifest-cp-proprietary-64" value="${manifest-cp-proprietary-63} lib/mail.jar"/>
+        <property name="manifest-cp-proprietary" value="${manifest-cp-proprietary-64}"/>
         <mkdir dir="build/example-proprietary"/>
         <manifest file="build/example-proprietary/manifest">
             <attribute name="Main-Class" value="org.apache.manifoldcf.jettyrunner.ManifoldCFJettyRunner"/>
diff --git a/tests/alfresco/build.xml b/tests/alfresco/build.xml
index 4415822..7b93cae 100644
--- a/tests/alfresco/build.xml
+++ b/tests/alfresco/build.xml
@@ -27,7 +27,6 @@
             <include name="saaj*.jar"/>	
             <include name="commons-discovery*.jar"/>
             <include name="jaxrpc*.jar"/>
-            <include name="mail*.jar"/>
             <include name="opensaml*.jar"/>
             <include name="wsdl4j*.jar"/>
             <include name="wss4j*.jar"/>