Email Server and EmailService in a working state.

- Fixed authentication
 - Fixed I18N
 - Fixed mimetype and encoding issues
Changed 'avm' remote ports and hosts to be 'alfresco.rmi.services' as the 'avm' was starting to creep into non-avm stuff.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@7281 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2007-11-02 13:55:07 +00:00
parent ae37e5c139
commit 488127f5a8
29 changed files with 684 additions and 658 deletions

View File

@@ -2,19 +2,12 @@
# Properties shared between the Alfresco server # Properties shared between the Alfresco server
# and its remote clients (e.g.: the virtualization server). # and its remote clients (e.g.: the virtualization server).
# #
# Ports used by Alfresco AVM # Ports used by Alfresco
# #
# Note: These ports are also used by the virtualization server # Note: These ports are also used by the virtualization server
# (hence, they're in a seperate file that's can be copied easily). # (hence, they're in a seperate file that can be copied easily).
# Name of the host running AVM
avm.remote.host=localhost
# AVMRemote API
avm.remote.port=50500
# Remote RMI services # Remote RMI services
rmi.services.remote.port=${avm.remote.port} alfresco.rmi.services.port=50500
rmi.services.remote.host=${avm.remote.host} alfresco.rmi.services.host=localhost

View File

@@ -15,7 +15,7 @@
<import resource="classpath:alfresco/node-services-context.xml" /> <import resource="classpath:alfresco/node-services-context.xml" />
<import resource="classpath:alfresco/scheduled-jobs-context.xml" /> <import resource="classpath:alfresco/scheduled-jobs-context.xml" />
<import resource="classpath:alfresco/network-protocol-context.xml" /> <import resource="classpath:alfresco/network-protocol-context.xml" />
<import resource="classpath:alfresco/email-service-context.xml" /> <import resource="classpath:alfresco/emailserver/email-service-context.xml" />
<import resource="classpath:alfresco/content-services-context.xml" /> <import resource="classpath:alfresco/content-services-context.xml" />
<import resource="classpath*:alfresco/extension/mt/mt-contentstore-context.xml"/> <import resource="classpath*:alfresco/extension/mt/mt-contentstore-context.xml"/>
<import resource="classpath:alfresco/hibernate-context.xml" /> <import resource="classpath:alfresco/hibernate-context.xml" />

View File

@@ -100,6 +100,10 @@
<prop key="path">/${alfresco_user_store.system_container.childname}</prop> <prop key="path">/${alfresco_user_store.system_container.childname}</prop>
<prop key="location">alfresco/bootstrap/alfrescoAuthorityStorePermission.xml</prop> <prop key="location">alfresco/bootstrap/alfrescoAuthorityStorePermission.xml</prop>
</props> </props>
<props>
<prop key="path">/${alfresco_user_store.system_container.childname}/sys:authorities</prop>
<prop key="location">alfresco/bootstrap/emailServer.xml</prop>
</props>
</list> </list>
</property> </property>
</bean> </bean>
@@ -432,12 +436,25 @@
</bean> </bean>
<!-- Email Server --> <!-- Email Server -->
<!--
<bean id="emailServer" class="org.alfresco.email.server.impl.subetha.SubethaEmailServer"> <bean id="emailServer" class="org.alfresco.email.server.impl.subetha.SubethaEmailServer">
<constructor-arg> <property name="enabled">
<ref bean="emailServerConfiguration"/> <value>${email.server.enabled}</value>
</constructor-arg> </property>
<property name="domain">
<value>${email.server.domain}</value>
</property>
<property name="port">
<value>${email.server.port}</value>
</property>
<property name="blockedSenders">
<value>${email.server.blocked.senders}</value>
</property>
<property name="allowedSenders">
<value>${email.server.allowed.senders}</value>
</property>
<property name="emailService">
<ref bean="EmailService" />
</property>
</bean> </bean>
-->
</beans> </beans>

View File

@@ -55,7 +55,7 @@
<bean id="alfrescoMBeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/> <bean id="alfrescoMBeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"> <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
<property name="port" value="${avm.remote.port}"/> <property name="port" value="${alfresco.rmi.services.port}"/>
</bean> </bean>
<!-- MBeanServer Connector (registers itself with custom alfrescoMBeanServer) --> <!-- MBeanServer Connector (registers itself with custom alfrescoMBeanServer) -->
@@ -65,7 +65,7 @@
<property name="server" ref="alfrescoMBeanServer"/> <property name="server" ref="alfrescoMBeanServer"/>
<property name="objectName" value="connector:name=rmi"/> <property name="objectName" value="connector:name=rmi"/>
<property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:${avm.remote.port}/alfresco/jmxrmi" /> <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:${alfresco.rmi.services.port}/alfresco/jmxrmi" />
<property name="environment"> <property name="environment">
<map> <map>

View File

@@ -1,6 +0,0 @@
mail.inbound.enabled=false
email.server.enabled=false
email.server.port=25
email.server.domain=alfresco.com
email.server.allowed.senders=
email.server.blocked.senders=

View File

@@ -0,0 +1,13 @@
#
# Alfresco Email Service and Email Server
# - See the sample config for descriptions of the properties
#
email.inbound.unknownUser=anonymous
email.inbound.enabled=false
email.server.enabled=false
email.server.port=25
email.server.domain=alfresco.com
email.server.allowed.senders=
email.server.blocked.senders=

View File

@@ -13,45 +13,17 @@
</property> </property>
</bean> </bean>
<bean id="emailServerConfigurationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <bean id="emailServerConfigurationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders"> <property name="ignoreUnresolvablePlaceholders">
<value>true</value> <value>true</value>
</property> </property>
<property name="locations"> <property name="locations">
<list> <list>
<value>classpath:alfresco/email-server.properties</value> <value>classpath:alfresco/emailserver/email-server.properties</value>
</list> </list>
</property> </property>
</bean> </bean>
<bean id="emailServerConfiguration" class="org.alfresco.email.server.EmailServerConfiguration">
<property name="domain">
<value>${email.server.domain}</value>
</property>
<property name="port">
<value>${email.server.port}</value>
</property>
<property name="enabled">
<value>${email.server.enabled}</value>
</property>
<property name="blockedSenders">
<value>${email.server.blocked.senders}</value>
</property>
<property name="allowedSenders">
<value>${email.server.allowed.senders}</value>
</property>
<property name="emailService">
<ref bean="emailService" />
</property>
</bean>
<bean class="org.springframework.remoting.rmi.RmiServiceExporter"> <bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="service"> <property name="service">
<ref bean="emailService"/> <ref bean="emailService"/>
@@ -60,55 +32,74 @@
<value>org.alfresco.service.cmr.email.EmailService</value> <value>org.alfresco.service.cmr.email.EmailService</value>
</property> </property>
<property name="serviceName"> <property name="serviceName">
<value>emailService</value> <value>EmailService</value>
</property> </property>
<property name="registryPort"> <property name="registryPort">
<value>${avm.remote.port}</value> <value>${alfresco.rmi.services.port}</value>
</property> </property>
</bean> </bean>
<alias name="emailService" alias="EmailService"/>
<bean id="emailService" class="org.alfresco.email.server.EmailServiceImpl"> <bean id="emailService" class="org.alfresco.email.server.EmailServiceImpl">
<property name="emailInboundEnabled">
<property name="mailInboundEnabled"> <value>${email.inbound.enabled}</value>
<value>${mail.inbound.enabled}</value> </property>
<property name="unknownUser">
<value>${email.inbound.unknownUser}</value>
</property> </property>
<property name="unknownUser" value="admin" />
<property name="emailMessageHandlerMap"> <property name="emailMessageHandlerMap">
<map> <map>
<entry key="folder"> <entry key="cm:folder">
<ref bean="folderEmailMessageHandler"></ref> <ref bean="folderEmailMessageHandler"></ref>
</entry> </entry>
<entry key="forum"> <entry key="cm:content">
<ref bean="forumEmailMessageHandler"></ref>
</entry>
<entry key="discussion">
<ref bean="forumEmailMessageHandler"></ref>
</entry>
<entry key="topic">
<ref bean="topicEmailMessageHandler"></ref>
</entry>
<entry key="post">
<ref bean="topicEmailMessageHandler"></ref>
</entry>
<entry key="content">
<ref bean="documentEmailMessageHandler"></ref> <ref bean="documentEmailMessageHandler"></ref>
</entry> </entry>
<entry key="fm:forum">
<ref bean="forumEmailMessageHandler"></ref>
</entry>
<entry key="fm:discussion">
<ref bean="forumEmailMessageHandler"></ref>
</entry>
<entry key="fm:topic">
<ref bean="topicEmailMessageHandler"></ref>
</entry>
<entry key="fm:post">
<ref bean="topicEmailMessageHandler"></ref>
</entry>
</map> </map>
</property> </property>
<property name="namespaceService">
<ref bean="NamespaceService" />
</property>
<property name="nodeService"> <property name="nodeService">
<ref bean="nodeService" /> <ref bean="NodeService" />
</property> </property>
<property name="searchService"> <property name="searchService">
<ref bean="searchService" /> <ref bean="SearchService" />
</property> </property>
<property name="retryingTransactionHelper"> <property name="retryingTransactionHelper">
<ref bean="retryingTransactionHelper" /> <ref bean="retryingTransactionHelper" />
</property> </property>
</bean>
<bean id="emailMessageHandlerBase" abstract="true">
<property name="dictionaryService">
<ref bean="DictionaryService" />
</property>
<property name="nodeService">
<ref bean="NodeService" />
</property>
<property name="searchService">
<ref bean="SearchService" />
</property>
<property name="contentService">
<ref bean="ContentService" />
</property>
<property name="mimetypeService">
<ref bean="MimetypeService" />
</property>
</bean> </bean>
<bean id="folderEmailMessageHandler" <bean id="folderEmailMessageHandler"
@@ -127,28 +118,7 @@
parent="emailMessageHandlerBase" parent="emailMessageHandlerBase"
class="org.alfresco.email.server.handler.TopicEmailMessageHandler" /> class="org.alfresco.email.server.handler.TopicEmailMessageHandler" />
<bean id="emailMessageHandlerBase" abstract="true"> <bean id="aliasableAspect"
<property name="authenticationService">
<ref bean="authenticationService" />
</property>
<property name="authenticationComponent">
<ref bean="authenticationComponent" />
</property>
<property name="nodeService">
<ref bean="nodeService" />
</property>
<property name="personService">
<ref bean="personService" />
</property>
<property name="searchService">
<ref bean="searchService" />
</property>
<property name="contentService">
<ref bean="contentService" />
</property>
</bean>
<bean id="alisableAspect"
class="org.alfresco.email.server.AliasableAspect" class="org.alfresco.email.server.AliasableAspect"
init-method="initialise"> init-method="initialise">
<property name="nodeService"> <property name="nodeService">
@@ -162,5 +132,4 @@
</property> </property>
</bean> </bean>
</beans> </beans>

View File

@@ -32,7 +32,7 @@
<value>org.alfresco.FileFolderRemote</value> <value>org.alfresco.FileFolderRemote</value>
</property> </property>
<property name="registryPort"> <property name="registryPort">
<value>${rmi.services.remote.port}</value> <value>${alfresco.rmi.services.port}</value>
</property> </property>
</bean> </bean>
@@ -70,7 +70,7 @@
<value>org.alfresco.LoaderRemote</value> <value>org.alfresco.LoaderRemote</value>
</property> </property>
<property name="registryPort"> <property name="registryPort">
<value>${rmi.services.remote.port}</value> <value>${alfresco.rmi.services.port}</value>
</property> </property>
</bean> </bean>

View File

@@ -1,17 +1,24 @@
email.server.denied-address="Address {0} is in black list" email.server.msg.received_by_smtp=Received by SMTP from ''{0}''.
email.server.not-white-address="Address {0} is not in white list" email.server.msg.default_subject=Email-{0}
email.server.incorrect-node-ref="Incorrect node ref"
email.server.incorrect-node-address="Incorrect node address" email.server.err.sender_blocked=''{0}'' has been denied access.
email.server.incorrect-node-type="Incorrect node type" email.server.err.inbound_mail_disabled=The Alfresco server is not configured to accept inbound emails.
email.server.handler-not-found="Hanlder wasn't found" email.server.err.access_denied=''{0}'' has been denied access to ''{1}''.
email.server.unknown-user="Unknown user is not in email contribute group" email.server.err.invalid_subject=The subject line must be a valid file name.
email.server.not-contribute-user="User {0} is not in contribute group" email.server.err.unknown_source_address=The 'from' email address was not recognised: {0}.
email.server.contribute-group-not-exist="Email contribute group is not created" email.server.err.user_not_email_contributor=The user ''{0}'' in not in the email contributor group.
email.server.content-error="Content error" email.server.err.no_email_contributor_group=The Email Contributor Group doesn't exist.
email.server.incorrect-message-part="Incorrect message part" email.server.err.invalid_node_address=The email address ''{0}'' does not reference a valid accessible node.
email.server.error-getting-message-content="Couldn't get message part content" email.server.err.handler_not_found=Email message handler was not found for node type ''{0}''.
email.server.error-getting-content-stream="Couldn't get stream of the message part content" email.server.err.mail_read_error=An error occured while reading the mail message: {0}
email.server.error-creating-message="Couldn't create MIME message from input stream" email.server.err.failed_to_create_mime_message=Failed to create MIME message from input stream: {0}
email.server.error-parse-message="Couldn't parse the message" email.server.err.extracting_from_address=Failed to extract the 'from' address: {0}
email.server.mail-inbound-disabled="Email behaviour to be disabled completely on the server" email.server.err.no_from_address=The message has no 'from' address.
email.server.usupported-encoding="Encoding {0} is not support" email.server.err.extracting_to_address=Failed to extract the 'to' address: {0}
email.server.err.no_to_address=The message has no 'to' address.
email.server.err.extracting_subject=Failed to extract the message subject: {0}
email.server.err.extracting_sent_date=Failed to extract the 'sent on' date: {0}
email.server.err.parse_message=Failed to parse the email message: {0}
email.server.err.usupported_encoding=Encoding ''{0}'' is not supported
email.server.err.failed_to_read_content_stream=Failed to read the message part content: {0}
email.server.err.incorrect_message_part=Incorrect message part: {0}

View File

@@ -177,3 +177,5 @@ patch.customMessages.description=Adds Messages space to Data Dictionary.
patch.customWebClientExtension.description=Adds Web Client Extension space to Data Dictionary. patch.customWebClientExtension.description=Adds Web Client Extension space to Data Dictionary.
patch.customWorkflowDefs.description=Adds Workflow Definitions space to Data Dictionary. patch.customWorkflowDefs.description=Adds Workflow Definitions space to Data Dictionary.
patch.emailContributorGroup.description=Adds the 'GROUP_EMAIL_CONTRIBUTORS' group.

View File

@@ -25,6 +25,13 @@
prefix="emailserver" /> prefix="emailserver" />
</namespaces> </namespaces>
<constraints>
<constraint name="emailserver:alias" type="REGEX">
<parameter name="expression"><value><![CDATA[[a-zA-Z0-9.\-]*]]></value></parameter>
<parameter name="requiresMatch"><value>true</value></parameter>
</constraint>
</constraints>
<aspects> <aspects>
<aspect name="emailserver:attached"> <aspect name="emailserver:attached">
@@ -51,12 +58,15 @@
</aspect> </aspect>
<aspect name="emailserver:aliasable"> <aspect name="emailserver:aliasable">
<title>Aliasable</title> <title>Email Alias</title>
<properties> <properties>
<property name="emailserver:alias"> <property name="emailserver:alias">
<title>Alias</title> <title>Alias</title>
<type>d:text</type> <type>d:text</type>
<mandatory>true</mandatory> <mandatory>true</mandatory>
<constraints>
<constraint ref="emailserver:alias" />
</constraints>
</property> </property>
</properties> </properties>
</aspect> </aspect>

View File

@@ -1038,4 +1038,25 @@
</property> </property>
</bean> </bean>
<bean id="patch.emailContributorGroup" class="org.alfresco.repo.admin.patch.impl.GenericBootstrapPatch" parent="basePatch" >
<property name="id"><value>patch.emailContributorGroup</value></property>
<property name="description"><value>patch.emailContributorGroup.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>108</value></property>
<property name="targetSchema"><value>109</value></property>
<!-- bootstrap view -->
<property name="importerBootstrap">
<ref bean="userBootstrap" />
</property>
<property name="checkPath">
<value>/${alfresco_user_store.system_container.childname}/sys:authorities/usr:GROUP_EMAIL_CONTRIBUTORS</value>
</property>
<property name="bootstrapView">
<props>
<prop key="path">/${alfresco_user_store.system_container.childname}/sys:authorities</prop>
<prop key="location">alfresco/bootstrap/emailServer.xml</prop>
</props>
</property>
</bean>
</beans> </beans>

View File

@@ -29,7 +29,7 @@
<value>avm</value> <value>avm</value>
</property> </property>
<property name="registryPort"> <property name="registryPort">
<value>${avm.remote.port}</value> <value>${alfresco.rmi.services.port}</value>
</property> </property>
</bean> </bean>
@@ -53,7 +53,7 @@
<value>avmsync</value> <value>avmsync</value>
</property> </property>
<property name="registryPort"> <property name="registryPort">
<value>${avm.remote.port}</value> <value>${alfresco.rmi.services.port}</value>
</property> </property>
</bean> </bean>
@@ -78,7 +78,7 @@
<value>attributes</value> <value>attributes</value>
</property> </property>
<property name="registryPort"> <property name="registryPort">
<value>${avm.remote.port}</value> <value>${alfresco.rmi.services.port}</value>
</property> </property>
</bean> </bean>
@@ -94,7 +94,7 @@
<value>authentication</value> <value>authentication</value>
</property> </property>
<property name="registryPort"> <property name="registryPort">
<value>${avm.remote.port}</value> <value>${alfresco.rmi.services.port}</value>
</property> </property>
</bean> </bean>
@@ -161,7 +161,7 @@
<value>repo</value> <value>repo</value>
</property> </property>
<property name="registryPort"> <property name="registryPort">
<value>${avm.remote.port}</value> <value>${alfresco.rmi.services.port}</value>
</property> </property>
</bean> </bean>
@@ -187,7 +187,7 @@
<value>action</value> <value>action</value>
</property> </property>
<property name="registryPort"> <property name="registryPort">
<value>${avm.remote.port}</value> <value>${alfresco.rmi.services.port}</value>
</property> </property>
</bean> </bean>
</beans> </beans>

View File

@@ -19,4 +19,4 @@ version.build=@build-number@
# Schema number # Schema number
version.schema=108 version.schema=109

View File

@@ -54,8 +54,9 @@ public class AliasableAspect implements NodeServicePolicies.OnAddAspectPolicy, N
private SearchService searchService; private SearchService searchService;
public static final String SEARCH_TEMPLATE = "ASPECT:\"" + EmailServerModel.ASPECT_ALIASABLE + "\" +@" + NamespaceService.EMAILSERVER_MODEL_PREFIX + "\\:" public static final String SEARCH_TEMPLATE =
+ EmailServerModel.PROP_ALIAS.getLocalName() + ":\"%s\""; "ASPECT:\"" + EmailServerModel.ASPECT_ALIASABLE +
"\" +@" + NamespaceService.EMAILSERVER_MODEL_PREFIX + "\\:" + EmailServerModel.PROP_ALIAS.getLocalName() + ":\"%s\"";
/** /**
* @param searchService Alfresco Search Service * @param searchService Alfresco Search Service

View File

@@ -24,9 +24,16 @@
*/ */
package org.alfresco.email.server; package org.alfresco.email.server;
import org.alfresco.i18n.I18NUtil; import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailService;
import org.alfresco.util.AbstractLifecycleBean; import org.alfresco.util.AbstractLifecycleBean;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.AbstractApplicationContext;
@@ -38,61 +45,162 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
*/ */
public abstract class EmailServer extends AbstractLifecycleBean public abstract class EmailServer extends AbstractLifecycleBean
{ {
protected EmailServerConfiguration configuration; private static final String ERR_SENDER_BLOCKED = "email.server.err.sender_blocked";
private boolean enabled;
private String domain;
private int port;
private Set<String> blockedSenders;
private Set<String> allowedSenders;
private EmailService emailService;
/** /**
* @param serverConfiguration Server configuration * @param serverConfiguration Server configuration
*/ */
protected EmailServer(EmailServerConfiguration serverConfiguration) protected EmailServer()
{ {
this.configuration = serverConfiguration; this.enabled = false;
this.port = 25;
this.domain = null;
this.blockedSenders = new HashSet<String>(23);
this.allowedSenders = new HashSet<String>(23);
}
/**
* @param enabled Enable/disable server
*/
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
protected String getDomain()
{
return domain;
}
public void setDomain(String domain)
{
this.domain = domain;
}
protected int getPort()
{
return port;
}
/**
* @param port SMTP port (25 is default)
*/
public void setPort(int port)
{
this.port = port;
}
/**
* Set the blocked senders as a comma separated list. The entries will be trimmed of
* all whitespace.
*
* @param blockedSenders a comman separated list of blocked senders
*/
public void setBlockedSenders(String blockedSenders)
{
StringTokenizer tokenizer = new StringTokenizer(blockedSenders, ",", false);
while (tokenizer.hasMoreTokens())
{
String sender = tokenizer.nextToken().trim();
this.blockedSenders.add(sender);
}
}
/**
* @param blockedSenders a list of senders that are not allowed to email in
*/
public void setBlockedSenders(List<String> blockedSenders)
{
this.blockedSenders.addAll(blockedSenders);
}
/**
* Set the allowed senders as a comma separated list. The entries will be trimmed of
* all whitespace.
*
* @param allowedSenders a comman separated list of blocked senders
*/
public void setAllowedSenders(String allowedSenders)
{
StringTokenizer tokenizer = new StringTokenizer(allowedSenders, ",", false);
while (tokenizer.hasMoreTokens())
{
String sender = tokenizer.nextToken().trim();
if (sender.length() == 0)
{
// Nothing
continue;
}
this.allowedSenders.add(sender);
}
}
/**
* @param allowedSenders a list of senders that are allowed to email in
*/
public void setAllowedSenders(List<String> allowedSenders)
{
this.allowedSenders.addAll(allowedSenders);
}
/**
* @return the service interface to interact with
*/
protected EmailService getEmailService()
{
return emailService;
}
/**
* @param emailService the service interface to interact with
*/
public void setEmailService(EmailService emailService)
{
this.emailService = emailService;
} }
/** /**
* Filter incoming message by its sender e-mail address. * Filter incoming message by its sender e-mail address.
* *
* @param sender An e-mail address of sender * @param sender An e-mail address of sender
* @throws EmailMessageException Exception will be thrown if the e-mail is rejected accordingly with blocked and allowed lists. * @throws EmailMessageException if the e-mail is rejected accordingly with blocked and allowed lists
*/ */
public void blackAndWhiteListFiltering(String sender) protected void filterSender(String sender)
{ {
// At first try to find sender in the black list // Check if the sender is in the blocked list
String[] blackList = configuration.getArrayBlockedSenders(); for (String blockedSender : blockedSenders)
String[] whiteList = configuration.getArrayAllowedSenders();
// At first try to find sender in the black list
// If sender is found, mail will be rejected at once
if (blackList != null)
{ {
for (String deniedAddress : blackList) if (sender.matches(blockedSender))
{ {
if (sender.matches(deniedAddress)) throw new EmailMessageException(ERR_SENDER_BLOCKED, sender);
{
throw new EmailMessageException(I18NUtil.getMessage("email.server.denied-address", sender));
}
} }
} }
// Sender wasn't found in black list or black list is empty // If there are any restrictions in the allowed list, then a positive match
// Try to find sender in the white list // is absolutely required
// If sender is found in white list, if (!allowedSenders.isEmpty())
// the message will be accepted at once.
if (whiteList != null)
{ {
boolean accept = false; boolean matched = false;
for (String acceptedAddress : whiteList) for (String allowedSender : allowedSenders)
{ {
if (sender.matches(acceptedAddress)) if (sender.matches(allowedSender))
{ {
if (log.isInfoEnabled()) matched = true;
log.info("Sender with address \"" + sender + "\"matches to expression \"" + acceptedAddress + "\" in the white list. The message was accepted.");
accept = true;
break; break;
} }
} }
if (!accept) if (!matched)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.not-white-address", sender)); throw new EmailMessageException(ERR_SENDER_BLOCKED, sender);
} }
} }
} }
@@ -113,10 +221,19 @@ public abstract class EmailServer extends AbstractLifecycleBean
@Override @Override
protected void onBootstrap(ApplicationEvent event) protected void onBootstrap(ApplicationEvent event)
{ {
if (configuration.isEnabled()) if (!enabled)
{ {
startup(); return;
} }
// Check properties
PropertyCheck.mandatory(this, "domain", domain);
if (port <= 0 || port > 65535)
{
throw new AlfrescoRuntimeException("Property 'port' is incorrect");
}
PropertyCheck.mandatory(this, "emailService", emailService);
// Startup
startup();
} }
/** /**
@@ -125,7 +242,7 @@ public abstract class EmailServer extends AbstractLifecycleBean
@Override @Override
protected void onShutdown(ApplicationEvent event) protected void onShutdown(ApplicationEvent event)
{ {
if (configuration.isEnabled()) if (enabled)
{ {
shutdown(); shutdown();
} }

View File

@@ -1,215 +0,0 @@
/*
* Copyright (C) 2005-2007 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.email.server;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.email.EmailService;
import org.alfresco.util.AbstractLifecycleBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
/**
* Encapsulation of setting controlling the email server.
* @since 2.2
*/
public class EmailServerConfiguration extends AbstractLifecycleBean
{
private final static Log log = LogFactory.getLog(EmailServerConfiguration.class);
private boolean enabled = false;
private String domain;
private int port = 25;
private String[] blockedSenders;
private String[] allowedSenders;
private EmailService emailService;
/**
* @return True if server is enabled.
*/
public boolean isEnabled()
{
return enabled;
}
/**
* @param enabled Enable/disable server
*/
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
/**
* @return Domain
*/
public String getDomain()
{
return domain;
}
/**
* @param domain Domain
*/
public void setDomain(String domain)
{
this.domain = domain;
}
/**
* @return SMTP port (25 is default)
*/
public int getPort()
{
return port;
}
/**
* @param port SMTP port (25 is default)
*/
public void setPort(int port)
{
this.port = port;
}
/**
* @return Array of e-mail addresses. If an incoming e-mail has a sender from this list, message will be rejected.
*/
public String[] getArrayBlockedSenders()
{
return blockedSenders;
}
/**
* @param Comma separated blackSenders of e-mail addresses. If an incoming e-mail has a sender from this list, message will be rejected.
*/
public void setBlockedSenders(String blockedSenders)
{
if (blockedSenders != null && blockedSenders.trim().length() > 0)
{
this.blockedSenders = blockedSenders.split(";");
}
else
{
this.blockedSenders = null;
}
}
/**
* @return Array of e-mail addresses. If an incoming e-mail has a sender from this list, message will be accepted.
*/
public String[] getArrayAllowedSenders()
{
return allowedSenders;
}
/**
* @param Comma separated whiteSenders of e-mail addresses. If an incoming e-mail has a sender from this list, message will be accepted.
*/
public void setAllowedSenders(String allowedSenders)
{
if (allowedSenders != null && allowedSenders.trim().length() > 0)
{
this.allowedSenders = allowedSenders.split(";");
}
else
{
this.allowedSenders = null;
}
}
/**
* @return Email Service
*/
public EmailService getEmailService()
{
return emailService;
}
/**
* @param emailService Email Service
*/
public void setEmailService(EmailService emailService)
{
this.emailService = emailService;
}
/**
* Method checks that all mandatory fiedls are set.
*
* @throws AlfrescoRuntimeException Exception is thrown if at least one mandatory field isn't set.
*/
private void check()
{
if (domain == null)
{
throw new AlfrescoRuntimeException("Property 'domain' not set");
}
if (port <= 0 || port > 65535)
{
throw new AlfrescoRuntimeException("Property 'port' is incorrect");
}
if (emailService == null)
{
throw new AlfrescoRuntimeException("Property 'emailService' not set");
}
if (blockedSenders == null)
{
if (log.isDebugEnabled())
{
log.debug("Property 'blockedSenders' not set");
}
}
if (allowedSenders == null)
{
if (log.isDebugEnabled())
{
log.debug("Property 'allowedSenders' not set");
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onBootstrap(ApplicationEvent event)
{
check();
}
/**
* {@inheritDoc}
* <p/>
* NO-OP
*/
@Override
protected void onShutdown(ApplicationEvent event)
{
}
}

View File

@@ -28,10 +28,12 @@ import java.util.Collection;
import java.util.Map; import java.util.Map;
import org.alfresco.email.server.handler.EmailMessageHandler; import org.alfresco.email.server.handler.EmailMessageHandler;
import org.alfresco.i18n.I18NUtil; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
@@ -43,28 +45,47 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
import org.apache.commons.logging.Log; import org.alfresco.service.namespace.NamespaceService;
import org.apache.commons.logging.LogFactory; import org.alfresco.service.namespace.QName;
import org.alfresco.util.ParameterCheck;
/** /**
* Concrete email service implementation. This is responsible for routing the * Concrete email service implementation. This is responsible for routing the
* emails into the server. * emails into the server.
*
* @since 2.2 * @since 2.2
*/ */
public class EmailServiceImpl implements EmailService public class EmailServiceImpl implements EmailService
{ {
private static final Log log = LogFactory.getLog(EmailServiceImpl.class); private static final String ERR_INBOUND_EMAIL_DISABLED = "email.server.err.inbound_mail_disabled";
private static final String ERR_INVALID_SUBJECT = "email.server.err.invalid_subject";
private static final String ERR_ACCESS_DENIED = "email.server.err.access_denied";
private static final String ERR_UNKNOWN_SOURCE_ADDRESS = "email.server.err.unknown_source_address";
private static final String ERR_USER_NOT_EMAIL_CONTRIBUTOR = "email.server.err.user_not_email_contributor";
private static final String ERR_NO_EMAIL_CONTRIBUTOR_GROUP = "email.server.err.no_email_contributor_group";
private static final String ERR_INVALID_NODE_ADDRESS = "email.server.err.invalid_node_address";
private static final String ERR_HANDLER_NOT_FOUND = "email.server.err.handler_not_found";
private NamespaceService namespaceService;
private NodeService nodeService; private NodeService nodeService;
private SearchService searchService; private SearchService searchService;
private RetryingTransactionHelper retryingTransactionHelper; private RetryingTransactionHelper retryingTransactionHelper;
private boolean mailInboundEnabled; private boolean emailInboundEnabled;
/** Login of user that is set as unknown. */ /** Login of user that is set as unknown. */
private String unknownUser; private String unknownUser;
/** List of message handlers */ /** List of message handlers */
private Map<String, EmailMessageHandler> emailMessageHandlerMap; private Map<String, EmailMessageHandler> emailMessageHandlerMap;
/**
*
* @param namespaceService the service to resolve namespace prefixes
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/** /**
* @param nodeService Alfresco Node Service * @param nodeService Alfresco Node Service
*/ */
@@ -113,9 +134,9 @@ public class EmailServiceImpl implements EmailService
this.unknownUser = unknownUser; this.unknownUser = unknownUser;
} }
public void setMailInboundEnabled(boolean mailInboundEnabled) public void setEmailInboundEnabled(boolean mailInboundEnabled)
{ {
this.mailInboundEnabled = mailInboundEnabled; this.emailInboundEnabled = mailInboundEnabled;
} }
/** /**
@@ -143,87 +164,114 @@ public class EmailServiceImpl implements EmailService
*/ */
private void processMessage(final NodeRef nodeRef, final EmailMessage message) private void processMessage(final NodeRef nodeRef, final EmailMessage message)
{ {
if (!mailInboundEnabled) if (!emailInboundEnabled)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.mail-inbound-disabled")); throw new EmailMessageException(ERR_INBOUND_EMAIL_DISABLED);
} }
try try
{ {
RetryingTransactionCallback<Object> callback = new RetryingTransactionCallback<Object>() // Get the username for the process using the system account
final RetryingTransactionCallback<String> getUsernameCallback = new RetryingTransactionCallback<String>()
{ {
public Object execute() public String execute() throws Throwable
{ {
final String userName = authenticate(message.getFrom()); String from = message.getFrom();
return getUsername(from);
}
};
RunAsWork<String> getUsernameRunAsWork = new RunAsWork<String>()
{
public String doWork() throws Exception
{
return retryingTransactionHelper.doInTransaction(getUsernameCallback, false);
}
};
String username = AuthenticationUtil.runAs(getUsernameRunAsWork, AuthenticationUtil.SYSTEM_USER_NAME);
AuthenticationUtil.runAs(new RunAsWork<Object>() // Process the message using the username's account
final RetryingTransactionCallback<Object> processMessageCallback = new RetryingTransactionCallback<Object>()
{
public Object execute() throws Throwable
{
String recipient = message.getTo();
NodeRef targetNodeRef = null;
if (nodeRef == null)
{ {
public Object doWork() throws Exception targetNodeRef = getTargetNode(recipient);
{ }
String recepient = message.getTo(); else
NodeRef targetNodeRef = null; {
if (nodeRef == null) targetNodeRef = nodeRef;
{ }
targetNodeRef = getTargetNode(recepient); EmailMessageHandler messageHandler = getMessageHandler(targetNodeRef);
} messageHandler.processMessage(targetNodeRef, message);
else
{
targetNodeRef = nodeRef;
}
EmailMessageHandler messageHandler = getMessageHandler(targetNodeRef);
messageHandler.processMessage(targetNodeRef, message);
return null;
}
}, userName);
return null; return null;
} }
}; };
retryingTransactionHelper.doInTransaction(callback, false); RunAsWork<Object> processMessageRunAsWork = new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
return retryingTransactionHelper.doInTransaction(processMessageCallback, false);
}
};
AuthenticationUtil.runAs(processMessageRunAsWork, username);
}
catch (EmailMessageException e)
{
// These are email-specific errors
throw e;
}
catch (AccessDeniedException e)
{
throw new EmailMessageException(ERR_ACCESS_DENIED, message.getFrom(), message.getTo());
}
catch (IntegrityException e)
{
throw new EmailMessageException(ERR_INVALID_SUBJECT);
} }
catch (Throwable e) catch (Throwable e)
{ {
log.error("Error process email message", e); throw new AlfrescoRuntimeException("Email message processing failed", e);
throw new EmailMessageException(e.getMessage());
} }
} }
/** /**
* @param nodeRef Target node * @param nodeRef Target node
* @return Handler that can process message addressed to specific node (target node). * @return Handler that can process message addressed to specific node (target node).
* @throws EmailMessageException Exception is thrown if <code>nodeRef</code> is <code>null</code> or suitable message handler isn't found. * @throws EmailMessageException is thrown if a suitable message handler isn't found.
*/ */
private EmailMessageHandler getMessageHandler(NodeRef nodeRef) private EmailMessageHandler getMessageHandler(NodeRef nodeRef)
{ {
if (nodeRef == null) ParameterCheck.mandatory("nodeRef", nodeRef);
{
throw new EmailMessageException(I18NUtil.getMessage("email.server.incorrect-node-ref")); QName nodeTypeQName = nodeService.getType(nodeRef);
} String prefixedNodeTypeStr = nodeTypeQName.toPrefixString(namespaceService);
String nodeTypeLocalName = nodeService.getType(nodeRef).getLocalName(); EmailMessageHandler handler = emailMessageHandlerMap.get(prefixedNodeTypeStr);
EmailMessageHandler handler = emailMessageHandlerMap.get(nodeTypeLocalName);
if (handler == null) if (handler == null)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.handler-not-found")); throw new EmailMessageException(ERR_HANDLER_NOT_FOUND, prefixedNodeTypeStr);
} }
return handler; return handler;
} }
/** /**
* Method determines target node by recepient e-mail address. * Method determines target node by recipient e-mail address.
* *
* @param recepient An e-mail address of a receipient * @param recipient An e-mail address of a recipient
* @return Referance to the target node. * @return Reference to the target node
* @exception EmailMessageException Exception is thrown if the target node couldn't be determined by some reasons. * @throws EmailMessageException is thrown if the target node couldn't be determined by some reasons.
*/ */
private NodeRef getTargetNode(String recepient) private NodeRef getTargetNode(String recipient)
{ {
if (recepient == null || recepient.length() == 0) if (recipient == null || recipient.length() == 0)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.incorrect-node-address")); throw new EmailMessageException(ERR_INVALID_NODE_ADDRESS, recipient);
} }
String[] parts = recepient.split("@"); String[] parts = recipient.split("@");
if (parts.length != 2) if (parts.length != 2)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.incorrect-node-address")); throw new EmailMessageException(ERR_INVALID_NODE_ADDRESS, recipient);
} }
// Ok, address looks well, let's try to find related alias // Ok, address looks well, let's try to find related alias
@@ -243,14 +291,14 @@ public class EmailServiceImpl implements EmailService
} }
} }
// Ok, alias wasn't found, let's try to interpret recepient address as 'node-bdid' value // Ok, alias wasn't found, let's try to interpret recipient address as 'node-bdid' value
query = "@sys\\:node-dbid:" + parts[0]; query = "@sys\\:node-dbid:" + parts[0];
resultSet = searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, query); resultSet = searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, query);
if (resultSet.length() > 0) if (resultSet.length() > 0)
{ {
return resultSet.getNodeRef(0); return resultSet.getNodeRef(0);
} }
throw new EmailMessageException(I18NUtil.getMessage("email.server.incorrect-node-address")); throw new EmailMessageException(ERR_INVALID_NODE_ADDRESS, recipient);
} }
/** /**
@@ -260,7 +308,7 @@ public class EmailServiceImpl implements EmailService
* @return User name * @return User name
* @throws EmailMessageException Exception will be thrown if authentication is failed. * @throws EmailMessageException Exception will be thrown if authentication is failed.
*/ */
private String authenticate(String from) private String getUsername(String from)
{ {
String userName = null; String userName = null;
StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
@@ -270,10 +318,13 @@ public class EmailServiceImpl implements EmailService
if (resultSet.length() == 0) if (resultSet.length() == 0)
{ {
userName = unknownUser; if (unknownUser == null || unknownUser.length() == 0)
if (userName == null || !isEmailContributeUser(userName))
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.unknown-user")); throw new EmailMessageException(ERR_UNKNOWN_SOURCE_ADDRESS, from);
}
else
{
userName = unknownUser;
} }
} }
else else
@@ -281,17 +332,21 @@ public class EmailServiceImpl implements EmailService
NodeRef userNode = resultSet.getNodeRef(0); NodeRef userNode = resultSet.getNodeRef(0);
if (nodeService.exists(userNode)) if (nodeService.exists(userNode))
{ {
userName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(userNode, ContentModel.PROP_USERNAME)); userName = DefaultTypeConverter.INSTANCE.convert(
if (!isEmailContributeUser(userName)) String.class,
{ nodeService.getProperty(userNode, ContentModel.PROP_USERNAME));
throw new EmailMessageException(I18NUtil.getMessage("email.server.not-contribute-user", userName));
}
} }
else else
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.contribute-group-not-exist")); // The Lucene index returned a dead result
throw new EmailMessageException(ERR_UNKNOWN_SOURCE_ADDRESS, from);
} }
} }
// Ensure that the user is part of the Email Contributors group
if (userName == null || !isEmailContributeUser(userName))
{
throw new EmailMessageException(ERR_USER_NOT_EMAIL_CONTRIBUTOR, userName);
}
return userName; return userName;
} }
@@ -310,16 +365,22 @@ public class EmailServiceImpl implements EmailService
if (resultSet.length() == 0) if (resultSet.length() == 0)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.contribute-group-not-exist")); throw new EmailMessageException(ERR_NO_EMAIL_CONTRIBUTOR_GROUP);
} }
NodeRef groupNode = resultSet.getNodeRef(0); NodeRef groupNode = resultSet.getNodeRef(0);
Collection<String> memberCollection = DefaultTypeConverter.INSTANCE.getCollection(String.class, nodeService.getProperty(groupNode, ContentModel.PROP_MEMBERS)); Collection<String> memberCollection = DefaultTypeConverter.INSTANCE.getCollection(
String.class,
nodeService.getProperty(groupNode, ContentModel.PROP_MEMBERS));
if (memberCollection.contains(userName)) if (memberCollection.contains(userName))
{
return true; return true;
}
return false; else
{
return false;
}
} }
} }

View File

@@ -24,26 +24,28 @@
*/ */
package org.alfresco.email.server.handler; package org.alfresco.email.server.handler;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.alfresco.email.server.EmailServerModel; import org.alfresco.email.server.EmailServerModel;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.content.encoding.ContentCharsetFinder;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter; 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.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -60,17 +62,16 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler
{ {
private static final Log log = LogFactory.getLog(EmailMessageHandler.class); private static final Log log = LogFactory.getLog(EmailMessageHandler.class);
private AuthenticationService authenticationService; private DictionaryService dictionaryService;
private AuthenticationComponent authenticationComponent;
private NodeService nodeService; private NodeService nodeService;
private PersonService personService;
private SearchService searchService; private SearchService searchService;
private ContentService contentService; private ContentService contentService;
private MimetypeService mimetypeService;
/** /**
* @return Alfresco Content Service. * @return Alfresco Content Service.
*/ */
public ContentService getContentService() protected ContentService getContentService()
{ {
return contentService; return contentService;
} }
@@ -84,41 +85,25 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler
} }
/** /**
* @return Alfresco Authentication Component. * @return the Alfresco dictionary service
*/ */
public AuthenticationComponent getAuthenticationComponent() protected DictionaryService getDictionaryService()
{ {
return authenticationComponent; return dictionaryService;
} }
/** /**
* @param authenticationComponent Alfresco Authentication Component. * @param dictionaryService Alfresco dictionary service
*/ */
public void setAuthenticationComponent(AuthenticationComponent authenticationComponent) public void setDictionaryService(DictionaryService dictionaryService)
{ {
this.authenticationComponent = authenticationComponent; this.dictionaryService = dictionaryService;
}
/**
* @return Alfresco Authentication Service.
*/
public AuthenticationService getAuthenticationService()
{
return authenticationService;
}
/**
* @param authenticationService Alfresco Authentication Service.
*/
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
} }
/** /**
* @return Alfresco Node Service. * @return Alfresco Node Service.
*/ */
public NodeService getNodeService() protected NodeService getNodeService()
{ {
return nodeService; return nodeService;
} }
@@ -131,39 +116,30 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler
this.nodeService = nodeService; this.nodeService = nodeService;
} }
/**
* @return Alfesco Person Service.
*/
public PersonService getPersonService()
{
return personService;
}
/**
* @param personService Alfresco Person Service.
*/
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* @return Alfresco Search Service.
*/
public SearchService getSearchService()
{
return searchService;
}
/** /**
* @param searchService Alfresco Search Service. * @param searchService Alfresco Search Service.
*/ */
public void setSearchService(SearchService searchService) public void setSearchService(SearchService searchService)
{ {
this.searchService = searchService; this.searchService = searchService;
} }
/**
* @return the service used to determine mimeypte and encoding
*/
protected MimetypeService getMimetypeService()
{
return mimetypeService;
}
/**
* @param mimetypeService the the service to determine mimetype and encoding
*/
public void setMimetypeService(MimetypeService mimetypeService)
{
this.mimetypeService = mimetypeService;
}
/** /**
* @param to Email address which user part specifies node-dbid * @param to Email address which user part specifies node-dbid
* @return Referance to requested node. * @return Referance to requested node.
@@ -206,12 +182,12 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler
* *
* @param nodeRef Target node. * @param nodeRef Target node.
* @param content Text for writting. * @param content Text for writting.
* @param contentType MIME content type. For exaple you can set this parameter to "text/html" or "text/xml", etc. * @param mimetype MIME content type. For exaple you can set this parameter to "text/html" or "text/xml", etc.
*/ */
protected void writeContent(NodeRef nodeRef, String content, String contentType) protected void writeContent(NodeRef nodeRef, String content, String mimetype)
{ {
InputStream inputStream = new ByteArrayInputStream(content.getBytes()); InputStream inputStream = new ByteArrayInputStream(content.getBytes(Charset.forName("UTF-8")));
writeContent(nodeRef, inputStream, contentType, "UTF-8"); writeContent(nodeRef, inputStream, mimetype, "UTF-8");
} }
/** /**
@@ -219,20 +195,35 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler
* *
* @param nodeRef Target node. * @param nodeRef Target node.
* @param content Content stream. * @param content Content stream.
* @param contentType MIME content type. * @param mimetype MIME content type.
* @param encoding Encoding. Can be null for non text based content. * @param encoding Encoding. Can be null for non text based content.
*/ */
protected void writeContent(NodeRef nodeRef, InputStream content, String contentType, String encoding) protected void writeContent(NodeRef nodeRef, InputStream content, String mimetype, String encoding)
{ {
InputStream bis = new BufferedInputStream(content, 4092);
// Guess the encoding if it is text
if (mimetypeService.isText(mimetype))
{
ContentCharsetFinder charsetFinder = mimetypeService.getContentCharsetFinder();
encoding = charsetFinder.getCharset(bis, mimetype).name();
}
else if (encoding == null)
{
encoding = "UTF-8";
}
if (log.isDebugEnabled()) if (log.isDebugEnabled())
{ {
log.debug("Write content (MimeType=\"" + contentType + "\", Encoding=\"" + encoding + "\""); log.debug("Write content (MimeType=\"" + mimetype + "\", Encoding=\"" + encoding + "\"");
} }
ContentService contentService = getContentService(); ContentService contentService = getContentService();
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
writer.setMimetype(contentType); writer.setMimetype(mimetype);
writer.setEncoding(encoding); writer.setEncoding(encoding);
writer.putContent(content); writer.putContent(bis);
} }
/** /**

View File

@@ -41,6 +41,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyMap;
/** /**
* Abstact class implements common logic for forum processing email mesages. * Abstact class implements common logic for forum processing email mesages.
@@ -62,12 +63,20 @@ public abstract class AbstractForumEmailMessageHandler extends AbstractEmailMess
Date now = new Date(); Date now = new Date();
String nodeName = "posted-" + new SimpleDateFormat("dd-MM-yyyy-hh-mm-ss").format(now) + ".html"; String nodeName = "posted-" + new SimpleDateFormat("dd-MM-yyyy-hh-mm-ss").format(now) + ".html";
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(1); PropertyMap properties = new PropertyMap(3);
properties.put(ContentModel.PROP_NAME, nodeName); properties.put(ContentModel.PROP_NAME, nodeName);
ChildAssociationRef childAssoc = nodeService.createNode(nodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, nodeName), NodeRef postNode = nodeService.getChildByName(nodeRef, ContentModel.ASSOC_CONTAINS, nodeName);
ForumModel.TYPE_POST, properties); if (postNode == null)
NodeRef postNode = childAssoc.getChildRef(); {
ChildAssociationRef childAssoc = nodeService.createNode(
nodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, nodeName),
ForumModel.TYPE_POST,
properties);
postNode = childAssoc.getChildRef();
}
// Add necessary aspects // Add necessary aspects
properties.clear(); properties.clear();
@@ -116,26 +125,34 @@ public abstract class AbstractForumEmailMessageHandler extends AbstractEmailMess
/** /**
* Adds topic node into Alfresco repository * Adds topic node into Alfresco repository
* *
* @param parentNode Parent node * @param parentNode Parent node
* @param name Topic name * @param name Topic name
* @return Reference to created node * @return Reference to created node
*/ */
protected NodeRef addTopicNode(NodeRef parentNode, String name) protected NodeRef addTopicNode(NodeRef parentNode, String name)
{ {
NodeService nodeService = getNodeService();
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(1); Map<QName, Serializable> properties = new HashMap<QName, Serializable>(1);
properties.put(ContentModel.PROP_NAME, name); properties.put(ContentModel.PROP_NAME, name);
ChildAssociationRef association = getNodeService().createNode(parentNode, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), NodeRef topicNode = nodeService.getChildByName(parentNode, ContentModel.ASSOC_CONTAINS, name);
ForumModel.TYPE_TOPIC, properties); if (topicNode == null)
NodeRef topic = association.getChildRef(); {
ChildAssociationRef association = nodeService.createNode(
parentNode,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name),
ForumModel.TYPE_TOPIC,
properties);
topicNode = association.getChildRef();
}
// Add necessary aspects // Add necessary aspects
properties.clear(); properties.clear();
properties.put(ApplicationModel.PROP_ICON, "topic"); properties.put(ApplicationModel.PROP_ICON, "topic");
getNodeService().addAspect(topic, ApplicationModel.ASPECT_UIFACETS, properties); getNodeService().addAspect(topicNode, ApplicationModel.ASPECT_UIFACETS, properties);
return topic; return topicNode;
} }
} }

View File

@@ -30,12 +30,12 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.i18n.I18NUtil; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ApplicationModel; import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel; import org.alfresco.model.ForumModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
@@ -64,9 +64,10 @@ public class DocumentEmailMessageHandler extends AbstractForumEmailMessageHandle
messageSubject = "EMPTY_SUBJECT_" + System.currentTimeMillis(); messageSubject = "EMPTY_SUBJECT_" + System.currentTimeMillis();
} }
QName contentType = getNodeService().getType(nodeRef); QName nodeTypeQName = getNodeService().getType(nodeRef);
if (contentType.equals(ContentModel.TYPE_CONTENT)) DictionaryService dictionaryService = getDictionaryService();
if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_CONTENT))
{ {
NodeRef forumNode = getForumNode(nodeRef); NodeRef forumNode = getForumNode(nodeRef);
@@ -87,7 +88,9 @@ public class DocumentEmailMessageHandler extends AbstractForumEmailMessageHandle
} }
else else
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.incorrect-node-type")); throw new AlfrescoRuntimeException("\n" +
"Message handler " + this.getClass().getName() + " cannot handle type " + nodeTypeQName + ".\n" +
"Check the message handler mappings.");
} }
} }
@@ -99,7 +102,7 @@ public class DocumentEmailMessageHandler extends AbstractForumEmailMessageHandle
*/ */
private NodeRef addForumNode(NodeRef nodeRef) private NodeRef addForumNode(NodeRef nodeRef)
{ {
NodeService nodeService=getNodeService(); NodeService nodeService = getNodeService();
//Add discussable aspect to content node //Add discussable aspect to content node
if (!nodeService.hasAspect(nodeRef, ForumModel.ASPECT_DISCUSSABLE)) if (!nodeService.hasAspect(nodeRef, ForumModel.ASPECT_DISCUSSABLE))
{ {

View File

@@ -25,19 +25,25 @@
package org.alfresco.email.server.handler; package org.alfresco.email.server.handler;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import org.alfresco.email.server.EmailServerModel; import org.alfresco.email.server.EmailServerModel;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.i18n.I18NUtil; import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailMessagePart; import org.alfresco.service.cmr.email.EmailMessagePart;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
@@ -53,6 +59,10 @@ import org.apache.commons.logging.LogFactory;
*/ */
public class FolderEmailMessageHandler extends AbstractEmailMessageHandler public class FolderEmailMessageHandler extends AbstractEmailMessageHandler
{ {
private static final String MSG_RECEIVED_BY_SMTP = "email.server.msg.received_by_smtp";
private static final String MSG_DEFAULT_SUBJECT = "email.server.msg.default_subject";
private static final String ERR_MAIL_READ_ERROR = "email.server.err.mail_read_error";
private static final Log log = LogFactory.getLog(FolderEmailMessageHandler.class); private static final Log log = LogFactory.getLog(FolderEmailMessageHandler.class);
/** /**
@@ -67,90 +77,82 @@ public class FolderEmailMessageHandler extends AbstractEmailMessageHandler
try try
{ {
// Check type of the node. It must be a SPACE // Check type of the node. It must be a SPACE
QName nodeTypeName = getNodeService().getType(nodeRef); QName nodeTypeQName = getNodeService().getType(nodeRef);
if (nodeTypeName.equals(ContentModel.TYPE_FOLDER)) if (getDictionaryService().isSubClass(nodeTypeQName, ContentModel.TYPE_FOLDER))
{ {
// Add the content into the system // Add the content into the system
addAlfrescoContent(nodeRef, message, null); addAlfrescoContent(nodeRef, message);
} }
else else
{ {
if (log.isDebugEnabled()) throw new AlfrescoRuntimeException("\n" +
{ "Message handler " + this.getClass().getName() + " cannot handle type " + nodeTypeQName + ".\n" +
log.debug("Addressed node type isn't a folder. Message has been passed without any actions."); "Check the message handler mappings.");
}
} }
} }
catch (IOException ex) catch (IOException ex)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.content-error"), ex); throw new EmailMessageException(ERR_MAIL_READ_ERROR, ex.getMessage());
} }
} }
/** /**
* Add content to Alfresco repository * Add content to Alfresco repository
* *
* @param spaceNodeRef Addressed node * @param spaceNodeRef Addressed node
* @param mailParser Mail message * @param mailParser Mail message
* @param nameConflictResolver String that can be used as part of name for resolving name conflict. * @throws IOException Exception can be thrown while saving a content into Alfresco repository.
* @throws IOException Exception can be thrown while saving a content into Alfresco repository. * @throws MessagingException Exception can be thrown while parsing e-mail message.
* @throws MessagingException Exception can be thrown while parsing e-mail message.
*/ */
public void addAlfrescoContent(NodeRef spaceNodeRef, EmailMessage message, String nameConflictResolver) throws IOException public void addAlfrescoContent(NodeRef spaceNodeRef, EmailMessage message) throws IOException
{ {
// Set default values for email fields // Set default values for email fields
if (nameConflictResolver == null) String messageSubject = message.getSubject();
nameConflictResolver = ""; if (messageSubject == null || messageSubject.length() == 0)
String messageSubject = "EMPTY_SUBJECT_" + nameConflictResolver;
if (message.getSubject().length() != 0)
{ {
messageSubject = message.getSubject() + nameConflictResolver; Date now = new Date();
messageSubject = I18NUtil.getMessage(MSG_DEFAULT_SUBJECT, new SimpleDateFormat("dd-MM-yyyy-hh-mm-ss").format(now));
} }
// Create node // Create main content node
if (log.isDebugEnabled())
{
log.debug("Adding main content node ...");
}
NodeRef contentNodeRef; NodeRef contentNodeRef;
contentNodeRef = addContentNode(getNodeService(), spaceNodeRef, messageSubject); contentNodeRef = addContentNode(getNodeService(), spaceNodeRef, messageSubject);
// Add titled aspect // Add titled aspect
addTitledAspect(contentNodeRef, messageSubject); addTitledAspect(contentNodeRef, messageSubject, message.getFrom());
// Add emailed aspect // Add emailed aspect
addEmailedAspect(contentNodeRef, message); addEmailedAspect(contentNodeRef, message);
// Write the message content // Write the message content
if (message.getBody() != null) if (message.getBody() != null)
writeContent(contentNodeRef, message.getBody().getContent(), message.getBody().getContentType(), message.getBody().getEncoding());
else
writeContent(contentNodeRef, "<The message was empty>");
if (log.isDebugEnabled())
{ {
log.debug("Main content node has been added."); 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);
} }
// Add attachments // Add attachments
EmailMessagePart[] attachments = message.getAttachments(); EmailMessagePart[] attachments = message.getAttachments();
for (EmailMessagePart attachment : attachments) for (EmailMessagePart attachment : attachments)
{ {
NodeRef attachmentNode;
String fileName = attachment.getFileName(); String fileName = attachment.getFileName();
// Add name conflict resolver if necessary InputStream contentIs = attachment.getContent();
if (nameConflictResolver.length() != 0)
{
if (fileName.lastIndexOf('.') != -1)
fileName = fileName.substring(0, fileName.lastIndexOf('.')) + " (" + nameConflictResolver + ")" + fileName.substring(fileName.lastIndexOf('.'));
else
fileName += " (" + nameConflictResolver + ")";
}
attachmentNode = addAttachment(getNodeService(), spaceNodeRef, contentNodeRef, fileName); MimetypeService mimetypeService = getMimetypeService();
writeContent(attachmentNode, attachment.getContent(), attachment.getContentType(), attachment.getEncoding()); String mimetype = mimetypeService.guessMimetype(fileName);
String encoding = attachment.getEncoding();
NodeRef attachmentNode = addAttachment(getNodeService(), spaceNodeRef, contentNodeRef, fileName);
writeContent(attachmentNode, contentIs, mimetype, encoding);
} }
} }
@@ -178,11 +180,25 @@ public class FolderEmailMessageHandler extends AbstractEmailMessageHandler
*/ */
private NodeRef addContentNode(NodeService nodeService, NodeRef parent, String name, QName assocType) private NodeRef addContentNode(NodeService nodeService, NodeRef parent, String name, QName assocType)
{ {
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>(); NodeRef childNodeRef = nodeService.getChildByName(parent, assocType, name);
contentProps.put(ContentModel.PROP_NAME, name); if (childNodeRef != null)
ChildAssociationRef associationRef = nodeService.createNode(parent, assocType, QName.createQName(NamespaceService.CONTENT_MODEL_PREFIX, name), ContentModel.TYPE_CONTENT, {
contentProps); // The node is present already. Make sure the name csae is correct
return associationRef.getChildRef(); nodeService.setProperty(childNodeRef, ContentModel.PROP_NAME, name);
}
else
{
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
contentProps.put(ContentModel.PROP_NAME, name);
ChildAssociationRef associationRef = nodeService.createNode(
parent,
assocType,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name),
ContentModel.TYPE_CONTENT,
contentProps);
childNodeRef = associationRef.getChildRef();
}
return childNodeRef;
} }
/** /**
@@ -203,9 +219,15 @@ public class FolderEmailMessageHandler extends AbstractEmailMessageHandler
NodeRef attachmentNode = addContentNode(nodeService, folder, fileName); NodeRef attachmentNode = addContentNode(nodeService, folder, fileName);
// Remove 'attached' aspect so that we work with the document in its clean form
if (nodeService.hasAspect(attachmentNode, EmailServerModel.ASPECT_ATTACHED))
{
nodeService.removeAspect(attachmentNode, EmailServerModel.ASPECT_ATTACHED);
}
// Add attached aspect // Add attached aspect
Map<QName, Serializable> attachedProps = new HashMap<QName, Serializable>(); nodeService.addAspect(attachmentNode, EmailServerModel.ASPECT_ATTACHED, null);
nodeService.addAspect(attachmentNode, EmailServerModel.ASPECT_ATTACHED, attachedProps); // Recreate the association
nodeService.createAssociation(attachmentNode, mainContentNode, EmailServerModel.ASSOC_ATTACHMENT); nodeService.createAssociation(attachmentNode, mainContentNode, EmailServerModel.ASSOC_ATTACHMENT);
if (log.isDebugEnabled()) if (log.isDebugEnabled())
@@ -221,11 +243,11 @@ public class FolderEmailMessageHandler extends AbstractEmailMessageHandler
* @param nodeRef Target node. * @param nodeRef Target node.
* @param title Title * @param title Title
*/ */
private void addTitledAspect(NodeRef nodeRef, String title) private void addTitledAspect(NodeRef nodeRef, String title, String from)
{ {
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>(); Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>();
titledProps.put(ContentModel.PROP_TITLE, title); titledProps.put(ContentModel.PROP_TITLE, title);
titledProps.put(ContentModel.PROP_DESCRIPTION, "Received by SMTP"); titledProps.put(ContentModel.PROP_DESCRIPTION, I18NUtil.getMessage(MSG_RECEIVED_BY_SMTP, from));
getNodeService().addAspect(nodeRef, ContentModel.ASPECT_TITLED, titledProps); getNodeService().addAspect(nodeRef, ContentModel.ASPECT_TITLED, titledProps);
if (log.isDebugEnabled()) if (log.isDebugEnabled())

View File

@@ -24,10 +24,9 @@
*/ */
package org.alfresco.email.server.handler; package org.alfresco.email.server.handler;
import org.alfresco.i18n.I18NUtil; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ForumModel; import org.alfresco.model.ForumModel;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -55,9 +54,9 @@ public class ForumEmailMessageHandler extends AbstractForumEmailMessageHandler
messageSubject = "EMPTY_SUBJECT_" + System.currentTimeMillis(); messageSubject = "EMPTY_SUBJECT_" + System.currentTimeMillis();
} }
QName nodeType = getNodeService().getType(nodeRef); QName nodeTypeQName = getNodeService().getType(nodeRef);
if (nodeType.equals(ForumModel.TYPE_FORUM)) if (getDictionaryService().isSubClass(nodeTypeQName, ForumModel.TYPE_FORUM))
{ {
NodeRef topicNode = getTopicNode(nodeRef, messageSubject); NodeRef topicNode = getTopicNode(nodeRef, messageSubject);
@@ -69,7 +68,9 @@ public class ForumEmailMessageHandler extends AbstractForumEmailMessageHandler
} }
else else
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.incorrect-node-type")); throw new AlfrescoRuntimeException("\n" +
"Message handler " + this.getClass().getName() + " cannot handle type " + nodeTypeQName + ".\n" +
"Check the message handler mappings.");
} }
} }
} }

View File

@@ -24,10 +24,9 @@
*/ */
package org.alfresco.email.server.handler; package org.alfresco.email.server.handler;
import org.alfresco.i18n.I18NUtil; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ForumModel; import org.alfresco.model.ForumModel;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -44,20 +43,26 @@ public class TopicEmailMessageHandler extends AbstractForumEmailMessageHandler
*/ */
public void processMessage(NodeRef nodeRef, EmailMessage message) public void processMessage(NodeRef nodeRef, EmailMessage message)
{ {
QName nodeType = getNodeService().getType(nodeRef); QName nodeTypeQName = getNodeService().getType(nodeRef);
NodeRef topicNode = null; NodeRef topicNode = null;
if (nodeType.equals(ForumModel.TYPE_TOPIC)) if (getDictionaryService().isSubClass(nodeTypeQName, ForumModel.TYPE_TOPIC))
{ {
topicNode = nodeRef; topicNode = nodeRef;
} }
else if (nodeType.equals(ForumModel.TYPE_POST)) else if (getDictionaryService().isSubClass(nodeTypeQName, ForumModel.TYPE_POST))
{ {
topicNode = getNodeService().getPrimaryParent(nodeRef).getChildRef(); topicNode = getNodeService().getPrimaryParent(nodeRef).getParentRef();
if (topicNode == null)
{
throw new AlfrescoRuntimeException("A POST node has no primary parent: " + nodeRef);
}
} }
if (topicNode == null) else
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.incorrect-node-ref")); throw new AlfrescoRuntimeException("\n" +
"Message handler " + this.getClass().getName() + " cannot handle type " + nodeTypeQName + ".\n" +
"Check the message handler mappings.");
} }
addPostNode(topicNode, message); addPostNode(topicNode, message);
} }

View File

@@ -39,7 +39,6 @@ import javax.mail.Session;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility; import javax.mail.internet.MimeUtility;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailMessagePart; import org.alfresco.service.cmr.email.EmailMessagePart;
@@ -53,6 +52,15 @@ import org.apache.commons.logging.LogFactory;
*/ */
public class SubethaEmailMessage implements EmailMessage public class SubethaEmailMessage implements EmailMessage
{ {
private static final String ERR_FAILED_TO_CREATE_MIME_MESSAGE = "email.server.err.failed_to_create_mime_message";
private static final String ERR_EXTRACTING_FROM_ADDRESS = "email.server.err.extracting_from_address";
private static final String ERR_NO_FROM_ADDRESS = "email.server.err.no_from_address";
private static final String ERR_EXTRACTING_TO_ADDRESS = "email.server.err.extracting_to_address";
private static final String ERR_NO_TO_ADDRESS = "email.server.err.no_to_address";
private static final String ERR_EXTRACTING_SUBJECT = "email.server.err.extracting_subject";
private static final String ERR_EXTRACTING_SENT_DATE = "email.server.err.extracting_sent_date";
private static final String ERR_PARSE_MESSAGE = "email.server.err.parse_message";
private static final long serialVersionUID = -3735187524926395261L; private static final long serialVersionUID = -3735187524926395261L;
private static final Log log = LogFactory.getLog(SubethaEmailMessage.class); private static final Log log = LogFactory.getLog(SubethaEmailMessage.class);
@@ -98,7 +106,7 @@ public class SubethaEmailMessage implements EmailMessage
} }
catch (MessagingException e) catch (MessagingException e)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.error-creating-message"), e); throw new EmailMessageException(ERR_FAILED_TO_CREATE_MIME_MESSAGE, e.getMessage());
} }
processMimeMessage(mimeMessage); processMimeMessage(mimeMessage);
@@ -115,11 +123,11 @@ public class SubethaEmailMessage implements EmailMessage
} }
catch (MessagingException e) catch (MessagingException e)
{ {
throw new EmailMessageException("Error extract from.", e); throw new EmailMessageException(ERR_EXTRACTING_FROM_ADDRESS, e.getMessage());
} }
if (addresses == null || addresses.length == 0) if (addresses == null || addresses.length == 0)
{ {
throw new EmailMessageException("There is no one from."); throw new EmailMessageException(ERR_NO_FROM_ADDRESS);
} }
from = addresses[0].toString(); from = addresses[0].toString();
} }
@@ -133,11 +141,11 @@ public class SubethaEmailMessage implements EmailMessage
} }
catch (MessagingException e) catch (MessagingException e)
{ {
throw new EmailMessageException("Error extract recepient.", e); throw new EmailMessageException(ERR_EXTRACTING_TO_ADDRESS, e.getMessage());
} }
if (addresses == null || addresses.length == 0) if (addresses == null || addresses.length == 0)
{ {
throw new EmailMessageException("There is no one recepient."); throw new EmailMessageException(ERR_NO_TO_ADDRESS);
} }
to = addresses[0].toString(); to = addresses[0].toString();
} }
@@ -148,7 +156,7 @@ public class SubethaEmailMessage implements EmailMessage
} }
catch (MessagingException e) catch (MessagingException e)
{ {
throw new EmailMessageException("Error extract subject.", e); throw new EmailMessageException(ERR_EXTRACTING_SUBJECT, e.getMessage());
} }
if (subject == null) if (subject == null)
{ {
@@ -161,7 +169,7 @@ public class SubethaEmailMessage implements EmailMessage
} }
catch (MessagingException e) catch (MessagingException e)
{ {
throw new EmailMessageException("Error extract sentDate.", e); throw new EmailMessageException(ERR_EXTRACTING_SENT_DATE, e.getMessage());
} }
if (sentDate == null) if (sentDate == null)
{ {
@@ -261,11 +269,11 @@ public class SubethaEmailMessage implements EmailMessage
} }
catch (IOException e) catch (IOException e)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.error-parse-message"), e); throw new EmailMessageException(ERR_PARSE_MESSAGE, e.getMessage());
} }
catch (MessagingException e) catch (MessagingException e)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.error-parse-message"), e); throw new EmailMessageException(ERR_PARSE_MESSAGE, e.getMessage());
} }
} }

View File

@@ -35,9 +35,9 @@ import java.util.regex.Pattern;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.mail.Part; import javax.mail.Part;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailMessagePart; import org.alfresco.service.cmr.email.EmailMessagePart;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.remote.RemotableInputStream; import org.alfresco.util.remote.RemotableInputStream;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -47,6 +47,10 @@ import org.apache.commons.logging.LogFactory;
*/ */
public class SubethaEmailMessagePart implements EmailMessagePart public class SubethaEmailMessagePart implements EmailMessagePart
{ {
private static final String ERR_UNSUPPORTED_ENCODING = "email.server.err.usupported_encoding";
private static final String ERR_FAILED_TO_READ_CONTENT_STREAM = "email.server.err.failed_to_read_content_stream";
private static final String ERR_INCORRECT_MESSAGE_PART = "email.server.err.incorrect_message_part";
private static final long serialVersionUID = -8530238872199733096L; private static final long serialVersionUID = -8530238872199733096L;
static final Log log = LogFactory.getLog(SubethaEmailMessagePart.class); static final Log log = LogFactory.getLog(SubethaEmailMessagePart.class);
@@ -74,8 +78,7 @@ public class SubethaEmailMessagePart implements EmailMessagePart
*/ */
public SubethaEmailMessagePart(Part messagePart) public SubethaEmailMessagePart(Part messagePart)
{ {
if (messagePart == null) ParameterCheck.mandatory("messagePart", messagePart);
throw new IllegalArgumentException("messagePart");
try try
{ {
@@ -89,7 +92,7 @@ public class SubethaEmailMessagePart implements EmailMessagePart
encoding = matcher.group(1); encoding = matcher.group(1);
if (!Charset.isSupported(encoding)) if (!Charset.isSupported(encoding))
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.usupported-encoding", encoding)); throw new EmailMessageException(ERR_UNSUPPORTED_ENCODING, encoding);
} }
} }
@@ -99,12 +102,12 @@ public class SubethaEmailMessagePart implements EmailMessagePart
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.error-getting-content-stream"), ex); throw new EmailMessageException(ERR_FAILED_TO_READ_CONTENT_STREAM, ex.getMessage());
} }
} }
catch (MessagingException e) catch (MessagingException e)
{ {
throw new EmailMessageException(I18NUtil.getMessage("email.server.incorrect-message-part"), e); throw new EmailMessageException(ERR_INCORRECT_MESSAGE_PART, e.getMessage());
} }
} }

View File

@@ -31,7 +31,6 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.alfresco.email.server.EmailServer; import org.alfresco.email.server.EmailServer;
import org.alfresco.email.server.EmailServerConfiguration;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailMessageException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@@ -53,17 +52,17 @@ public class SubethaEmailServer extends EmailServer
private SMTPServer serverImpl; private SMTPServer serverImpl;
protected SubethaEmailServer(EmailServerConfiguration serverConfiguration) protected SubethaEmailServer()
{ {
super(serverConfiguration); super();
serverImpl = new SMTPServer(new HandlerFactory());
serverImpl.setPort(serverConfiguration.getPort());
serverImpl.setHostName(serverConfiguration.getDomain());
} }
@Override @Override
public void startup() public void startup()
{ {
serverImpl = new SMTPServer(new HandlerFactory());
serverImpl.setPort(getPort());
serverImpl.setHostName(getDomain());
serverImpl.start(); serverImpl.start();
log.info("Email Server has started successfully"); log.info("Email Server has started successfully");
} }
@@ -112,7 +111,7 @@ public class SubethaEmailServer extends EmailServer
this.from = from; this.from = from;
try try
{ {
blackAndWhiteListFiltering(from); filterSender(from);
} }
catch (EmailMessageException e) catch (EmailMessageException e)
{ {
@@ -168,12 +167,17 @@ public class SubethaEmailServer extends EmailServer
try try
{ {
emailMessage = new SubethaEmailMessage(from, delivery.getRecipient(), data); emailMessage = new SubethaEmailMessage(from, delivery.getRecipient(), data);
configuration.getEmailService().importMessage(emailMessage); getEmailService().importMessage(emailMessage);
} }
catch (EmailMessageException e) catch (EmailMessageException e)
{ {
throw new RejectException(554, e.getMessage()); throw new RejectException(554, e.getMessage());
} }
catch (Throwable e)
{
log.error(e.getMessage(), e);
throw new RejectException(554, "An internal error prevented mail delivery.");
}
} }
public List<String> getAuthenticationMechanisms() public List<String> getAuthenticationMechanisms()

View File

@@ -24,7 +24,12 @@
*/ */
package org.alfresco.service.cmr.email; package org.alfresco.service.cmr.email;
import org.alfresco.i18n.I18NUtil;
/** /**
* A checked and handled exception indicating a specific and well-known
* email error condition.
*
* @since 2.2 * @since 2.2
*/ */
public class EmailMessageException extends RuntimeException public class EmailMessageException extends RuntimeException
@@ -32,36 +37,13 @@ public class EmailMessageException extends RuntimeException
private static final long serialVersionUID = 5039365329619219256L; private static final long serialVersionUID = 5039365329619219256L;
/** /**
* Empty contructor * @param message exception message.
* @param params message arguments for I18N
*
* @see I18NUtil#getMessage(String, Object[])
*/ */
public EmailMessageException() public EmailMessageException(String message, Object ... params)
{ {
super(I18NUtil.getMessage(message, params));
super();
}
/**
* @param message exception message.
* @param cause throwable object.
*/
public EmailMessageException(String message, Throwable cause)
{
super(message, cause);
}
/**
* @param message exception message.
*/
public EmailMessageException(String message)
{
super(message);
}
/**
* @param cause throwable object
*/
public EmailMessageException(Throwable cause)
{
super(cause);
} }
} }