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

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

View File

@@ -473,6 +473,9 @@
<property name="companyHomePath">
<value>/${spaces.company_home.childname}</value>
</property>
<property name="webApplicationContextUrl">
<value>${web.application.context.url}</value>
</property>
</bean>
<bean id="counter" class="org.alfresco.repo.action.executer.CounterIncrementActionExecuter" parent="action-executer">

View File

@@ -52,6 +52,7 @@
<import resource="classpath:alfresco/form-services-context.xml"/>
<import resource="classpath:alfresco/invitation-service-context.xml"/>
<import resource="classpath:alfresco/cmis-api-context.xml" />
<import resource="classpath:alfresco/imap-server-context.xml"/>
<import resource="classpath*:alfresco/patch/*-context.xml" />
<import resource="classpath*:alfresco/domain/*-context.xml" />

View File

@@ -1,6 +1,7 @@
<view:view xmlns:view="http://www.alfresco.org/view/repository/1.0"
xmlns:cm="http://www.alfresco.org/model/content/1.0"
xmlns:app="http://www.alfresco.org/model/application/1.0">
xmlns:app="http://www.alfresco.org/model/application/1.0"
xmlns:emailserver="http://www.alfresco.org/model/emailserver/1.0">
<!-- NOTE: all replaced properties referenced from repository.properties file must also be
mapped in the bootstrap-context.xml spacesBootstrap/configuration section -->
@@ -24,34 +25,34 @@
<view:permission>Consumer</view:permission>
</view:ace>
</view:acl>
<app:uifacets/>
<cm:name>${spaces.dictionary.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.dictionary.name}</cm:title>
<cm:description>${spaces.dictionary.description}</cm:description>
<cm:contains>
<cm:folder view:childName="${spaces.templates.childname}">
<app:uifacets/>
<cm:name>${spaces.templates.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.templates.name}</cm:title>
<cm:description>${spaces.templates.description}</cm:description>
</cm:folder>
<cm:folder
view:childName="${spaces.templates.content.childname}">
<app:uifacets/>
<cm:name>${spaces.templates.content.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.templates.content.name}</cm:title>
<cm:description>${spaces.templates.content.description}</cm:description>
</cm:folder>
<cm:folder
view:childName="${spaces.templates.email.childname}">
<app:uifacets/>
<cm:name>${spaces.templates.email.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.templates.email.name}</cm:title>
<cm:description>${spaces.templates.email.description}</cm:description>
<app:uifacets/>
<cm:name>${spaces.dictionary.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.dictionary.name}</cm:title>
<cm:description>${spaces.dictionary.description}</cm:description>
<cm:contains>
<cm:folder view:childName="${spaces.templates.childname}">
<app:uifacets/>
<cm:name>${spaces.templates.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.templates.name}</cm:title>
<cm:description>${spaces.templates.description}</cm:description>
</cm:folder>
<cm:folder
view:childName="${spaces.templates.content.childname}">
<app:uifacets/>
<cm:name>${spaces.templates.content.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.templates.content.name}</cm:title>
<cm:description>${spaces.templates.content.description}</cm:description>
</cm:folder>
<cm:folder
view:childName="${spaces.templates.email.childname}">
<app:uifacets/>
<cm:name>${spaces.templates.email.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.templates.email.name}</cm:title>
<cm:description>${spaces.templates.email.description}</cm:description>
<cm:contains>
<cm:folder
view:childName="${spaces.templates.email.invite.childname}">
@@ -80,69 +81,68 @@
</cm:contains>
</cm:folder>
</cm:contains>
</cm:folder>
<cm:folder
view:childName="${spaces.templates.rss.childname}">
<view:acl>
<view:ace view:access="ALLOWED">
<view:authority>${alfresco_user_store.guestusername}</view:authority>
<view:permission>Consumer</view:permission>
</view:ace>
</view:acl>
<app:uifacets/>
<cm:name>${spaces.templates.rss.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.templates.rss.name}</cm:title>
<cm:description>${spaces.templates.rss.description}</cm:description>
</cm:folder>
<cm:folder
view:childName="${spaces.savedsearches.childname}">
<view:acl view:inherit="false">
<view:ace view:access="ALLOWED">
<view:authority>GROUP_EVERYONE</view:authority>
<view:permission>Contributor</view:permission>
</view:ace>
</view:acl>
<app:uifacets/>
<cm:name>${spaces.savedsearches.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.savedsearches.name}</cm:title>
<cm:description>${spaces.savedsearches.description}</cm:description>
</cm:folder>
<cm:folder view:childName="${spaces.scripts.childname}">
<app:uifacets/>
<cm:name>${spaces.scripts.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.scripts.name}</cm:title>
<cm:description>${spaces.scripts.description}</cm:description>
</cm:folder>
</cm:contains>
</cm:folder>
<cm:folder view:childName="${spaces.guest_home.childname}">
<view:acl view:inherit="false">
<view:ace view:access="ALLOWED">
<view:authority>${alfresco_user_store.guestusername}</view:authority>
<view:permission>Consumer</view:permission>
</view:ace>
<view:ace view:access="ALLOWED">
<view:authority>GROUP_EVERYONE</view:authority>
<view:permission>Consumer</view:permission>
</view:ace>
</view:acl>
<app:uifacets/>
<cm:name>${spaces.guest_home.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.guest_home.name}</cm:title>
<cm:description>${spaces.guest_home.description}</cm:description>
</cm:folder>
<cm:folder view:childName="${spaces.user_homes.childname}">
<app:uifacets/>
<cm:name>${spaces.user_homes.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.user_homes.name}</cm:title>
<cm:description>${spaces.user_homes.description}</cm:description>
</cm:folder>
</cm:contains>
</cm:folder>
</cm:folder>
<cm:folder
view:childName="${spaces.templates.rss.childname}">
<view:acl>
<view:ace view:access="ALLOWED">
<view:authority>${alfresco_user_store.guestusername}</view:authority>
<view:permission>Consumer</view:permission>
</view:ace>
</view:acl>
<app:uifacets/>
<cm:name>${spaces.templates.rss.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.templates.rss.name}</cm:title>
<cm:description>${spaces.templates.rss.description}</cm:description>
</cm:folder>
<cm:folder
view:childName="${spaces.savedsearches.childname}">
<view:acl view:inherit="false">
<view:ace view:access="ALLOWED">
<view:authority>GROUP_EVERYONE</view:authority>
<view:permission>Contributor</view:permission>
</view:ace>
</view:acl>
<app:uifacets/>
<cm:name>${spaces.savedsearches.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.savedsearches.name}</cm:title>
<cm:description>${spaces.savedsearches.description}</cm:description>
</cm:folder>
<cm:folder view:childName="${spaces.scripts.childname}">
<app:uifacets/>
<cm:name>${spaces.scripts.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.scripts.name}</cm:title>
<cm:description>${spaces.scripts.description}</cm:description>
</cm:folder>
</cm:contains>
</cm:folder>
<cm:folder view:childName="${spaces.guest_home.childname}">
<view:acl view:inherit="false">
<view:ace view:access="ALLOWED">
<view:authority>${alfresco_user_store.guestusername}</view:authority>
<view:permission>Consumer</view:permission>
</view:ace>
<view:ace view:access="ALLOWED">
<view:authority>GROUP_EVERYONE</view:authority>
<view:permission>Consumer</view:permission>
</view:ace>
</view:acl>
<app:uifacets/>
<cm:name>${spaces.guest_home.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.guest_home.name}</cm:title>
<cm:description>${spaces.guest_home.description}</cm:description>
</cm:folder>
<cm:folder view:childName="${spaces.user_homes.childname}">
<app:uifacets/>
<cm:name>${spaces.user_homes.name}</cm:name>
<app:icon>space-icon-default</app:icon>
<cm:title>${spaces.user_homes.name}</cm:title>
<cm:description>${spaces.user_homes.description}</cm:description>
</cm:folder>
</cm:contains>
</cm:folder>
</view:view>

View File

@@ -49,7 +49,7 @@
<cm:userName>${alfresco_user_store.adminusername}</cm:userName>
<cm:firstName>Administrator</cm:firstName>
<cm:lastName></cm:lastName>
<cm:email></cm:email>
<cm:email>admin@alfresco.com</cm:email>
<cm:organizationId></cm:organizationId>
<cm:homeFolder>/${spaces.company_home.childname}</cm:homeFolder>
<cm:homeFolderProvider>bootstrapHomeFolderProvider</cm:homeFolderProvider>

View File

@@ -928,6 +928,7 @@
<value>alfresco/model/bpmModel.xml</value>
<value>alfresco/model/wcmModel.xml</value>
<value>alfresco/model/forumModel.xml</value>
<value>alfresco/model/imapModel.xml</value>
<!-- Content models -->
<value>alfresco/model/applicationModel.xml</value>

View File

@@ -0,0 +1,19 @@
<?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="imapServerConfigurationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders">
<value>true</value>
</property>
<property name="locations">
<list>
<value>classpath:alfresco/imap-server.properties</value>
<!-- Override -->
<value>classpath:alfresco/extension/custom-imap-server.properties</value>
</list>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,2 @@
imap.server.enabled=true
imap.server.port=143

View File

@@ -0,0 +1,40 @@
<?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>

14
config/alfresco/imap-config.xml Executable file
View File

@@ -0,0 +1,14 @@
<alfresco-config>
<config>
<imapConfig>
<imap name="Repository_virtual" mode="virtual">
<store>workspace://SpacesStore</store>
<rootPath>/app:company_home</rootPath>
</imap>
<imap name="Repository_archive" mode="archive">
<store>workspace://SpacesStore</store>
<rootPath>/app:company_home</rootPath>
</imap>
</imapConfig>
</config>
</alfresco-config>

View File

@@ -0,0 +1,91 @@
<?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="imapResourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
<property name="resourceBundles">
<list>
<value>alfresco.messages.imap-service</value>
</list>
</property>
</bean>
<bean id="imapServerConfigurationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders">
<value>true</value>
</property>
<property name="locations">
<list>
<value>classpath:alfresco/imap-server.properties</value>
</list>
</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="configService" ref="webClientConfigService" />
<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="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="port">
<value>${imap.server.port}</value>
</property>
<property name="imapServerEnabled">
<value>${imap.server.enabled}</value>
</property>
<property name="imapHostManager">
<ref local="imapHostManager"/>
</property>
<property name="imapUserManager">
<ref local="imapUserManager"/>
</property>
<property name="imapHelper">
<ref local="imapHelper"/>
</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" />
</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" />
</bean>
</beans>

View File

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

View File

@@ -0,0 +1,77 @@
<import resource="/Company Home/Data Dictionary/Scripts/command-utils.js">
function processCommand()
{
var isEmailed = document.hasAspect("emailserver:emailed");
logger.log("Command Processor: isEmailed=" + isEmailed);
if (isEmailed)
{
// Delete email attachments
var attachments = document.assocs["attachments"];
if (attachments != null)
{
for (var i = 0; i < attachments.length; i++)
{
attachments[i].remove();
}
}
var command = document.properties["cm:title"];
logger.log("Command Processor: command=" + command);
var parts = new Array();
var str = command;
var i = 0;
while (true)
{
var index = str.indexOf("-");
if (index == -1)
{
parts[i] = str;
break;
}
parts[i] = str.substring(0, index);
str = str.substr(index + 1);
i++;
}
// do-<commandName>-<arg1>-...-<argN>
if (parts.length < 3 || parts[0].toLowerCase() != "do")
{
var message = "Unknown command: " + command;
logger.log(message);
createEmail(message, message, message, false);
return;
}
var commandName = parts[1].toLowerCase();
var commandFolder = space.childByNamePath(commandName);
logger.log("Found '" + commandName + "' command folder: '" + commandFolder + "'");
if (commandFolder == null)
{
var message = "Command Processor: wrong command=" + command;
createEmail(message, message, message, false);
logger.log(message);
return;
}
document.move(commandFolder);
}
}
processCommand();

View File

@@ -0,0 +1,243 @@
<import resource="/Company Home/Data Dictionary/Scripts/command-utils.js">
/**
* Resources
*/
/* var templatePath = "/Data Dictionary/Imap Configs/Templates/imap_search_response_text_html.ftl";*/
var errorParameter = "Error: The query parameter is not set!";
var errorXPathNotValid = "Error: The Xpath query is not valid.";
var unknownCommand = "Unknown command";
/**
* Globals
*/
var title;
var command;
/**
* Create content for e-mail in text format
*
* @nodes (Array) ScriptNodes array
* @return content for e-mail in text format
*/
function createContentTextPlain(nodes)
{
var content = "Command: " + title + "\n\n";
for (var i = 0; i < nodes.length; i++)
{
content = content + "Name: " + nodes[i].getName() + "\nUrl: " +
webApplicationContextUrl + nodes[i].getUrl();
if (nodes[i].isDocument)
{
content = content + "\nDownload Url: " +
webApplicationContextUrl + nodes[i].getDownloadUrl();
}
content = content + "\n\n";
}
return content;
}
/**
* This for possible processing. It need to be investigated.
* The possible solution is to send a search request into FreeMarker template and let the template do search!
* @param nodes
* @return
*/
function createResponseTextHtml(nodes)
{
var template = companyhome.childByNamePath(templatePath);
var result;
if (template != null)
{
var args = new Array();
args["title"] = title;
args["nodes"] = nodes; /*it does not work; need to investigate how to send this to freemarker processing*/
args["webApplicationContextUrl"] = webApplicationContextUrl;
result = document.processTemplate(template, args);
logger.log("Response template is found. Response body is created using template.");
}
else
{
result = createContentTextHtml(nodes);
logger.log("Response template is NOT found. Response is created using default function.");
}
return result;
}
/**
* Create content for e-mail in html format
*
* @nodes (Array) ScriptNodes array
* @return content for e-mail in html format
*/
function createContentTextHtml(nodes)
{
var content = "Command: " + title + "\n<br/><br/>\n";
content += "<table class=\"links\">\n";
content += "<thead align=\"center\">";
content += "<tr>";
content += "<td>Name</td>";
content += "<td>Url</td>";
content += "<td>Download Url</td>";
content += "</tr>";
content += "</thead>\n"
for (var i = 0; i < nodes.length; i++)
{
content += "<tr>\n";
content += "<td>" + nodes[i].getName() + "</td>";
content += "<td><a href=\"" + webApplicationContextUrl + nodes[i].getUrl() + "\">" +
webApplicationContextUrl + nodes[i].getUrl() + "</a></td>";
content += "<td>&nbsp;";
if (nodes[i].isDocument)
{
content += "<a href=\"" + webApplicationContextUrl + nodes[i].getDownloadUrl() + "\">" +
webApplicationContextUrl + nodes[i].getDownloadUrl() + "</a>";
}
content += "</td>\n";
content += "</tr>\n";
}
content += "</table>";
return content;
}
/**
* Execute search command
*
* @params (string) command parameters
*/
function commandSearch(params)
{
var store = "workspace://SpacesStore";
var query;
var subject = "Search result";
var type = "lucene";
var paramArray = params.split(";");
for (var i = 0; i < paramArray.length; i++)
{
var param = paramArray[i].split("=");
param[0] = param[0].toLowerCase();
switch (param[0])
{
case "store": store = param[1]; break;
case "query": query = param[1]; break;
case "subject": subject = param[1]; break;
case "type": type = param[1].toLowerCase(); break;
}
}
if (query == null)
{
createEmail(errorParameter, errorParameter, errorParameter, false);
return;
}
var nodes;
try
{
switch (type)
{
case "lucene":
nodes = search.luceneSearch(store, query);
break;
case "xpath":
var isValid = search.isValidXpathQuery(query);
if (isValid == true)
{
nodes = search.xpathSearch(store, query);
}
else
{
createEmail(errorXPathNotValid, errorXPathNotValid, errorXPathNotValid, false);
return;
}
break;
case "node":
var node = search.findNode(query);
if (node == null) break;
nodes = new Array(node);
break;
case "tag":
nodes = search.tagSearch(store, query);
break;
}
}
catch (exception)
{
createEmail(exception.message, exception.message, "Search Error");
return;
}
if (nodes == null || nodes.length == 0)
{
var message = "Nothing was found using query: '" + subject + "'.";
createEmail(message, message, subject);
return;
}
/*createEmail(createResponseTextHtml(nodes), createContentTextPlain(nodes), subject, true);*/
createEmail(createContentTextHtml(nodes), createContentTextPlain(nodes), subject, false);
}
/**
* Decode subject
*
* @subject (string) subject
*/
function decodeSubject(subject)
{
var s = new Array();
s[0] = new Array("\\", "%5c");
s[1] = new Array("/", "%2f");
s[2] = new Array("*", "%2a");
s[3] = new Array("|", "%7c");
s[4] = new Array(":", "%3a");
s[5] = new Array("\"", "%22");
s[6] = new Array("<", "%3c");
s[7] = new Array(">", "%3e");
s[8] = new Array("?", "%3f");
for (var i = 0; i < s.length; i++)
{
var re = new RegExp(s[i][1], 'g');
subject = subject.replace(re, s[i][0]);
}
return subject;
}
function main()
{
title = decodeSubject(document.properties["cm:title"]);
command = title.split("-");
if (command[0].toLowerCase() == "do")
{
if (command[1].toLowerCase() == "search")
{
commandSearch(title.substring(4 + command[1].length));
}
else
{
var message = unknownCommand + ": '" + title + "'";
createEmail(message, message, message, false);
}
}
else
{
var message = unknownCommand + ": '" + title + "'";
createEmail(message, message, message, false);
}
document.remove();
}
logger.log("Start search command.");
main();
logger.log("End search command.");

View File

@@ -0,0 +1,56 @@
/**
* Create e-mail
* contentTextHtml (string) html content
* contentTextPlain (string) text content
*/
function createEmail(contentTextHtml, contentTextPlain, subject, templateUsed)
{
var command = document.properties["cm:title"];
var userName = person.properties["cm:userName"];
var inboxFolder = companyhome.childByNamePath("IMAP Home/" + userName + "/INBOX");
if (inboxFolder == null)
{
logger.log("Command Processor: INBOX folder does't exists.");
return;
}
var nextMessageUID = inboxFolder.properties["imap:nextMessageUID"];
inboxFolder.properties["imap:nextMessageUID"] = nextMessageUID + 1;
inboxFolder.save();
var response = inboxFolder.createNode("response" + Date.now(), "imap:imapContent");
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.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();
}

View File

@@ -326,6 +326,9 @@
<prop key="spaces.user_homes.childname">${spaces.user_homes.childname}</prop>
<prop key="spaces.sites.childname">${spaces.sites.childname}</prop>
<prop key="spaces.templates.email.invite.childname">${spaces.templates.email.invite.childname}</prop>
<prop key="spaces.imap_home.childname">${spaces.imap_home.childname}</prop>
<prop key="spaces.imapConfig.childname">${spaces.imapConfig.childname}</prop>
<prop key="spaces.imap_templates.childname">${spaces.imap_templates.childname}</prop>
</props>
</property>
</bean>
@@ -516,6 +519,7 @@
<prop key="location">alfresco/bootstrap/sitesSpace.xml</prop>
<prop key="messages">alfresco/messages/bootstrap-spaces</prop>
</props>
</list>
</property>
</bean>

View File

@@ -0,0 +1,12 @@
imap.command_processor.name=command-processor.js
imap.command_processor.title=Command Processor
imap.command_processor.description=Email Command Processor Script
imap.command_search.name=command-search.js
imap.command_search.title=Search Command
imap.command_search.description=Email Search Command Script
imap.command_utils.name=command-utils.js
imap.command_utils.title=Command Utils
imap.command_utils.description=Email Command Utils

View File

@@ -6,6 +6,12 @@ spaces.company_home.description=The company root space
spaces.dictionary.name=Data Dictionary
spaces.dictionary.description=User managed definitions
spaces.imapConfig.name=Imap Configs
spaces.imapConfig.description=Imap Configs
spaces.imap_templates.name=Templates
spaces.imap_templates.description=Templates for IMAP generated messages
spaces.templates.name=Space Templates
spaces.templates.description=Space folder templates
@@ -44,3 +50,6 @@ spaces.sites.description=Site Collaboration Spaces
spaces.templates.email.invite.name=invite
spaces.templates.email.invite.description=Invite email templates
spaces.imap_home.name=IMAP Home
spaces.imap_home.description=IMAP Home

View File

@@ -0,0 +1,10 @@
#
# Imap I18N messages
#
# Information messages. prefix 'imap.server.info'
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."

View File

@@ -1,4 +1,4 @@
# PatchService messages
# PatchService messages
patch.service.preceeded_by_alternative=Preceeded by alternative patch ''{0}''.
patch.service.not_relevant=Not relevant to schema {0}
patch.executer.checking=Checking for patches to apply ...
@@ -261,3 +261,11 @@ patch.mtShareExistingTenants.result=Update existing tenants for MT Share.
patch.mtShareExistingTenants.result.not_applicable=Patch applied, although no changes made since MT is not enabled.
patch.redeployInvitationProcess.description=Re-deploy Invitation Process Definitions.
patch.imapFolders.description=Creates folders tree necessary for IMAP functionality
patch.imapFolders.result.exists=The 'Imap Configs' folder already exists
patch.imapFolders.result.created=The 'Imap Configs' folder was successfully created
patch.imapUserFolders.description=Creates folders tree necessary for IMAP functionality
patch.imapUserFolders.result.exists=The 'IMAP Home' folder already exists
patch.imapUserFolders.result.created=The 'IMAP Home' folder was successfully created

View File

@@ -0,0 +1,100 @@
<model name="imap:imapmodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<description>IMAP Content Model</description>
<author>Alfresco</author>
<published>2009-01-20</published>
<version>1.0</version>
<imports>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" />
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" />
</imports>
<namespaces>
<namespace uri="http://www.alfresco.org/model/imap/1.0" prefix="imap" />
</namespaces>
<types>
<type name="imap:imapFolder">
<title>IMAP Folder</title>
<parent>cm:folder</parent>
<properties>
<property name="imap:subscribed">
<type>d:boolean</type>
</property>
<property name="imap:selectable">
<type>d:boolean</type>
<mandatory>true</mandatory>
<default>true</default>
</property>
</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>
<properties>
<property name="imap:attachID">
<type>d:text</type>
</property>
</properties>
</type>
<type name="imap:imapBody">
<title>Body of the IMAP message</title>
<parent>cm:content</parent>
</type>
</types>
<aspects>
<aspect name="imap:flaggable">
<properties>
<property name="imap:flagAnswered">
<type>d:boolean</type>
</property>
<property name="imap:flagDeleted">
<type>d:boolean</type>
</property>
<property name="imap:flagDraft">
<type>d:boolean</type>
</property>
<property name="imap:flagSeen">
<type>d:boolean</type>
</property>
<property name="imap:flagRecent">
<type>d:boolean</type>
</property>
<property name="imap:flagFlagged">
<type>d:boolean</type>
</property>
</properties>
</aspect>
<aspect name="imap:subscribed"/>
<aspect name="imap:nonselectable"/>
</aspects>
</model>

View File

@@ -6,6 +6,8 @@ repository.name=Main Repository
dir.root=./alf_data
web.application.context.url=http://localhost:8080/alfresco
dir.contentstore=${dir.root}/contentstore
dir.contentstore.deleted=${dir.root}/contentstore.deleted
@@ -226,6 +228,8 @@ spaces.company_home.childname=app:company_home
spaces.guest_home.childname=app:guest_home
spaces.dictionary.childname=app:dictionary
spaces.templates.childname=app:space_templates
spaces.imapConfig.childname=app:imap_configs
spaces.imap_templates.childname=app:imap_templates
spaces.templates.content.childname=app:content_templates
spaces.templates.email.childname=app:email_templates
spaces.templates.rss.childname=app:rss_templates
@@ -235,6 +239,7 @@ spaces.wcm.childname=app:wcm
spaces.wcm_content_forms.childname=app:wcm_forms
spaces.content_forms.childname=app:forms
spaces.user_homes.childname=app:user_homes
spaces.imap_home.childname=imap:imap_home
spaces.sites.childname=st:sites
spaces.templates.email.invite.childname=cm:invite

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="Generator" content="Alfresco Repository">
<style type="text/css">
body {
background-color:#FFFFFF;
color:#000000;
}
* {
font-family:Verdana,Arial,sans-serif;
font-size:11px;
}
h1 {
text-align:left;
font-size:15px;
}
h2 {
text-align:left;
font-size:13px;
}
fieldset {
border:1px dotted #555555;
margin:15px 5px 15px 5px;
}
fieldset legend {
font-weight:bold;
border:1px dotted #555555;
background-color: #FFFFFF;
padding:7px;
}
.links {
border:0;
border-collapse:collapse;
width:99%;
}
.links td {
border:0;
padding:5px;
}
.description {
border:0;
border-collapse:collapse;
width:99%;
}
.description td {
/*border:1px dotted #555555;*/
padding:5px;
}
#start_workflow input, #start_workflow select, #start_workflow textarea
{
border:1px solid #555555;
}
</style>
</head>
<body>
<hr/>
<h1> Document (name): ${document.name} </h1>
<hr/>
<fieldset>
<legend> Metadata </legend>
<table class="description">
<#if document.properties.title?exists>
<tr><td valign="top">Title:</td><td>${document.properties.title}</td></tr>
<#else>
<tr><td valign="top">Title:</td><td>&nbsp;</td></tr>
</#if>
<#if document.properties.description?exists>
<tr><td valign="top">Description:</td><td>${document.properties.description}</td></tr>
<#else>
<tr><td valign="top">Description:</td><td>&nbsp;</td></tr>
</#if>
<tr><td>Creator:</td><td>${document.properties.creator}</td></tr>
<tr><td>Created:</td><td>${document.properties.created?datetime}</td></tr>
<tr><td>Modifier:</td><td>${document.properties.modifier}</td></tr>
<tr><td>Modified:</td><td>${document.properties.modified?datetime}</td></tr>
<tr><td>Size:</td><td>${document.size / 1024} Kb</td></tr>
</table>
</fieldset>
<fieldset>
<legend> Content links </legend>
<table class="links">
<tr>
<td>Content folder:</td><td><a href="${contextUrl}${document.displayPath}">${contextUrl}${document.displayPath}</a></td>
</tr>
<tr>
<td>Content URL:</td><td><a href="${contextUrl}${document.url}">${contextUrl}${document.url}</a></td>
</tr>
<tr>
<td>Download URL:</td><td><a href="${contextUrl}${document.downloadUrl}">${contextUrl}${document.downloadUrl}</a></td>
</tr>
<tr>
<td>WebDAV URL:</td><td><a href="${contextUrl}${document.webdavUrl}">${contextUrl}${document.webdavUrl}</a></td>
</tr>
</table>
</fieldset>
<fieldset>
<legend> Start Workflow </legend>
<form id="start_workflow"
name="start_workflow"
method="get"
action="${contextUrl}/service/imap/start-workflow"
enctype="application/x-www-form-urlencoded">
<input type="hidden" name="alfTicket" value="${alfTicket}" />
<input type="hidden" name="nodeRefId" value="${document.id}" />
<table>
<tr>
<td>Workflow type:</td>
<td>
<!-- Workflow list is hardcoded for now -->
<select id="workflowType" name="workflowType" style="width:134px">
<option value="adhoc">Adhoc Task</option>
<option value="review">Review & Approve</option>
</select>
</td>
</tr>
<tr>
<td>Asign to:</td>
<td>
<input type="text" id="assignTo" name="assignTo"/>
</td>
</tr>
<tr>
<td>Priority:</td>
<td>
<select size="1" name="workflowPriority" id="workflowPriority">
<option value="1">1</option>
<option selected="selected" value="2">2</option>
<option value="3">3</option>
</select>
</td>
<tr>
<td>Due date:</td>
<td>
Day: <select size="1" name="workflowDueDateDay" id="workflowDueDateDay">
<option selected="selected" value="1">1</option>
<#list 2..31 as i>
<option value="${i}">${i}</option>
</#list>
</select>
</select>&nbsp;
Month: <select size="1" name="workflowDueDateMonth" id="workflowDueDateMonth">
<option selected="selected" value="1">1</option>
<#list 2..12 as i>
<option value="${i}">${i}</option>
</#list>
</select>&nbsp;
Year: <input type="text" size="5" id="workflowDueDateYear" name="workflowDueDateYear"/>
</td>
</tr>
<tr>
<td>Description:</td><td><textarea style="width:132px" id="description" name="description"></textarea></td>
</tr>
<tr>
<td colspan="2"><center><input id="submitButton" type="submit" value="Start workflow" style="margin:20px 5px 5px 5px"/></center></td>
</tr>
</table>
</form>
</fieldset>
<!--
JBPM ids for debug....
id=jbpm$4,name=jbpm$wcmwf:changerequest,version=1,title=Change Request
id=jbpm$3,name=jbpm$wcmwf:submit,version=1,title=Web Site Submission
id=jbpm$5,name=jbpm$wcmwf:submitdirect,version=1,title=Web Site Submission (Direct)
id=jbpm$2,name=jbpm$wf:adhoc,version=1,title=Adhoc Task
id=jbpm$6,name=jbpm$wf:invite,version=1,title=Site Invite
id=jbpm$1,name=jbpm$wf:review,version=1,title=Review & Approve
-->
</body>
</html>

View File

@@ -0,0 +1,35 @@
------------------------------------------------------------------------------
Document name: ${document.name}
------------------------------------------------------------------------------
<#if document.properties.title?exists>
Title: ${document.properties.title}
<#else>
Title: NONE
</#if>
<#if document.properties.description?exists>
Description: ${document.properties.description}
<#else>
Description: NONE
</#if>
Creator: ${document.properties.creator}
Created: ${document.properties.created?datetime}
Modifier: ${document.properties.modifier}
Modified: ${document.properties.modified?datetime}
Size: ${document.size / 1024} Kb
CONTENT LINKS
Content folder: ${contextUrl}${document.displayPath}
Content URL: ${contextUrl}${document.url}
Download URL: ${contextUrl}${document.downloadUrl}
WebDAV URL: ${contextUrl}${document.webdavUrl}
START WORKFLOW
{It is not possible to create a customizible workflow in txt format!
It is possible to create static links to IMAP Workflow Handler webscript,
But, in this case all parameters must be hardcoded in the link.
See http://localhost:8080/alfresco/service/description/org/alfresco/imap/start-workflow.get
for usage information.}

View File

@@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="Generator" content="Alfresco Repository">
<style type="text/css">
body {
background-color:#FFFFFF;
color:#000000;
}
* {
font-family:Verdana,Arial,sans-serif;
font-size:11px;
}
h1 {
text-align:left;
font-size:15px;
}
h2 {
text-align:left;
font-size:13px;
}
fieldset {
border:1px dotted #555555;
margin:15px 5px 15px 5px;
}
fieldset legend {
font-weight:bold;
border:1px dotted #555555;
background-color: #FFFFFF;
padding:7px;
}
.links {
border:0;
border-collapse:collapse;
width:99%;
}
.links td {
border:0;
padding:5px;
}
.description {
border:0;
border-collapse:collapse;
width:99%;
}
.description td {
/*border:1px dotted #555555;*/
padding:5px;
}
#start_workflow input, #start_workflow select, #start_workflow textarea
{
border:1px solid #555555;
}
</style>
</head>
<body>
<hr/>
<h1> Document (name): ${document.name} </h1>
<hr/>
<fieldset>
<legend> Metadata </legend>
<table class="description">
<#if document.properties.title?exists>
<tr><td valign="top">Title:</td><td>${document.properties.title}</td></tr>
<#else>
<tr><td valign="top">Title:</td><td>&nbsp;</td></tr>
</#if>
<#if document.properties.description?exists>
<tr><td valign="top">Description:</td><td>${document.properties.description}</td></tr>
<#else>
<tr><td valign="top">Description:</td><td>&nbsp;</td></tr>
</#if>
<tr><td>Creator:</td><td>${document.properties.creator}</td></tr>
<tr><td>Created:</td><td>${document.properties.created?datetime}</td></tr>
<tr><td>Modifier:</td><td>${document.properties.modifier}</td></tr>
<tr><td>Modified:</td><td>${document.properties.modified?datetime}</td></tr>
<tr><td>Size:</td><td>${document.size / 1024} Kb</td></tr>
</table>
</fieldset>
<fieldset>
<legend> Content links </legend>
<table class="links">
<tr>
<td>Content folder:</td><td><a href="${contextUrl}${document.displayPath}">${contextUrl}${document.displayPath}</a></td>
</tr>
<tr>
<td>Content URL:</td><td><a href="${contextUrl}${document.url}">${contextUrl}${document.url}</a></td>
</tr>
<tr>
<td>Download URL:</td><td><a href="${contextUrl}${document.downloadUrl}">${contextUrl}${document.downloadUrl}</a></td>
</tr>
<tr>
<td>WebDAV URL:</td><td><a href="${contextUrl}${document.webdavUrl}">${contextUrl}${document.webdavUrl}</a></td>
</tr>
</table>
</fieldset>
<fieldset>
<legend> Start Workflow </legend>
<form id="start_workflow"
name="start_workflow"
method="get"
action="${contextUrl}/service/imap/start-workflow"
enctype="application/x-www-form-urlencoded">
<input type="hidden" name="alfTicket" value="${alfTicket}" />
<input type="hidden" name="nodeRefId" value="${document.id}" />
<table>
<tr>
<td>Workflow type:</td>
<td>
<!-- Workflow list is hardcoded for now -->
<select id="workflowType" name="workflowType" style="width:134px">
<option value="adhoc">Adhoc Task</option>
<option value="review">Review & Approve</option>
</select>
</td>
</tr>
<tr>
<td>Asign to:</td>
<td>
<input type="text" id="assignTo" name="assignTo"/>
</td>
</tr>
<tr>
<td>Priority:</td>
<td>
<select size="1" name="workflowPriority" id="workflowPriority">
<option value="1">1</option>
<option selected="selected" value="2">2</option>
<option value="3">3</option>
</select>
</td>
<tr>
<td>Due date:</td><td><input type="text" id="dueDate" name="dueDate"/></td>
</tr>
<tr>
<td>Description:</td><td><textarea style="width:132px" id="description" name="description"></textarea></td>
</tr>
<tr>
<td colspan="2"><center><input id="submitButton" type="submit" value="Start workflow" style="margin:20px 5px 5px 5px"/></center></td>
</tr>
</table>
</form>
</fieldset>
<!--
JBPM ids for debug....
id=jbpm$4,name=jbpm$wcmwf:changerequest,version=1,title=Change Request
id=jbpm$3,name=jbpm$wcmwf:submit,version=1,title=Web Site Submission
id=jbpm$5,name=jbpm$wcmwf:submitdirect,version=1,title=Web Site Submission (Direct)
id=jbpm$2,name=jbpm$wf:adhoc,version=1,title=Adhoc Task
id=jbpm$6,name=jbpm$wf:invite,version=1,title=Site Invite
id=jbpm$1,name=jbpm$wf:review,version=1,title=Review & Approve
-->
</body>
</html>

View File

@@ -0,0 +1,11 @@
<webscript>
<shortname>IMAP Server Status</shortname>
<description>
This script return status of IMAP server (enabled/diabled)
</description>
<url>/imap/servstatus</url>
<format default="html">extension</format>
<authentication>user</authentication>
<transaction>required</transaction>
<family>IMAP</family>
</webscript>

View File

@@ -0,0 +1,12 @@
<webscript>
<shortname>IMAP Workflow Handler</shortname>
<description>
This webscript starts a different workflows. It have used in the IMAP email body links.
(The optional feature is the reply email with a report.)
</description>
<url>/imap/start-workflow?alfTicket={ticket}&amp;nodeRefId={id}&amp;workflowType={wt}&amp;assignTo={at}&amp;workflowDueDateDay={ddd}&amp;workflowDueDateMonth={ddm}&amp;workflowDueDateYear={ddy}&amp;description={desc}</url>
<format default="html">extension</format>
<authentication>user</authentication>
<transaction>required</transaction>
<family>IMAP</family>
</webscript>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="Generator" content="Alfresco Repository">
<style type="text/css">
body {
background-color:#FFFFFF;
color:#000000;
}
* {
font-family:Verdana,Arial,sans-serif;
font-size:11px;
}
h1 {
text-align:left;
font-size:15px;
}
h2 {
text-align:left;
font-size:13px;
}
.links {
border:0;
border-collapse:collapse;
width:99%;
}
.links td {
border:0;
padding:5px;
}
.description {
border:0;
border-collapse:collapse;
width:99%;
}
.description td {
border:1px dotted #555555;
padding:5px;
}
</style>
</head>
<body>
<h1>Workflow has started successfully.</h1>
<table class="description">
<tr>
<td>Ticket:</td><td>${args.alfTicket}</td>
</tr>
<tr>
<td>NodeRef id:</td><td>${args.nodeRefId}</td>
</tr>
<tr>
<td>Workflow type:</td><td>${args.workflowType}</td>
</tr>
<tr>
<td>Asign to:</td><td>${args.assignTo}</td>
</tr>
<tr>
<td>Due date:</td><td>${args.workflowDueDateDay}/${args.workflowDueDateMonth}/${args.workflowDueDateYear}</td>
</tr>
<tr>
<td>Description:</td><td>${args.description}</td>
</tr>
<tr>
<td>Result:</td><td> --- </td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,42 @@
function main()
{
logger.log("Start workflow form 'Start workflow' webscript");
var docNode = search.findNode("workspace://SpacesStore/" + args.nodeRefId);
if (docNode == undefined)
{
status.code = 404;
status.message = "Content with NodeRef id '" + args.nodeRefId + "' not found.";
status.redirect = true;
return;
}
var workflowType = "jbpm$wf:" + args.workflowType;
var assignTo = people.getPerson(args.assignTo);
if (assignTo == undefined)
{
status.code = 404;
status.message = "Person with username '" + args.assignTo + "' not found.";
status.redirect = true;
return;
}
var day = args.workflowDueDateDay;
var month = args.workflowDueDateMonth;
var year = args.workflowDueDateYear;
if (year != null && year.length == 2)
{
year = "20" + year;
}
var dueDate = new Date(year, month - 1, day);
var description = args.description;
var workflow = actions.create("start-workflow");
workflow.parameters.workflowName = workflowType;
workflow.parameters["bpm:workflowDescription"] = description;
workflow.parameters["bpm:assignee"] = assignTo;
workflow.parameters["bpm:workflowPriority"] = args.workflowPriority;
if (dueDate != null)
{
workflow.parameters["bpm:workflowDueDate"] = dueDate;
}
workflow.execute(docNode);
}
main();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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