Initial cut of IMAP support (disabled by default, to enable move imap sample files into extension folder)

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14279 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gavin Cornwell
2009-05-12 13:41:08 +00:00
parent 1a5d0fa8e6
commit 3cc38f4289
49 changed files with 6250 additions and 111 deletions

View File

@@ -40,6 +40,8 @@ import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
@@ -120,24 +122,54 @@ public class FolderEmailMessageHandler extends AbstractEmailMessageHandler
// Write the message content
if (message.getBody() != null)
{
InputStream contentIs = message.getBody().getContent();
// The message body is plain text, unless an extension has been provided
MimetypeService mimetypeService = getMimetypeService();
String mimetype = mimetypeService.guessMimetype(messageSubject);
if (mimetype.equals(MimetypeMap.MIMETYPE_BINARY))
if (message.getBody().getSize() == -1)
{
mimetype= MimetypeMap.MIMETYPE_TEXT_PLAIN;
// If message body is empty we write space as a content
// to make possible rule processing
// (Rules don't work on empty documents)
writeSpace(contentNodeRef);
}
else
{
InputStream contentIs = message.getBody().getContent();
// The message body is plain text, unless an extension has been provided
MimetypeService mimetypeService = getMimetypeService();
String mimetype = mimetypeService.guessMimetype(messageSubject);
if (mimetype.equals(MimetypeMap.MIMETYPE_BINARY))
{
mimetype = MimetypeMap.MIMETYPE_TEXT_PLAIN;
}
// Use the default encoding. It will get overridden if the body is text.
String encoding = message.getBody().getEncoding();
writeContent(contentNodeRef, contentIs, mimetype, encoding);
}
// Use the default encoding. It will get overridden if the body is text.
String encoding = message.getBody().getEncoding();
writeContent(contentNodeRef, contentIs, mimetype, encoding);
}
// Add attachments
addAttachments(spaceNodeRef, contentNodeRef, message);
}
/**
* This method writes space as a content. We need this space because rules doesn't proceed documents with empty content. We need rule processing for command email messages with
* empty body.
*
* @param nodeRef Reference to the parent node
*/
private void writeSpace(NodeRef nodeRef)
{
if (log.isDebugEnabled())
{
log.debug("Write space string");
}
ContentService contentService = getContentService();
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
writer.setEncoding("UTF-8");
writer.putContent(" ");
}
/**
* Adds titled aspect to the specified node.
*

View File

@@ -153,7 +153,7 @@ public class SubethaEmailMessage implements EmailMessage
try
{
subject = mimeMessage.getSubject();
subject = encodeSubject(mimeMessage.getSubject());
}
catch (MessagingException e)
{
@@ -373,7 +373,8 @@ public class SubethaEmailMessage implements EmailMessage
for (EmailMessagePart attachment : attachments)
{
if (attachment instanceof SubethaEmailMessagePart) {
if (attachment instanceof SubethaEmailMessagePart)
{
((SubethaEmailMessagePart) attachment).setRmiRegistry(rmiRegistryHost, rmiRegistryPort);
}
}
@@ -410,4 +411,24 @@ public class SubethaEmailMessage implements EmailMessage
return attachments;
}
/**
* Replaces characters \/*|:"<>? on their hex values. Subject field is used as name of the content, so we need to replace characters that are forbidden in content names.
*
* @param subject String representing subject
* @return Encoded string
*/
static private String encodeSubject(String subject)
{
String result = subject.trim();
String[][] s = new String[][] { { "\\", "%5c" }, { "/", "%2f" }, { "*", "%2a" }, { "|", "%7c" }, { ":", "%3a" }, { "\"", "%22" }, { "<", "%3c" }, { ">", "%3e" },
{ "?", "%3f" } };
for (int i = 0; i < s.length; i++)
{
result = result.replace(s[i][0], s[i][1]);
}
return result;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.model;
import org.alfresco.service.namespace.QName;
/**
* IMAP Model Constants
*
* @author Mike Shavnev
*/
public interface ImapModel
{
static final String IMAP_MODEL_1_0_URI = "http://www.alfresco.org/model/imap/1.0";
static final QName ASPECT_IMAP_FOLDER_SUBSCRIBED = QName.createQName(IMAP_MODEL_1_0_URI, "subscribed");
static final QName ASPECT_IMAP_FOLDER_NONSELECTABLE = QName.createQName(IMAP_MODEL_1_0_URI, "nonselectable");
static final QName TYPE_IMAP_CONTENT = QName.createQName(IMAP_MODEL_1_0_URI, "imapContent");
static final QName PROP_MESSAGE_FROM = QName.createQName(IMAP_MODEL_1_0_URI, "messageFrom");
static final QName PROP_MESSAGE_TO = QName.createQName(IMAP_MODEL_1_0_URI, "messageTo");
static final QName PROP_MESSAGE_CC = QName.createQName(IMAP_MODEL_1_0_URI, "messageCc");
static final QName PROP_MESSAGE_SUBJECT = QName.createQName(IMAP_MODEL_1_0_URI, "messageSubject");
static final QName ASPECT_FLAGGABLE = QName.createQName(IMAP_MODEL_1_0_URI, "flaggable");
static final QName PROP_FLAG_ANSWERED = QName.createQName(IMAP_MODEL_1_0_URI, "flagAnswered");
static final QName PROP_FLAG_DELETED = QName.createQName(IMAP_MODEL_1_0_URI, "flagDeleted");
static final QName PROP_FLAG_DRAFT = QName.createQName(IMAP_MODEL_1_0_URI, "flagDraft");
static final QName PROP_FLAG_SEEN = QName.createQName(IMAP_MODEL_1_0_URI, "flagSeen");
static final QName PROP_FLAG_RECENT = QName.createQName(IMAP_MODEL_1_0_URI, "flagRecent");
static final QName PROP_FLAG_FLAGGED = QName.createQName(IMAP_MODEL_1_0_URI, "flagFlagged");
static final QName TYPE_IMAP_BODY = QName.createQName(IMAP_MODEL_1_0_URI, "imapBody");
static final QName TYPE_IMAP_ATTACH = QName.createQName(IMAP_MODEL_1_0_URI, "imapAttach");
static final QName PROP_ATTACH_ID = QName.createQName(IMAP_MODEL_1_0_URI, "attachID");
}

View File

@@ -58,7 +58,8 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase
private String companyHomePath;
private StoreRef storeRef;
private ScriptLocation scriptLocation;
private String webApplicationContextUrl;
/**
* @param serviceRegistry The serviceRegistry to set.
*/
@@ -94,7 +95,17 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase
{
this.scriptLocation = scriptLocation;
}
/**
* Set the web application context url
*
* @param webApplicationContextUrl web application context url
*/
public void setWebApplicationContextUrl(String webApplicationContextUrl)
{
this.webApplicationContextUrl = webApplicationContextUrl;
}
/**
* Allow adhoc properties to be passed to this action
*
@@ -149,7 +160,9 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase
// Add the action to the default model
ScriptAction scriptAction = new ScriptAction(this.serviceRegistry, action, this.actionDefinition);
model.put("action", scriptAction);
model.put("webApplicationContextUrl", webApplicationContextUrl);
Object result = null;
if (this.scriptLocation == null)
{

View File

@@ -0,0 +1,264 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.admin.patch.impl;
import java.io.IOException;
import java.util.List;
import java.util.Properties;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.importer.ACPImportPackageHandler;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.admin.PatchException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.view.Location;
import org.springframework.context.MessageSource;
import org.springframework.core.io.ClassPathResource;
/**
* Builds folders tree necessary for IMAP functionality and imports email action scripts.
*
* 1. Company Home > Data Dictionary > Imap Config > Templates
* 2. Company Home > Data Dictionary > Email Actions > search
* 3. Company Home > Data Dictionary > Scripts > command-processor.js, command-search.js
*
* @author Arseny Kovalchuk
*/
public class ImapFoldersPatch extends AbstractPatch
{
// messages' ids
private static final String MSG_EXISTS = "patch.imapFolders.result.exists";
private static final String MSG_CREATED = "patch.imapFolders.result.created";
// folders' names for path building
private static final String PROPERTY_COMPANY_HOME_CHILDNAME = "spaces.company_home.childname";
private static final String PROPERTY_DICTIONARY_CHILDNAME = "spaces.dictionary.childname";
private static final String PROPERTY_SCRIPTS_CHILDNAME = "spaces.scripts.childname";
private static final String PROPERTY_IMAP_CONFIG_CHILDNAME = "spaces.imapConfig.childname";
private ImporterBootstrap importerBootstrap;
private MessageSource messageSource;
protected Properties configuration;
private ImporterService importerService;
private NodeRef companyHomeNodeRef;
private NodeRef dictionaryNodeRef;
private NodeRef scriptsNodeRef;
private NodeRef imapConfigFolderNodeRef;
private String configFoldersACP;
private String emailActionsACP;
private String scriptsACP;
public void setImporterBootstrap(ImporterBootstrap importerBootstrap)
{
this.importerBootstrap = importerBootstrap;
}
public void setMessageSource(MessageSource messageSource)
{
this.messageSource = messageSource;
}
public void setImporterService(ImporterService importerService)
{
this.importerService = importerService;
}
public void setConfigFoldersACP(String configFoldersACP)
{
this.configFoldersACP = configFoldersACP;
}
public void setEmailActionsACP(String emailActionsACP)
{
this.emailActionsACP = emailActionsACP;
}
public void setScriptsACP(String scriptsACP)
{
this.scriptsACP = scriptsACP;
}
protected void checkCommonProperties() throws Exception
{
checkPropertyNotNull(importerBootstrap, "importerBootstrap");
checkPropertyNotNull(messageSource, "messageSource");
checkPropertyNotNull(importerService, "importerService");
}
protected void setUp() throws Exception
{
// get the node store that we must work against
StoreRef storeRef = importerBootstrap.getStoreRef();
if (storeRef == null)
{
throw new PatchException("Bootstrap store has not been set");
}
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
this.configuration = importerBootstrap.getConfiguration();
// get the association names that form the path
String companyHomeChildName = configuration.getProperty(PROPERTY_COMPANY_HOME_CHILDNAME);
if (companyHomeChildName == null || companyHomeChildName.length() == 0)
{
throw new PatchException("Bootstrap property '" + PROPERTY_COMPANY_HOME_CHILDNAME + "' is not present");
}
String dictionaryChildName = configuration.getProperty(PROPERTY_DICTIONARY_CHILDNAME);
if (dictionaryChildName == null || dictionaryChildName.length() == 0)
{
throw new PatchException("Bootstrap property '" + PROPERTY_DICTIONARY_CHILDNAME + "' is not present");
}
String scriptsChildName = configuration.getProperty(PROPERTY_SCRIPTS_CHILDNAME);
if (scriptsChildName == null || scriptsChildName.length() == 0)
{
throw new PatchException("Bootstrap property '" + PROPERTY_SCRIPTS_CHILDNAME + "' is not present");
}
String imapConfigChildName = configuration.getProperty(PROPERTY_IMAP_CONFIG_CHILDNAME);
if (imapConfigChildName == null || imapConfigChildName.length() == 0)
{
throw new PatchException("Bootstrap property '" + PROPERTY_IMAP_CONFIG_CHILDNAME + "' is not present");
}
// build the search string to get the company home node
StringBuilder sb = new StringBuilder(256);
sb.append("/").append(companyHomeChildName);
String xpath = sb.toString();
// get the company home
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
if (nodeRefs.size() == 0)
{
throw new PatchException("XPath didn't return any results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath);
}
else if (nodeRefs.size() > 1)
{
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
}
this.companyHomeNodeRef = nodeRefs.get(0);
// build the search string to get the dictionary node
sb.append("/").append(dictionaryChildName);
xpath = sb.toString();
// get the dictionary node
nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
if (nodeRefs.size() == 0)
{
throw new PatchException("XPath didn't return any results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath);
}
else if (nodeRefs.size() > 1)
{
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
}
this.dictionaryNodeRef = nodeRefs.get(0);
sb.append("/").append(scriptsChildName);
xpath = sb.toString();
nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
if (nodeRefs.size() == 0)
{
throw new PatchException("XPath didn't return any results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath);
}
else if (nodeRefs.size() > 1)
{
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
}
this.scriptsNodeRef = nodeRefs.get(0);
// get the ImapConfig node
sb.delete((sb.length() - (scriptsChildName.length() + 1)), sb.length());
sb.append("/").append(imapConfigChildName);
xpath = sb.toString();
nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
if (nodeRefs.size() > 1)
{
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
}
else if (nodeRefs.size() == 0)
{
this.imapConfigFolderNodeRef = null;
}
else
{
this.imapConfigFolderNodeRef = nodeRefs.get(0);
}
}
@Override
protected String applyInternal() throws Exception
{
checkCommonProperties();
setUp();
String msg = null;
if (imapConfigFolderNodeRef == null)
{
// import the content
RunAsWork<Object> importRunAs = new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
importImapConfig();
importScripts();
importEmailActions();
return null;
}
};
AuthenticationUtil.runAs(importRunAs, authenticationContext.getSystemUserName());
msg = I18NUtil.getMessage(MSG_CREATED);
}
else
{
msg = I18NUtil.getMessage(MSG_EXISTS, imapConfigFolderNodeRef);
}
return msg;
}
private void importImapConfig() throws IOException
{
importInternal(this.configFoldersACP, this.dictionaryNodeRef);
}
private void importEmailActions() throws IOException
{
importInternal(this.emailActionsACP, this.dictionaryNodeRef);
}
private void importScripts() throws IOException
{
importInternal(this.scriptsACP, this.scriptsNodeRef);
}
private void importInternal(String acpName, NodeRef space) throws IOException
{
ClassPathResource acpResource = new ClassPathResource(acpName);
ACPImportPackageHandler acpHandler = new ACPImportPackageHandler(acpResource.getFile(), null);
Location importLocation = new Location(space);
importerService.importView(acpHandler, importLocation, null, null);
}
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of.
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.admin.patch.impl;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.service.cmr.admin.PatchException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.springframework.context.MessageSource;
/**
* @author Dmitry Vaserin
*/
public class ImapUsersPatch extends AbstractPatch
{
// messages' ids
private static final String MSG_EXISTS = "patch.imapUserFolders.result.exists";
private static final String MSG_CREATED = "patch.imapUserFolders.result.created";
// folders' names for path building
private static final String PROPERTY_COMPANY_HOME_CHILDNAME = "spaces.company_home.childname";
private static final String PROPERTY_IMAP_HOME_FOLDER_CHILDNAME = "spaces.imap_home.childname";
private static final String PROPERTY_ICON = "space-icon-default";
private static final String MSG_IMAP_HOME_FOLDER_NAME = "spaces.imap_home.name";
private static final String MSG_IMAP_HOME_FOLDER_DESCRIPTION = "spaces.imap_home.description";
private static final String INBOX_NAME = "INBOX";
private static final String INBOX_DECSRIPTION = "INBOX mail box";
private ImporterBootstrap importerBootstrap;
private MessageSource messageSource;
protected NodeRef companyHomeNodeRef;
protected Properties configuration;
protected NodeRef imapHomeNodeRef;
public void setImporterBootstrap(ImporterBootstrap importerBootstrap)
{
this.importerBootstrap = importerBootstrap;
}
public void setMessageSource(MessageSource messageSource)
{
this.messageSource = messageSource;
}
/**
* Ensure that required common properties have been set
*/
protected void checkCommonProperties() throws Exception
{
checkPropertyNotNull(importerBootstrap, "importerBootstrap");
checkPropertyNotNull(namespaceService, "namespaceService");
checkPropertyNotNull(searchService, "searchService");
checkPropertyNotNull(nodeService, "nodeService");
checkPropertyNotNull(messageSource, "messageSource");
}
/**
* Extracts pertinent references and properties that are common to execution of this and derived patches.
*/
protected void setUp() throws Exception
{
// get the node store that we must work against
StoreRef storeRef = importerBootstrap.getStoreRef();
if (storeRef == null)
{
throw new PatchException("Bootstrap store has not been set");
}
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
this.configuration = importerBootstrap.getConfiguration();
// get the association names that form the path
String companyHomeChildName = configuration.getProperty(PROPERTY_COMPANY_HOME_CHILDNAME);
if (companyHomeChildName == null || companyHomeChildName.length() == 0)
{
throw new PatchException("Bootstrap property '" + PROPERTY_COMPANY_HOME_CHILDNAME + "' is not present");
}
String imapHomeChildName = configuration.getProperty(PROPERTY_IMAP_HOME_FOLDER_CHILDNAME);
if (imapHomeChildName == null || imapHomeChildName.length() == 0)
{
throw new PatchException("Bootstrap property '" + PROPERTY_IMAP_HOME_FOLDER_CHILDNAME + "' is not present");
}
// build the search string to get the company home node
StringBuilder sb = new StringBuilder(256);
sb.append("/").append(companyHomeChildName);
String xpath = sb.toString();
// get the company home
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
if (nodeRefs.size() == 0)
{
throw new PatchException("XPath didn't return any results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath);
}
else if (nodeRefs.size() > 1)
{
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
}
this.companyHomeNodeRef = nodeRefs.get(0);
xpath = imapHomeChildName;
nodeRefs = searchService.selectNodes(companyHomeNodeRef, xpath, null, namespaceService, false);
if (nodeRefs.size() > 1)
{
throw new PatchException("XPath returned too many results: \n" + " dictionary node: " + companyHomeNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: "
+ nodeRefs);
}
else if (nodeRefs.size() == 0)
{
// the node does not exist
this.imapHomeNodeRef = null;
}
else
{
this.imapHomeNodeRef = nodeRefs.get(0);
}
}
@Override
protected String applyInternal() throws Exception
{
// properties must be set
checkCommonProperties();
// get useful values
setUp();
String msg = null;
if (imapHomeNodeRef == null)
{
// create it
createImapHomeFolders();
msg = I18NUtil.getMessage(MSG_CREATED, imapHomeNodeRef);
}
else
{
// it already exists
msg = I18NUtil.getMessage(MSG_EXISTS, imapHomeNodeRef);
}
// done
return msg;
}
private void createImapHomeFolders()
{
// get required properties
String imapHomeChildName = configuration.getProperty(PROPERTY_IMAP_HOME_FOLDER_CHILDNAME);
if (imapHomeChildName == null)
{
throw new PatchException("Bootstrap property '" + PROPERTY_IMAP_HOME_FOLDER_CHILDNAME + "' is not present");
}
String name = messageSource.getMessage(MSG_IMAP_HOME_FOLDER_NAME, null, I18NUtil.getLocale());
if (name == null || name.length() == 0)
{
throw new PatchException("Bootstrap property '" + MSG_IMAP_HOME_FOLDER_NAME + "' is not present");
}
String description = messageSource.getMessage(MSG_IMAP_HOME_FOLDER_DESCRIPTION, null, I18NUtil.getLocale());
if (description == null || description.length() == 0)
{
throw new PatchException("Bootstrap property '" + MSG_IMAP_HOME_FOLDER_DESCRIPTION + "' is not present");
}
imapHomeNodeRef = createSpace(companyHomeNodeRef, name, description, imapHomeChildName);
// Create IMAP Home and "INBOX" for each user
createImapUserHomes();
}
private void createImapUserHomes()
{
StringBuilder query = new StringBuilder(128);
query.append("@").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:userName:*");
SearchParameters params = new SearchParameters();
params.setLanguage(SearchService.LANGUAGE_LUCENE);
params.addStore(importerBootstrap.getStoreRef());
params.setQuery(query.toString());
ResultSet results = searchService.query(params);
List<NodeRef> people;
try
{
people = results.getNodeRefs();
}
finally
{
results.close();
}
for (NodeRef nodeRef : people)
{
String userName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME);
String desc = userName + " " + messageSource.getMessage(MSG_IMAP_HOME_FOLDER_NAME, null, I18NUtil.getLocale());
NodeRef userHome = createSpace(imapHomeNodeRef, userName, desc, userName);
// Create Inbox
createSpace(userHome, INBOX_NAME, INBOX_DECSRIPTION, INBOX_NAME);
}
}
private NodeRef createSpace(NodeRef parent, String name, String desc, String childName)
{
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(7);
properties.put(ContentModel.PROP_NAME, name);
properties.put(ContentModel.PROP_TITLE, name);
properties.put(ContentModel.PROP_DESCRIPTION, desc);
properties.put(ApplicationModel.PROP_ICON, PROPERTY_ICON);
// create the node
ChildAssociationRef childAssocRef = nodeService.createNode(parent, ContentModel.ASSOC_CONTAINS, QName.resolveToQName(namespaceService, childName),
ContentModel.TYPE_FOLDER, properties);
NodeRef result = childAssocRef.getChildRef();
// add the required aspects
nodeService.addAspect(result, ApplicationModel.ASPECT_UIFACETS, null);
return result;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap;
/**
* @author Mike Shavnev
*/
public interface AlfrescoImapConst
{
public static final char HIERARCHY_DELIMITER = '.';
public static final String NAMESPACE_PREFIX = "#";
public static final String USER_NAMESPACE = "#mail";
public static final String INBOX_NAME = "INBOX";
public static final String BODY_TEXT_PLAIN_NAME = "Body.txt";
public static final String BODY_TEXT_HTML_NAME = "Body.html";
public static final String MESSAGE_PREFIX = "Message_";
// Separator for user enties in flag and subscribe properties
public static final String USER_SEPARATOR = ";";
/**
* Defines {@link AlfrescoImapMailFolder} view mode as archive mode. Used for Email Archive View.
*/
public static final String MODE_ARCHIVE = "archive";
/**
* Defines {@link AlfrescoImapMailFolder} view mode as virtual mode. Used for IMAP Virtualised View.
*/
public static final String MODE_VIRTUAL = "virtual";
// Default content model email message templates
public static final String CLASSPATH_TEXT_PLAIN_TEMPLATE = "/alfresco/templates/imap/imap_message_text_plain.ftl";
public static final String CLASSPATH_TEXT_HTML_TEMPLATE = "/alfresco/templates/imap/imap_message_text_html.ftl";
public static final String DICTIONARY_TEMPLATE_PREFIX = "emailbody";
public static final String PREF_IMAP_FAVOURITE_SITES = "org.alfresco.share.sites.imap.favourites";
// AlfrescoImapMessage constants
public static final String MIME_VERSION = "MIME-Version";
public static final String CONTENT_TYPE = "Content-Type";
public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
public static final String MULTIPART_MIXED = "mixed";
public static final String CONTENT_ID = "Content-ID";
public static final String X_ALF_NODEREF_ID = "X-Alfresco-NodeRef-ID"; // The NodeRef id header
public static final String X_ALF_SERVER_UID = "X-Alfresco-Server-UID"; // The unique identifier of Alfresco server
public static final String EIGHT_BIT_ENCODING = "8bit";
public static final String BASE_64_ENCODING = "base64";
public static final String UTF_8 = "UTF-8";
}

View File

@@ -0,0 +1,958 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ImapModel;
import org.alfresco.repo.imap.config.ImapConfigElement.ImapConfig;
import org.alfresco.repo.imap.exception.AlfrescoImapFolderException;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.icegreen.greenmail.imap.AuthorizationException;
import com.icegreen.greenmail.imap.ImapHostManager;
import com.icegreen.greenmail.store.FolderException;
import com.icegreen.greenmail.store.MailFolder;
import com.icegreen.greenmail.user.GreenMailUser;
import com.icegreen.greenmail.util.GreenMailUtil;
/**
* @author Mike Shavnev
*/
public class AlfrescoImapHostManager implements ImapHostManager
{
private Log logger = LogFactory.getLog(AlfrescoImapHostManager.class);
private ServiceRegistry serviceRegistry;
private NodeService nodeService;
private FileFolderService fileFolderService;
private ImapHelper imapHelper;
/**
* Returns the hierarchy delimiter for mailboxes on this host.
*
* @return The hierarchy delimiter character.
*/
public char getHierarchyDelimiter()
{
return AlfrescoImapConst.HIERARCHY_DELIMITER;
}
/**
* Returns an collection of mailboxes. Method searches mailboxes under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email
* Archive Views. This method serves LIST command of the IMAP protocol.
*
* @param user User making the request
* @param mailboxPattern String name of a mailbox possible including a wildcard.
* @return Collection of mailboxes matching the pattern.
* @throws com.icegreen.greenmail.store.FolderException
*/
public Collection<MailFolder> listMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException
{
mailboxPattern = GreenMailUtil.convertFromUtf7(mailboxPattern);
if (logger.isDebugEnabled())
{
logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern);
}
mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern);
if (logger.isDebugEnabled())
{
logger.debug("Listing mailboxes: mailboxPattern in alfresco=" + mailboxPattern);
}
Collection<MailFolder> result = new LinkedList<MailFolder>();
Map<String, NodeRef> mountPoints = imapHelper.getMountPoints();
Map<String, ImapConfig> imapConfigs = imapHelper.getImapConfigs();
NodeRef mountPoint;
for (String mountPointName : mountPoints.keySet())
{
mountPoint = mountPoints.get(mountPointName);
FileInfo mountPointFileInfo = imapHelper.getFileFolderService().getFileInfo(mountPoint);
NodeRef mountParent = imapHelper.getNodeService().getParentAssocs(mountPoint).get(0).getParentRef();
String mode = imapConfigs.get(mountPointName).getMode();
if (!mailboxPattern.equals("*"))
{
mountPoint = mountParent;
}
boolean isVirtualView = imapConfigs.get(mountPointName).getMode().equals(AlfrescoImapConst.MODE_VIRTUAL);
Collection<FileInfo> folders = listFolder(mountPoint, user, mailboxPattern, isVirtualView);
if (folders != null)
{
for (FileInfo folder : folders)
{
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), folder, folder.getName(), mode, mountParent, mountPointName, imapHelper));
}
}
if (mailboxPattern.equals("*"))
{
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, mode, mountParent, mountPointName, imapHelper));
}
}
mountPoint = imapHelper.getUserImapHomeRef(user.getLogin());
Collection<FileInfo> imapFolders = listFolder(mountPoint, user, mailboxPattern, false);
if (imapFolders != null)
{
for (FileInfo imapFolder : imapFolders)
{
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), imapFolder, imapFolder.getName(), AlfrescoImapConst.MODE_ARCHIVE, mountPoint, null,
imapHelper));
}
}
return result;
}
private Collection<FileInfo> listFolder(NodeRef root, GreenMailUser user, String mailboxPattern, boolean isVirtualView) throws FolderException
{
if (logger.isDebugEnabled())
{
logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern);
}
Collection<FileInfo> result = new LinkedList<FileInfo>();
int index = mailboxPattern.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
String name = null;
String remainName = null;
if (index < 0)
{
name = mailboxPattern;
}
else
{
name = mailboxPattern.substring(0, index);
remainName = mailboxPattern.substring(index + 1);
}
if (logger.isDebugEnabled())
{
logger.debug("Listing mailboxes: name=" + name);
}
if (index < 0)
{
if ("*".equals(name))
{
List<FileInfo> list = imapHelper.searchFolders(root, name, true, isVirtualView);
if (list.size() > 0)
{
return list;
}
return null;
}
else if (name.endsWith("*"))
{
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
if (list.size() > 0)
{
result.addAll(list);
for (FileInfo fileInfo : list)
{
List<FileInfo> childList = imapHelper.searchFolders(fileInfo.getNodeRef(), "*", true, isVirtualView);
result.addAll(childList);
}
return result;
}
return null;
}
else if (name.contains("%") || name.contains("*"))
{
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
if (list.size() > 0)
{
return list;
}
return null;
}
else
{
List<FileInfo> list = imapHelper.searchFolders(root, name, false, isVirtualView);
if (list.size() > 0)
{
return list;
}
return null;
}
}
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
for (FileInfo folder : list)
{
Collection<FileInfo> childFolders = listFolder(folder.getNodeRef(), user, remainName, isVirtualView);
if (childFolders != null)
{
result.addAll(childFolders);
}
}
if (result.isEmpty())
{
return null;
}
return result;
}
/**
* Returns an collection of subscribed mailboxes. To appear in search result mailboxes should have {http://www.alfresco.org/model/imap/1.0}subscribed property specified for
* user. Method searches subscribed mailboxes under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email Archive Views. This
* method serves LSUB command of the IMAP protocol.
*
* @param user User making the request
* @param mailboxPattern String name of a mailbox possible including a wildcard.
* @return Collection of mailboxes matching the pattern.
* @throws com.icegreen.greenmail.store.FolderException
*/
public Collection<MailFolder> listSubscribedMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException
{
if (logger.isDebugEnabled())
{
logger.debug("Listing subscribed mailboxes: mailboxPattern=" + mailboxPattern);
}
mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern);
if (logger.isDebugEnabled())
{
logger.debug("Listing subscribed mailboxes: mailboxPattern in alfresco=" + mailboxPattern);
}
Collection<MailFolder> result = new LinkedList<MailFolder>();
Map<String, NodeRef> mountPoints = imapHelper.getMountPoints();
Map<String, ImapConfig> imapConfigs = imapHelper.getImapConfigs();
NodeRef mountPoint;
for (String mountPointName : mountPoints.keySet())
{
mountPoint = mountPoints.get(mountPointName);
FileInfo mountPointFileInfo = imapHelper.getFileFolderService().getFileInfo(mountPoint);
NodeRef mountParent = imapHelper.getNodeService().getParentAssocs(mountPoint).get(0).getParentRef();
String viewMode = imapConfigs.get(mountPointName).getMode();
if (!mailboxPattern.equals("*"))
{
mountPoint = mountParent;
}
boolean isVirtualView = imapConfigs.get(mountPointName).getMode().equals(AlfrescoImapConst.MODE_VIRTUAL);
Collection<MailFolder> folders = listSubscribedFolder(mountPoint, mountPoint, user, mailboxPattern, isVirtualView);
if (folders != null)
{
for (MailFolder mailFolder : folders)
{
AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) mailFolder;
folder.setMountPointName(mountPointName);
folder.setViewMode(viewMode);
folder.setMountParent(mountParent);
}
result.addAll(folders);
}
if (mailboxPattern.equals("*"))
{
if (isSubscribed(mountPointFileInfo, user.getLogin()))
{
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, imapHelper));
}
// \NoSelect
else if (hasSubscribedChild(mountPointFileInfo, user.getLogin(), isVirtualView))
{
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, imapHelper,
false));
}
}
}
NodeRef root = imapHelper.getSpacesStoreNodeRef();
root = imapHelper.getUserImapHomeRef(user.getLogin());
Collection<MailFolder> imapFolders = listSubscribedFolder(root, root, user, mailboxPattern, false);
if (imapFolders != null)
{
result.addAll(imapFolders);
}
return result;
}
private Collection<MailFolder> listSubscribedFolder(NodeRef mailboxRoot, NodeRef root, GreenMailUser user, String mailboxPattern, boolean isVirtualView) throws FolderException
{
if (logger.isDebugEnabled())
{
logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern);
}
int index = mailboxPattern.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
String name = null;
String remainName = null;
if (index < 0)
{
name = mailboxPattern;
}
else
{
name = mailboxPattern.substring(0, index);
remainName = mailboxPattern.substring(index + 1);
}
if (logger.isDebugEnabled())
{
logger.debug("Listing mailboxes: name=" + name);
}
if (index < 0)
{
if ("*".equals(name))
{
List<FileInfo> list = imapHelper.searchFolders(root, name, true, isVirtualView);
Collection<FileInfo> subscribedList = getSubscribed(list, user.getLogin());
if (subscribedList.size() > 0)
{
return createMailFolderList(user, subscribedList, mailboxRoot);
}
return null;
}
else if (name.endsWith("*"))
{
List<FileInfo> fullList = new LinkedList<FileInfo>();
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
Collection<FileInfo> subscribedList = getSubscribed(list, user.getLogin());
if (list.size() > 0)
{
fullList.addAll(subscribedList);
for (FileInfo fileInfo : list)
{
List<FileInfo> childList = imapHelper.searchFolders(fileInfo.getNodeRef(), "*", true, isVirtualView);
fullList.addAll(getSubscribed(childList, user.getLogin()));
}
return createMailFolderList(user, fullList, mailboxRoot);
}
return null;
}
else if ("%".equals(name))
{
List<FileInfo> list = imapHelper.searchFolders(root, "*", false, isVirtualView);
LinkedList<MailFolder> subscribedList = new LinkedList<MailFolder>();
for (FileInfo fileInfo : list)
{
if (isSubscribed(fileInfo, user.getLogin()))
{
// folderName, viewMode, mountPointName will be setted in listSubscribedMailboxes() method
subscribedList.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), fileInfo, null, null, mailboxRoot, null, imapHelper));
}
// \NoSelect
else if (hasSubscribedChild(fileInfo, user.getLogin(), isVirtualView))
{
// folderName, viewMode, mountPointName will be setted in listSubscribedMailboxes() method
subscribedList.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), fileInfo, null, null, mailboxRoot, null, imapHelper, false));
}
}
return subscribedList;
}
else if (name.contains("%") || name.contains("*"))
{
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
Collection<FileInfo> subscribedList = getSubscribed(list, user.getLogin());
if (subscribedList.size() > 0)
{
return createMailFolderList(user, subscribedList, mailboxRoot);
}
return null;
}
else
{
List<FileInfo> list = imapHelper.searchFolders(root, name, false, isVirtualView);
Collection<FileInfo> subscribedList = getSubscribed(list, user.getLogin());
if (subscribedList.size() > 0)
{
return createMailFolderList(user, subscribedList, mailboxRoot);
}
return null;
}
}
// If (index != -1) this is not the last level
Collection<MailFolder> result = new LinkedList<MailFolder>();
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
for (FileInfo folder : list)
{
Collection<MailFolder> childFolders = listSubscribedFolder(mailboxRoot, folder.getNodeRef(), user, remainName, isVirtualView);
if (childFolders != null)
{
result.addAll(childFolders);
}
}
if (result.isEmpty())
{
return null;
}
return result;
}
/**
* Renames an existing mailbox. The specified mailbox must already exist, the requested name must not exist already but must be able to be created and the user must have rights
* to delete the existing mailbox and create a mailbox with the new name. Any inferior hierarchical names must also be renamed. If INBOX is renamed, the contents of INBOX are
* transferred to a new mailbox with the new name, but INBOX is not deleted. If INBOX has inferior mailbox these are not renamed. This method serves RENAME command of the IMAP
* protocol. <p/> Method searches mailbox under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email Archive Views.
*
* @param user User making the request.
* @param oldMailboxName String name of the existing folder
* @param newMailboxName String target new name
* @throws com.icegreen.greenmail.store.FolderException if an existing folder with the new name.
* @throws AlfrescoImapFolderException if user does not have rights to create the new mailbox.
*/
public void renameMailbox(GreenMailUser user, String oldMailboxName, String newMailboxName) throws FolderException, AuthorizationException
{
oldMailboxName = GreenMailUtil.convertFromUtf7(oldMailboxName);
newMailboxName = GreenMailUtil.convertFromUtf7(newMailboxName);
if (logger.isDebugEnabled())
{
logger.debug("Renaming folder: oldMailboxName=" + oldMailboxName + " newMailboxName=" + newMailboxName);
}
AlfrescoImapMailFolder sourceNode = (AlfrescoImapMailFolder) getFolder(user, GreenMailUtil.convertInUtf7(oldMailboxName));
NodeRef root = imapHelper.getMailboxRootRef(oldMailboxName, user.getLogin());
String mailboxRepoName = imapHelper.getMailPathInRepo(newMailboxName);
StringTokenizer tokenizer = new StringTokenizer(mailboxRepoName, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER));
NodeRef parentNodeRef = root;
while (tokenizer.hasMoreTokens())
{
String folderName = tokenizer.nextToken();
if (!tokenizer.hasMoreTokens())
{
try
{
if (oldMailboxName.equalsIgnoreCase(AlfrescoImapConst.INBOX_NAME))
{
// If you trying to rename INBOX
// - just copy it to another folder with new name
// and leave INBOX (with children) intact.
fileFolderService.copy(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
List<FileInfo> itemsForRemove = fileFolderService.list(sourceNode.getFolderInfo().getNodeRef());
for (FileInfo fileInfo : itemsForRemove)
{
fileFolderService.delete(fileInfo.getNodeRef());
}
}
else
{
fileFolderService.move(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
}
return;
}
catch (FileExistsException e)
{
throw new FolderException(FolderException.ALREADY_EXISTS_LOCALLY);
}
catch (FileNotFoundException e)
{
if (logger.isDebugEnabled())
{
logger.error(e);
}
}
}
else
{
List<FileInfo> folders = imapHelper.searchFolders(parentNodeRef, folderName, false, true);
if (folders.size() == 0)
{
AccessStatus status = imapHelper.hasPermission(parentNodeRef, PermissionService.WRITE);
if (status == AccessStatus.DENIED)
{
if (logger.isDebugEnabled())
{
logger.debug("Creating folder: Cant't create folder - Permission denied");
}
throw new AlfrescoImapFolderException(AlfrescoImapFolderException.PERMISSION_DENIED);
}
if (logger.isDebugEnabled())
{
logger.debug("Create mailBox: " + folderName);
}
FileFolderServiceImpl.makeFolders(fileFolderService, parentNodeRef, Arrays.asList(folderName), ContentModel.TYPE_FOLDER);
}
else
{
parentNodeRef = folders.get(0).getNodeRef();
if (logger.isDebugEnabled())
{
logger.debug("MailBox: " + folderName + " already exists");
}
}
}
}
}
/**
* Returns a reference to a newly created mailbox. The request should specify a mailbox that does not already exist on this server, that could exist on this server and that the
* user has rights to create. This method serves CREATE command of the IMAP protocol.
*
* @param user User making the request.
* @param mailboxName String name of the target
* @return an Mailbox reference.
* @throws com.icegreen.greenmail.store.FolderException if mailbox already exists
* @throws AlfrescoImapFolderException if user does not have rights to create the new mailbox.
*/
public MailFolder createMailbox(GreenMailUser user, String mailboxName) throws AuthorizationException, FolderException
{
mailboxName = GreenMailUtil.convertFromUtf7(mailboxName);
if (logger.isDebugEnabled())
{
logger.debug("Creating folder: " + mailboxName);
}
NodeRef root = imapHelper.getMailboxRootRef(mailboxName, user.getLogin());
String mountPointName = imapHelper.getMountPointName(mailboxName);
String mailboxRepoNam = imapHelper.getMailPathInRepo(mailboxName);
StringTokenizer tokenizer = new StringTokenizer(mailboxRepoNam, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER));
NodeRef parentNodeRef = root;
while (tokenizer.hasMoreTokens())
{
String folderName = tokenizer.nextToken();
List<FileInfo> folders = imapHelper.searchFolders(parentNodeRef, folderName, false, true);
if (folders.size() == 0)
{
AccessStatus status = imapHelper.hasPermission(parentNodeRef, PermissionService.WRITE);
if (status == AccessStatus.DENIED)
{
if (logger.isDebugEnabled())
{
logger.debug("Creating folder: Cant't create folder - Permission denied");
}
throw new AlfrescoImapFolderException(AlfrescoImapFolderException.PERMISSION_DENIED);
}
if (logger.isDebugEnabled())
{
logger.debug("Create mailBox: " + mailboxName);
}
FileInfo mailFolder = FileFolderServiceImpl.makeFolders(fileFolderService, parentNodeRef, Arrays.asList(folderName), ContentModel.TYPE_FOLDER);
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mailFolder, folderName, imapHelper.getViewMode(mailboxName), root, mountPointName, imapHelper);
}
else
{
parentNodeRef = folders.get(0).getNodeRef();
if (logger.isDebugEnabled())
{
logger.debug("MailBox: " + folderName + " already exists");
}
}
}
throw new FolderException(FolderException.ALREADY_EXISTS_LOCALLY);
}
/**
* Deletes an existing MailBox. Specified mailbox must already exist on this server, and the user must have rights to delete it. <p/> This method serves DELETE command of the
* IMAP protocol.
*
* @param user User making the request.
* @param mailboxName String name of the target
* @throws com.icegreen.greenmail.store.FolderException if mailbox has a non-selectable store with children
*/
public void deleteMailbox(GreenMailUser user, String mailboxName) throws FolderException, AuthorizationException
{
AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) getFolder(user, mailboxName);
NodeRef nodeRef = folder.getFolderInfo().getNodeRef();
List<FileInfo> childFolders = imapHelper.searchFolders(nodeRef, "*", false, false);
if (childFolders.isEmpty())
{
folder.signalDeletion();
// Delete child folders and messages
fileFolderService.delete(nodeRef);
}
else
{
if (folder.isSelectable())
{
// Delete all messages for this folder
// Don't delete subfolders and their messages
List<FileInfo> messages = imapHelper.searchFiles(nodeRef, "*", ImapModel.TYPE_IMAP_CONTENT, false);
for (FileInfo message : messages)
{
fileFolderService.delete(message.getNodeRef());
}
nodeService.addAspect(nodeRef, ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE, null);
}
else
{
throw new FolderException(mailboxName + " - Can't delete a non-selectable store with children.");
}
}
}
/**
* Returns a reference to an existing Mailbox. The requested mailbox must already exists on this server and the requesting user must have at least lookup rights. <p/> It is
* also can be used by to obtain hierarchy delimiter by the LIST command: <p/> C: 2 list "" "" <p/> S: * LIST () "." "" <p/> S: 2 OK LIST completed. <p/> Method searches
* mailbox under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email Archive Views.
*
* @param user User making the request.
* @param mailboxName String name of the target.
* @return an Mailbox reference.
*/
public MailFolder getFolder(GreenMailUser user, String mailboxName)
{
mailboxName = GreenMailUtil.convertFromUtf7(mailboxName);
if (logger.isDebugEnabled())
{
logger.debug("Getting folder: " + mailboxName);
}
// If MailFolder object is used to obtain hierarchy delimiter by LIST command:
// Example:
// C: 2 list "" ""
// S: * LIST () "." ""
// S: 2 OK LIST completed.
if ("".equals(mailboxName))
{
if (logger.isDebugEnabled())
{
logger.debug("Request for the hierarchy delimiter");
}
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), null, null, null, null, null, null);
}
NodeRef root = imapHelper.getMailboxRootRef(mailboxName, user.getLogin());
String mountPointName = imapHelper.getMountPointName(mailboxName);
String mailboxRepoName = imapHelper.getMailPathInRepo(mailboxName);
StringTokenizer tokenizer = new StringTokenizer(mailboxRepoName, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER));
int count = tokenizer.countTokens();
NodeRef nodeRef = root;
while (tokenizer.hasMoreTokens())
{
String t = tokenizer.nextToken();
if (logger.isDebugEnabled())
{
logger.debug("token=" + t);
}
count--;
List<FileInfo> list = imapHelper.searchFolders(nodeRef, t, false, true);
if (count == 0)
{
if (!list.isEmpty())
{
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), list.get(0), list.get(0).getName(), imapHelper.getViewMode(mailboxName), root,
mountPointName, imapHelper);
}
else
{
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), null, null, null, null, null, null);
}
}
else
{
if (!list.isEmpty())
{
nodeRef = list.get(0).getNodeRef();
}
else
{
return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), null, null, null, null, null, null);
}
}
}
throw new IllegalStateException("Error state");
}
/**
* Simply calls {@link #getFolder(GreenMailUser, String)}. <p/> Added to implement {@link ImapHostManager}.
*/
public MailFolder getFolder(GreenMailUser user, String mailboxName, boolean mustExist) throws FolderException
{
return getFolder(user, mailboxName);
}
/**
* Returns a reference to the user's INBOX.
*
* @param user The user making the request.
* @return The user's Inbox.
*/
public MailFolder getInbox(GreenMailUser user) throws FolderException
{
return getFolder(user, AlfrescoImapConst.INBOX_NAME);
}
/**
* Not supported. May be used by GreenMailUser.create() method. <p/> Added to implement {@link ImapHostManager}.
*/
public void createPrivateMailAccount(GreenMailUser user) throws FolderException
{
throw new UnsupportedOperationException();
}
/**
* Subscribes a user to a mailbox. The mailbox must exist locally and the user must have rights to modify it. <p/> This method serves SUBSCRIBE command of the IMAP protocol.
*
* @param user User making the request
* @param mailbox String representation of a mailbox name.
*/
public void subscribe(final GreenMailUser user, final String mailbox) throws FolderException
{
if (logger.isDebugEnabled())
{
logger.debug("Subscribing: " + mailbox);
}
AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox);
nodeService.addAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED, null);
// This is a multiuser support. Commented due new requirements
// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
// {
// public Void doWork() throws Exception
// {
// AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox);
// FileInfo fileInfo = mailFolder.getFolderInfo();
// if (fileInfo != null)
// {
// String subscribedList = (String) nodeService.getProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED);
// if (subscribedList == null)
// {
// subscribedList = "";
// }
// subscribedList = subscribedList.replaceAll(imapHelper.formatUserEntry(user.getLogin()), "");
// subscribedList += imapHelper.formatUserEntry(user.getLogin());
// nodeService.setProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED, subscribedList);
// }
// else
// {
// logger.debug("MailBox: " + mailbox + "doesn't exsist. Maybe it was deleted earlier.");
// }
// return null;
// }
// }, AuthenticationUtil.getSystemUserName());
}
/**
* Unsubscribes from a given mailbox. <p/> This method serves UNSUBSCRIBE command of the IMAP protocol.
*
* @param user User making the request
* @param mailbox String representation of a mailbox name.
*/
public void unsubscribe(final GreenMailUser user, final String mailbox) throws FolderException
{
if (logger.isDebugEnabled())
{
logger.debug("Unsubscribing: " + mailbox);
}
AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox);
nodeService.removeAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED);
// This is a multiuser support. Commented due new requirements
// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
// {
// public Void doWork() throws Exception
// {
// AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox);
// if (mailFolder.getFolderInfo() != null)
// {
// FileInfo fileInfo = mailFolder.getFolderInfo();
// String subscribedList = (String) nodeService.getProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED);
// if (subscribedList == null)
// {
// subscribedList = "";
// }
// subscribedList = subscribedList.replaceAll(imapHelper.formatUserEntry(user.getLogin()), "");
// nodeService.setProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED, subscribedList);
// }
// else
// {
// logger.debug("MailBox: " + mailbox + " doesn't exsist. Maybe it was deleted earlier.");
// }
//
// return null;
// }
// }, AuthenticationUtil.getSystemUserName());
}
/**
* Not supported. Used by GreenMail class.
*/
public List getAllMessages()
{
throw new UnsupportedOperationException();
}
private boolean isSubscribed(FileInfo fileInfo, String userName)
{
return nodeService.hasAspect(fileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED);
// This is a multiuser support. Commented due new requirements
// Map<QName, Serializable> properties = fileInfo.getProperties();
// String subscribedList = (String) properties.get(ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED);
// if (subscribedList == null)
// {
// return false;
// }
// else
// {
// return subscribedList.contains(imapHelper.formatUserEntry(userName));
// }
}
private Collection<FileInfo> getSubscribed(List<FileInfo> list, String userName)
{
Collection<FileInfo> result = new LinkedList<FileInfo>();
for (FileInfo folderInfo : list)
{
if (isSubscribed(folderInfo, userName))
{
result.add(folderInfo);
}
}
return result;
}
private boolean hasSubscribedChild(FileInfo parent, String userName, boolean isVirtualView)
{
List<FileInfo> list = imapHelper.searchFolders(parent.getNodeRef(), "*", true, isVirtualView);
for (FileInfo fileInfo : list)
{
if (isSubscribed(fileInfo, userName))
{
return true;
}
}
return false;
}
private Collection<MailFolder> createMailFolderList(GreenMailUser user, Collection<FileInfo> list, NodeRef imapUserHomeRef)
{
Collection<MailFolder> result = new LinkedList<MailFolder>();
for (FileInfo folderInfo : list)
{
// folderName, viewMode, mountPointName will be setted in listSubscribedMailboxes() method
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), folderInfo, null, null, imapUserHomeRef, null, imapHelper));
}
return result;
}
// ----------------------Getters and Setters----------------------------
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
public ServiceRegistry getServiceRegistry()
{
return serviceRegistry;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
public void setImapHelper(ImapHelper imapHelper)
{
this.imapHelper = imapHelper;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,508 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap;
import static org.alfresco.repo.imap.AlfrescoImapConst.BASE_64_ENCODING;
import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_ID;
import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TRANSFER_ENCODING;
import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TYPE;
import static org.alfresco.repo.imap.AlfrescoImapConst.MIME_VERSION;
import static org.alfresco.repo.imap.AlfrescoImapConst.UTF_8;
import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_NODEREF_ID;
import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_SERVER_UID;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Address;
import javax.mail.Flags;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.ContentType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.util.ByteArrayDataSource;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ImapModel;
import org.alfresco.repo.imap.ImapHelper.EmailBodyType;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Extended MimeMessage to represent a content stored in the Alfresco repository.
*
* @author Arseny Kovalchuk
*/
public class AlfrescoImapMessage extends MimeMessage
{
/** Used if imapHelper.getDefaultFromAddress is not set */
private static final String DEFAULT_EMAIL_FROM = "alfresco@alfresco.org";
private static final String DEFAULT_EMAIL_TO = DEFAULT_EMAIL_FROM;
private static final String KOI8R_CHARSET = "koi8-r";
private static Log logger = LogFactory.getLog(AlfrescoImapMessage.class);
private ImapHelper imapHelper;
private FileInfo messageInfo;
/**
* Constructs {@link AlfrescoImapMessage} object.
*
* @param fileInfo - reference to the {@link FileInfo} object representing the message.
* @param imapHelper - reference to the {@link ImapHelper} object.
* @param generateBody - if {@code true} message body will be generated.
*
* @throws MessagingException if generation of the body fails.
*/
public AlfrescoImapMessage(FileInfo fileInfo, ImapHelper imapHelper, boolean generateBody) throws MessagingException
{
super(Session.getDefaultInstance(new Properties()));
this.messageInfo = fileInfo;
this.imapHelper = imapHelper;
if (generateBody)
{
setMessageHeaders();
buildMessage();
}
}
/**
* Constructs {@link AlfrescoImapMessage} object.
*
* @param fileInfo - reference to the {@link FileInfo} object representing the message.
* @param imapHelper - reference to the {@link ImapHelper} object.
* @param message - {@link MimeMessage}
* @throws MessagingException
*/
public AlfrescoImapMessage(FileInfo fileInfo, ImapHelper imapHelper, MimeMessage message) throws MessagingException
{
super(message);
this.messageInfo = fileInfo;
this.imapHelper = imapHelper;
setMessageHeaders();
final NodeRef nodeRef = fileInfo.getNodeRef();
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ImapModel.PROP_MESSAGE_FROM, InternetAddress.toString(message.getFrom()));
props.put(ImapModel.PROP_MESSAGE_TO, InternetAddress.toString(message.getRecipients(RecipientType.TO)));
props.put(ImapModel.PROP_MESSAGE_CC, InternetAddress.toString(message.getRecipients(RecipientType.CC)));
String[] subj = message.getHeader("Subject");
if (subj.length > 0)
{
props.put(ImapModel.PROP_MESSAGE_SUBJECT, subj[0]);
imapHelper.getNodeService().setProperty(nodeRef, ContentModel.PROP_DESCRIPTION, subj[0]);
}
Map<QName, Serializable> allprops = imapHelper.getNodeService().getProperties(fileInfo.getNodeRef());
allprops.putAll(props);
imapHelper.getNodeService().setProperties(nodeRef, allprops);
// setContent(buildMultipart(fileInfo)); - disabled for better performance.
}
/**
* Returns message flags.
*
* @return {@link Flags}
*/
@Override
public synchronized Flags getFlags()
{
return imapHelper.getFlags(messageInfo);
}
/**
* Sets message flags.
*
* @param flags - {@link Flags} object.
* @param value - flags value.
*/
@Override
public synchronized void setFlags(Flags flags, boolean value) throws MessagingException
{
imapHelper.setFlags(messageInfo, flags, value);
}
/**
* Returns {@link FileInfo} object representing message in Alfresco.
*
* @return reference to the {@link FileInfo} object.
*/
public FileInfo getMessageInfo()
{
return messageInfo;
}
private void setMessageHeaders() throws MessagingException
{
setHeader(MIME_VERSION, "1.0");
// Optional headers for further implementation of multiple Alfresco server support.
setHeader(X_ALF_NODEREF_ID, messageInfo.getNodeRef().getId());
setHeader(X_ALF_SERVER_UID, imapHelper.getAlfrescoServerUID());
}
/**
* This method builds MimeMessage based on either ImapModel or ContentModel type.
*
* @param fileInfo - Source file information {@link FileInfo}
* @throws MessagingException
*/
private void buildMessage() throws MessagingException
{
final NodeRef nodeRef = messageInfo.getNodeRef();
if (ImapModel.TYPE_IMAP_CONTENT.equals(imapHelper.getNodeService().getType(nodeRef)))
{
buildImapModelMessage();
}
else
{
buildContentModelMessage();
}
}
/**
* This method builds MimeMessage based on {@link ImapModel}
*
* @param fileInfo - Source file information {@link FileInfo}
* @throws MessagingException
*/
private void buildImapModelMessage() throws MessagingException
{
Map<QName, Serializable> properties = messageInfo.getProperties();
setSentDate(messageInfo.getModifiedDate());
String prop = (String) properties.get(ImapModel.PROP_MESSAGE_FROM);
addFromInternal(prop);
prop = (String) properties.get(ImapModel.PROP_MESSAGE_TO);
if (prop != null && prop.length() > 0)
{
addRecipients(RecipientType.TO, InternetAddress.parse(prop));
}
else
{
addRecipients(RecipientType.TO, DEFAULT_EMAIL_TO);
}
prop = (String) properties.get(ImapModel.PROP_MESSAGE_CC);
if (prop != null && prop.length() > 0)
{
addRecipients(RecipientType.CC, InternetAddress.parse(prop));
}
prop = (String) properties.get(ImapModel.PROP_MESSAGE_SUBJECT);
setSubject(prop == null ? messageInfo.getName() : prop);
setContent(buildImapModelMultipart());
}
/**
* This method builds {@link MimeMessage} based on {@link ContentModel}
*
* @param fileInfo - Source file information {@link FileInfo}
* @throws MessagingException
*/
private void buildContentModelMessage() throws MessagingException
{
Map<QName, Serializable> properties = messageInfo.getProperties();
String prop = null;
setSentDate(messageInfo.getModifiedDate());
// Add FROM address
Address[] addressList = buildSenderFromAddress(properties);
addFrom(addressList);
// Add TO address
addressList = buildRecipientToAddress();
addRecipients(RecipientType.TO, addressList);
prop = (String) properties.get(ContentModel.PROP_TITLE);
try
{
prop = (prop == null) ? MimeUtility.encodeText(messageInfo.getName(), KOI8R_CHARSET, null) : MimeUtility.encodeText(prop, KOI8R_CHARSET, null);
}
catch (UnsupportedEncodingException e)
{
// ignore
}
setSubject(prop);
setContent(buildContentModelMultipart());
}
/**
* This method builds {@link Multipart} based on {@link ContentModel}
*
* @param fileInfo - Source file information {@link FileInfo}
* @throws MessagingException
*/
private Multipart buildContentModelMultipart() throws MessagingException
{
MimeMultipart rootMultipart = new MimeMultipart("alternative");
// Cite MOB-395: "email agent will be used to select an appropriate template" - we are not able to
// detect an email agent so we use a default template for all messages.
// See AlfrescoImapConst to see the possible templates to use.
String bodyTxt = imapHelper.getEmailBodyText(messageInfo.getNodeRef(), EmailBodyType.TEXT_PLAIN);
rootMultipart.addBodyPart(getTextBodyPart(bodyTxt, EmailBodyType.TEXT_PLAIN.getSubtype()));
String bodyHtml = imapHelper.getEmailBodyText(messageInfo.getNodeRef(), EmailBodyType.TEXT_HTML);
rootMultipart.addBodyPart(getTextBodyPart(bodyHtml, EmailBodyType.TEXT_HTML.getSubtype()));
return rootMultipart;
}
private MimeBodyPart getTextBodyPart(String bodyText, String subtype) throws MessagingException
{
MimeBodyPart result = new MimeBodyPart();
result.setText(bodyText, UTF_8, subtype);
result.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING);
return result;
}
/**
* This method builds {@link Multipart} based on {@link ImapModel}
*
* @param fileInfo - Source file information {@link FileInfo}
* @throws MessagingException
*/
private Multipart buildImapModelMultipart() throws MessagingException
{
DataSource source = null;
String errorMessage = null;
// Root multipart - multipart/mixed
MimeMultipart rootMultipart = new MimeMultipart("mixed");
// Message body - multipart/alternative - consists of two parts: text/plain and text/html
MimeMultipart messageBody = new MimeMultipart("alternative");
// <------------------------ text html body part ------------------------>
List<FileInfo> bodyHtmls = imapHelper.searchFiles(messageInfo.getNodeRef(), "*.html", ImapModel.TYPE_IMAP_BODY, false);
ContentType contentType = null;
MimeBodyPart textHtmlBodyPart = null;
if (bodyHtmls != null && bodyHtmls.size() > 0)
{
textHtmlBodyPart = new MimeBodyPart();
FileInfo bodyHtml = bodyHtmls.get(0);
contentType = new ContentType(bodyHtml.getContentData().getMimetype());
ContentReader reader = imapHelper.getFileFolderService().getReader(bodyHtml.getNodeRef());
try
{
source = new ByteArrayDataSource(reader.getContentInputStream(), contentType.toString());
}
catch (IOException e)
{
logger.error(e);
errorMessage = e.getMessage();
}
if (source != null)
{
textHtmlBodyPart.setDataHandler(new DataHandler(source));
textHtmlBodyPart.addHeader(CONTENT_TYPE, bodyHtml.getContentData().getMimetype());
// textHtmlBodyPart.addHeader(CONTENT_TRANSFER_ENCODING, EIGHT_BIT_ENCODING);
textHtmlBodyPart.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING);
}
else
{
textHtmlBodyPart.setText(errorMessage, UTF_8);
}
messageBody.addBodyPart(textHtmlBodyPart);
}
// </------------------------ text html body part ------------------------>
// <------------------------ text plain body part ------------------------>
List<FileInfo> results = imapHelper.searchFiles(messageInfo.getNodeRef(), "*.txt", ImapModel.TYPE_IMAP_BODY, false);
MimeBodyPart textPlainBodyPart = null;
String text = null;
if (results != null && results.size() > 0)
{
textPlainBodyPart = new MimeBodyPart();
FileInfo bodyTxt = results.get(0);
text = imapHelper.getFileFolderService().getReader(bodyTxt.getNodeRef()).getContentString();
contentType = new ContentType(bodyTxt.getContentData().getMimetype());
}
else if (textHtmlBodyPart == null)
{
text = I18NUtil.getMessage("imap.server.info.message_body_not_found");
contentType = new ContentType(EmailBodyType.TEXT_PLAIN.getMimeType() + "; charset=UTF-8");
}
textPlainBodyPart.setText(text, contentType.getParameter("charset"), contentType.getSubType());
textPlainBodyPart.addHeader(CONTENT_TYPE, contentType.toString());
messageBody.addBodyPart(textPlainBodyPart);
// </------------------------ text plain body part ------------------------>
// Body part for multipart/alternative
MimeBodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(messageBody);
// Add multipart/alternative into root multipart/mixed...
rootMultipart.addBodyPart(messageBodyPart);
// Process attachments
List<FileInfo> attaches = imapHelper.searchFiles(messageInfo.getNodeRef(), "*", ImapModel.TYPE_IMAP_ATTACH, false);
for (FileInfo attach : attaches)
{
try
{
errorMessage = null;
messageBodyPart = new MimeBodyPart();
ContentReader reader = imapHelper.getFileFolderService().getReader(attach.getNodeRef());
source = new ByteArrayDataSource(reader.getContentInputStream(), attach.getContentData().getMimetype());
}
catch (IOException e)
{
logger.error(e);
errorMessage = e.getMessage();
}
if (source != null)
{
String attachID = (String) imapHelper.getNodeService().getProperty(attach.getNodeRef(), ImapModel.PROP_ATTACH_ID);
if (attachID != null)
{
messageBodyPart.addHeader(CONTENT_ID, attachID);
}
StringBuilder ct = new StringBuilder(attach.getContentData().getMimetype()).append("; name=\"").append(attach.getName()).append("\"");
messageBodyPart.addHeader(CONTENT_TYPE, ct.toString());
messageBodyPart.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING);
messageBodyPart.setDataHandler(new DataHandler(source));
try
{
messageBodyPart.setFileName(MimeUtility.encodeText(attach.getName(), KOI8R_CHARSET, null));
}
catch (UnsupportedEncodingException e)
{
// ignore
}
}
else
{
messageBodyPart.setText(errorMessage, UTF_8);
}
rootMultipart.addBodyPart(messageBodyPart);
}
return rootMultipart;
}
private void addFromInternal(String addressesString) throws MessagingException
{
if (addressesString != null)
{
addFrom(InternetAddress.parse(addressesString));
}
else
{
addFrom(new Address[] { new InternetAddress(DEFAULT_EMAIL_FROM) });
}
}
/**
* TODO USE CASE 2: "The To/addressee will be the first email alias found in the parent folders or a default one (TBD)". It seems to be more informative as alike
* {@code <user>@<current.domain>}...
*
* @return Generated TO address {@code <user>@<current.domain>}
* @throws AddressException
*/
private InternetAddress[] buildRecipientToAddress() throws AddressException
{
InternetAddress[] result = null;
String defaultEmailTo = null;
// TODO : search first email alias found in the parent folders
// if (found) defaultEmailTo = foundAlias
// else
final String escapedUserName = imapHelper.getCurrentUser().replaceAll("[/,\\,@]", ".");
final String userDomain = DEFAULT_EMAIL_TO.split("@")[1];
defaultEmailTo = escapedUserName + "@" + userDomain;
try
{
result = InternetAddress.parse(defaultEmailTo);
}
catch (AddressException e)
{
logger.error(String.format("Wrong email address '%s'.", defaultEmailTo), e);
result = InternetAddress.parse(DEFAULT_EMAIL_TO);
}
return result;
}
/**
* Builds the InternetAddress from the Content Author name if provided. If name not specified, it takes Content Creator name. If content creator does not exists, the default
* from address will be returned.
*
* @param contentAuthor The content author full name.
* @return Generated InternetAddress[] array.
* @throws AddressException
*/
private InternetAddress[] buildSenderFromAddress(Map<QName, Serializable> properties) throws AddressException
{
// Generate FROM address (Content author)
InternetAddress[] addressList = null;
String prop = (String) properties.get(ContentModel.PROP_AUTHOR);
String defaultFromAddress = imapHelper.getDefaultFromAddress();
defaultFromAddress = defaultFromAddress == null ? DEFAULT_EMAIL_FROM : defaultFromAddress;
try
{
if (prop != null)
{
StringBuilder contentAuthor = new StringBuilder();
contentAuthor.append("\"").append(prop).append("\" <").append(defaultFromAddress).append(">");
addressList = InternetAddress.parse(contentAuthor.toString());
}
else
{
prop = (String) properties.get(ContentModel.PROP_CREATOR);
if (prop != null)
{
StringBuilder creator = new StringBuilder();
creator.append("\"").append(prop).append("\" <").append(defaultFromAddress).append(">");
addressList = InternetAddress.parse(creator.toString());
}
else
{
throw new AddressException(I18NUtil.getMessage("imap.server.error.properties_dont_exist"));
}
}
}
catch (AddressException e)
{
addressList = InternetAddress.parse(DEFAULT_EMAIL_FROM);
}
return addressList;
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap;
import org.alfresco.util.AbstractLifecycleBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import com.icegreen.greenmail.Managers;
import com.icegreen.greenmail.imap.ImapHostManager;
import com.icegreen.greenmail.imap.ImapServer;
import com.icegreen.greenmail.user.UserManager;
import com.icegreen.greenmail.util.ServerSetup;
/**
* @author Mike Shavnev
*/
public class AlfrescoImapServer extends AbstractLifecycleBean
{
private static Log logger = LogFactory.getLog(AlfrescoImapServer.class);
private ImapServer serverImpl;
private int port = 143;
private ImapHostManager imapHostManager;
private UserManager imapUserManager;
private boolean imapServerEnabled;
private ImapHelper imapHelper;
public void setImapServerEnabled(boolean imapServerEnabled)
{
this.imapServerEnabled = imapServerEnabled;
}
public void setPort(int port)
{
this.port = port;
}
public void setImapHostManager(ImapHostManager imapHostManager)
{
this.imapHostManager = imapHostManager;
}
public void setImapUserManager(UserManager imapUserManager)
{
this.imapUserManager = imapUserManager;
}
public void setImapHelper(ImapHelper imapHelper)
{
this.imapHelper = imapHelper;
}
protected void onBootstrap(ApplicationEvent event)
{
if (imapServerEnabled && imapHelper.isPatchApplied())
{
Managers imapManagers = new Managers()
{
public ImapHostManager getImapHostManager()
{
return imapHostManager;
}
public UserManager getUserManager()
{
return imapUserManager;
}
};
serverImpl = new ImapServer(new ServerSetup(port, null, ServerSetup.PROTOCOL_IMAP), imapManagers);
serverImpl.startService(null);
if (logger.isInfoEnabled())
{
logger.info("IMAP service started on port " + this.port + ".");
}
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("IMAP service is disabled.");
}
}
}
protected void onShutdown(ApplicationEvent event)
{
if (serverImpl != null)
{
serverImpl.stopService(null);
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap;
import javax.mail.internet.MimeMessage;
import com.icegreen.greenmail.imap.ImapHostManager;
import com.icegreen.greenmail.mail.MovingMessage;
import com.icegreen.greenmail.user.GreenMailUser;
import com.icegreen.greenmail.user.UserException;
/**
* Alfresco implementation of the GreenMailUser interface.
*
* @author Arseny Kovalchuk
*/
public class AlfrescoImapUser implements GreenMailUser
{
private String userName;
private char[] password;
private String email;
private ImapHostManager imapHostManager;
public AlfrescoImapUser(String email, String login, String password, ImapHostManager imapHostManager)
{
this.email = email;
this.userName = login;
this.password = password.toCharArray();
this.imapHostManager = imapHostManager;
}
public void authenticate(String password) throws UserException
{
throw new UnsupportedOperationException();
// This method is used in the POP3 greenmail implementation, so it is disabled for IMAP
// See AlfrescoImapUserManager.test() method.
}
public void create() throws UserException
{
throw new UnsupportedOperationException();
}
public void delete() throws UserException
{
throw new UnsupportedOperationException();
}
public void deliver(MovingMessage msg) throws UserException
{
}
public void deliver(MimeMessage msg) throws UserException
{
}
public String getEmail()
{
return this.email;
}
public String getLogin()
{
return this.userName;
}
public String getPassword()
{
return new String(this.password);
}
public String getQualifiedMailboxName()
{
return userName;
}
public void setPassword(String password)
{
this.password = password.toCharArray();
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.icegreen.greenmail.imap.ImapHostManager;
import com.icegreen.greenmail.user.GreenMailUser;
import com.icegreen.greenmail.user.UserException;
import com.icegreen.greenmail.user.UserManager;
/**
* @author Arseny Kovalchuk
*/
public class AlfrescoImapUserManager extends UserManager
{
private Log logger = LogFactory.getLog(AlfrescoImapUserManager.class);
protected Map<String, GreenMailUser> userMap = Collections.synchronizedMap(new HashMap<String, GreenMailUser>());
protected ImapHostManager imapHostManager;
protected AuthenticationService authenticationService;
protected PersonService personService;
protected NodeService nodeService;
public AlfrescoImapUserManager()
{
super(null);
}
public AlfrescoImapUserManager(ImapHostManager imapHostManager)
{
this();
this.imapHostManager = imapHostManager;
}
public GreenMailUser createUser(String email, String login, String password) throws UserException
{
// TODO: User creation/addition code should be implemented here (in the AlfrescoImapUserManager).
// Following code is not need and not used in the current implementation.
GreenMailUser user = new AlfrescoImapUser(email, login, password, imapHostManager);
user.create();
addUser(user);
return user;
}
protected void addUser(GreenMailUser user)
{
userMap.put(user.getLogin(), user);
}
public GreenMailUser getUser(String login)
{
return (GreenMailUser) userMap.get(login);
}
public GreenMailUser getUserByEmail(String email)
{
GreenMailUser ret = getUser(email);
if (null == ret)
{
for (GreenMailUser user : userMap.values())
{
// TODO: NPE!
if (user.getEmail().trim().equalsIgnoreCase(email.trim()))
{
return user;
}
}
}
return ret;
}
public void deleteUser(GreenMailUser user) throws UserException
{
user = (GreenMailUser) userMap.remove(user.getLogin());
if (user != null)
{
user.delete();
}
}
/**
* The login method.
*
* @see com.icegreen.greenmail.imap.commands.LoginCommand#doProcess()
*/
public boolean test(String userid, String password)
{
try
{
authenticationService.authenticate(userid, password.toCharArray());
String email = null;
if (personService.personExists(userid))
{
NodeRef personNodeRef = personService.getPerson(userid);
email = (String) nodeService.getProperty(personNodeRef, ContentModel.PROP_EMAIL);
}
GreenMailUser user = new AlfrescoImapUser(email, userid, password, imapHostManager);
addUser(user);
}
catch (AuthenticationException ex)
{
logger.error("IMAP authentication failed for userid: " + userid);
return false;
}
return true;
}
public ImapHostManager getImapHostManager()
{
return this.imapHostManager;
}
public void setImapHostManager(ImapHostManager imapHostManager)
{
this.imapHostManager = imapHostManager;
}
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
}

View File

@@ -0,0 +1,980 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap;
import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_HTML_TEMPLATE;
import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_PLAIN_TEMPLATE;
import static org.alfresco.repo.imap.AlfrescoImapConst.DICTIONARY_TEMPLATE_PREFIX;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.mail.Flags;
import javax.mail.Flags.Flag;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigService;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ImapModel;
import org.alfresco.repo.admin.patch.PatchInfo;
import org.alfresco.repo.admin.patch.PatchService;
import org.alfresco.repo.imap.config.ImapConfigElement;
import org.alfresco.repo.imap.config.ImapConfigElement.ImapConfig;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.repo.template.TemplateNode;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.preference.PreferenceService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.AbstractLifecycleBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
/**
* Helper class to access repository services by IMAP components. Also contains a common helper methods to search and manage IMAP content and other usefull methods. Configured as
* {@code <bean id="imapHelper" class="org.alfresco.repo.imap.ImapHelper">} in the {@code imap-server-context.xml} file.
*
* @author Dmitry Vaserin
*/
public class ImapHelper extends AbstractLifecycleBean
{
private static Log logger = LogFactory.getLog(ImapHelper.class);
private static String PATCH_ID = "patch.imapFolders";
private NodeService nodeService;
private SearchService searchService;
private FileFolderService fileFolderService;
private TemplateService templateService;
private NamespaceService namespaceService;
private PermissionService permissionService;
private ConfigService configService;
private DictionaryService dictionaryService;
private PreferenceService preferenceService;
private SiteService siteService;
private ServiceRegistry serviceRegistry;
private PatchService patchService;
private String defaultFromAddress;
private String webApplicationContextUrl = "http://localhost:8080/alfresco";
private String repositoryTemplatePath;
private String imapRoot;
private NodeRef spacesStoreNodeRef;
private NodeRef companyHomeNodeRef;
private NodeRef imapRootNodeRef;
private boolean patchApplied = false;
private final static Map<QName, Flags.Flag> qNameToFlag;
private final static Map<Flags.Flag, QName> flagToQname;
static
{
qNameToFlag = new HashMap<QName, Flags.Flag>();
qNameToFlag.put(ImapModel.PROP_FLAG_ANSWERED, Flags.Flag.ANSWERED);
qNameToFlag.put(ImapModel.PROP_FLAG_DELETED, Flags.Flag.DELETED);
qNameToFlag.put(ImapModel.PROP_FLAG_DRAFT, Flags.Flag.DRAFT);
qNameToFlag.put(ImapModel.PROP_FLAG_SEEN, Flags.Flag.SEEN);
qNameToFlag.put(ImapModel.PROP_FLAG_RECENT, Flags.Flag.RECENT);
qNameToFlag.put(ImapModel.PROP_FLAG_FLAGGED, Flags.Flag.FLAGGED);
flagToQname = new HashMap<Flags.Flag, QName>();
flagToQname.put(Flags.Flag.ANSWERED, ImapModel.PROP_FLAG_ANSWERED);
flagToQname.put(Flags.Flag.DELETED, ImapModel.PROP_FLAG_DELETED);
flagToQname.put(Flags.Flag.DRAFT, ImapModel.PROP_FLAG_DRAFT);
flagToQname.put(Flags.Flag.SEEN, ImapModel.PROP_FLAG_SEEN);
flagToQname.put(Flags.Flag.RECENT, ImapModel.PROP_FLAG_RECENT);
flagToQname.put(Flags.Flag.FLAGGED, ImapModel.PROP_FLAG_FLAGGED);
}
public static enum EmailBodyType
{
TEXT_PLAIN, TEXT_HTML;
public String getSubtype()
{
return name().toLowerCase().substring(5);
}
public String getTypeSubtype()
{
return name().toLowerCase().replaceAll("_", "");
}
public String getMimeType()
{
return name().toLowerCase().replaceAll("_", "/");
}
}
@Override
protected void onShutdown(ApplicationEvent event)
{
// Do nothing
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
{
public Void doWork() throws Exception
{
List<PatchInfo> patches = getPatchService().getPatches(null, null);
for (PatchInfo patch : patches)
{
if (patch.getId().equals(PATCH_ID))
{
patchApplied = true;
break;
}
}
if (!patchApplied)
{
return null;
}
int indexOfStoreDelim = imapRoot.indexOf(StoreRef.URI_FILLER);
if (indexOfStoreDelim == -1)
{
throw new RuntimeException("Bad path format, " + StoreRef.URI_FILLER + " not found");
}
indexOfStoreDelim += StoreRef.URI_FILLER.length();
int indexOfPathDelim = imapRoot.indexOf("/", indexOfStoreDelim);
if (indexOfPathDelim == -1)
{
throw new java.lang.RuntimeException("Bad path format, / not found");
}
String storePath = imapRoot.substring(0, indexOfPathDelim);
String rootPathInStore = imapRoot.substring(indexOfPathDelim);
StoreRef storeRef = new StoreRef(storePath);
if (nodeService.exists(storeRef) == false)
{
throw new RuntimeException("No store for path: " + storeRef);
}
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPathInStore, null, namespaceService, false);
if (nodeRefs.size() > 1)
{
throw new RuntimeException("Multiple possible roots for : \n" + " root path: " + rootPathInStore + "\n" + " results: " + nodeRefs);
}
else if (nodeRefs.size() == 0)
{
throw new RuntimeException("No root found for : \n" + " root path: " + rootPathInStore);
}
imapRootNodeRef = nodeRefs.get(0);
// Get "Company Home" node reference
StoreRef store = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
ResultSet rs = searchService.query(store, SearchService.LANGUAGE_XPATH, "/app:company_home");
try
{
if (rs.length() == 0)
{
throw new AlfrescoRuntimeException("'Company Home' space doesn't exists.");
}
companyHomeNodeRef = rs.getNodeRef(0);
}
finally
{
rs.close();
}
spacesStoreNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
/**
* Search for files in specified context
*
* @param contextNodeRef context folder for search
* @param namePattern name pattern for search
* @param searchType type for search
* @param includeSubFolders include SubFolders
* @return list of files with specifed type
*/
public List<FileInfo> searchFiles(NodeRef contextNodeRef, String namePattern, QName searchType, boolean includeSubFolders)
{
return search(contextNodeRef, namePattern, searchType, true, false, includeSubFolders);
}
/**
* Search for mailboxes in specified context
*
* @param contextNodeRef context folder for search
* @param namePattern name pattern for search
* @param includeSubFolders include SubFolders
* @param isVirtualView is folder in "Virtual" View
* @return list of mailboxes
*/
public List<FileInfo> searchFolders(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders, boolean isVirtualView)
{
QName searchType = ContentModel.TYPE_FOLDER;
if (isVirtualView)
{
searchType = null;
}
List<FileInfo> result = search(contextNodeRef, namePattern, searchType, false, true, includeSubFolders);
if (isVirtualView)
{
List<SiteInfo> nonFavSites = getNonFavouriteSites(getCurrentUser());
for (SiteInfo siteInfo : nonFavSites)
{
FileInfo nonFavSite = fileFolderService.getFileInfo(siteInfo.getNodeRef());
List<FileInfo> siteChilds = search(nonFavSite.getNodeRef(), namePattern, null, false, true, true);
result.removeAll(siteChilds);
result.remove(nonFavSite);
}
}
else
{
// Remove folders from Sites
List<SiteInfo> sites = siteService.listSites(getCurrentUser());
for (SiteInfo siteInfo : sites)
{
List<FileInfo> siteChilds = search(siteInfo.getNodeRef(), namePattern, null, false, true, true);
result.removeAll(siteChilds);
}
}
return result;
}
/**
* Search for emails in specified folder depend on view mode.
*
* @param contextNodeRef context folder for search
* @param namePattern name pattern for search
* @param viewMode context folder view mode
* @param includeSubFolders includeSubFolders
* @return list of emails that context folder contains.
*/
public List<FileInfo> searchMails(NodeRef contextNodeRef, String namePattern, String viewMode, boolean includeSubFolders)
{
List<FileInfo> result = new LinkedList<FileInfo>();
if (viewMode.equals(AlfrescoImapConst.MODE_ARCHIVE))
{
result = search(contextNodeRef, namePattern, ImapModel.TYPE_IMAP_CONTENT, false, true, includeSubFolders);
}
else
{
if (viewMode.equals(AlfrescoImapConst.MODE_VIRTUAL))
{
result = search(contextNodeRef, namePattern, null, true, false, includeSubFolders);
}
}
return result;
}
private List<FileInfo> search(NodeRef contextNodeRef, String namePattern, QName searchType, boolean fileSearch, boolean folderSearch, boolean includeSubFolders)
{
List<FileInfo> result = new LinkedList<FileInfo>();
List<FileInfo> searchResult = fileFolderService.search(contextNodeRef, namePattern, fileSearch, folderSearch, includeSubFolders);
if (searchType == null)
{
return searchResult;
}
for (FileInfo fileInfo : searchResult)
{
if (nodeService.getType(fileInfo.getNodeRef()).equals(searchType))
{
result.add(fileInfo);
}
}
return result;
}
/**
* Get root reference for the specified mailbox
*
* @param mailboxName mailbox name in IMAP client.
* @param userName
* @return
*/
public NodeRef getMailboxRootRef(String mailboxName, String userName)
{
String rootFolder;
int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
if (index > 0)
{
rootFolder = mailboxName.substring(0, index);
}
else
{
rootFolder = mailboxName;
}
Map<String, ImapConfig> imapConfigs = getImapConfigs();
if (imapConfigs.keySet().contains(rootFolder))
{
Map<String, NodeRef> mountPoints = getMountPoints();
NodeRef mountRef = mountPoints.get(rootFolder);
return nodeService.getParentAssocs(mountRef).get(0).getParentRef();
}
else
{
return getUserImapHomeRef(userName);
}
}
/**
* @param userName user name
* @return user IMAP home reference
*/
public NodeRef getUserImapHomeRef(String userName)
{
return fileFolderService.searchSimple(imapRootNodeRef, userName);
}
public String getCurrentUser()
{
return AuthenticationUtil.getFullyAuthenticatedUser();
}
public String getUserImapHomeId(String userName)
{
return getUserImapHomeRef(userName).getId();
}
public NodeRef getImapRootNodeRef()
{
return imapRootNodeRef;
}
public NodeRef getCompanyHomeNodeRef()
{
return companyHomeNodeRef;
}
public NodeRef getSpacesStoreNodeRef()
{
return spacesStoreNodeRef;
}
public void setImapRoot(String imapRoot)
{
this.imapRoot = imapRoot;
}
public String getDefaultFromAddress()
{
return defaultFromAddress;
}
public void setDefaultFromAddress(String defaultFromAddress)
{
this.defaultFromAddress = defaultFromAddress;
}
public String getWebApplicationContextUrl()
{
return this.webApplicationContextUrl;
}
public void setWebApplicationContextUrl(String webApplicationContextUrl)
{
this.webApplicationContextUrl = webApplicationContextUrl;
}
public String getRepositoryTemplatePath()
{
return repositoryTemplatePath;
}
public void setRepositoryTemplatePath(String repositoryTemplatePath)
{
this.repositoryTemplatePath = repositoryTemplatePath;
}
/**
* Return flags that belong to the specified imap folder.
*
* @param messageInfo imap folder info.
* @return flags.
*/
public Flags getFlags(FileInfo messageInfo)
{
Flags flags = new Flags();
checkForFlaggableAspect(messageInfo.getNodeRef());
Map<QName, Serializable> props = nodeService.getProperties(messageInfo.getNodeRef());
for (QName key : qNameToFlag.keySet())
{
Boolean value = (Boolean) props.get(key);
if (value != null && value)
{
flags.add(qNameToFlag.get(key));
}
}
// This is a multiuser flag support. Commented due new requirements
// for (QName key : qNameToFlag.keySet())
// {
// if (key.equals(ImapModel.PROP_FLAG_DELETED))
// {
// Boolean value = (Boolean) props.get(key);
// if (value != null && value)
// {
// flags.add(qNameToFlag.get(key));
// }
// }
// else
// {
// String users = (String) props.get(key);
//
// if (users != null && users.indexOf(formatUserEntry(getCurrentUser())) >= 0)
// {
// flags.add(qNameToFlag.get(key));
// }
// }
// }
return flags;
}
/**
* Set flags to the specified imapFolder.
*
* @param messageInfo FileInfo of imap Folder.
* @param flags flags to set.
* @param value value to set.
*/
public void setFlags(FileInfo messageInfo, Flags flags, boolean value)
{
checkForFlaggableAspect(messageInfo.getNodeRef());
for (Flags.Flag flag : flags.getSystemFlags())
{
setFlag(messageInfo, flag, value);
}
}
/**
* Set flags to the specified imapFolder.
*
* @param messageInfo FileInfo of imap Folder
* @param flag flag to set.
* @param value value value to set.
*/
public void setFlag(final FileInfo messageInfo, final Flag flag, final boolean value)
{
checkForFlaggableAspect(messageInfo.getNodeRef());
nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), value);
// This is a multiuser flag support. Commented due new requirements
// if (flagToQname.get(flag).equals(ImapModel.PROP_FLAG_DELETED))
// {
// nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), value);
// }
// else
// {
// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
// {
// public Void doWork() throws Exception
// {
//
// String users = (String) nodeService.getProperty(messageInfo.getNodeRef(), flagToQname.get(flag));
// if (value)
// {
// if (users == null)
// {
// users = "";
// }
// users += formatUserEntry(getCurrentUser());
//
// }
// else if (users != null)
// {
// users = users.replace(formatUserEntry(getCurrentUser()), "");
//
// }
// nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), users);
// return null;
// }
// }, AuthenticationUtil.getSystemUserName());
// }
}
/**
* Check that the given authentication has a particular permission for the given node.
*
* @param nodeRef nodeRef of the node
* @param permission permission for check
* @return the access status
*/
public AccessStatus hasPermission(NodeRef nodeRef, String permission)
{
return permissionService.hasPermission(nodeRef, permission);
}
/**
* Change userName into following format ;userName;
*
* @param userName
* @return
*/
public String formatUserEntry(String userName)
{
return AlfrescoImapConst.USER_SEPARATOR + userName + AlfrescoImapConst.USER_SEPARATOR;
}
/**
* This method should returns a unique identifier of Alfresco server. The possible UID may be calculated based on IP address, Server port, MAC address, Web Application context.
* This UID should be parseable into initial components. This necessary for the implementation of the following case: If the message being copied (e.g. drag-and-drop) between
* two different Alfresco accounts in the IMAP client, we must unambiguously identify from which Alfresco server this message being copied. The message itself does not contain
* content data, so we must download it from the initial server (e.g. using download content servlet) and save it into destination repository.
*
* @return String representation of unique identifier of Alfresco server
*/
public String getAlfrescoServerUID()
{
// TODO Implement as javadoc says.
return "Not-Implemented";
}
/**
* Map of mount points. Name of mount point == key in the map.
*
* @return Map of mount points.
*/
public Map<String, NodeRef> getMountPoints()
{
Map<String, ImapConfig> imapConfigs = getImapConfigs();
Map<String, NodeRef> mountPoints = new HashMap<String, NodeRef>();
for (ImapConfig config : imapConfigs.values())
{
// Get node reference
StoreRef store = new StoreRef(config.getStore());
ResultSet rs = searchService.query(store, SearchService.LANGUAGE_XPATH, config.getRootPath());
if (rs.length() == 0)
{
logger.warn("Didn't find " + config.getName());
}
else
{
NodeRef nodeRef = rs.getNodeRef(0);
mountPoints.put(config.getName(), nodeRef);
}
rs.close();
}
return mountPoints;
}
/**
* Return map of imap configs. Name of config == key in the map
*
* @return map of imap configs.
*/
public Map<String, ImapConfig> getImapConfigs()
{
Config imapConfig = configService.getConfig("imapConfig");
ImapConfigElement imapConfigElement = (ImapConfigElement) imapConfig.getConfigElement(ImapConfigElement.CONFIG_ELEMENT_ID);
return imapConfigElement.getImapConfigs();
}
/**
* Return view mode ("virtual" or "archive") for specified mailbox.
*
* @param mailboxName name of the mailbox in IMAP client.
* @return view mode of the specified mailbox.
*/
public String getViewMode(String mailboxName)
{
String rootFolder;
int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
if (index > 0)
{
rootFolder = mailboxName.substring(0, index);
}
else
{
rootFolder = mailboxName;
}
Map<String, ImapConfig> imapConfigs = getImapConfigs();
if (imapConfigs.keySet().contains(rootFolder))
{
return imapConfigs.get(rootFolder).getMode();
}
else
{
return AlfrescoImapConst.MODE_ARCHIVE;
}
}
/**
* Return mount point name, which was specified in imap-config.xml for the current mailbox.
*
* @param mailboxName mailbox name in IMAP client.
* @return mount point name or null.
*/
public String getMountPointName(String mailboxName)
{
String rootFolder;
int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
if (index > 0)
{
rootFolder = mailboxName.substring(0, index);
}
else
{
rootFolder = mailboxName;
}
Map<String, ImapConfig> imapConfigs = getImapConfigs();
if (imapConfigs.keySet().contains(rootFolder))
{
return rootFolder;
}
else
{
return null;
}
}
/**
* Convert mailpath from IMAP client representation to the alfresco representation view. (e.g. with default settings "getMailPathInRepo(Repository_virtual.Imap Home)" will
* return "Company Home.Imap Home")
*
* @param mailPath mailbox path in IMAP client
* @return mailbox path in alfresco
*/
public String getMailPathInRepo(String mailPath)
{
String rootFolder;
String remain = "";
int index = mailPath.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER);
if (index > 0)
{
rootFolder = mailPath.substring(0, index);
remain = mailPath.substring(index);
}
else
{
rootFolder = mailPath;
}
Map<String, ImapConfig> imapConfigs = getImapConfigs();
if (imapConfigs.keySet().contains(rootFolder))
{
Map<String, NodeRef> mountPoints = getMountPoints();
NodeRef rootRef = mountPoints.get(rootFolder);
String rootName = nodeService.getProperty(rootRef, ContentModel.PROP_NAME).toString();
return rootName + remain;
}
else
{
return mailPath;
}
}
/**
* Return list of sites, that belong to the specified user and not marked as "Imap favourite"
*
* @param userName name of user
* @return List of nonFavourite sites.
*/
public List<SiteInfo> getNonFavouriteSites(String userName)
{
List<SiteInfo> nonFavSites = new LinkedList<SiteInfo>();
Map<String, Serializable> prefs = preferenceService.getPreferences(userName, AlfrescoImapConst.PREF_IMAP_FAVOURITE_SITES);
List<SiteInfo> sites = siteService.listSites(userName);
for (SiteInfo siteInfo : sites)
{
String key = AlfrescoImapConst.PREF_IMAP_FAVOURITE_SITES + "." + siteInfo.getShortName();
Boolean isImapFavourite = (Boolean) prefs.get(key);
if (isImapFavourite == null || !isImapFavourite)
{
nonFavSites.add(siteInfo);
}
}
return nonFavSites;
}
/**
* Returns the text representing email body for ContentModel node.
*
* @param nodeRef NodeRef of the target content.
* @param type The type of the returned body. May be the one of {@link EmailBodyType}.
* @return Text representing email body for ContentModel node.
*/
public String getEmailBodyText(NodeRef nodeRef, EmailBodyType type)
{
return templateService.processTemplate(getDefaultEmailBodyTemplate(type), createEmailTemplateModel(nodeRef));
}
/**
* Returns default email body template. This method trying to find a template on the path in the repository first e.g. {@code "Data Dictionary > IMAP Templates >"}. This path
* should be set as the property of the "imapHelper" bean. In this case it returns {@code NodeRef.toString()} of the template. If there are no template in the repository it
* returns a default template on the classpath.
*
* @param type One of the {@link EmailBodyType}.
* @return String representing template classpath path or NodeRef.toString().
*/
public String getDefaultEmailBodyTemplate(EmailBodyType type)
{
String result = null;
switch (type)
{
case TEXT_HTML:
result = CLASSPATH_TEXT_HTML_TEMPLATE;
break;
case TEXT_PLAIN:
result = CLASSPATH_TEXT_PLAIN_TEMPLATE;
break;
}
final StringBuilder templateName = new StringBuilder(DICTIONARY_TEMPLATE_PREFIX).append("-").append(type.getTypeSubtype()).append(".ftl");
int indexOfStoreDelim = repositoryTemplatePath.indexOf(StoreRef.URI_FILLER);
if (indexOfStoreDelim == -1)
{
logger.error("Bad path format, " + StoreRef.URI_FILLER + " not found");
return result;
}
indexOfStoreDelim += StoreRef.URI_FILLER.length();
int indexOfPathDelim = repositoryTemplatePath.indexOf("/", indexOfStoreDelim);
if (indexOfPathDelim == -1)
{
logger.error("Bad path format, / not found");
return result;
}
final String storePath = repositoryTemplatePath.substring(0, indexOfPathDelim);
final String rootPathInStore = repositoryTemplatePath.substring(indexOfPathDelim);
final String query = String.format("+PATH:\"%1$s/*\" +@cm\\:name:\"%2$s\"", rootPathInStore, templateName.toString());
if (logger.isDebugEnabled())
{
logger.debug("Using template path :" + repositoryTemplatePath + "/" + templateName);
logger.debug("Query: " + query);
}
StoreRef storeRef = new StoreRef(storePath);
ResultSet resultSet = searchService.query(storeRef, "lucene", query);
if (resultSet == null || resultSet.length() == 0)
{
logger.error(String.format("IMAP message template '%1$s' does not exist in the path '%2$s'.", templateName, repositoryTemplatePath));
return result;
}
result = resultSet.getNodeRef(0).toString();
return result;
}
/**
* Builds default email template model for TemplateProcessor
*
* @param ref NodeRef of the target content.
* @return Map that includes template model objects.
*/
private Map<String, Object> createEmailTemplateModel(NodeRef ref)
{
Map<String, Object> model = new HashMap<String, Object>(8, 1.0f);
TemplateNode tn = new TemplateNode(ref, serviceRegistry, null);
model.put("document", tn);
NodeRef parent = nodeService.getPrimaryParent(ref).getParentRef();
model.put("space", new TemplateNode(parent, serviceRegistry, null));
model.put("date", new Date());
model.put("contextUrl", new String(getWebApplicationContextUrl()));
model.put("alfTicket", new String(serviceRegistry.getAuthenticationService().getCurrentTicket()));
return model;
}
private void checkForFlaggableAspect(NodeRef nodeRef)
{
if (!nodeService.hasAspect(nodeRef, ImapModel.ASPECT_FLAGGABLE))
{
Map<QName, Serializable> aspectProperties = new HashMap<QName, Serializable>();
nodeService.addAspect(nodeRef, ImapModel.ASPECT_FLAGGABLE, aspectProperties);
}
}
public boolean isPatchApplied()
{
return patchApplied;
}
// ----------------------Getters and Setters----------------------------
public NodeService getNodeService()
{
return nodeService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public SearchService getSearchService()
{
return searchService;
}
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
public FileFolderService getFileFolderService()
{
return fileFolderService;
}
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
public TemplateService getTemplateService()
{
return templateService;
}
public void setTemplateService(TemplateService templateService)
{
this.templateService = templateService;
}
public NamespaceService getNamespaceService()
{
return namespaceService;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
public PermissionService getPermissionService()
{
return permissionService;
}
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
public ConfigService getConfigService()
{
return configService;
}
public void setConfigService(ConfigService configService)
{
this.configService = configService;
}
public DictionaryService getDictionaryService()
{
return dictionaryService;
}
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
public PreferenceService getPreferenceService()
{
return preferenceService;
}
public void setPreferenceService(PreferenceService preferenceService)
{
this.preferenceService = preferenceService;
}
public SiteService getSiteService()
{
return siteService;
}
public void setSiteService(SiteService siteService)
{
this.siteService = siteService;
}
public ServiceRegistry getServiceRegistry()
{
return serviceRegistry;
}
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
public PatchService getPatchService()
{
return patchService;
}
public void setPatchService(PatchService patchService)
{
this.patchService = patchService;
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap.config;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.element.ConfigElementAdapter;
public class ImapConfigElement extends ConfigElementAdapter
{
private static final long serialVersionUID = -6911139959296875159L;
public static final String CONFIG_ELEMENT_ID = "imapConfig";
private Map<String, ImapConfig> imapConfigs = new LinkedHashMap<String, ImapConfig>(8, 10f);
public ImapConfigElement()
{
super(CONFIG_ELEMENT_ID);
}
public ImapConfigElement(String name)
{
super(name);
}
@Override
public ConfigElement combine(ConfigElement configElement)
{
ImapConfigElement combined = new ImapConfigElement();
// add all the imapConfigs from this element
for (ImapConfig imapConfig : getImapConfigs().values())
{
combined.addImapConfig(imapConfig);
}
// add all the imapConfigs from the given element
for (ImapConfig imapConfig : ((ImapConfigElement) configElement).getImapConfigs().values())
{
combined.addImapConfig(imapConfig);
}
return combined;
}
public Map<String, ImapConfig> getImapConfigs()
{
return imapConfigs;
}
public ImapConfig getImapConfig(String name)
{
return imapConfigs.get(name);
}
void addImapConfig(ImapConfig imapConfig)
{
imapConfigs.put(imapConfig.getName(), imapConfig);
}
public static class ImapConfig implements Serializable
{
private static final long serialVersionUID = 424330549937129149L;
private String name;
private String mode;
private String store;
private String rootPath;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getMode()
{
return mode;
}
public void setMode(String mode)
{
this.mode = mode;
}
public String getStore()
{
return store;
}
public void setStore(String store)
{
this.store = store;
}
public String getRootPath()
{
return rootPath;
}
public void setRootPath(String rootPath)
{
this.rootPath = rootPath;
}
public static long getSerialVersionUID()
{
return serialVersionUID;
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap.config;
import java.util.Iterator;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigException;
import org.alfresco.config.xml.elementreader.ConfigElementReader;
import org.alfresco.repo.imap.config.ImapConfigElement.ImapConfig;
import org.dom4j.Element;
public class ImapElementReader implements ConfigElementReader
{
private static final String ELEMENT_IMAP_CONFIG = "imapConfig";
private static final String ELEMENT_IMAP = "imap";
private static final String ELEMENT_STORE = "store";
private static final String ELEMENT_ROOTPATH = "rootPath";
private static final String ATTR_NAME = "name";
private static final String ATTR_MODE = "mode";
public ConfigElement parse(Element element)
{
ImapConfigElement configElement = null;
if (element != null)
{
String elementName = element.getName();
if (elementName.equals(ELEMENT_IMAP_CONFIG) == false)
{
throw new ConfigException("ImapElementReader can parse '" + ELEMENT_IMAP_CONFIG + "' elements only, the element passed is '" + elementName + "'");
}
configElement = new ImapConfigElement();
for (Iterator<Element> items = element.elementIterator(ELEMENT_IMAP); items.hasNext();)
{
Element item = items.next();
String name = item.attributeValue(ATTR_NAME);
String mode = item.attributeValue(ATTR_MODE);
String store = item.element(ELEMENT_STORE).getStringValue();
String rootPath = item.element(ELEMENT_ROOTPATH).getStringValue();
ImapConfig imapConfig = new ImapConfig();
imapConfig.setName(name);
imapConfig.setMode(mode);
imapConfig.setStore(store);
imapConfig.setRootPath(rootPath);
configElement.addImapConfig(imapConfig);
}
}
return configElement;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.imap.exception;
import com.icegreen.greenmail.store.FolderException;
/**
* Thrown on an inappropriate attempt to modify a folder.
*
* @author Ivan Rybnikov
*/
public class AlfrescoImapFolderException extends FolderException
{
private static final long serialVersionUID = -2721708848846740336L;
public final static String PERMISSION_DENIED = "Can't create folder - Permission denied";
public AlfrescoImapFolderException(String message)
{
super(message);
}
}

View File

@@ -48,6 +48,8 @@ import org.dom4j.io.SAXReader;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import com.werken.saxpath.XPathReader;
/**
* Search component for use by the ScriptService.
* <p>
@@ -215,6 +217,26 @@ public final class Search extends BaseScopableProcessorExtension
}
}
/**
* Validation Xpath query
*
* @param query xpath query
* @return true if xpath query valid
*/
public boolean isValidXpathQuery(String query)
{
try
{
XPathReader reader = new XPathReader();
reader.parse(query);
}
catch (Exception e)
{
return false;
}
return true;
}
/**
* Execute a Lucene search
*