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"/>