Merge IMAP in

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14947 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2009-06-26 08:48:25 +00:00
parent 3421f84fdc
commit 5500f162b4
58 changed files with 7451 additions and 5171 deletions

View File

@@ -35,14 +35,18 @@ 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_NONSUBSCRIBED = QName.createQName(IMAP_MODEL_1_0_URI, "nonSubscribed");
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 ASPECT_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 PROP_MESSAGE_ID = QName.createQName(IMAP_MODEL_1_0_URI, "messageId");
static final QName PROP_THREAD_INDEX = QName.createQName(IMAP_MODEL_1_0_URI, "threadIndex");
static final QName ASSOC_IMAP_ATTACHMENT = QName.createQName(IMAP_MODEL_1_0_URI, "attachment");
static final QName ASSOC_IMAP_ATTACHMENTS_FOLDER = QName.createQName(IMAP_MODEL_1_0_URI, "attachmentsFolder");
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");

View File

@@ -1,257 +0,0 @@
/*
* 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

@@ -79,6 +79,7 @@ public class MimetypeMap implements MimetypeService
public static final String MIMETYPE_ATOM = "application/atom+xml";
public static final String MIMETYPE_RSS = "application/rss+xml";
public static final String MIMETYPE_RFC822 = "message/rfc822";
public static final String MIMETYPE_OUTLOOK_MSG = "application/vnd.ms-outlook";
// Open Document
public static final String MIMETYPE_OPENDOCUMENT_TEXT = "application/vnd.oasis.opendocument.text";
public static final String MIMETYPE_OPENDOCUMENT_TEXT_TEMPLATE = "application/vnd.oasis.opendocument.text-template";

View File

@@ -0,0 +1,118 @@
/*
* 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.content.transform;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.TransformationOptions;
public class EMLTransformer extends AbstractContentTransformer2
{
public boolean isTransformable(String sourceMimetype, String targetMimetype, TransformationOptions options)
{
if (!MimetypeMap.MIMETYPE_RFC822.equals(sourceMimetype) || !MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(targetMimetype))
{
// only support RFC822 -> TEXT
return false;
}
else
{
return true;
}
}
@Override
protected void transformInternal(ContentReader reader, ContentWriter writer, TransformationOptions options) throws Exception
{
InputStream is = null;
try
{
is = reader.getContentInputStream();
MimeMessage mimeMessage = new MimeMessage(Session.getDefaultInstance(new Properties()), is);
final StringBuilder sb = new StringBuilder();
Object content = mimeMessage.getContent();
if (content instanceof Multipart)
{
Multipart multipart = (Multipart) content;
Part part = multipart.getBodyPart(0);
if (part.getContent() instanceof Multipart)
{
multipart = (Multipart) part.getContent();
for (int i = 0, n = multipart.getCount(); i < n; i++)
{
part = multipart.getBodyPart(i);
if (part.getContentType().contains("text"))
{
sb.append(part.getContent().toString()).append("\n");
}
}
}
else if (part.getContentType().contains("text"))
{
sb.append(part.getContent().toString());
}
}
else
{
sb.append(content.toString());
}
writer.putContent(sb.toString());
}
finally
{
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}

View File

@@ -56,7 +56,7 @@ public class MailContentTransformer extends AbstractContentTransformer2
*/
public boolean isTransformable(String sourceMimetype, String targetMimetype, TransformationOptions options)
{
if (!MimetypeMap.MIMETYPE_RFC822.equals(sourceMimetype) ||
if (!MimetypeMap.MIMETYPE_OUTLOOK_MSG.equals(sourceMimetype) ||
!MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(targetMimetype))
{
// only support MSG -> TEXT

View File

@@ -0,0 +1,695 @@
/*
* 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.Date;
import java.util.LinkedList;
import java.util.List;
import javax.mail.Flags;
import javax.mail.internet.MimeMessage;
import javax.mail.search.SearchTerm;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import com.icegreen.greenmail.foedus.util.MsgRangeFilter;
import com.icegreen.greenmail.mail.MovingMessage;
import com.icegreen.greenmail.store.FolderException;
import com.icegreen.greenmail.store.FolderListener;
import com.icegreen.greenmail.store.MailFolder;
import com.icegreen.greenmail.store.SimpleStoredMessage;
/**
* Implementation of greenmail MailFolder. It represents an Alfresco content folder and handles
* appendMessage, copyMessage, expunge (delete), getMessages, getMessage and so requests.
*
* @author Ivan Rybnikov
*/
public abstract class AbstractImapFolder implements MailFolder
{
private List<FolderListener> listeners = new LinkedList<FolderListener>();
protected ServiceRegistry serviceRegistry;
protected static int MAX_RETRIES = 1;
public AbstractImapFolder(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
/**
* Method that checks mandatory parameter.
* @param The parameter instance to check.
* @param The name of the parameter.
*/
protected void checkParameter(Object parameter, String name)
{
if (parameter == null)
{
throw new IllegalArgumentException(name + " parameter is null.");
}
}
/**
* Appends message to the folder.
*
* @param message - message.
* @param flags - message flags.
* @param internalDate - not used. Current date used instead.
* @return
*/
public long appendMessage(final MimeMessage message, final Flags flags, final Date internalDate) throws FolderException
{
if (isReadOnly())
{
throw new FolderException("Can't append message - Permission denied");
}
CommandCallback<Long> command = new CommandCallback<Long>()
{
public Long command() throws Throwable
{
return appendMessageInternal(message, flags, internalDate);
}
};
return command.runFeedback();
}
/**
* Copies message with the given UID to the specified {@link MailFolder}.
*
* @param uid - UID of the message
* @param toFolder - reference to the destination folder.
*/
public void copyMessage(final long uid, final MailFolder toFolder) throws FolderException
{
AbstractImapFolder toImapMailFolder = (AbstractImapFolder) toFolder;
if (toImapMailFolder.isReadOnly())
{
throw new FolderException("Can't create folder - Permission denied");
}
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
copyMessageInternal(uid, toFolder);
return null;
}
};
command.runFeedback();
}
/**
* Marks all messages in the folder as deleted using {@link Flags.Flag#DELETED} flag.
*/
public void deleteAllMessages() throws FolderException
{
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
deleteAllMessagesInternal();
return null;
}
};
command.runFeedback();
}
/**
* Deletes messages marked with {@link Flags.Flag#DELETED}. Note that this message deletes all messages with this flag.
*/
public void expunge() throws FolderException
{
if (isReadOnly())
{
throw new FolderException("Can't expunge - Permission denied");
}
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
expungeInternal();
return null;
}
};
command.runFeedback();
}
/**
* Returns the number of the first unseen message.
*
* @return Number of the first unseen message.
*/
public int getFirstUnseen()
{
return getFirstUnseenInternal();
}
/**
* Returns full name of the folder with namespace and full path delimited with the hierarchy delimiter
* (see {@link AlfrescoImapConst#HIERARCHY_DELIMITER})
* <p/> E.g.: <br/>
* #mail.admin."Repository_archive.Data Dictionary.Space Templates.Software Engineering Project" <br/>
* This is required by GreenMail implementation.
*/
public String getFullName()
{
CommandCallback<String> command = new CommandCallback<String>()
{
public String command() throws Throwable
{
return getFullNameInternal();
}
};
return command.run();
}
/**
* Returns message by its UID.
*
* @param uid - UID of the message.
* @return message.
*/
public SimpleStoredMessage getMessage(final long uid)
{
CommandCallback<SimpleStoredMessage> command = new CommandCallback<SimpleStoredMessage>()
{
public SimpleStoredMessage command() throws Throwable
{
return getMessageInternal(uid);
}
};
return command.run();
}
/**
* Returns count of the messages in the folder.
*
* @return Count of the messages.
*/
public int getMessageCount()
{
CommandCallback<Integer> command = new CommandCallback<Integer>()
{
public Integer command() throws Throwable
{
return getMessageCountInternal();
}
};
return command.run();
}
/**
* Returns list of all messages in the folder.
*
* @return list of {@link SimpleStoredMessage} objects.
*/
public List<SimpleStoredMessage> getMessages()
{
CommandCallback<List<SimpleStoredMessage>> command = new CommandCallback<List<SimpleStoredMessage>>()
{
public List<SimpleStoredMessage> command() throws Throwable
{
return getMessagesInternal();
}
};
return command.run();
}
/**
* Returns list of messages by filter.
*
* @param msgRangeFilter - {@link MsgRangeFilter} object representing filter.
* @return list of filtered messages.
*/
public List<SimpleStoredMessage> getMessages(final MsgRangeFilter msgRangeFilter)
{
CommandCallback <List<SimpleStoredMessage>> command = new CommandCallback <List<SimpleStoredMessage>>()
{
public List<SimpleStoredMessage> command() throws Throwable
{
return getMessagesInternal(msgRangeFilter);
}
};
return command.run();
}
/**
* Returns message sequence number in the folder by its UID.
*
* @param uid - message UID.
* @return message sequence number.
* @throws FolderException if no message with given UID.
*/
public int getMsn(final long uid) throws FolderException
{
CommandCallback<Integer> command = new CommandCallback<Integer>()
{
public Integer command() throws Throwable
{
return getMsnInternal(uid);
}
};
return command.runFeedback(true);
}
/**
* Returns folder name.
*
* @return folder name.
*/
public String getName()
{
CommandCallback<String> command = new CommandCallback<String>()
{
public String command() throws Throwable
{
return getNameInternal();
}
};
return command.run();
}
/**
* Returns UIDs of all messages in the folder.
*
* @return UIDS of the messages.
*/
public long[] getMessageUids()
{
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
return getMessageUidsInternal();
}
};
return (long[])command.run();
}
/**
* Returns the list of messages that have no {@link Flags.Flag#DELETED} flag set for current user.
*
* @return the list of non-deleted messages.
*/
public List<SimpleStoredMessage> getNonDeletedMessages()
{
CommandCallback <List<SimpleStoredMessage>> command = new CommandCallback<List<SimpleStoredMessage>>()
{
public List<SimpleStoredMessage> command() throws Throwable
{
return getNonDeletedMessagesInternal();
}
};
List<SimpleStoredMessage> result = (List<SimpleStoredMessage>)command.run();
return result;
}
/**
* Returns permanent flags.
*
* @return {@link Flags} object containing flags.
*/
public Flags getPermanentFlags()
{
CommandCallback<Flags> command = new CommandCallback<Flags>()
{
public Flags command() throws Throwable
{
return getPermanentFlagsInternal();
}
};
return command.run(true);
}
/**
* Returns count of messages with {@link Flags.Flag#RECENT} flag. If {@code reset} parameter is {@code true} -
* removes {@link Flags.Flag#RECENT} flag from the message for current user.
*
* @param reset - if true the {@link Flags.Flag#RECENT} will be deleted for current user if exists.
* @return returns count of recent messages.
*/
public int getRecentCount(final boolean reset)
{
CommandCallback<Integer> command = new CommandCallback<Integer>()
{
public Integer command() throws Throwable
{
return getRecentCountInternal(reset);
}
};
return command.run(true);
}
/**
* Returns UIDNEXT value of the folder.
*
* @return UIDNEXT value.
*/
public long getUidNext()
{
CommandCallback<Long> command = new CommandCallback<Long>()
{
public Long command() throws Throwable
{
return getUidNextInternal();
}
};
return command.run(true);
}
/**
* Returns UIDVALIDITY value of the folder.
*
* @return UIDVALIDITY value.
*/
public long getUidValidity()
{
CommandCallback<Long> command = new CommandCallback<Long>()
{
public Long command() throws Throwable
{
return getUidValidityInternal();
}
};
return command.run(true);
}
/**
* Returns count of the messages with {@link Flags.Flag#SEEN} in the folder for the current user.
*
* @return Count of the unseen messages for current user.
*/
public int getUnseenCount()
{
CommandCallback<Integer> command = new CommandCallback<Integer>()
{
public Integer command() throws Throwable
{
return getUnseenCountInternal();
}
};
return command.run();
}
/**
* Whether the folder is selectable.
*
* @return {@code boolean}.
*/
public boolean isSelectable()
{
CommandCallback<Boolean> command = new CommandCallback<Boolean>()
{
public Boolean command() throws Throwable
{
return isSelectableInternal();
}
};
return command.run(true);
}
/**
* Replaces flags for the message with the given UID. If {@code addUid} is set to
* {@code true} {@link FolderListener} objects defined for this folder will be notified.
* {@code silentListener} can be provided - this listener wouldn't be notified.
*
* @param flags - new flags.
* @param uid - message UID.
* @param silentListener - listener that shouldn't be notified.
* @param addUid - defines whether or not listeners be notified.
*/
public void replaceFlags(final Flags flags, final long uid, final FolderListener silentListener, final boolean addUid) throws FolderException
{
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
replaceFlagsInternal(flags, uid, silentListener, addUid);
return null;
}
};
command.runFeedback(true);
}
/**
* Simply returns UIDs of all messages in the folder.
*
* @param searchTerm - not used
* @return UIDs of the messages
*/
public long[] search(SearchTerm searchTerm)
{
return getMessageUids();
}
/**
* Sets flags for the message with the given UID. If {@code addUid} is set to {@code true}
* {@link FolderListener} objects defined for this folder will be notified.
* {@code silentListener} can be provided - this listener wouldn't be notified.
*
* @param flags - new flags.
* @param value - flags value.
* @param uid - message UID.
* @param silentListener - listener that shouldn't be notified.
* @param addUid - defines whether or not listeners be notified.
*/
public void setFlags(
final Flags flags,
final boolean value,
final long uid,
final FolderListener silentListener,
final boolean addUid)
throws FolderException
{
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
setFlagsInternal(flags, value, uid, silentListener, addUid);
return null;
}
};
command.runFeedback();
}
/**
* Not supported. Added to implement {@link MailFolder#store(MovingMessage)}.
*/
public void store(MovingMessage mail) throws Exception
{
throw new UnsupportedOperationException("Method store(MovingMessage) is not suppoted.");
}
/**
* Not supported. Added to implement {@link MailFolder#store(MimeMessage)}.
*/
public void store(MimeMessage message) throws Exception
{
throw new UnsupportedOperationException("Method store(MimeMessage) is not suppoted.");
}
/**
* Adds {@link FolderListener} to the folder.
*
* @param listener - new listener.
*/
public void addListener(FolderListener listener)
{
listeners.add(listener);
}
/**
* Removes {@link FolderListener} from the folder.
*
* @param listener - Listener to remove.
*/
public void removeListener(FolderListener listener)
{
listeners.remove(listener);
}
/**
* Method is called before the deletion of the folder. Notifies {@link FolderListener} objects with
* {@link FolderListener#mailboxDeleted()} method calls.
*/
public void signalDeletion()
{
synchronized (listeners)
{
for (int i = 0; i < listeners.size(); i++)
{
FolderListener listener = (FolderListener) listeners.get(i);
listener.mailboxDeleted();
}
}
}
protected void notifyFlagUpdate(int msn, Flags flags, Long uidNotification, FolderListener silentListener)
{
synchronized (listeners)
{
for (FolderListener listener : listeners)
{
if (listener == silentListener)
{
continue;
}
listener.flagsUpdated(msn, flags, uidNotification);
}
}
}
protected abstract boolean isReadOnly();
protected abstract long appendMessageInternal(MimeMessage message, Flags flags, Date internalDate) throws Exception;
protected abstract void copyMessageInternal(long uid, MailFolder toFolder) throws Exception;
protected abstract void deleteAllMessagesInternal() throws Exception;
protected abstract void expungeInternal() throws Exception;
protected abstract int getFirstUnseenInternal();
protected abstract String getFullNameInternal() throws Exception;
protected abstract SimpleStoredMessage getMessageInternal(long uid) throws Exception;
protected abstract int getMessageCountInternal();
protected abstract List<SimpleStoredMessage> getMessagesInternal();
protected abstract List<SimpleStoredMessage> getMessagesInternal(MsgRangeFilter msgRangeFilter);
protected abstract int getMsnInternal(long uid) throws Exception;
protected abstract String getNameInternal();
protected abstract long[] getMessageUidsInternal();
protected abstract List<SimpleStoredMessage> getNonDeletedMessagesInternal();
protected abstract Flags getPermanentFlagsInternal();
protected abstract int getRecentCountInternal(boolean reset);
protected abstract long getUidNextInternal();
protected abstract long getUidValidityInternal();
protected abstract int getUnseenCountInternal();
protected abstract boolean isSelectableInternal();
protected abstract void replaceFlagsInternal(Flags flags, long uid, FolderListener silentListener, boolean addUid) throws Exception;
protected abstract void setFlagsInternal(Flags flags, boolean value, long uid, FolderListener silentListener, boolean addUid) throws Exception;
protected abstract class CommandCallback<T>
{
public abstract T command() throws Throwable;
public T runFeedback() throws FolderException
{
return this.runFeedback(false);
}
public T runFeedback(boolean readOnly) throws FolderException
{
try
{
RetryingTransactionHelper txHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper();
txHelper.setMaxRetries(MAX_RETRIES);
txHelper.setReadOnly(readOnly);
T result = txHelper.doInTransaction(
new RetryingTransactionCallback<T>()
{
public T execute() throws Throwable
{
return command();
}
}, readOnly);
return result;
}
catch (Exception e)
{
Throwable cause = e.getCause();
String message;
if (cause != null)
{
message = cause.getMessage();
}
else
{
message = e.getMessage();
}
throw new FolderException(message);
}
}
public T run()
{
return this.run(false);
}
public T run(boolean readOnly)
{
RetryingTransactionHelper txHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper();
txHelper.setMaxRetries(MAX_RETRIES);
txHelper.setReadOnly(readOnly);
T result = txHelper.doInTransaction(
new RetryingTransactionCallback<T>()
{
public T execute() throws Throwable
{
return command();
}
}, readOnly);
return result;
}
}
}

View File

@@ -0,0 +1,371 @@
/*
* 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 static org.alfresco.repo.imap.AlfrescoImapConst.MIME_VERSION;
import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_NODEREF_ID;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Flags;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.template.TemplateNode;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileInfo;
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.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Arseny Kovalchuk
*/
public abstract class AbstractMimeMessage extends MimeMessage
{
/** Used if imapHelper.getDefaultFromAddress is not set */
protected static final String DEFAULT_EMAIL_FROM = "alfresco@alfresco.org";
protected static final String DEFAULT_EMAIL_TO = DEFAULT_EMAIL_FROM;
protected static final String KOI8R_CHARSET = "koi8-r";
protected static int MAX_RETRIES = 1;
private Log logger = LogFactory.getLog(AbstractMimeMessage.class);
protected boolean generateBody = true;
protected ServiceRegistry serviceRegistry;
protected ImapService imapService;
protected FileInfo messageFileInfo;
protected MimeMessage wrappedMessage;
protected AbstractMimeMessage(Session session)
{
super(session);
}
protected AbstractMimeMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, boolean generateBody) throws MessagingException
{
super(Session.getDefaultInstance(new Properties()));
this.generateBody = generateBody;
buildMessage(fileInfo, serviceRegistry);
}
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("_", "/");
}
}
protected void buildMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry) throws MessagingException
{
checkParameter(serviceRegistry, "ServiceRegistry");
this.content = null;
this.serviceRegistry = serviceRegistry;
this.imapService = serviceRegistry.getImapService();
this.messageFileInfo = fileInfo;
RetryingTransactionHelper txHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper();
txHelper.setMaxRetries(MAX_RETRIES);
txHelper.doInTransaction(new RetryingTransactionCallback<Object>() {
public Object execute() throws Throwable
{
buildMessageInternal();
return null;
}
});
}
/**
* Method must be implemented in subclasses. It usually should be used to generate message body.
*
* @throws MessagingException
*/
public abstract void buildMessageInternal() throws MessagingException;
/**
* Method that checks mandatory parameter.
* @param The parameter instance to check.
* @param The name of the parameter.
*/
protected void checkParameter(Object parameter, String name)
{
if (parameter == null)
{
throw new IllegalArgumentException(name + " parameter is null.");
}
}
protected void setMessageHeaders() throws MessagingException
{
setHeader(MIME_VERSION, "1.0");
// Optional headers for further implementation of multiple Alfresco server support.
setHeader(X_ALF_NODEREF_ID, messageFileInfo.getNodeRef().getId());
// setHeader(X_ALF_SERVER_UID, imapService.getAlfrescoServerUID());
}
/**
* 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
*/
protected InternetAddress[] buildSenderFromAddress() throws AddressException
{
// Generate FROM address (Content author)
InternetAddress[] addressList = null;
Map<QName, Serializable> properties = messageFileInfo.getProperties();
String prop = (String) properties.get(ContentModel.PROP_AUTHOR);
String defaultFromAddress = imapService.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;
}
/**
* Returns {@link FileInfo} object representing message in Alfresco.
*
* @return reference to the {@link FileInfo} object.
*/
public FileInfo getMessageInfo()
{
return messageFileInfo;
}
/**
* Returns message flags.
*
* @return {@link Flags}
*/
@Override
public Flags getFlags()
{
return imapService.getFlags(messageFileInfo);
}
/**
* Sets message flags.
*
* @param flags - {@link Flags} object.
* @param value - flags value.
*/
@Override
public void setFlags(Flags flags, boolean value) throws MessagingException
{
imapService.setFlags(messageFileInfo, flags, value);
}
/**
* 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(EmailBodyType type)
{
return serviceRegistry.getTemplateService().processTemplate(
getDefaultEmailBodyTemplate(type),
createEmailTemplateModel(messageFileInfo.getNodeRef()));
}
/**
* 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
*/
protected InternetAddress[] buildRecipientToAddress() throws AddressException
{
InternetAddress[] result = null;
String defaultEmailTo = null;
final String escapedUserName = AuthenticationUtil.getFullyAuthenticatedUser().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;
}
protected void addFromInternal(String addressesString) throws MessagingException
{
if (addressesString != null)
{
addFrom(InternetAddress.parse(addressesString));
}
else
{
addFrom(new Address[] { new InternetAddress(DEFAULT_EMAIL_FROM) });
}
}
/**
* 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().
*/
private 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");
final String repositoryTemplatePath = imapService.getRepositoryTemplatePath();
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 = serviceRegistry.getSearchService().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 = serviceRegistry.getNodeService().getPrimaryParent(ref).getParentRef();
model.put("space", new TemplateNode(parent, serviceRegistry, null));
model.put("date", new Date());
model.put("contextUrl", new String(imapService.getWebApplicationContextUrl()));
model.put("alfTicket", new String(serviceRegistry.getAuthenticationService().getCurrentTicket()));
return model;
}
}

View File

@@ -30,7 +30,7 @@ package org.alfresco.repo.imap;
public interface AlfrescoImapConst
{
public static final char HIERARCHY_DELIMITER = '.';
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";
@@ -38,17 +38,23 @@ public interface AlfrescoImapConst
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_";
public static final String EML_EXTENSION = ".eml";
// 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.
* Defines {@link AlfrescoImapFolder} 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.
* Defines {@link AlfrescoImapFolder} view mode as virtual mode. Used for IMAP Virtualised View.
*/
public static final String MODE_VIRTUAL = "virtual";
/**
* Defines {@link AlfrescoImapFolder} view mode as mixed mode. Used for IMAP Mixed View.
*/
public static final String MODE_MIXED = "mixed";
// Default content model email message templates
public static final String CLASSPATH_TEXT_PLAIN_TEMPLATE = "/alfresco/templates/imap/imap_message_text_plain.ftl";

File diff suppressed because it is too large Load Diff

View File

@@ -24,50 +24,26 @@
*/
package org.alfresco.repo.imap;
import java.util.Arrays;
import java.util.ArrayList;
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.ImapConfigBean;
import org.alfresco.repo.imap.exception.AlfrescoImapFolderException;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.alfresco.service.transaction.TransactionService;
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;
private ImapService imapService;
private TransactionService transactionService;
/**
* Returns the hierarchy delimiter for mailboxes on this host.
@@ -80,8 +56,9 @@ public class AlfrescoImapHostManager implements ImapHostManager
}
/**
* 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.
* 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.
@@ -90,24 +67,27 @@ public class AlfrescoImapHostManager implements ImapHostManager
*/
public Collection<MailFolder> listMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException
{
mailboxPattern = GreenMailUtil.convertFromUtf7(mailboxPattern);
if (logger.isDebugEnabled())
try
{
logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern);
return new ArrayList<MailFolder>(
imapService.listMailboxes(
new AlfrescoImapUser(
user.getEmail(),
user.getLogin(),
user.getPassword()),
mailboxPattern));
}
mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern);
if (logger.isDebugEnabled())
catch (Throwable e)
{
logger.debug("Listing mailboxes: mailboxPattern in alfresco=" + mailboxPattern);
throw new FolderException(e.getMessage());
}
return listMailboxes(user, mailboxPattern, false);
}
/**
* 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.
* 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.
@@ -116,258 +96,30 @@ public class AlfrescoImapHostManager implements ImapHostManager
*/
public Collection<MailFolder> listSubscribedMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException
{
mailboxPattern = GreenMailUtil.convertFromUtf7(mailboxPattern);
if (logger.isDebugEnabled())
try
{
return new ArrayList<MailFolder>(
imapService.listSubscribedMailboxes(
new AlfrescoImapUser(
user.getEmail(),
user.getLogin(),
user.getPassword()),
mailboxPattern));
}
catch (Throwable e)
{
logger.debug("Listing subscribed mailboxes: mailboxPattern=" + mailboxPattern);
throw new FolderException(e.getMessage());
}
mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern);
if (logger.isDebugEnabled())
{
logger.debug("Listing subscribed mailboxes: mailboxPattern in alfresco=" + mailboxPattern);
}
return listMailboxes(user, mailboxPattern, true);
}
/**
* Depend on listSubscribed param, list Mailboxes or list subscribed Mailboxes
*/
private Collection<MailFolder> listMailboxes(GreenMailUser user, String mailboxPattern, boolean listSubscribed) throws FolderException
{
Collection<MailFolder> result = new LinkedList<MailFolder>();
Map<String, NodeRef> mountPoints = imapHelper.getMountPoints();
Map<String, ImapConfigBean> imapConfigs = imapHelper.getImapConfig();
NodeRef mountPoint;
// List mailboxes that are in mount points
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 = listFolder(mountPoint, mountPoint, user, mailboxPattern, listSubscribed, isVirtualView);
if (folders != null)
{
for (MailFolder mailFolder : folders)
{
AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) mailFolder;
folder.setMountPointName(mountPointName);
folder.setViewMode(viewMode);
folder.setMountParent(mountParent);
}
result.addAll(folders);
}
// Add mount point to the result list
if (mailboxPattern.equals("*"))
{
if ((listSubscribed && isSubscribed(mountPointFileInfo, user.getLogin())) || (!listSubscribed))
{
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, imapHelper));
}
// \NoSelect
else if (listSubscribed && hasSubscribedChild(mountPointFileInfo, user.getLogin(), isVirtualView))
{
result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, imapHelper,
false));
}
}
}
// List mailboxes that are in user IMAP Home
NodeRef root = imapHelper.getUserImapHomeRef(user.getLogin());
Collection<MailFolder> imapFolders = listFolder(root, root, user, mailboxPattern, listSubscribed, false);
if (imapFolders != null)
{
for (MailFolder mailFolder : imapFolders)
{
AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) mailFolder;
folder.setViewMode(AlfrescoImapConst.MODE_ARCHIVE);
folder.setMountParent(root);
}
result.addAll(imapFolders);
}
return result;
}
private Collection<MailFolder> listFolder(NodeRef mailboxRoot, NodeRef root, GreenMailUser user, String mailboxPattern, boolean listSubscribed, 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))
{
Collection<FileInfo> list = imapHelper.searchFolders(root, name, true, isVirtualView);
if (listSubscribed)
{
list = getSubscribed(list, user.getLogin());
}
if (list.size() > 0)
{
return createMailFolderList(user, list, 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 = list;
if (listSubscribed)
{
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);
if (listSubscribed)
{
fullList.addAll(getSubscribed(childList, user.getLogin()));
}
else
{
fullList.addAll(childList);
}
}
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>();
if (listSubscribed)
{
for (FileInfo fileInfo : list)
{
if (isSubscribed(fileInfo, user.getLogin()))
{
// folderName, viewMode, mountPointName will be setted in listMailboxes() 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 listMailboxes() method
subscribedList.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), fileInfo, null, null, mailboxRoot, null, imapHelper, false));
}
}
}
else
{
return createMailFolderList(user, list, mailboxRoot);
}
return subscribedList;
}
else if (name.contains("%") || name.contains("*"))
{
List<FileInfo> list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView);
Collection<FileInfo> subscribedList = list;
if (listSubscribed)
{
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 = list;
if (listSubscribed)
{
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 = listFolder(mailboxRoot, folder.getNodeRef(), user, remainName, listSubscribed, 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.
* 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
@@ -375,102 +127,22 @@ public class AlfrescoImapHostManager implements ImapHostManager
* @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());
}
imapService.renameMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), oldMailboxName, newMailboxName);
}
else
catch (Throwable e)
{
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");
}
}
}
throw new FolderException(e.getMessage());
}
}
/**
* 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.
* 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
@@ -480,63 +152,19 @@ public class AlfrescoImapHostManager implements ImapHostManager
*/
public MailFolder createMailbox(GreenMailUser user, String mailboxName) throws AuthorizationException, FolderException
{
mailboxName = GreenMailUtil.convertFromUtf7(mailboxName);
if (logger.isDebugEnabled())
{
logger.debug("Creating folder: " + mailboxName);
try
{
return imapService.createMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName);
}
catch (Throwable e)
{
throw new FolderException(e.getMessage());
}
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.
* 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
@@ -544,41 +172,23 @@ public class AlfrescoImapHostManager implements ImapHostManager
*/
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)
try
{
fileFolderService.delete(message.getNodeRef());
imapService.deleteMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName);
}
nodeService.addAspect(nodeRef, ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE, null);
}
else
catch (Throwable e)
{
throw new FolderException(mailboxName + " - Can't delete a non-selectable store with children.");
}
throw new FolderException(e.getMessage());
}
}
/**
* 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.
* 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.
@@ -586,80 +196,22 @@ public class AlfrescoImapHostManager implements ImapHostManager
*/
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 imapService.getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName);
}
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);
public MailFolder getFolder(final GreenMailUser user, final String mailboxName, boolean mustExist) throws FolderException
{
try
{
return getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName);
}
catch (Throwable e)
{
throw new FolderException(e.getMessage());
}
}
/**
@@ -670,7 +222,14 @@ public class AlfrescoImapHostManager implements ImapHostManager
*/
public MailFolder getInbox(GreenMailUser user) throws FolderException
{
return getFolder(user, AlfrescoImapConst.INBOX_NAME);
try
{
return getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), AlfrescoImapConst.INBOX_NAME);
}
catch (Throwable e)
{
throw new FolderException(e.getMessage());
}
}
/**
@@ -682,44 +241,23 @@ public class AlfrescoImapHostManager implements ImapHostManager
}
/**
* 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.
* 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
public void subscribe(GreenMailUser user, String mailbox) throws FolderException
{
if (logger.isDebugEnabled())
try
{
logger.debug("Subscribing: " + mailbox);
imapService.subscribe(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailbox);
}
catch (Throwable e)
{
throw new FolderException(e.getMessage());
}
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());
}
/**
@@ -728,137 +266,46 @@ public class AlfrescoImapHostManager implements ImapHostManager
* @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
public void unsubscribe(GreenMailUser user, String mailbox) throws FolderException
{
if (logger.isDebugEnabled())
try
{
logger.debug("Unsubscribing: " + mailbox);
imapService.unsubscribe(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailbox);
}
catch (Throwable e)
{
throw new FolderException(e.getMessage());
}
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()
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(Collection<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)
public ImapService getImapService()
{
this.serviceRegistry = serviceRegistry;
return imapService;
}
public ServiceRegistry getServiceRegistry()
public void setImapService(ImapService imapService)
{
return serviceRegistry;
this.imapService = imapService;
}
public void setNodeService(NodeService nodeService)
public TransactionService getTransactionService()
{
this.nodeService = nodeService;
return transactionService;
}
public void setFileFolderService(FileFolderService fileFolderService)
public void setTransactionService(TransactionService transactionService)
{
this.fileFolderService = fileFolderService;
}
public void setImapHelper(ImapHelper imapHelper)
{
this.imapHelper = imapHelper;
this.transactionService = transactionService;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,508 +0,0 @@
/*
* 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

@@ -46,15 +46,13 @@ public class AlfrescoImapServer extends AbstractLifecycleBean
private ImapServer serverImpl;
private int port = 143;
private String host = "localhost";
private ImapHostManager imapHostManager;
private UserManager imapUserManager;
private boolean imapServerEnabled;
private ImapHelper imapHelper;
public void setImapServerEnabled(boolean imapServerEnabled)
{
this.imapServerEnabled = imapServerEnabled;
@@ -65,6 +63,11 @@ public class AlfrescoImapServer extends AbstractLifecycleBean
this.port = port;
}
public void setHost(String host)
{
this.host = host;
}
public void setImapHostManager(ImapHostManager imapHostManager)
{
this.imapHostManager = imapHostManager;
@@ -75,14 +78,9 @@ public class AlfrescoImapServer extends AbstractLifecycleBean
this.imapUserManager = imapUserManager;
}
public void setImapHelper(ImapHelper imapHelper)
{
this.imapHelper = imapHelper;
}
protected void onBootstrap(ApplicationEvent event)
{
if (imapServerEnabled && imapHelper.isPatchApplied())
if (imapServerEnabled)
{
Managers imapManagers = new Managers()
{
@@ -96,11 +94,11 @@ public class AlfrescoImapServer extends AbstractLifecycleBean
return imapUserManager;
}
};
serverImpl = new ImapServer(new ServerSetup(port, null, ServerSetup.PROTOCOL_IMAP), imapManagers);
serverImpl = new ImapServer(new ServerSetup(port, host, ServerSetup.PROTOCOL_IMAP), imapManagers);
serverImpl.startService(null);
if (logger.isInfoEnabled())
{
logger.info("IMAP service started on port " + this.port + ".");
logger.info("IMAP service started on host:port " + this.host + ":" + this.port + ".");
}
}
else

View File

@@ -1,107 +1,103 @@
/*
* 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();
}
}
/*
* 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.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;
public AlfrescoImapUser(String email, String login, String password)
{
this.email = email;
this.userName = login;
this.password = password.toCharArray();
}
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
{
throw new UnsupportedOperationException();
}
public void deliver(MimeMessage msg) throws UserException
{
throw new UnsupportedOperationException();
}
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

@@ -1,168 +1,150 @@
/*
* 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;
}
}
/*
* 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.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 AuthenticationService authenticationService;
protected PersonService personService;
protected NodeService nodeService;
public AlfrescoImapUserManager()
{
super(null);
}
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);
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);
addUser(user);
}
catch (AuthenticationException ex)
{
logger.error("IMAP authentication failed for userid: " + userid);
return false;
}
return true;
}
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,100 @@
package org.alfresco.repo.imap;
import static org.alfresco.repo.imap.AlfrescoImapConst.BASE_64_ENCODING;
import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TRANSFER_ENCODING;
import static org.alfresco.repo.imap.AlfrescoImapConst.UTF_8;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.namespace.QName;
public class ContentModelMessage extends AbstractMimeMessage
{
public ContentModelMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, boolean generateBody) throws MessagingException
{
super(fileInfo, serviceRegistry, generateBody);
}
@Override
public void buildMessageInternal() throws MessagingException
{
if (generateBody != false)
{
setMessageHeaders();
buildContentModelMessage();
}
}
/**
* 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 = messageFileInfo.getProperties();
String prop = null;
setSentDate(messageFileInfo.getModifiedDate());
// Add FROM address
Address[] addressList = buildSenderFromAddress();
addFrom(addressList);
// Add TO address
addressList = buildRecipientToAddress();
addRecipients(RecipientType.TO, addressList);
prop = (String) properties.get(ContentModel.PROP_TITLE);
try
{
prop = (prop == null) ? messageFileInfo.getName() : prop;
prop = 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 = getEmailBodyText(EmailBodyType.TEXT_PLAIN);
rootMultipart.addBodyPart(getTextBodyPart(bodyTxt, EmailBodyType.TEXT_PLAIN.getSubtype()));
String bodyHtml = getEmailBodyText(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;
}
}

View File

@@ -1,994 +0,0 @@
/*
* 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.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.mail.Flags;
import javax.mail.Flags.Flag;
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.ImapConfigBean;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
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.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
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
*/
/*package*/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 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;
private Map<String, ImapConfigBean> imapConfigBeans = Collections.emptyMap();
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, ImapConfigBean> imapConfigs = getImapConfig();
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 and create it if it doesn't exist.
*/
public NodeRef getUserImapHomeRef(final String userName)
{
NodeRef userHome = fileFolderService.searchSimple(imapRootNodeRef, userName);
if (userHome == null)
{
// create user home
userHome = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<NodeRef>()
{
public NodeRef doWork() throws Exception
{
NodeRef result = fileFolderService.create(imapRootNodeRef, userName, ContentModel.TYPE_FOLDER).getNodeRef();
nodeService.setProperty(result, ContentModel.PROP_DESCRIPTION, userName);
// create inbox
fileFolderService.create(result, AlfrescoImapConst.INBOX_NAME, ContentModel.TYPE_FOLDER);
return result;
}
}, AuthenticationUtil.getSystemUserName());
}
return userHome;
}
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, ImapConfigBean> imapConfigs = getImapConfig();
Map<String, NodeRef> mountPoints = new HashMap<String, NodeRef>();
for (ImapConfigBean 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;
}
public void setImapConfigBeans(ImapConfigBean[] imapConfigBeans)
{
this.imapConfigBeans = new LinkedHashMap<String, ImapConfigBean>(imapConfigBeans.length * 2);
for (ImapConfigBean bean : imapConfigBeans)
{
this.imapConfigBeans.put(bean.getName(), bean);
}
}
/**
* Return map of imap configs. Name of config == key in the map
*
* @return map of imap configs.
*/
public Map<String, ImapConfigBean> getImapConfig()
{
return this.imapConfigBeans;
}
/**
* 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, ImapConfigBean> imapConfigs = getImapConfig();
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, ImapConfigBean> imapConfigs = getImapConfig();
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, ImapConfigBean> imapConfigs = getImapConfig();
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 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,140 @@
/*
* 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.IOException;
import java.io.InputStream;
import javax.mail.MessagingException;
import javax.mail.util.SharedByteArrayInputStream;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
/**
* Extended MimeMessage to represent a content stored in the Alfresco repository.
*
* @author Arseny Kovalchuk
*/
public class ImapModelMessage extends AbstractMimeMessage
{
/**
* Constructs {@link ImapModelMessage} 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 ImapModelMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, boolean generateBody) throws MessagingException
{
super(fileInfo, serviceRegistry, generateBody);
}
@Override
public void buildMessageInternal() throws MessagingException
{
if (generateBody != false)
{
setMessageHeaders();
buildImapMessage();
}
}
/**
* This method builds MimeMessage based on either ImapModel or ContentModel type.
*
* @param fileInfo - Source file information {@link FileInfo}
* @throws MessagingException
*/
private void buildImapMessage() throws MessagingException
{
modified = false;
saved = false;
buildRFC822Message();
saved = true;
}
private void buildRFC822Message() throws MessagingException
{
ContentService contentService = serviceRegistry.getContentService();
ContentReader reader = contentService.getReader(messageFileInfo.getNodeRef(), ContentModel.PROP_CONTENT);
try
{
InputStream is = reader.getContentInputStream();
this.parse(is);
is.close();
is = null;
}
catch (ContentIOException e)
{
//logger.error(e);
throw new MessagingException("The error occured during message creation from content stream.", e);
}
catch (IOException e)
{
//logger.error(e);
throw new MessagingException("The error occured during message creation from content stream.", e);
}
}
@Override
protected InputStream getContentStream() throws MessagingException
{
try
{
if (this.contentStream == null)
{
if (content != null)
{
return new SharedByteArrayInputStream(content);
}
else
{
throw new MessagingException("No content");
}
}
return this.contentStream;
}
catch (Exception e)
{
throw new MessagingException(e.getMessage(),e);
}
}
/*
protected void parse(InputStream inputstream) throws MessagingException
{
headers = createInternetHeaders(inputstream);
contentStream = inputstream;
}
*/
}

View File

@@ -0,0 +1,199 @@
/*
* 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.List;
import javax.mail.Flags;
import javax.mail.Flags.Flag;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* @author Arseny Kovalchuk
* @since 3.2
*/
public interface ImapService
{
/**
* Returns an collection of mailboxes. 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.
*/
public List<AlfrescoImapFolder> listMailboxes(AlfrescoImapUser user, String mailboxPattern);
/**
* Returns an collection of subscribed mailboxes. 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.
*/
public List<AlfrescoImapFolder> listSubscribedMailboxes(AlfrescoImapUser user, String mailboxPattern);
/**
* 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.
*/
public AlfrescoImapFolder createMailbox(AlfrescoImapUser user, String mailboxName);
/**
* Deletes an existing MailBox. Specified mailbox must already exist on this server, and the user must have rights to delete it. 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(AlfrescoImapUser user, String mailboxName);
/**
* 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.
*
* @param user User making the request.
* @param oldMailboxName String name of the existing folder
* @param newMailboxName String target new name
*/
public void renameMailbox(AlfrescoImapUser user, String oldMailboxName, String newMailboxName);
/**
* 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.
*
* @param user User making the request.
* @param mailboxName String name of the target.
* @return an Mailbox reference.
*/
public AlfrescoImapFolder getFolder(AlfrescoImapUser user, String mailboxName);
/**
* Get root reference for the specified mailbox
*
* @param mailboxName mailbox name in IMAP client.
* @param userName
* @return NodeRef of root reference for the specified mailbox
*/
public NodeRef getMailboxRootRef(String mailboxName, String userName);
/**
* 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(AlfrescoImapUser user, String mailbox);
/**
* 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(AlfrescoImapUser user, String mailbox);
/**
* Search for files in specified context
*
* @param contextNodeRef context folder for search
* @param namePattern name pattern for search
* @param includeSubFolders include SubFolders
* @return list of files
*/
public List<FileInfo> searchFiles(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders);
/**
* Search for mailboxes in specified context
*
* @param contextNodeRef context folder for search
* @param namePattern name pattern for search
* @param includeSubFolders include SubFolders
* @param viewMode (ARCHIVE, MIXED or VIRTUAL)
* @return list of mailboxes that are visible from specified view
*/
public List<FileInfo> searchFolders(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders, String viewMode);
/**
* Search for emails in specified folder depend on view mode.
*
* @param contextNodeRef context folder for search
* @param namePattern name pattern for search
* @param viewMode (ARCHIVE, MIXED or VIRTUAL)
* @param includeSubFolders includeSubFolders
* @return list of emails that context folder contains.
*/
public List<FileInfo> searchMails(NodeRef contextNodeRef, String namePattern, String viewMode, boolean includeSubFolders);
/**
* Return flags that belong to the specified imap folder.
*
* @param messageInfo imap folder info.
* @return flags.
*/
public Flags getFlags(FileInfo messageFileInfo);
/**
* 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 messageFileInfo, Flags flags, boolean value);
/**
* Set flag to the specified imapFolder.
*
* @param messageInfo FileInfo of imap Folder
* @param flag flag to set.
* @param value value value to set.
*/
public void setFlag(FileInfo messageFileInfo, Flag flag, boolean value);
/**
* @return Default From addreses
*/
public String getDefaultFromAddress();
/**
* @return Path to the folder containing templates, that will be used for generating body of message in VIRTUAL and MIXED views.
*/
public String getRepositoryTemplatePath();
/**
* @return Web application context url (e.g. http://localhost:8080/alfresco)
*/
public String getWebApplicationContextUrl();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,452 @@
package org.alfresco.repo.imap;
import java.io.IOException;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.mail.Flags;
import javax.transaction.UserTransaction;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ImapModel;
import org.alfresco.repo.importer.ACPImportPackageHandler;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
import org.alfresco.repo.node.integrity.IntegrityChecker;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
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.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.view.Location;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.PropertyMap;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
public class ImapServiceImplTest extends TestCase
{
private static final String USER_NAME = "admin";
private static final String USER_PASSWORD = "admin";
private static final String TEST_IMAP_FOLDER_NAME = "aaa";
private static final String MAILBOX_NAME_A = "mailbox_a";
private static final String MAILBOX_NAME_B = "mailbox_b";
private static final String MAILBOX_PATTERN = "mailbox*";
private static final String FOLDER_PATTERN = "___-___folder*";
private static final String FILE_PATTERN = "___-___file*";
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private TransactionService transactionService;
private NodeService nodeService;
private ImporterService importerService;
private PersonService personService;
private AuthenticationService authenticationService;
private PermissionService permissionService;
private SearchService searchService;
private NamespaceService namespaceService;
private FileFolderService fileFolderService;
private AlfrescoImapUser user;
private ImapService imapService;
private UserTransaction txn;
private NodeRef testImapFolderNodeRef;
private Flags flags;
String anotherUserName;
@Override
public void setUp() throws Exception
{
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
transactionService = serviceRegistry.getTransactionService();
nodeService = serviceRegistry.getNodeService();
importerService = serviceRegistry.getImporterService();
personService = serviceRegistry.getPersonService();
authenticationService = serviceRegistry.getAuthenticationService();
permissionService = serviceRegistry.getPermissionService();
imapService = serviceRegistry.getImapService();
searchService = serviceRegistry.getSearchService();
namespaceService = serviceRegistry.getNamespaceService();
fileFolderService = serviceRegistry.getFileFolderService();
flags = new Flags();
flags.add(Flags.Flag.SEEN);
flags.add(Flags.Flag.FLAGGED);
flags.add(Flags.Flag.ANSWERED);
flags.add(Flags.Flag.DELETED);
// start the transaction
txn = transactionService.getUserTransaction();
txn.begin();
authenticationService.authenticate(USER_NAME, USER_PASSWORD.toCharArray());
// downgrade integrity
IntegrityChecker.setWarnInTransaction();
anotherUserName = "user" + System.currentTimeMillis();
PropertyMap testUser = new PropertyMap();
testUser.put(ContentModel.PROP_USERNAME, anotherUserName);
testUser.put(ContentModel.PROP_FIRSTNAME, anotherUserName);
testUser.put(ContentModel.PROP_LASTNAME, anotherUserName);
testUser.put(ContentModel.PROP_EMAIL, anotherUserName + "@alfresco.com");
testUser.put(ContentModel.PROP_JOBTITLE, "jobTitle");
personService.createPerson(testUser);
// create the ACEGI Authentication instance for the new user
authenticationService.createAuthentication(anotherUserName, anotherUserName.toCharArray());
user = new AlfrescoImapUser(anotherUserName + "@alfresco.com", anotherUserName, anotherUserName);
String storePath = "workspace://SpacesStore";
String companyHomePathInStore = "/app:company_home";
StoreRef storeRef = new StoreRef(storePath);
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false);
NodeRef companyHomeNodeRef = nodeRefs.get(0);
ChildApplicationContextFactory imap = (ChildApplicationContextFactory) ctx.getBean("imap");
ApplicationContext imapCtx = imap.getApplicationContext();
ImapServiceImpl imapServiceImpl = (ImapServiceImpl)imapCtx.getBean("imapService");
// Creating IMAP test folder for IMAP root
LinkedList<String> folders = new LinkedList<String>();
folders.add(TEST_IMAP_FOLDER_NAME);
FileFolderServiceImpl.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER);
// Setting IMAP root
String imapRoot = storePath + companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_FOLDER_NAME;
imapServiceImpl.setImapRoot(imapRoot);
// Starting IMAP
imapServiceImpl.startup();
nodeRefs = searchService.selectNodes(storeRootNodeRef,
companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_FOLDER_NAME,
null,
namespaceService,
false);
testImapFolderNodeRef = nodeRefs.get(0);
/*
* Importing test folders:
*
* Test folder contains: "___-___folder_a"
*
* "___-___folder_a" contains: "___-___folder_a_a",
* "___-___file_a",
* "Message_485.eml" (this is IMAP Message)
*
* "___-___folder_a_a" contains: "____-____file_a_a"
*
*/
importInternal("imapservice_test_folder_a.acp", testImapFolderNodeRef);
reauthenticate(anotherUserName, anotherUserName);
}
public void tearDown() throws Exception
{
try
{
txn.rollback();
}
catch (Throwable e)
{
e.printStackTrace();
}
}
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);
}
private boolean checkMailbox(AlfrescoImapUser user, String mailboxName)
{
AlfrescoImapFolder mailFolder = (AlfrescoImapFolder)imapService.getFolder(user, mailboxName);
if (mailFolder.getFolderInfo() == null)
{
return false;
}
return true;
}
private boolean checkSubscribedMailbox(AlfrescoImapUser user, String mailboxName)
{
List<AlfrescoImapFolder> aifs = imapService.listSubscribedMailboxes(user, mailboxName);
boolean present = false;
for (AlfrescoImapFolder aif : aifs)
{
if (aif.getName().equals(mailboxName))
{
present = true;
break;
}
}
return present;
}
private void reauthenticate(String name, String password)
{
authenticationService.invalidateTicket(authenticationService.getCurrentTicket());
authenticationService.clearCurrentSecurityContext();
authenticationService.authenticate(name, password.toCharArray());
}
public void testGetFolder() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_A);
assertTrue(checkMailbox(user, MAILBOX_NAME_A));
}
public void testListMailbox() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_A);
imapService.createMailbox(user, MAILBOX_NAME_B);
List<AlfrescoImapFolder> mf = imapService.listMailboxes(user, MAILBOX_PATTERN);
assertEquals(mf.size(), 2);
}
public void testListSubscribedMailbox() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_A);
imapService.createMailbox(user, MAILBOX_NAME_B);
imapService.subscribe(user, MAILBOX_NAME_A);
imapService.subscribe(user, MAILBOX_NAME_B);
List<AlfrescoImapFolder> aif = imapService.listSubscribedMailboxes(user, MAILBOX_PATTERN);
assertEquals(aif.size(), 2);
}
public void testCreateMailbox() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_A);
assertTrue("Mailbox isn't created", checkMailbox(user, MAILBOX_NAME_A));
}
public void testDuplicateMailboxes() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_A);
try
{
imapService.createMailbox(user, MAILBOX_NAME_A);
fail("Duplicate Mailbox was created");
}
catch (AlfrescoRuntimeException e)
{
// expected
}
}
public void testRenameMailbox() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_A);
imapService.renameMailbox(user, MAILBOX_NAME_A, MAILBOX_NAME_B);
assertFalse("Can't rename mailbox", checkMailbox(user, MAILBOX_NAME_A));
assertTrue("Can't rename mailbox", checkMailbox(user, MAILBOX_NAME_B));
}
public void testRenameMailboxDuplicate() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_A);
imapService.createMailbox(user, MAILBOX_NAME_B);
try
{
imapService.renameMailbox(user, MAILBOX_NAME_A, MAILBOX_NAME_B);
fail("Mailbox was renamed to existing one but shouldn't");
}
catch (AlfrescoRuntimeException e)
{
// expected
}
}
public void testDeleteMailbox() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_B);
imapService.deleteMailbox(user, MAILBOX_NAME_B);
assertFalse("Can't delete mailbox", checkMailbox(user, MAILBOX_NAME_B));
}
public void testSearchFoldersInArchive() throws Exception
{
List<FileInfo> fi = imapService.searchFolders(testImapFolderNodeRef, FOLDER_PATTERN, true, AlfrescoImapConst.MODE_ARCHIVE);
assertNotNull("Can't find folders in Archive Mode", fi);
assertEquals("Can't find folders in Archive Mode", fi.size(), 2);
fi = imapService.searchFolders(testImapFolderNodeRef, FOLDER_PATTERN, false, AlfrescoImapConst.MODE_ARCHIVE);
assertNotNull("Can't find folders in Archive Mode", fi);
assertEquals("Can't find folders in Archive Mode", fi.size(), 1);
}
public void testSearchFoldersInVirtual() throws Exception
{
List<FileInfo> fi = imapService.searchFolders(testImapFolderNodeRef, FOLDER_PATTERN, true, AlfrescoImapConst.MODE_VIRTUAL);
assertNotNull("Can't find folders in Virtual Mode", fi);
assertEquals("Can't find folders in Virtual Mode", fi.size(), 2);
fi = imapService.searchFolders(testImapFolderNodeRef, FOLDER_PATTERN, false, AlfrescoImapConst.MODE_VIRTUAL);
assertNotNull("Can't find folders in Virtual Mode", fi);
assertEquals("Can't find folders in Virtual Mode", fi.size(), 1);
}
public void testSearchFoldersInMixed() throws Exception
{
List<FileInfo> fi = imapService.searchFolders(testImapFolderNodeRef, FOLDER_PATTERN, true, AlfrescoImapConst.MODE_MIXED);
assertNotNull("Can't find folders in Mixed Mode", fi);
assertEquals("Can't find folders in Mixed Mode", fi.size(), 2);
fi = imapService.searchFolders(testImapFolderNodeRef, FOLDER_PATTERN, false, AlfrescoImapConst.MODE_MIXED);
assertNotNull("Can't find folders in Mixed Mode", fi);
assertEquals("Can't find folders in Mixed Mode", fi.size(), 1);
}
public void testSearchFiles() throws Exception
{
List<FileInfo> fi = imapService.searchFiles(testImapFolderNodeRef, FILE_PATTERN, true);
assertNotNull(fi);
assertTrue(fi.size() > 0);
}
public void testSearchMails() throws Exception
{
List<FileInfo> fi = imapService.searchMails(testImapFolderNodeRef, "*", AlfrescoImapConst.MODE_MIXED, true);
assertNotNull(fi);
assertTrue(fi.size() > 0);
}
public void testSubscribe() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_A);
imapService.subscribe(user, MAILBOX_NAME_A);
assertTrue("Can't subscribe mailbox", checkSubscribedMailbox(user, MAILBOX_NAME_A));
}
public void testUnsubscribe() throws Exception
{
imapService.createMailbox(user, MAILBOX_NAME_A);
imapService.subscribe(user, MAILBOX_NAME_A);
imapService.unsubscribe(user, MAILBOX_NAME_A);
assertFalse("Can't unsubscribe mailbox", checkSubscribedMailbox(user, MAILBOX_NAME_A));
}
private void setFlags(FileInfo messageFileInfo) throws Exception
{
imapService.setFlags(messageFileInfo, flags, true);
NodeRef messageNodeRef = messageFileInfo.getNodeRef();
Map<QName, Serializable> props = nodeService.getProperties(messageNodeRef);
assertTrue("Can't set SEEN flag", props.containsKey(ImapModel.PROP_FLAG_SEEN));
assertTrue("Can't set FLAGGED flag", props.containsKey(ImapModel.PROP_FLAG_FLAGGED));
assertTrue("Can't set ANSWERED flag", props.containsKey(ImapModel.PROP_FLAG_ANSWERED));
assertTrue("Can't set DELETED flag", props.containsKey(ImapModel.PROP_FLAG_DELETED));
}
public void testSetFlags() throws Exception
{
List<FileInfo> fis = imapService.searchMails(testImapFolderNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true);
if (fis != null && fis.size() > 0)
{
FileInfo messageFileInfo = fis.get(0);
try
{
setFlags(messageFileInfo);
fail("Can't set flags");
}
catch (Exception e)
{
if (e instanceof AccessDeniedException)
{
// expected
}
else
{
throw e;
}
}
reauthenticate(USER_NAME, USER_PASSWORD);
permissionService.setPermission(testImapFolderNodeRef, anotherUserName, PermissionService.WRITE, true);
reauthenticate(anotherUserName, anotherUserName);
setFlags(messageFileInfo);
}
}
public void testSetFlag() throws Exception
{
List<FileInfo> fis = imapService.searchMails(testImapFolderNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true);
if (fis != null && fis.size() > 0)
{
FileInfo messageFileInfo = fis.get(0);
reauthenticate(USER_NAME, USER_PASSWORD);
permissionService.setPermission(testImapFolderNodeRef, anotherUserName, PermissionService.WRITE, true);
reauthenticate(anotherUserName, anotherUserName);
imapService.setFlag(messageFileInfo, Flags.Flag.RECENT, true);
Serializable prop = nodeService.getProperty(messageFileInfo.getNodeRef(), ImapModel.PROP_FLAG_RECENT);
assertNotNull("Can't set RECENT flag", prop);
}
}
public void testGetFlags() throws Exception
{
List<FileInfo> fis = imapService.searchMails(testImapFolderNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true);
if (fis != null && fis.size() > 0)
{
FileInfo messageFileInfo = fis.get(0);
reauthenticate(USER_NAME, USER_PASSWORD);
permissionService.setPermission(testImapFolderNodeRef, anotherUserName, PermissionService.WRITE, true);
imapService.setFlags(messageFileInfo, flags, true);
reauthenticate(anotherUserName, anotherUserName);
Flags fl = imapService.getFlags(messageFileInfo);
assertTrue(fl.contains(flags));
}
}
}

View File

@@ -0,0 +1,166 @@
/*
* 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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ImapModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.FileCopyUtils;
/**
* This class is used to serve incoming IMAP message. E.g. when message is copied /moved into some IMAP older.
*
* @author Arseny Kovalchuk
*/
public class IncomingImapMessage extends AbstractMimeMessage
{
private Log logger = LogFactory.getLog(IncomingImapMessage.class);
private ContentReader contentReader;
/**
* Constructs {@link IncomingImapMessage} object based on {@link MimeMessage}
*
* @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 IncomingImapMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, MimeMessage message) throws MessagingException
{
super(Session.getDefaultInstance(new Properties()));
this.wrappedMessage = message; // temporary save it and then destroyed in writeContent() (to avoid memory leak with byte[] MimeMessage.content field)
this.buildMessage(fileInfo, serviceRegistry);
}
@Override
public void buildMessageInternal() throws MessagingException
{
setMessageHeaders();
// Add Imap Content Aspect with properties
NodeService nodeService = serviceRegistry.getNodeService();
nodeService.addAspect(this.messageFileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_CONTENT, null);
// Write content
writeContent();
imapService.setFlags(messageFileInfo, flags, true);
final NodeRef nodeRef = messageFileInfo.getNodeRef();
Map<QName, Serializable> newProperties = new HashMap<QName, Serializable>();
newProperties.put(ImapModel.PROP_MESSAGE_FROM, InternetAddress.toString(this.getFrom()));
newProperties.put(ImapModel.PROP_MESSAGE_TO, InternetAddress.toString(this.getRecipients(RecipientType.TO)));
newProperties.put(ImapModel.PROP_MESSAGE_CC, InternetAddress.toString(this.getRecipients(RecipientType.CC)));
newProperties.put(ImapModel.PROP_MESSAGE_ID, this.getMessageID());
String[] threadIndexes = this.getHeader("Thread-Index");
String threadIndex = (threadIndexes == null || threadIndexes.length == 0) ? null : threadIndexes[0];
newProperties.put(ImapModel.PROP_THREAD_INDEX, threadIndex);
String[] subj = this.getHeader("Subject");
if (subj != null && subj.length > 0)
{
String decodedSubject = subj[0];
try
{
decodedSubject = MimeUtility.decodeText(decodedSubject);
}
catch (UnsupportedEncodingException e)
{
logger.warn(e.toString());
}
newProperties.put(ImapModel.PROP_MESSAGE_SUBJECT, decodedSubject);
newProperties.put(ContentModel.PROP_TITLE, decodedSubject);
newProperties.put(ContentModel.PROP_DESCRIPTION, decodedSubject);
}
serviceRegistry.getNodeService().addProperties(nodeRef, newProperties);
}
/**
* Writes the content of incoming message into Alfresco repository.
*
* @throws MessagingException
*/
private void writeContent() throws MessagingException
{
ContentWriter writer = serviceRegistry.getContentService().getWriter(messageFileInfo.getNodeRef(), ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_RFC822);
try
{
OutputStream outputStream = writer.getContentOutputStream();
wrappedMessage.writeTo(outputStream);
outputStream.close();
wrappedMessage = null; // it is not used any more and it is available to GC (to avoid memory leak with byte[] MimeMessage.content field)
this.contentReader = serviceRegistry.getContentService().getReader(messageFileInfo.getNodeRef(), ContentModel.PROP_CONTENT);
}
catch (ContentIOException e)
{
throw new MessagingException(e.getMessage(), e);
}
catch (IOException e)
{
throw new MessagingException(e.getMessage(), e);
}
}
@Override
protected InputStream getContentStream() throws MessagingException
{
try
{
if (this.contentStream == null)
{
this.contentStream = this.contentReader.getContentInputStream();
}
return this.contentStream;
}
catch (Exception e)
{
throw new MessagingException(e.getMessage(),e);
}
}
}

View File

@@ -0,0 +1,145 @@
package org.alfresco.repo.imap;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import javax.mail.Flags;
import org.alfresco.repo.importer.ACPImportPackageHandler;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileInfo;
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.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.view.Location;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.util.ApplicationContextHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
import com.icegreen.greenmail.imap.ImapConstants;
import com.icegreen.greenmail.store.SimpleStoredMessage;
import junit.framework.TestCase;
public class LoadTester extends TestCase
{
private Log logger = LogFactory.getLog(LoadTester.class);
private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private ImapService imapService;
private ImporterService importerService;
private AlfrescoImapUser user;
private static final String USER_NAME = "admin";
private static final String USER_PASSWORD = "admin";
private static final String TEST_DATA_FOLDER_NAME = "test_data";
private static final String TEST_FOLDER_NAME = "test_imap1000";
private static final long MESSAGE_QUANTITY = 1000;
@Override
public void setUp() throws Exception
{
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
AuthenticationService authenticationService = serviceRegistry.getAuthenticationService();
imapService = serviceRegistry.getImapService();
importerService = serviceRegistry.getImporterService();
NodeService nodeService = serviceRegistry.getNodeService();
SearchService searchService = serviceRegistry.getSearchService();
NamespaceService namespaceService = serviceRegistry.getNamespaceService();
user = new AlfrescoImapUser(USER_NAME + "@alfresco.com", USER_NAME, USER_PASSWORD);
authenticationService.authenticate(USER_NAME, USER_PASSWORD.toCharArray());
StoreRef storeRef = new StoreRef("workspace://SpacesStore");
NodeRef rootRef = nodeService.getRootNode(storeRef);
// Delete test folder
List<NodeRef> nodeRefs = searchService.selectNodes(rootRef, "/app:company_home/imap:imap_home/cm:admin/cm:" + TEST_FOLDER_NAME, null, namespaceService, false);
if (nodeRefs.size() == 1)
{
NodeRef ch = nodeRefs.get(0);
nodeService.deleteNode(ch);
}
// Delete test data folder
nodeRefs = searchService.selectNodes(rootRef, "/app:company_home/imap:imap_home/cm:admin/cm:" + TEST_DATA_FOLDER_NAME, null, namespaceService, false);
if (nodeRefs.size() == 1)
{
NodeRef ch = nodeRefs.get(0);
nodeService.deleteNode(ch);
}
NodeRef adminNodeRef = searchService.selectNodes(rootRef, "/app:company_home/imap:imap_home/cm:admin", null, namespaceService, false).get(0);
importTestData("test-resources/load_test_data.acp", adminNodeRef);
AlfrescoImapFolder testDataFolder = imapService.getFolder(user, TEST_DATA_FOLDER_NAME);
SimpleStoredMessage m = testDataFolder.getMessages().get(0);
m = testDataFolder.getMessage(m.getUid());
AlfrescoImapFolder folder = imapService.createMailbox(user, TEST_FOLDER_NAME);
logger.info("Creating folders...");
long t = System.currentTimeMillis();
try
{
for (int i = 0; i < MESSAGE_QUANTITY; i++)
{
System.out.println("i = " + i);
folder.appendMessage(m.getMimeMessage(), new Flags(), new Date());
}
}
catch (Exception e)
{
logger.error(e, e);
}
t = System.currentTimeMillis() - t;
logger.info("Create time: " + t + " ms (" + t/1000 + " s (" + t/60000 + " min))");
}
public void tearDown() throws Exception
{
}
public void testList()
{
logger.info("Listing folders...");
long t = System.currentTimeMillis();
List<AlfrescoImapFolder> list = imapService.listMailboxes(user, TEST_FOLDER_NAME + "*");
t = System.currentTimeMillis() - t;
logger.info("List time: " + t + " ms (" + t/1000 + " s)");
logger.info("List size: " + list.size());
}
private void importTestData(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,163 @@
package org.alfresco.repo.imap;
import java.io.BufferedInputStream;
import java.io.FilterInputStream;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.MimeMultipart;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.sun.mail.util.BASE64DecoderStream;
public class RemoteLoadTester extends TestCase
{
private Log logger = LogFactory.getLog(RemoteLoadTester.class);
private static final String USER_NAME = "admin";
private static final String USER_PASSWORD = "admin";
private static final String TEST_FOLDER_NAME = "test_imap1000";
@Override
public void setUp() throws Exception
{
}
public void tearDown() throws Exception
{
}
public void testMailbox()
{
logger.info("Getting folder...");
long t = System.currentTimeMillis();
String host = "localhost";
// Create empty properties
Properties props = new Properties();
props.setProperty("mail.imap.partialfetch", "false");
// Get session
Session session = Session.getDefaultInstance(props, null);
Store store = null;
Folder folder = null;
try
{
// Get the store
store = session.getStore("imap");
store.connect(host, USER_NAME, USER_PASSWORD);
// Get folder
folder = store.getFolder(TEST_FOLDER_NAME);
folder.open(Folder.READ_ONLY);
// Get directory
Message message[] = folder.getMessages();
for (int i = 0, n = message.length; i < n; i++)
{
message[i].getAllHeaders();
Address[] from = message[i].getFrom();
System.out.print(i + ": ");
if (from != null)
{
System.out.print(message[i].getFrom()[0] + "\t");
}
System.out.println(message[i].getSubject());
Object content = message[i].getContent();
if (content instanceof MimeMultipart)
{
for (int j = 0, m = ((MimeMultipart)content).getCount(); j < m; j++)
{
BodyPart part = ((MimeMultipart)content).getBodyPart(j);
Object partContent = part.getContent();
if (partContent instanceof String)
{
String body = (String)partContent;
}
else if (partContent instanceof FilterInputStream)
{
FilterInputStream fis = (FilterInputStream)partContent;
BufferedInputStream bis = new BufferedInputStream(fis);
/* while (bis.available() > 0)
{
bis.read();
}*/
byte[] bytes = new byte[524288];
while (bis.read(bytes) != -1)
{
}
bis.close();
fis.close();
}
}
}
int nn = 0;
}
t = System.currentTimeMillis() - t;
logger.info("Time: " + t + " ms (" + t/1000 + " s)");
logger.info("Length: " + message.length);
}
catch (Exception e)
{
logger.error(e.getMessage(), e);
fail(e.getMessage());
}
finally
{
// Close connection
try
{
if (folder != null)
{
folder.close(false);
}
}
catch (MessagingException e)
{
logger.error(e.getMessage(), e);
fail(e.getMessage());
}
try
{
if (store != null)
{
store.close();
}
}
catch (MessagingException e)
{
logger.error(e.getMessage(), e);
fail(e.getMessage());
}
}
}
}

View File

@@ -27,7 +27,7 @@ package org.alfresco.repo.imap.config;
import org.springframework.beans.factory.BeanNameAware;
/**
* Provides the parameters for an IMAP mount point (a mapping from an Alfresco node path to an IMAP folder name).
* Standard ImapConfig bean.
*/
public class ImapConfigBean implements BeanNameAware
{
@@ -35,9 +35,6 @@ public class ImapConfigBean implements BeanNameAware
/** The IMAP folder name. */
private String name;
/** The mode (virtual or archive). */
private String mode;
/** The Alfresco store name. */
private String store;
@@ -63,27 +60,6 @@ public class ImapConfigBean implements BeanNameAware
this.name = name;
}
/**
* Gets the mode.
*
* @return the mode (virtual or archive)
*/
public String getMode()
{
return this.mode;
}
/**
* Sets the mode.
*
* @param mode
* the new mode (virtual or archive)
*/
public void setMode(String mode)
{
this.mode = mode;
}
/**
* Gets the Alfresco store name.
*

View File

@@ -0,0 +1,59 @@
/*
* 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 received 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;
/**
* Provides the parameters for an IMAP mount point (a mapping from an Alfresco node path to an IMAP folder name).
*/
public class ImapConfigMountPointsBean extends ImapConfigBean
{
/** The mode (virtual, archive or mixed). */
private String mode;
/**
* Gets the mode.
*
* @return the mode (virtual or archive)
*/
public String getMode()
{
return this.mode;
}
/**
* Sets the mode.
*
* @param mode
* the new mode (virtual or archive)
*/
public void setMode(String mode)
{
this.mode = mode;
}
}

View File

@@ -0,0 +1,73 @@
package org.alfresco.repo.jscript;
import org.alfresco.repo.model.Repository;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.ParameterCheck;
public final class Imap extends BaseScopableProcessorExtension
{
/** Service registry */
private ServiceRegistry services;
/** Default store reference */
private StoreRef storeRef;
/** Repository helper */
private Repository repository;
/**
* Set the default store reference
*
* @param storeRef the default store reference
*/
public void setStoreUrl(String storeRef)
{
// ensure this is not set again by a script instance!
if (this.storeRef != null)
{
throw new IllegalStateException("Default store URL can only be set once.");
}
this.storeRef = new StoreRef(storeRef);
}
/**
* Set the service registry
*
* @param services the service registry
*/
public void setServiceRegistry(ServiceRegistry services)
{
this.services = services;
}
/**
* Set the repository helper
*
* @param repository the repository helper
*/
public void setRepositoryHelper(Repository repository)
{
this.repository = repository;
}
/**
* Searches NodeRef to the IMAP home for specified user
*
* @param mailboxName the name of the mailbox
* @param userName the name of the user
*/
public ScriptNode getImapHomeRef(String mailboxName, String userName)
{
ScriptNode result = null;
NodeRef nodeRef = services.getImapService().getMailboxRootRef(mailboxName, userName);
if (nodeRef != null)
{
result = new ScriptNode(nodeRef, this.services, getScope());
}
return result;
}
}

File diff suppressed because it is too large Load Diff

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.util;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import com.beetstra.jutf7.CharsetProvider;
/**
*
* @author Mike Shavnev
*
*/
public class Utf7
{
public static final String UTF7 = "UTF-7";
public static final String UTF7_OPTIONAL = "X-UTF-7-OPTIONAL";
public static final String UTF7_MODIFIED = "X-MODIFIED-UTF-7";
/**
* Convert string from UTF-7 characters
*
* @param string Input string for decoding
* @return Decoded string
*/
public static String decode(String string, String charsetName)
{
if (string.length() <= 1)
{
return string;
}
CharsetProvider provider = new CharsetProvider();
Charset charset = provider.charsetForName(charsetName);
CharBuffer charBuffer = charset.decode(ByteBuffer.wrap(string.getBytes()));
return charBuffer.toString();
}
/**
* Convert string to UTF-7 characters
*
* @param string Input string for decoding
* @return Encoded string
*/
public static String encode(String string, String charsetName)
{
if (string.length() <= 1)
{
return string;
}
CharsetProvider provider = new CharsetProvider();
Charset charset = provider.charsetForName(charsetName);
ByteBuffer byteBuffer = charset.encode(string);
return new String(byteBuffer.array()).substring(0, byteBuffer.limit());
}
}