Merged DEV/IMAP3 to HEAD

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14654 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2009-06-11 11:14:55 +00:00
parent 5b6ff13d94
commit 1a0c0dc758
51 changed files with 4726 additions and 2796 deletions

View File

@@ -414,12 +414,30 @@
<property name="compositePropertyTypes">
<map>
<entry key="imap.server.mountPoints">
<value>org.alfresco.repo.imap.config.ImapConfigMountPointsBean</value>
</entry>
<entry key="imap.ignore.extraction">
<value>org.alfresco.repo.imap.config.ImapConfigBean</value>
</entry>
</map>
</property>
</bean>
<bean id="ImapService" class="org.alfresco.repo.management.subsystems.SubsystemProxyFactory">
<property name="sourceApplicationContextFactory">
<ref bean="imap" />
</property>
<property name="sourceBeanName">
<value>ImapService</value>
</property>
<property name="interfaces">
<list>
<value>org.alfresco.repo.imap.ImapService</value>
</list>
</property>
</bean>
<bean id="fileServerConfiguration" class="org.alfresco.repo.management.subsystems.SubsystemProxyFactory">
<property name="sourceApplicationContextFactory">
<ref bean="fileServers" />

View File

@@ -437,6 +437,10 @@
class="org.alfresco.repo.content.transform.MailContentTransformer"
parent="baseContentTransformer" />
<bean id="transformer.RFC822"
class="org.alfresco.repo.content.transform.EMLTransformer"
parent="baseContentTransformer" />
<!-- Import the ImageMagick transformer from the third party subsystem -->
<bean id="transformer.worker.ImageMagick" class="org.alfresco.repo.management.subsystems.SubsystemProxyFactory">
<property name="sourceApplicationContextFactory">

View File

@@ -1,40 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<bean id="patch.imapFolders" class="org.alfresco.repo.admin.patch.impl.ImapFoldersPatch" parent="basePatch" >
<property name="id"><value>patch.imapFolders</value></property>
<property name="description"><value>patch.imapFolders.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>${version.schema}</value></property>
<property name="targetSchema"><value>10000</value></property>
<property name="importerBootstrap">
<ref bean="spacesBootstrap" />
</property>
<property name="messageSource">
<ref bean="bootstrapSpacesMessageSource" />
</property>
<property name="importerService">
<ref bean="importerComponent" />
</property>
<property name="configFoldersACP"><value>alfresco/templates/imap/imap_config_space.acp</value></property>
<property name="emailActionsACP"><value>alfresco/templates/imap/email_actions_space.acp</value></property>
<property name="scriptsACP"><value>alfresco/templates/imap/command_processor_scripts.acp</value></property>
</bean>
<bean id="patch.imapUserFolders" class="org.alfresco.repo.admin.patch.impl.ImapUsersPatch" parent="basePatch" >
<property name="id"><value>patch.imapUserFolders</value></property>
<property name="description"><value>patch.imapUserFolders.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>${version.schema}</value></property>
<property name="targetSchema"><value>10000</value></property>
<!-- helper beans for execution -->
<property name="messageSource">
<ref bean="bootstrapSpacesMessageSource" />
</property>
<property name="importerBootstrap">
<ref bean="spacesBootstrap" />
</property>
</bean>
</beans>

View File

@@ -44,7 +44,7 @@ function processCommand()
{
var message = "Unknown command: " + command;
logger.log(message);
createEmail(message, message, message, false);
createEmail(message, message, message);
return;
}
@@ -54,7 +54,7 @@ function processCommand()
if (commandFolder == null)
{
var message = "Command Processor: wrong command=" + command;
createEmail(message, message, message, false);
createEmail(message, message, message);
logger.log(message);
return;
}

View File

@@ -75,7 +75,17 @@ function createResponseTextHtml(nodes)
*/
function createContentTextHtml(nodes)
{
var content = "Command: " + title + "\n<br/><br/>\n";
var content ="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" +
"<html><head>" +
"<meta http-equiv=Content-Type content=\"text/html; charset=UTF-8\">" +
"<style type=\"text/css\">" +
"* {font-family:Verdana,Arial,sans-serif;font-size:11px;}" +
".links {border:1px dotted #555555;border-collapse:collapse;width:99%;}" +
".links td {border:1px dotted #555555;padding:5px;}" +
"</style>" +
"</head>" +
"<body>" +
"<div>" + "Command: " + title + "\n<br/><br/>\n";
content += "<table class=\"links\">\n";
content += "<thead align=\"center\">";
content += "<tr>";
@@ -83,7 +93,7 @@ function createContentTextHtml(nodes)
content += "<td>Url</td>";
content += "<td>Download Url</td>";
content += "</tr>";
content += "</thead>\n"
content += "</thead>\n" + "</div></body></html>";
for (var i = 0; i < nodes.length; i++)
@@ -133,7 +143,7 @@ function commandSearch(params)
if (query == null)
{
createEmail(errorParameter, errorParameter, errorParameter, false);
createEmail(errorParameter, errorParameter, errorParameter);
return;
}
@@ -154,7 +164,7 @@ function commandSearch(params)
}
else
{
createEmail(errorXPathNotValid, errorXPathNotValid, errorXPathNotValid, false);
createEmail(errorXPathNotValid, errorXPathNotValid, errorXPathNotValid);
return;
}
break;
@@ -180,8 +190,8 @@ function commandSearch(params)
createEmail(message, message, subject);
return;
}
/*createEmail(createResponseTextHtml(nodes), createContentTextPlain(nodes), subject, true);*/
createEmail(createContentTextHtml(nodes), createContentTextPlain(nodes), subject, false);
/*createEmail(createContentTextPlain(nodes), createResponseTextHtml(nodes), subject);*/
createEmail(createContentTextPlain(nodes), createContentTextHtml(nodes), subject);
}
/**
* Decode subject
@@ -224,13 +234,13 @@ function main()
else
{
var message = unknownCommand + ": '" + title + "'";
createEmail(message, message, message, false);
createEmail(message, message, message);
}
}
else
{
var message = unknownCommand + ": '" + title + "'";
createEmail(message, message, message, false);
createEmail(message, message, message);
}
document.remove();

View File

@@ -1,9 +1,8 @@
/**
* Create e-mail
* contentTextHtml (string) html content
* contentTextPlain (string) text content
* contentEML (string) content message
*/
function createEmail(contentTextHtml, contentTextPlain, subject, templateUsed)
function createEmail(messageTXT, messageHTML, subject)
{
var command = document.properties["cm:title"];
var userName = person.properties["cm:userName"];
@@ -15,42 +14,42 @@ function createEmail(contentTextHtml, contentTextPlain, subject, templateUsed)
return;
}
var nextMessageUID = inboxFolder.properties["imap:nextMessageUID"];
inboxFolder.properties["imap:nextMessageUID"] = nextMessageUID + 1;
inboxFolder.save();
var response = inboxFolder.createNode("response" + Date.now(), "imap:imapContent");
var response = inboxFolder.createNode("response" + Date.now() + ".eml", "cm:content");
response.properties["imap:messageFrom"] = "command@alfresco.com";
response.properties["imap:messageSubject"] = subject;
response.properties["imap:messageTo"] = document.properties["cm:originator"];
response.properties["imap:messageCc"] = "";
response.properties["imap:messageUID"] = nextMessageUID;
response.addAspect("imap:imapContent", null);
response.content = createRFC822Message("command@alfresco.com", document.properties["cm:originator"], subject, messageTXT, messageHTML);
response.save();
var textBody = response.createNode("Body.txt", "imap:imapBody");
textBody.content = contentTextPlain;
textBody.save();
var htmlBody = response.createNode("Body.html", "imap:imapBody");
if (templateUsed == true)
{
htmlBody.content = contentTextHtml;
}
else
{
htmlBody.content = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" +
"<html><head>" +
"<meta http-equiv=Content-Type content=\"text/html; charset=UTF-8\">" +
"<style type=\"text/css\">" +
"* {font-family:Verdana,Arial,sans-serif;font-size:11px;}" +
".links {border:1px dotted #555555;border-collapse:collapse;width:99%;}" +
".links td {border:1px dotted #555555;padding:5px;}" +
"</style>" +
"</head>" +
"<body>" +
"<div>" + contentTextHtml + "</div></body></html>";
}
htmlBody.save();
function createRFC822Message(from, to, subject, textPart, htmlPart)
{
var id = new Number(Date.now()).toString(16);
var boundary = "----------" + id;
var date = new Date().toGMTString();
var messageHeaders = "MIME-Version: 1.0\r\n" +
"Date: " + date + "\r\n" +
"From: " + from + "\r\n" +
"To: " + to + "\r\n" +
"Subject: " + subject + "\r\n" +
"Message-ID: " + id + "\r\n" +
"X-Priority: 3 (Normal)\r\n" +
"Content-Type: multipart/alternative; boundary=\"" + boundary + "\"\r\n\r\n";
var messageBody = "";
messageBody += messageHeaders;
messageBody += "--" + boundary + "\r\n";
messageBody += "Content-Type: text/plain; charset=utf-8\r\n";
//TODO Content-Transfer-Encoding
messageBody += "\r\n";
messageBody += textPart + "\r\n\r\n";
messageBody += "--" + boundary + "\r\n";
messageBody += "Content-Type: text/html; charset=utf-8\r\n";
//TODO Content-Transfer-Encoding
messageBody += "\r\n";
messageBody += htmlPart + "\r\n\r\n";
messageBody += "--" + boundary + "--\r\n\r\n";
return messageBody;
}

View File

@@ -7,4 +7,8 @@ imap.server.info.message_body_not_found = "The message body parts are not found.
# Error messages. prefix 'imap.server.error'
imap.server.error.properties_dont_exist = "Appropriate properties do not exist."
imap.server.error.permission_denied = "Cannot create folder - Permission denied."
imap.server.error.folder_already_exist = "Folder already exists."
imap.server.error.mailbox_name_is_mandatory = "Mailbox name is mandatory parameter."
imap.server.error.cannot_get_a_folder = "Cannot get a folder with name ''{0}''."

View File

@@ -353,6 +353,9 @@
</mimetype>
<mimetype mimetype="image/x-dwt" display="AutoCAD Template">
<extension>dwt</extension>
</mimetype>
<mimetype mimetype="message/rfc822" display="EMail">
<extension>eml</extension>
</mimetype>
<mimetype mimetype="application/vnd.ms-outlook" display="Outlook MSG">
<extension>msg</extension>

View File

@@ -19,7 +19,7 @@
<title>IMAP Folder</title>
<parent>cm:folder</parent>
<properties>
<property name="imap:subscribed">
<property name="imap:nonSubscribed">
<type>d:boolean</type>
</property>
<property name="imap:selectable">
@@ -30,29 +30,6 @@
</properties>
</type>
<type name="imap:imapContent">
<title>IMAP File</title>
<parent>cm:folder</parent>
<properties>
<!-- Message -->
<property name="imap:messageFrom">
<type>d:text</type>
</property>
<property name="imap:messageTo">
<type>d:text</type>
</property>
<property name="imap:messageCc">
<type>d:text</type>
</property>
<property name="imap:messageSubject">
<type>d:text</type>
</property>
</properties>
<mandatory-aspects>
<aspect>imap:flaggable</aspect>
</mandatory-aspects>
</type>
<type name="imap:imapAttach">
<title>Attachment to the IMAP message</title>
<parent>cm:content</parent>
@@ -69,11 +46,63 @@
</type>
</types>
<aspects>
<aspect name="imap:flaggable">
<properties>
<aspects>
<aspect name="imap:imapContent">
<title>IMAP File</title>
<parent>imap:flaggable</parent>
<properties>
<!-- Message -->
<property name="imap:messageFrom">
<type>d:text</type>
</property>
<property name="imap:messageTo">
<type>d:text</type>
</property>
<property name="imap:messageCc">
<type>d:text</type>
</property>
<property name="imap:messageSubject">
<type>d:text</type>
</property>
<property name="imap:messageId">
<type>d:text</type>
</property>
<property name="imap:threadIndex">
<type>d:text</type>
</property>
</properties>
<associations>
<association name="imap:attachment">
<title>Attachment</title>
<source>
<mandatory>false</mandatory>
<many>false</many>
</source>
<target>
<class>cm:cmobject</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</association>
<association name="imap:attachmentsFolder">
<title>Attachments Folder</title>
<source>
<mandatory>false</mandatory>
<many>false</many>
</source>
<target>
<class>cm:cmobject</class>
<mandatory>false</mandatory>
<many>false</many>
</target>
</association>
</associations>
</aspect>
<aspect name="imap:flaggable">
<properties>
<property name="imap:flagAnswered">
<type>d:boolean</type>
<type>d:boolean</type>
</property>
<property name="imap:flagDeleted">
<type>d:boolean</type>
@@ -90,11 +119,10 @@
<property name="imap:flagFlagged">
<type>d:boolean</type>
</property>
</properties>
</aspect>
<aspect name="imap:subscribed"/>
<aspect name="imap:nonselectable"/>
</aspect>
<aspect name="imap:nonSubscribed"/>
<aspect name="imap:nonselectable"/>
</aspects>
</model>

View File

@@ -1808,4 +1808,40 @@
</property>
</bean>
<bean id="patch.imapFolders" class="org.alfresco.repo.admin.patch.impl.ImapFoldersPatch" parent="basePatch" >
<property name="id"><value>patch.imapFolders</value></property>
<property name="description"><value>patch.imapFolders.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>2012</value></property>
<property name="targetSchema"><value>2013</value></property>
<property name="importerBootstrap">
<ref bean="spacesBootstrap" />
</property>
<property name="messageSource">
<ref bean="bootstrapSpacesMessageSource" />
</property>
<property name="importerService">
<ref bean="importerComponent" />
</property>
<property name="configFoldersACP"><value>alfresco/templates/imap/imap_config_space.acp</value></property>
<property name="emailActionsACP"><value>alfresco/templates/imap/email_actions_space.acp</value></property>
<property name="scriptsACP"><value>alfresco/templates/imap/command_processor_scripts.acp</value></property>
</bean>
<bean id="patch.imapUserFolders" class="org.alfresco.repo.admin.patch.impl.ImapUsersPatch" parent="basePatch" >
<property name="id"><value>patch.imapUserFolders</value></property>
<property name="description"><value>patch.imapUserFolders.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>2012</value></property>
<property name="targetSchema"><value>2013</value></property>
<!-- helper beans for execution -->
<property name="messageSource">
<ref bean="bootstrapSpacesMessageSource" />
</property>
<property name="importerBootstrap">
<ref bean="spacesBootstrap" />
</property>
</bean>
</beans>

View File

@@ -338,14 +338,22 @@ nfs.user.mappings.default.gid=0
# IMAP
imap.server.enabled=false
imap.server.port=143
imap.server.host=localhost
imap.server.attachments.extraction.enabled=true
# Default IMAP mount points
imap.server.mountPoints=Repository_virtual,Repository_archive
imap.server.mountPoints=Repository_virtual,Repository_archive,Repository_mixed
imap.server.mountPoints.default.store=${spaces.store}
imap.server.mountPoints.default.rootPath=/${spaces.company_home.childname}
imap.server.mountPoints.default.mode=virtual
imap.server.mountPoints.value.Repository_virtual.mode=virtual
imap.server.mountPoints.value.Repository_archive.mode=archive
imap.server.mountPoints.value.Repository_mixed.mode=mixed
# Folders that will be excluded from the automatic extraction capability
#imap.ignore.extraction=adminInbox
#imap.ignore.extraction.value.adminInbox.store=${spaces.store}
#imap.ignore.extraction.value.adminInbox.rootPath=/${spaces.company_home.childname}/imap:imap_home/cm:admin/cm:INBOX
# Activity feed max size and max age (eg. 44640 mins = 31 days)
activities.feed.max.size=100

View File

@@ -11,41 +11,10 @@
</property>
</bean>
<bean id="imapHelper" class="org.alfresco.repo.imap.ImapHelper">
<property name="nodeService" ref="NodeService" />
<property name="fileFolderService" ref="FileFolderService" />
<property name="searchService" ref="SearchService" />
<property name="namespaceService" ref="NamespaceService" />
<property name="templateService" ref="TemplateService" />
<property name="permissionService" ref="PermissionService" />
<property name="dictionaryService" ref="DictionaryService" />
<property name="preferenceService" ref="PreferenceService" />
<property name="siteService" ref="SiteService" />
<property name="patchService" ref="patchComponent" />
<property name="serviceRegistry" ref="ServiceRegistry" />
<property name="imapConfigBeans">
<ref bean="imap.server.mountPoints" />
</property>
<property name="defaultFromAddress">
<value>${mail.from.default}</value>
</property>
<property name="webApplicationContextUrl">
<value>${web.application.context.url}</value>
</property>
<property name="imapRoot">
<value>${spaces.store}/${spaces.company_home.childname}/${spaces.imap_home.childname}</value>
</property>
<property name="repositoryTemplatePath">
<value>${spaces.store}/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.imapConfig.childname}/${spaces.imap_templates.childname}</value>
</property>
</bean>
<bean id="imapServer" class="org.alfresco.repo.imap.AlfrescoImapServer">
<property name="host">
<value>${imap.server.host}</value>
</property>
<property name="port">
<value>${imap.server.port}</value>
</property>
@@ -58,16 +27,13 @@
<property name="imapUserManager">
<ref local="imapUserManager" />
</property>
<property name="imapHelper">
<ref local="imapHelper" />
</property>
</bean>
<!--The configurable list of mount points - actually a post-processed composite property! -->
<bean id="imap.server.mountPoints" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<bean id="Repository_virtual" class="org.alfresco.repo.imap.config.ImapConfigBean">
<bean id="Repository_virtual" class="org.alfresco.repo.imap.config.ImapConfigMountPointsBean">
<property name="mode">
<value>virtual</value>
</property>
@@ -78,7 +44,7 @@
<value>/${spaces.company_home.childname}</value>
</property>
</bean>
<bean id="Repository_archive" class="org.alfresco.repo.imap.config.ImapConfigBean">
<bean id="Repository_archive" class="org.alfresco.repo.imap.config.ImapConfigMountPointsBean">
<property name="mode">
<value>archive</value>
</property>
@@ -93,17 +59,101 @@
</property>
</bean>
<!--The configurable list of folders that ignore attachment extraction - actually a post-processed composite property! -->
<bean id="imap.ignore.extraction" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<bean id="ignored" class="org.alfresco.repo.imap.config.ImapConfigBean">
<property name="store">
<value>${spaces.store}</value>
</property>
<property name="rootPath">
<value>/${spaces.company_home.childname}</value>
</property>
</bean>
</list>
</property>
</bean>
<bean id="ImapService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="search*">${server.transaction.mode.readOnly}</prop>
<prop key="getFolder">${server.transaction.mode.default}</prop>
<prop key="getFlags">${server.transaction.mode.default}</prop>
<prop key="get*">${server.transaction.mode.readOnly}</prop>
<prop key="*">${server.transaction.mode.default}</prop>
</props>
</property>
</bean>
<bean id="imapService" class="org.alfresco.repo.imap.ImapServiceImpl" init-method="init">
<property name="fileFolderService">
<ref bean="FileFolderService"/>
</property>
<property name="nodeService">
<ref bean="NodeService"/>
</property>
<property name="serviceRegistry">
<ref bean="ServiceRegistry"/>
</property>
<property name="imapConfigMountPointsBeans">
<ref bean="imap.server.mountPoints" />
</property>
<property name="ignoreExtractionFolders">
<ref bean="imap.ignore.extraction" />
</property>
<property name="imapRoot">
<value>${spaces.store}/${spaces.company_home.childname}/${spaces.imap_home.childname}</value>
</property>
<property name="defaultFromAddress">
<value>${mail.from.default}</value>
</property>
<property name="webApplicationContextUrl">
<value>${web.application.context.url}</value>
</property>
<property name="repositoryTemplatePath">
<value>${spaces.store}/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.imapConfig.childname}/${spaces.imap_templates.childname}</value>
</property>
<property name="extractAttachmentsEnabled">
<value>${imap.server.attachments.extraction.enabled}</value>
</property>
</bean>
<!-- Public Imap Service -->
<bean id="ImapService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>org.alfresco.repo.imap.ImapService</value>
</list>
</property>
<property name="target">
<ref bean="imapService"/>
</property>
<property name="interceptorNames">
<list>
<idref local="ImapService_transaction"/>
</list>
</property>
</bean>
<bean id="imapHostManager" class="org.alfresco.repo.imap.AlfrescoImapHostManager">
<property name="serviceRegistry" ref="ServiceRegistry" />
<property name="nodeService" ref="NodeService" />
<property name="fileFolderService" ref="FileFolderService" />
<property name="imapHelper" ref="imapHelper" />
<property name="imapService">
<ref bean="ImapService" />
</property>
<property name="transactionService">
<ref bean="transactionService" />
</property>
</bean>
<bean id="imapUserManager" class="org.alfresco.repo.imap.AlfrescoImapUserManager">
<property name="imapHostManager">
<ref local="imapHostManager" />
</property>
<property name="authenticationService" ref="AuthenticationService" />
<property name="personService" ref="PersonService" />
<property name="nodeService" ref="NodeService" />

View File

@@ -1,4 +1,5 @@
imap.server.enabled=false
imap.server.host=localhost
imap.server.port=143
#imap.server.web.application.context.url=http://localhost:8080/alfresco

View File

@@ -84,7 +84,7 @@
<legend> Content links </legend>
<table class="links">
<tr>
<td>Content folder:</td><td><a href="${contextUrl}${document.displayPath}">${contextUrl}${document.displayPath}</a></td>
<td>Content folder:</td><td><a href="${contextUrl}/navigate/browse${document.displayPath}">${contextUrl}/navigate/browse${document.displayPath}</a></td>
</tr>
<tr>
<td>Content URL:</td><td><a href="${contextUrl}${document.url}">${contextUrl}${document.url}</a></td>
@@ -101,7 +101,7 @@
<legend> Start Workflow </legend>
<form id="start_workflow"
name="start_workflow"
method="get"
method="post"
action="${contextUrl}/service/imap/start-workflow"
enctype="application/x-www-form-urlencoded">
<input type="hidden" name="alfTicket" value="${alfTicket}" />

View File

@@ -21,7 +21,7 @@ Size: ${document.size / 1024} Kb
CONTENT LINKS
Content folder: ${contextUrl}${document.displayPath}
Content folder: ${contextUrl}/navigate/browse${document.displayPath}
Content URL: ${contextUrl}${document.url}
Download URL: ${contextUrl}${document.downloadUrl}
WebDAV URL: ${contextUrl}${document.webdavUrl}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
* 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

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
* 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
@@ -49,6 +49,8 @@ import org.apache.commons.logging.LogFactory;
/**
* Concrete representation of an email message as implemented for the SubEtha mail server.
*
* @author Mike Shavnev
*
* @since 2.2
*/
public class SubethaEmailMessage implements EmailMessage

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,5 +1,5 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
* 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

View File

@@ -41,6 +41,7 @@ import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.view.Location;
import org.springframework.context.MessageSource;
import org.springframework.core.io.ClassPathResource;
/**
* Builds folders tree necessary for IMAP functionality and imports email action scripts.
*
@@ -176,6 +177,7 @@ public class ImapFoldersPatch extends AbstractPatch
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
}
this.dictionaryNodeRef = nodeRefs.get(0);
sb.append("/").append(scriptsChildName);
xpath = sb.toString();
nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false);
@@ -188,6 +190,7 @@ public class ImapFoldersPatch extends AbstractPatch
throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs);
}
this.scriptsNodeRef = nodeRefs.get(0);
// get the ImapConfig node
sb.delete((sb.length() - (scriptsChildName.length() + 1)), sb.length());
sb.append("/").append(imapConfigChildName);

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,694 @@
/*
* 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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 static Log logger = LogFactory.getLog(AbstractImapFolder.class);
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.: <p/>
* #mail.admin."Repository_archive.Data Dictionary.Space Templates.Software Engineering Project" <p/> 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,368 @@
/*
* 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";

View File

@@ -24,27 +24,11 @@
*/
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.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -53,7 +37,6 @@ 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
@@ -63,11 +46,8 @@ 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.
@@ -90,18 +70,14 @@ 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);
}
/**
@@ -116,251 +92,14 @@ public class AlfrescoImapHostManager implements ImapHostManager
*/
public Collection<MailFolder> listSubscribedMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException
{
mailboxPattern = GreenMailUtil.convertFromUtf7(mailboxPattern);
if (logger.isDebugEnabled())
try
{
logger.debug("Listing subscribed mailboxes: mailboxPattern=" + mailboxPattern);
return new ArrayList<MailFolder>(imapService.listSubscribedMailboxes(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxPattern));
}
mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern);
if (logger.isDebugEnabled())
catch (Throwable e)
{
logger.debug("Listing subscribed mailboxes: mailboxPattern in alfresco=" + mailboxPattern);
throw new FolderException(e.getMessage());
}
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;
}
/**
@@ -375,97 +114,16 @@ 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())
try
{
logger.debug("Renaming folder: oldMailboxName=" + oldMailboxName + " newMailboxName=" + newMailboxName);
imapService.renameMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), oldMailboxName, 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())
catch (Throwable e)
{
String folderName = tokenizer.nextToken();
if (!tokenizer.hasMoreTokens())
{
try
{
if (oldMailboxName.equalsIgnoreCase(AlfrescoImapConst.INBOX_NAME))
{
// If you trying to rename INBOX
// - just copy it to another folder with new name
// and leave INBOX (with children) intact.
fileFolderService.copy(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
List<FileInfo> itemsForRemove = fileFolderService.list(sourceNode.getFolderInfo().getNodeRef());
for (FileInfo fileInfo : itemsForRemove)
{
fileFolderService.delete(fileInfo.getNodeRef());
}
}
else
{
fileFolderService.move(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName);
}
return;
}
catch (FileExistsException e)
{
throw new FolderException(FolderException.ALREADY_EXISTS_LOCALLY);
}
catch (FileNotFoundException e)
{
if (logger.isDebugEnabled())
{
logger.error(e);
}
}
}
else
{
List<FileInfo> folders = imapHelper.searchFolders(parentNodeRef, folderName, false, true);
if (folders.size() == 0)
{
AccessStatus status = imapHelper.hasPermission(parentNodeRef, PermissionService.WRITE);
if (status == AccessStatus.DENIED)
{
if (logger.isDebugEnabled())
{
logger.debug("Creating folder: Cant't create folder - Permission denied");
}
throw new AlfrescoImapFolderException(AlfrescoImapFolderException.PERMISSION_DENIED);
}
if (logger.isDebugEnabled())
{
logger.debug("Create mailBox: " + folderName);
}
FileFolderServiceImpl.makeFolders(fileFolderService, parentNodeRef, Arrays.asList(folderName), ContentModel.TYPE_FOLDER);
}
else
{
parentNodeRef = folders.get(0).getNodeRef();
if (logger.isDebugEnabled())
{
logger.debug("MailBox: " + folderName + " already exists");
}
}
}
throw new FolderException(e.getMessage());
}
}
/**
@@ -480,58 +138,14 @@ public class AlfrescoImapHostManager implements ImapHostManager
*/
public MailFolder createMailbox(GreenMailUser user, String mailboxName) throws AuthorizationException, FolderException
{
mailboxName = GreenMailUtil.convertFromUtf7(mailboxName);
if (logger.isDebugEnabled())
try
{
logger.debug("Creating folder: " + mailboxName);
return imapService.createMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName);
}
NodeRef root = imapHelper.getMailboxRootRef(mailboxName, user.getLogin());
String mountPointName = imapHelper.getMountPointName(mailboxName);
String mailboxRepoNam = imapHelper.getMailPathInRepo(mailboxName);
StringTokenizer tokenizer = new StringTokenizer(mailboxRepoNam, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER));
NodeRef parentNodeRef = root;
while (tokenizer.hasMoreTokens())
catch (Throwable e)
{
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(e.getMessage());
}
throw new FolderException(FolderException.ALREADY_EXISTS_LOCALLY);
}
/**
@@ -544,34 +158,13 @@ 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())
try
{
folder.signalDeletion();
// Delete child folders and messages
fileFolderService.delete(nodeRef);
imapService.deleteMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName);
}
else
catch (Throwable e)
{
if (folder.isSelectable())
{
// Delete all messages for this folder
// Don't delete subfolders and their messages
List<FileInfo> messages = imapHelper.searchFiles(nodeRef, "*", ImapModel.TYPE_IMAP_CONTENT, false);
for (FileInfo message : messages)
{
fileFolderService.delete(message.getNodeRef());
}
nodeService.addAspect(nodeRef, ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE, null);
}
else
{
throw new FolderException(mailboxName + " - Can't delete a non-selectable store with children.");
}
throw new FolderException(e.getMessage());
}
}
@@ -586,80 +179,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 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");
return imapService.getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName);
}
/**
* Simply calls {@link #getFolder(GreenMailUser, String)}. <p/> Added to implement {@link ImapHostManager}.
*/
public MailFolder getFolder(GreenMailUser user, String mailboxName, boolean mustExist) throws FolderException
public MailFolder getFolder(final GreenMailUser user, final String mailboxName, boolean mustExist) throws FolderException
{
return getFolder(user, mailboxName);
try
{
return getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName);
}
catch (Throwable e)
{
throw new FolderException(e.getMessage());
}
}
/**
@@ -670,7 +205,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());
}
}
/**
@@ -687,39 +229,16 @@ public class AlfrescoImapHostManager implements ImapHostManager
* @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 +247,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;
}
}

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

@@ -47,14 +47,14 @@ public class AlfrescoImapServer extends AbstractLifecycleBean
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 +65,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 +80,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 +96,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

@@ -26,7 +26,6 @@ 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;
@@ -42,14 +41,11 @@ public class AlfrescoImapUser implements GreenMailUser
private char[] password;
private String email;
private ImapHostManager imapHostManager;
public AlfrescoImapUser(String email, String login, String password, ImapHostManager imapHostManager)
public AlfrescoImapUser(String email, String login, String password)
{
this.email = email;
this.userName = login;
this.password = password.toCharArray();
this.imapHostManager = imapHostManager;
}
public void authenticate(String password) throws UserException
@@ -71,12 +67,12 @@ public class AlfrescoImapUser implements GreenMailUser
public void deliver(MovingMessage msg) throws UserException
{
throw new UnsupportedOperationException();
}
public void deliver(MimeMessage msg) throws UserException
{
throw new UnsupportedOperationException();
}
public String getEmail()

View File

@@ -37,7 +37,6 @@ 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;
@@ -50,7 +49,6 @@ 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;
@@ -61,17 +59,11 @@ public class AlfrescoImapUserManager extends UserManager
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);
GreenMailUser user = new AlfrescoImapUser(email, login, password);
user.create();
addUser(user);
return user;
@@ -129,7 +121,7 @@ public class AlfrescoImapUserManager extends UserManager
NodeRef personNodeRef = personService.getPerson(userid);
email = (String) nodeService.getProperty(personNodeRef, ContentModel.PROP_EMAIL);
}
GreenMailUser user = new AlfrescoImapUser(email, userid, password, imapHostManager);
GreenMailUser user = new AlfrescoImapUser(email, userid, password);
addUser(user);
}
catch (AuthenticationException ex)
@@ -140,16 +132,6 @@ public class AlfrescoImapUserManager extends UserManager
return true;
}
public ImapHostManager getImapHostManager()
{
return this.imapHostManager;
}
public void setImapHostManager(ImapHostManager imapHostManager)
{
this.imapHostManager = imapHostManager;
}
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;

View File

@@ -0,0 +1,99 @@
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) ? MimeUtility.encodeText(messageFileInfo.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 = 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,144 @@
/*
* 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;
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 ImapModelMessage extends AbstractMimeMessage
{
private static Log logger = LogFactory.getLog(ImapModelMessage.class);
/**
* 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,75 @@
/*
* 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
*/
public interface ImapService
{
public List<AlfrescoImapFolder> listMailboxes(AlfrescoImapUser user, String mailboxPattern);
public List<AlfrescoImapFolder> listSubscribedMailboxes(AlfrescoImapUser user, String mailboxPattern);
public AlfrescoImapFolder createMailbox(AlfrescoImapUser user, String mailboxName);
public void deleteMailbox(AlfrescoImapUser user, String mailboxName);
public void renameMailbox(AlfrescoImapUser user, String oldMailboxName, String newMailboxName);
public AlfrescoImapFolder getFolder(AlfrescoImapUser user, String mailboxName);
public void subscribe(AlfrescoImapUser user, String mailbox);
public void unsubscribe(AlfrescoImapUser user, String mailbox);
public List<FileInfo> searchFiles(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders);
public List<FileInfo> searchFolders(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders, String viewMode);
public List<FileInfo> searchMails(NodeRef contextNodeRef, String namePattern, String viewMode, boolean includeSubFolders);
public Flags getFlags(FileInfo messageFileInfo);
public void setFlags(FileInfo messageFileInfo, Flags flags, boolean value);
public void setFlag(FileInfo messageFileInfo, Flag flag, boolean value);
public String getDefaultFromAddress();
public String getRepositoryTemplatePath();
public String getWebApplicationContextUrl();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,417 @@
package org.alfresco.repo.imap;
import java.io.IOException;
import java.io.Serializable;
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.node.integrity.IntegrityChecker;
import org.alfresco.repo.security.permissions.AccessDeniedException;
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.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 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 final 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 AlfrescoImapUser user;
private ImapService imapService;
private UserTransaction txn;
private NodeRef companyHomeNodeRef;
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();
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);
companyHomeNodeRef = nodeRefs.get(0);
/*
* Importing test folders:
*
* "Company Home" 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("test-resources/imapservice_test_folder_a.acp", companyHomeNodeRef);
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(companyHomeNodeRef, 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(companyHomeNodeRef, 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(companyHomeNodeRef, 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(companyHomeNodeRef, 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(companyHomeNodeRef, 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(companyHomeNodeRef, 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(companyHomeNodeRef, FILE_PATTERN, true);
assertNotNull(fi);
assertTrue(fi.size() > 0);
}
public void testSearchMails() throws Exception
{
List<FileInfo> fi = imapService.searchMails(companyHomeNodeRef, "*", 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(companyHomeNodeRef, "*", 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(companyHomeNodeRef, anotherUserName, PermissionService.WRITE, true);
reauthenticate(anotherUserName, anotherUserName);
setFlags(messageFileInfo);
}
}
public void testSetFlag() throws Exception
{
List<FileInfo> fis = imapService.searchMails(companyHomeNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true);
if (fis != null && fis.size() > 0)
{
FileInfo messageFileInfo = fis.get(0);
reauthenticate(USER_NAME, USER_PASSWORD);
permissionService.setPermission(companyHomeNodeRef, 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(companyHomeNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true);
if (fis != null && fis.size() > 0)
{
FileInfo messageFileInfo = fis.get(0);
reauthenticate(USER_NAME, USER_PASSWORD);
permissionService.setPermission(companyHomeNodeRef, 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,154 @@
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 LoadTest extends TestCase
{
private Log logger = LogFactory.getLog(LoadTest.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_imap3";
private static final long MESSAGE_QUANTITY = 3;
@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);
// FileInfo fileInfo = imapService.searchMails(drafts.getFolderInfo().getNodeRef(), "*", AlfrescoImapConst.MODE_ARCHIVE, false).get(0);
// SimpleStoredMessage m = drafts.createImapMessage(fileInfo, (long)0, true);
// imapService.setExtractAttachmentsEnabled(false);
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.appendMessageInternal(message, new Flags(), new Date());
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,161 @@
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 RemoteLoadTest extends TestCase
{
private Log logger = LogFactory.getLog(LoadTest.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();
// 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();
}
}
}
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

@@ -0,0 +1,189 @@
/*
* 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_TRANSFER_ENCODING;
import static org.alfresco.repo.imap.AlfrescoImapConst.UTF_8;
import java.io.IOException;
import java.io.InputStream;
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.model.ImapModel;
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;
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 SimpleImapMessage extends AbstractMimeMessage
{
private static Log logger = LogFactory.getLog(SimpleImapMessage.class);
/**
* Constructs {@link SimpleImapMessage} 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 SimpleImapMessage(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
{
final NodeRef nodeRef = messageFileInfo.getNodeRef();
if (serviceRegistry.getNodeService().hasAspect(nodeRef, ImapModel.ASPECT_IMAP_CONTENT))
{
buildRFC822Message();
}
else
{
buildContentModelMessage();
}
}
private void buildRFC822Message() throws MessagingException
{
ContentService contentService = serviceRegistry.getContentService();
ContentReader reader = contentService.getReader(messageFileInfo.getNodeRef(), ContentModel.PROP_CONTENT);
try
{
InputStream inputStream = reader.getContentInputStream();
this.parse(inputStream);
inputStream.close();
}
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);
}
}
/**
* 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) ? MimeUtility.encodeText(messageFileInfo.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 = 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

@@ -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

@@ -31,6 +31,7 @@ import org.alfresco.cmis.CMISQueryService;
import org.alfresco.cmis.CMISServices;
import org.alfresco.mbeans.VirtServerRegistry;
import org.alfresco.repo.forms.FormService;
import org.alfresco.repo.imap.ImapService;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ActionService;
@@ -544,4 +545,13 @@ public class ServiceDescriptorRegistry
{
return (CMISQueryService)getService(CMIS_QUERY_SERVICE);
}
/*
* (non-Javadoc)
* @see org.alfresco.service.ServiceRegistry#getCMISQueryService()
*/
public ImapService getImapService()
{
return (ImapService)getService(IMAP_SERVICE);
}
}

View File

@@ -31,6 +31,7 @@ import org.alfresco.cmis.CMISQueryService;
import org.alfresco.cmis.CMISServices;
import org.alfresco.mbeans.VirtServerRegistry;
import org.alfresco.repo.forms.FormService;
import org.alfresco.repo.imap.ImapService;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.attributes.AttributeService;
@@ -149,6 +150,7 @@ public interface ServiceRegistry
static final QName CMIS_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CMISService");
static final QName CMIS_DICTIONARY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CMISDictionaryService");
static final QName CMIS_QUERY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CMISQueryService");
static final QName IMAP_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ImapService");
/**
@@ -506,4 +508,11 @@ public interface ServiceRegistry
*/
@NotAuditable
CMISQueryService getCMISQueryService();
/**
* Get the IMAP service (or null if one is not provided)
* @return the IMAP service
*/
@NotAuditable
ImapService getImapService();
}

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());
}
}

Binary file not shown.

Binary file not shown.