diff --git a/.secrets.baseline b/.secrets.baseline index 64703dba5e..78ef96fb85 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1377,7 +1377,7 @@ "filename": "repository/src/test/java/org/alfresco/repo/imap/ImapMessageTest.java", "hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997", "is_verified": false, - "line_number": 118, + "line_number": 116, "is_secret": false } ], @@ -1868,5 +1868,5 @@ } ] }, - "generated_at": "2025-02-11T13:28:51Z" + "generated_at": "2025-02-18T15:50:35Z" } diff --git a/amps/ags/rm-community/rm-community-repo/pom.xml b/amps/ags/rm-community/rm-community-repo/pom.xml index 2a8715fb99..bcfb20b7e7 100644 --- a/amps/ags/rm-community/rm-community-repo/pom.xml +++ b/amps/ags/rm-community/rm-community-repo/pom.xml @@ -63,10 +63,6 @@ jakarta.servlet jakarta.servlet-api - - jakarta.mail - jakarta.mail-api - org.alfresco.surf spring-webscripts diff --git a/packaging/distribution/src/main/resources/licenses/notice.txt b/packaging/distribution/src/main/resources/licenses/notice.txt index 3fa5e4c0a6..1bb810715f 100644 --- a/packaging/distribution/src/main/resources/licenses/notice.txt +++ b/packaging/distribution/src/main/resources/licenses/notice.txt @@ -180,13 +180,13 @@ Lightbox JS http://lokeshdhakar.com/projects/lightbox/ === Eclipse Public License 1.0 === +Angus Mail Provider https://eclipse-ee4j.github.io/angus-mail/ AspectJ http://eclipse.org/aspectj/ Bliki http://code.google.com/p/gwtwiki/ JUnit http://junit.org/ TrueLicense http://truelicense.java.net/ truezip http://truezip.java.net/ - === ICU License === icu4j http://icu-project.org/ diff --git a/packaging/tests/tas-integration/pom.xml b/packaging/tests/tas-integration/pom.xml index dad531730c..c8a908d234 100644 --- a/packaging/tests/tas-integration/pom.xml +++ b/packaging/tests/tas-integration/pom.xml @@ -57,8 +57,8 @@ - com.sun.mail - jakarta.mail + org.eclipse.angus + angus-mail diff --git a/pom.xml b/pom.xml index d438da32db..59c866d9ab 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,7 @@ 2.0.6.1 3.1.6 2.3.0 + 2.0.3 4.0.2 4.0.5 @@ -105,7 +106,7 @@ 3.0.0 2.0.1 3.0.0 - 2.0.1 + 2.1.3 2.0.1 3.1.0 1.2.0 @@ -219,15 +220,14 @@ - com.sun.mail - jakarta.mail - ${dependency.jakarta-ee-mail.version} + org.eclipse.angus + angus-mail + ${dependency.angus-mail.version} jakarta.mail jakarta.mail-api ${dependency.jakarta-ee-mail.version} - provided com.sun.activation diff --git a/repository/pom.xml b/repository/pom.xml index e695ca3df4..3fc185861a 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -56,8 +56,8 @@ jakarta.mail-api - com.sun.mail - jakarta.mail + org.eclipse.angus + angus-mail @@ -190,7 +190,7 @@ com.github.davidmoten subethasmtp - 6.0.6 + 7.1.3 diff --git a/repository/src/test/java/org/alfresco/email/server/EmailServiceImplTest.java b/repository/src/test/java/org/alfresco/email/server/EmailServiceImplTest.java index 3536da694b..f5d7205486 100644 --- a/repository/src/test/java/org/alfresco/email/server/EmailServiceImplTest.java +++ b/repository/src/test/java/org/alfresco/email/server/EmailServiceImplTest.java @@ -1,1220 +1,1190 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.email.server; - -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.Vector; -import java.util.concurrent.CountDownLatch; - -import jakarta.mail.Message; -import jakarta.mail.Session; -import jakarta.mail.internet.InternetAddress; - -import junit.framework.TestCase; - -import org.alfresco.email.server.handler.FolderEmailMessageHandler; -import org.alfresco.email.server.impl.subetha.SubethaEmailMessage; -import org.alfresco.model.ContentModel; -import org.alfresco.model.ForumModel; -import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transfer.TransferModel; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.cmr.email.EmailDelivery; -import org.alfresco.service.cmr.email.EmailMessageException; -import org.alfresco.service.cmr.email.EmailService; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.RegexQNamePattern; -import org.alfresco.test_category.OwnJVMTestsCategory; -import org.alfresco.util.ApplicationContextHelper; -import org.alfresco.util.testing.category.LuceneTests; -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.experimental.categories.Category; -import org.springframework.context.ApplicationContext; - -import com.sun.mail.smtp.SMTPMessage; - -/** - * Unit test of EmailServiceImplTest - * @author mrogers - * - */ -@Category({OwnJVMTestsCategory.class}) -public class EmailServiceImplTest extends TestCase -{ - /** - * Services used by the tests - */ - private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); - - private static Log logger = LogFactory.getLog(EmailServiceImplTest.class); - - private NodeService nodeService; - private EmailService emailService; - private PersonService personService; - private AuthorityService authorityService; - private SearchService searchService; - private NamespaceService namespaceService; - private FolderEmailMessageHandler folderEmailMessageHandler; - private RetryingTransactionHelper transactionHelper; - - String TEST_USER="EmailServiceImplTestUser"; - - @Override - public void setUp() throws Exception - { - AuthenticationUtil.setRunAsUserSystem(); - nodeService = (NodeService)ctx.getBean("NodeService"); - assertNotNull("nodeService", nodeService); - authorityService = (AuthorityService)ctx.getBean("AuthorityService"); - assertNotNull("authorityService", authorityService); - ChildApplicationContextFactory emailSubsystem = (ChildApplicationContextFactory) ctx.getBean("InboundSMTP"); - assertNotNull("emailSubsystem", emailSubsystem); - ApplicationContext emailCtx = emailSubsystem.getApplicationContext(); - emailService = (EmailService)emailCtx.getBean("emailService"); - assertNotNull("emailService", emailService); - personService = (PersonService)emailCtx.getBean("PersonService"); - assertNotNull("personService", personService); - namespaceService = (NamespaceService)emailCtx.getBean("NamespaceService"); - assertNotNull("namespaceService", namespaceService); - searchService = (SearchService)emailCtx.getBean("SearchService"); - assertNotNull("searchService", searchService); - folderEmailMessageHandler = (FolderEmailMessageHandler) emailCtx.getBean("folderEmailMessageHandler"); - assertNotNull("folderEmailMessageHandler", folderEmailMessageHandler); - transactionHelper = (RetryingTransactionHelper) emailCtx.getBean("retryingTransactionHelper"); - assertNotNull("transactionHelper", transactionHelper); - } - - public void tearDown() throws Exception - { - AuthenticationUtil.setRunAsUserSystem(); - try - { - personService.deletePerson(TEST_USER); - } - catch (Exception e) - { - } - } - - /** - * Test the from name. - * - * Step 1: - * User admin will map to the "unknownUser" which out of the box is "anonymous" - * Sending email From "admin" will fail. - * - * Step 2: - * Send from the test user to the test' user's home folder. - */ - public void testFromName() throws Exception - { - - folderEmailMessageHandler.setOverwriteDuplicates(true); - - logger.debug("Start testFromName"); - - String TEST_EMAIL="buffy@sunnydale.high"; - - // TODO Investigate why setting PROP_EMAIL on createPerson does not work. - NodeRef person = personService.getPerson(TEST_USER); - if(person == null) - { - logger.debug("new person created"); - Map props = new HashMap(); - props.put(ContentModel.PROP_USERNAME, TEST_USER); - props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); - person = personService.createPerson(props); - } - nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); - - Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); - if(!auths.contains(TEST_USER)) - { - authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); - } - - String companyHomePathInStore = "/app:company_home"; - String storePath = "workspace://SpacesStore"; - StoreRef storeRef = new StoreRef(storePath); - - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); - NodeRef companyHomeNodeRef = nodeRefs.get(0); - assertNotNull("company home is null", companyHomeNodeRef); - String companyHomeDBID = ((Long)nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - String testUserDBID = ((Long)nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - NodeRef testUserHomeFolder = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); - assertNotNull("testUserHomeFolder is null", testUserHomeFolder); - String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - - /** - * Step 1 - * Negative test - send from "Bert" who does not exist. - * User will be mapped to anonymous who is not an email contributor. - */ - try - { - String from = "admin"; - String to = "bertie"; - String content = "hello world"; - - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress("Bert")); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("JavaMail APIs transport.java Test"); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - fail("anonymous user not rejected"); - } - catch (EmailMessageException e) - { - // Check the exception is for the anonymous user. - assertTrue("Message is not for anonymous", e.getMessage().contains("anonymous")); - } - - /** - * Step 2 - * - * Send From the test user TEST_EMAIL to the test user's home - */ - { - logger.debug("Step 2"); - - String from = TEST_EMAIL; - String to = testUserHomeDBID; - String content = "hello world"; - - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("JavaMail APIs transport.java Test"); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - } - - /** - * Step 3 - * - * message.from From with "name" < name@ domain > format - * SMTP.FROM="dummy" - * - * Send From the test user to the test user's home - */ - { - logger.debug("Step 3"); - - String from = " \"Joe Bloggs\" <" + TEST_EMAIL + ">"; - String to = testUserHomeDBID; - String content = "hello world"; - - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(from)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("JavaMail APIs transport.java Test"); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(System.out); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - - EmailDelivery delivery = new EmailDelivery(to, "dummy", null); - - emailService.importMessage(delivery,m); - } - - /** - * Step 4 - * - * From with "name" < name@ domain > format - * - * Send From the test user to the test user's home - */ - { - logger.debug("Step 4"); - - String from = " \"Joe Bloggs\" <" + TEST_EMAIL + ">"; - String to = testUserHomeDBID; - String content = "hello world"; - - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(from)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("JavaMail APIs transport.java Test"); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(System.out); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - - InternetAddress a = new InternetAddress(from); - String x = a.getAddress(); - - EmailDelivery delivery = new EmailDelivery(to, x, null); - - emailService.importMessage(delivery,m); - } - -// /** -// * Step 5 -// * -// * From with format -// * -// * RFC3696 -// * -// * Send From the test user to the test user's home -// */ -// { -// logger.debug("Step 4 format"); -// -// String from = "\"Joe Bloggs\" "; -// String to = testUserHomeDBID; -// String content = "hello world"; -// -// Session sess = Session.getDefaultInstance(new Properties()); -// assertNotNull("sess is null", sess); -// SMTPMessage msg = new SMTPMessage(sess); -// InternetAddress[] toa = { new InternetAddress(to) }; -// -// msg.setFrom(new InternetAddress(from)); -// msg.setRecipients(Message.RecipientType.TO, toa); -// msg.setSubject("JavaMail APIs transport.java Test"); -// msg.setContent(content, "text/plain"); -// -// StringBuffer sb = new StringBuffer(); -// ByteArrayOutputStream bos = new ByteArrayOutputStream(); -// msg.writeTo(System.out); -// msg.writeTo(bos); -// InputStream is = IOUtils.toInputStream(bos.toString()); -// assertNotNull("is is null", is); -// -// SubethaEmailMessage m = new SubethaEmailMessage(is); -// -// emailService.importMessage(m); -// } - } - - - /** - * ALF-9544 - * ALF-751 - * - * Inbound email to a folder restricts file name to 86 characters or less. - * - * Also has tests for other variations of subject - */ - public void testFolderSubject() throws Exception - { - logger.debug("Start testFromName"); - - String TEST_EMAIL="buffy@sunnydale.high"; - - folderEmailMessageHandler.setOverwriteDuplicates(true); - - // TODO Investigate why setting PROP_EMAIL on createPerson does not work. - NodeRef person = personService.getPerson(TEST_USER); - if(person == null) - { - logger.debug("new person created"); - Map props = new HashMap(); - props.put(ContentModel.PROP_USERNAME, TEST_USER); - props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); - person = personService.createPerson(props); - } - nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); - - Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); - if(!auths.contains(TEST_USER)) - { - authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); - } - - String companyHomePathInStore = "/app:company_home"; - String storePath = "workspace://SpacesStore"; - StoreRef storeRef = new StoreRef(storePath); - - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); - NodeRef companyHomeNodeRef = nodeRefs.get(0); - assertNotNull("company home is null", companyHomeNodeRef); - String companyHomeDBID = ((Long)nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - String testUserDBID = ((Long)nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - NodeRef testUserHomeFolder = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); - assertNotNull("testUserHomeFolder is null", testUserHomeFolder); - String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - - /** - * Send From the test user TEST_EMAIL to the test user's home - */ - String from = TEST_EMAIL; - String to = testUserHomeDBID; - String content = "hello world"; - - { - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("This is a very very long name in particular it is greater than eitghty six characters which was a problem explored in ALF-9544"); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - } - - // Check import with subject containing some "illegal chars" - { - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("Illegal<>!*/\\.txt"); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - } - - // Check with null subject - { - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - //msg.setSubject(); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - } - - - // ALF-751 Email ends with period - { - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("Foobar."); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - } - - // ALF-751 Email ends with ... - { - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("Foobar..."); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - } - - // ALF-751 Email subject is blank " ... " - { - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject(" ... "); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - } - - // ALF-751 Email subject is a single . - { - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("."); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - } - - } - - /** - * ALF-1878 - * - * Duplicate incoming email Subjects over-write each other - */ - public void testMultipleMessagesToFolder() throws Exception - { - logger.debug("Start testFromName"); - - String TEST_EMAIL="buffy@sunnydale.high"; - - String TEST_SUBJECT="Practical Bee Keeping"; - - String TEST_LONG_SUBJECT = "This is a very very long name in particular it is greater than eitghty six characters which was a problem explored in ALF-9544"; - - - // TODO Investigate why setting PROP_EMAIL on createPerson does not work. - NodeRef person = personService.getPerson(TEST_USER); - if(person == null) - { - logger.debug("new person created"); - Map props = new HashMap(); - props.put(ContentModel.PROP_USERNAME, TEST_USER); - props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); - person = personService.createPerson(props); - } - nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); - - Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); - if(!auths.contains(TEST_USER)) - { - authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); - } - - String companyHomePathInStore = "/app:company_home"; - String storePath = "workspace://SpacesStore"; - StoreRef storeRef = new StoreRef(storePath); - - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); - NodeRef companyHomeNodeRef = nodeRefs.get(0); - assertNotNull("company home is null", companyHomeNodeRef); - String companyHomeDBID = ((Long)nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - String testUserDBID = ((Long)nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - NodeRef testUserHomeFolder = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); - assertNotNull("testUserHomeFolder is null", testUserHomeFolder); - String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - - // Clean up old messages in test folder - List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - for(ChildAssociationRef assoc : assocs) - { - nodeService.deleteNode(assoc.getChildRef()); - } - - /** - * Send From the test user TEST_EMAIL to the test user's home - */ - String from = TEST_EMAIL; - String to = testUserHomeDBID; - String content = "hello world"; - - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject(TEST_SUBJECT); - msg.setContent(content, "text/plain"); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - - /** - * Turn on overwriteDuplicates - */ - logger.debug("Step 1: turn on Overwite Duplicates"); - folderEmailMessageHandler.setOverwriteDuplicates(true); - - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - assertEquals("assocs not 1", 1, assocs.size()); - assertEquals("name of link not as expected", assocs.get(0).getQName(), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_SUBJECT)); - emailService.importMessage(delivery, m); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - assertEquals("assocs not 1", 1, assocs.size()); - - /** - * Turn off overwrite Duplicates - */ - logger.debug("Step 2: turn off Overwite Duplicates"); - folderEmailMessageHandler.setOverwriteDuplicates(false); - emailService.importMessage(delivery, m); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - assertEquals("assocs not 2", 2, assocs.size()); - emailService.importMessage(delivery, m); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - assertEquals("assocs not 3", 3, assocs.size()); - - /** - * Check assoc rename with long names. So truncation and rename need to work together. - */ - logger.debug("Step 3: turn off Overwite Duplicates with long subject name"); - msg.setSubject(TEST_LONG_SUBJECT); - ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); - msg.writeTo(bos2); - is = IOUtils.toInputStream(bos2.toString()); - assertNotNull("is is null", is); - m = new SubethaEmailMessage(is); - - folderEmailMessageHandler.setOverwriteDuplicates(false); - emailService.importMessage(delivery, m); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - assertEquals("assocs not 4", 4, assocs.size()); - emailService.importMessage(delivery, m); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - assertEquals("assocs not 5", 5, assocs.size()); - - /** - * Check assoc rename with long names and an extension. So truncation and rename need to - * work together and not muck up a .extension. - */ - logger.debug("Step 4: turn off Overwite Duplicates with long subject name with extension"); - String EXT_NAME = "Blob.xls"; - - msg.setSubject(EXT_NAME); - ByteArrayOutputStream bos3 = new ByteArrayOutputStream(); - msg.writeTo(bos3); - is = IOUtils.toInputStream(bos3.toString()); - assertNotNull("is is null", is); - m = new SubethaEmailMessage(is); - folderEmailMessageHandler.setOverwriteDuplicates(false); - emailService.importMessage(delivery, m); - emailService.importMessage(delivery, m); - emailService.importMessage(delivery, m); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - - List assocNames = new Vector(); - for(ChildAssociationRef assoc : assocs) - { - logger.debug("assocName: " + assoc.getQName()); - System.out.println(assoc.getQName()); - assocNames.add(assoc.getQName()); - } - assertTrue(EXT_NAME + "not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Blob.xls"))); - assertTrue("Blob(1).xls not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Blob(1).xls"))); - assertTrue("Blob(2).xls not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Blob(2).xls"))); - assertTrue(TEST_SUBJECT + "not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_SUBJECT))); - assertTrue(TEST_SUBJECT+"(1) not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Practical Bee Keeping(1)"))); - - /** - * Check concurrent deliver of the same message. Reuse message from the previous test. - */ - logger.debug("Step 5: turn off Overwite Duplicates and check concurrent deliver of the same message"); - folderEmailMessageHandler.setOverwriteDuplicates(false); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - int numBeforeConcurrentDeliver = assocs.size(); - deliverConcurrently(delivery, m); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - int numAfterConcurrentDeliver = assocs.size(); - assertEquals("Two messages must be added", numBeforeConcurrentDeliver + 2, numAfterConcurrentDeliver); - } - - private void deliverConcurrently(final EmailDelivery delivery, final SubethaEmailMessage m) throws Exception - { - final CountDownLatch cdl = new CountDownLatch(1); - class ConcurrentMessageImporter implements Runnable - { - private Throwable throwable; - @Override - public void run() - { - try - { - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - cdl.countDown(); - emailService.importMessage(delivery, m); - return null; - } - }, false, true); - } - catch (Throwable t) - { - throwable = t; - } - } - } - ConcurrentMessageImporter messageImporter = new ConcurrentMessageImporter(); - final Thread messageImporterThread = new Thread(messageImporter); - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - emailService.importMessage(delivery, m); - messageImporterThread.start(); - // wait until concurrent transaction has started - cdl.await(); - return null; - } - }, false, true); - messageImporterThread.join(); - if (null != messageImporter.throwable) - { - fail(messageImporter.throwable.getMessage()); - } - } - - /** - * MNT-9289 - * - * Change in case in email Subject causes DuplicateChildNodeNameException - */ - public void testCaseSensitivity() throws Exception - { - NodeRef person = personService.getPerson(TEST_USER); - String TEST_EMAIL="buffy@sunnydale.high"; - NodeRef testUserHomeFolder = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); - if(person == null) - { - logger.debug("new person created"); - Map props = new HashMap(); - props.put(ContentModel.PROP_USERNAME, TEST_USER); - props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); - person = personService.createPerson(props); - } - - nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); - - Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); - if(!auths.contains(TEST_USER)) - { - authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); - } - - String companyHomePathInStore = "/app:company_home"; - String storePath = "workspace://SpacesStore"; - StoreRef storeRef = new StoreRef(storePath); - - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); - NodeRef companyHomeNodeRef = nodeRefs.get(0); - assertNotNull("company home is null", companyHomeNodeRef); - - String TEST_CASE_SENSITIVITY_SUBJECT = "Test (Mail)"; - String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - - String from = TEST_EMAIL; - String to = testUserHomeDBID; - String content = "hello world"; - - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - EmailDelivery delivery = new EmailDelivery(to, from, null); - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setContent(content, "text/plain"); - - msg.setSubject(TEST_CASE_SENSITIVITY_SUBJECT); - ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); - msg.writeTo(bos1); - InputStream is = IOUtils.toInputStream(bos1.toString()); - assertNotNull("is is null", is); - SubethaEmailMessage m = new SubethaEmailMessage(is); - folderEmailMessageHandler.setOverwriteDuplicates(false); - emailService.importMessage(delivery, m); - - QName safeQName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_CASE_SENSITIVITY_SUBJECT); - List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, safeQName); - assertEquals(1, assocs.size()); - - msg.setSubject(TEST_CASE_SENSITIVITY_SUBJECT.toUpperCase()); - ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); - msg.writeTo(bos2); - is = IOUtils.toInputStream(bos2.toString()); - assertNotNull("is is null", is); - m = new SubethaEmailMessage(is); - folderEmailMessageHandler.setOverwriteDuplicates(false); - emailService.importMessage(delivery, m); - - safeQName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_CASE_SENSITIVITY_SUBJECT.toUpperCase() + "(1)"); - assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, safeQName); - assertEquals(1, assocs.size()); - } - - - /** - * ALF-12297 - * - * Test messages being sent to a cm:content node - */ - public void testMessagesToDocument() throws Exception - { - logger.debug("Start testMessagesToDocument"); - - String TEST_EMAIL="buffy@sunnydale.high"; - - String TEST_SUBJECT="Practical Bee Keeping"; - - String TEST_LONG_SUBJECT = "This is a very very long name in particular it is greater than eitghty six characters which was a problem explored in ALF-9544"; - - - // TODO Investigate why setting PROP_EMAIL on createPerson does not work. - NodeRef person = personService.getPerson(TEST_USER); - if(person == null) - { - logger.debug("new person created"); - Map props = new HashMap(); - props.put(ContentModel.PROP_USERNAME, TEST_USER); - props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); - person = personService.createPerson(props); - } - nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); - - Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); - if(!auths.contains(TEST_USER)) - { - authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); - } - - String companyHomePathInStore = "/app:company_home"; - String storePath = "workspace://SpacesStore"; - StoreRef storeRef = new StoreRef(storePath); - - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); - NodeRef companyHomeNodeRef = nodeRefs.get(0); - assertNotNull("company home is null", companyHomeNodeRef); - String companyHomeDBID = ((Long)nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - // String testUserDBID = ((Long)nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - NodeRef testUserHomeFolder = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); - assertNotNull("testUserHomeFolder is null", testUserHomeFolder); -// String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - - // Clean up old messages in test folder - List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - for(ChildAssociationRef assoc : assocs) - { - nodeService.deleteNode(assoc.getChildRef()); - } - - - Map properties = new HashMap(); - properties.put(ContentModel.PROP_NAME, "bees"); - properties.put(ContentModel.PROP_DESCRIPTION, "bees - test doc for email tests"); - ChildAssociationRef testDoc = nodeService.createNode(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "bees"), ContentModel.TYPE_CONTENT, properties); - NodeRef testDocNodeRef = testDoc.getChildRef(); - - String testDocDBID = ((Long)nodeService.getProperty(testDocNodeRef, ContentModel.PROP_NODE_DBID)).toString(); - - /** - * Send From the test user TEST_EMAIL to the test user's home - */ - String from = TEST_EMAIL; - String to = testDocDBID + "@alfresco.com"; - String content = "hello world"; - - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject(TEST_SUBJECT); - msg.setContent(content, "text/plain"); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - - /** - * Turn on overwriteDuplicates - */ - logger.debug("Step 1: send an email to a doc"); - - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - - assertTrue(nodeService.hasAspect(testDocNodeRef, ForumModel.ASPECT_DISCUSSABLE)); - - - - } // end of test sending to cm:content node - - - /** - * ENH-560 - Inbound email server not working with custom types - */ - public void testMessagesToSubTypeOfDocument() throws Exception - { - logger.debug("Start testMessagesToSubTypesOfDocument"); - - String TEST_EMAIL="buffy@sunnydale.high"; - - String TEST_SUBJECT="Practical Bee Keeping"; - - String TEST_LONG_SUBJECT = "This is a very very long name in particular it is greater than eitghty six characters which was a problem explored in ALF-9544"; - - - // TODO Investigate why setting PROP_EMAIL on createPerson does not work. - NodeRef person = personService.getPerson(TEST_USER); - if(person == null) - { - logger.debug("new person created"); - Map props = new HashMap(); - props.put(ContentModel.PROP_USERNAME, TEST_USER); - props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); - person = personService.createPerson(props); - } - nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); - - Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); - if(!auths.contains(TEST_USER)) - { - authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); - } - - String companyHomePathInStore = "/app:company_home"; - String storePath = "workspace://SpacesStore"; - StoreRef storeRef = new StoreRef(storePath); - - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); - NodeRef companyHomeNodeRef = nodeRefs.get(0); - assertNotNull("company home is null", companyHomeNodeRef); - String companyHomeDBID = ((Long)nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; -// String testUserDBID = ((Long)nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - NodeRef testUserHomeFolder = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); - assertNotNull("testUserHomeFolder is null", testUserHomeFolder); -// String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - - // Clean up old messages in test folder - List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - for(ChildAssociationRef assoc : assocs) - { - nodeService.deleteNode(assoc.getChildRef()); - } - - - Map properties = new HashMap(); - properties.put(ContentModel.PROP_NAME, "hamster"); - properties.put(ContentModel.PROP_DESCRIPTION, "syrian hamsters - test doc for email tests, sending to a subtype of cm:content"); - - // Transfer report is a subtype of cm:content - ChildAssociationRef testDoc = nodeService.createNode(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "bees"), TransferModel.TYPE_TRANSFER_REPORT, properties); - NodeRef testDocNodeRef = testDoc.getChildRef(); - - String testDocDBID = ((Long)nodeService.getProperty(testDocNodeRef, ContentModel.PROP_NODE_DBID)).toString(); - - /** - * Send From the test user TEST_EMAIL to the test user's home - */ - String from = TEST_EMAIL; - String to = testDocDBID + "@alfresco.com"; - String content = "hello world"; - - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject(TEST_SUBJECT); - msg.setContent(content, "text/plain"); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - - /** - * Turn on overwriteDuplicates - */ - logger.debug("Step 1: send an email to a transfer report"); - - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - - } // end of test sending to trx:transferReport - - - - - - /** - * The Email contributors authority controls who can add email. - * - * This test switches between the EMAIL_CONTRIBUTORS group and EVERYONE - */ - public void testEmailContributorsAuthority() throws Exception - { - EmailServiceImpl emailServiceImpl = (EmailServiceImpl)emailService; - - folderEmailMessageHandler.setOverwriteDuplicates(true); - - logger.debug("Start testEmailContributorsAuthority"); - - String TEST_EMAIL="buffy@sunnydale.high"; - - // TODO Investigate why setting PROP_EMAIL on createPerson does not work. - NodeRef person = personService.getPerson(TEST_USER); - if(person == null) - { - logger.debug("new person created"); - Map props = new HashMap(); - props.put(ContentModel.PROP_USERNAME, TEST_USER); - props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); - person = personService.createPerson(props); - } - nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); - - Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); - if(auths.contains(TEST_USER)) - { - authorityService.removeAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); - } - - String companyHomePathInStore = "/app:company_home"; - String storePath = "workspace://SpacesStore"; - StoreRef storeRef = new StoreRef(storePath); - - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); - NodeRef companyHomeNodeRef = nodeRefs.get(0); - assertNotNull("company home is null", companyHomeNodeRef); - String companyHomeDBID = ((Long)nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - String testUserDBID = ((Long)nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - NodeRef testUserHomeFolder = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); - assertNotNull("testUserHomeFolder is null", testUserHomeFolder); - String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; - - /** - * Step 1 - * Set the email contributors authority to EVERYONE - * - * Test that TEST_USER is allowed to send email - so even though TEST_USER is not - * a contributor - */ - emailServiceImpl.setEmailContributorsAuthority("EVERYONE"); - - String from = "admin"; - String to = testUserHomeDBID; - String content = "hello world"; - - Session sess = Session.getDefaultInstance(new Properties()); - assertNotNull("sess is null", sess); - SMTPMessage msg = new SMTPMessage(sess); - InternetAddress[] toa = { new InternetAddress(to) }; - - msg.setFrom(new InternetAddress(TEST_EMAIL)); - msg.setRecipients(Message.RecipientType.TO, toa); - msg.setSubject("JavaMail APIs transport.java Test"); - msg.setContent(content, "text/plain"); - - StringBuffer sb = new StringBuffer(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - msg.writeTo(bos); - InputStream is = IOUtils.toInputStream(bos.toString()); - assertNotNull("is is null", is); - - SubethaEmailMessage m = new SubethaEmailMessage(is); - - EmailDelivery delivery = new EmailDelivery(to, from, null); - - emailService.importMessage(delivery, m); - - /** - * Step 2 - * Negative test - * - * Send From the test user TEST_EMAIL to the test user's home - */ - try - { - logger.debug("Step 2"); - emailServiceImpl.setEmailContributorsAuthority("EMAIL_CONTRIBUTORS"); - emailService.importMessage(delivery, m); - fail("not thrown out"); - } - catch (EmailMessageException e) - { - // Check the exception is for the anonymous user. - // assertTrue(e.getMessage().contains("anonymous")); - } - } - -} // end of EmailServiceImplTest +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2025 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.email.server; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.Vector; +import java.util.concurrent.CountDownLatch; +import jakarta.mail.Message; +import jakarta.mail.Session; +import jakarta.mail.internet.InternetAddress; + +import junit.framework.TestCase; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.angus.mail.smtp.SMTPMessage; +import org.junit.experimental.categories.Category; +import org.springframework.context.ApplicationContext; + +import org.alfresco.email.server.handler.FolderEmailMessageHandler; +import org.alfresco.email.server.impl.subetha.SubethaEmailMessage; +import org.alfresco.model.ContentModel; +import org.alfresco.model.ForumModel; +import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transfer.TransferModel; +import org.alfresco.service.cmr.email.EmailDelivery; +import org.alfresco.service.cmr.email.EmailMessageException; +import org.alfresco.service.cmr.email.EmailService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.test_category.OwnJVMTestsCategory; +import org.alfresco.util.ApplicationContextHelper; + +/** + * Unit test of EmailServiceImplTest + * + * @author mrogers + * + */ +@Category({OwnJVMTestsCategory.class}) +public class EmailServiceImplTest extends TestCase +{ + /** + * Services used by the tests + */ + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private static Log logger = LogFactory.getLog(EmailServiceImplTest.class); + + private NodeService nodeService; + private EmailService emailService; + private PersonService personService; + private AuthorityService authorityService; + private SearchService searchService; + private NamespaceService namespaceService; + private FolderEmailMessageHandler folderEmailMessageHandler; + private RetryingTransactionHelper transactionHelper; + + String TEST_USER = "EmailServiceImplTestUser"; + + @Override + public void setUp() throws Exception + { + AuthenticationUtil.setRunAsUserSystem(); + nodeService = (NodeService) ctx.getBean("NodeService"); + assertNotNull("nodeService", nodeService); + authorityService = (AuthorityService) ctx.getBean("AuthorityService"); + assertNotNull("authorityService", authorityService); + ChildApplicationContextFactory emailSubsystem = (ChildApplicationContextFactory) ctx.getBean("InboundSMTP"); + assertNotNull("emailSubsystem", emailSubsystem); + ApplicationContext emailCtx = emailSubsystem.getApplicationContext(); + emailService = (EmailService) emailCtx.getBean("emailService"); + assertNotNull("emailService", emailService); + personService = (PersonService) emailCtx.getBean("PersonService"); + assertNotNull("personService", personService); + namespaceService = (NamespaceService) emailCtx.getBean("NamespaceService"); + assertNotNull("namespaceService", namespaceService); + searchService = (SearchService) emailCtx.getBean("SearchService"); + assertNotNull("searchService", searchService); + folderEmailMessageHandler = (FolderEmailMessageHandler) emailCtx.getBean("folderEmailMessageHandler"); + assertNotNull("folderEmailMessageHandler", folderEmailMessageHandler); + transactionHelper = (RetryingTransactionHelper) emailCtx.getBean("retryingTransactionHelper"); + assertNotNull("transactionHelper", transactionHelper); + } + + public void tearDown() throws Exception + { + AuthenticationUtil.setRunAsUserSystem(); + try + { + personService.deletePerson(TEST_USER); + } + catch (Exception e) + {} + } + + /** + * Test the from name. + * + * Step 1: User admin will map to the "unknownUser" which out of the box is "anonymous" Sending email From "admin" will fail. + * + * Step 2: Send from the test user to the test' user's home folder. + */ + public void testFromName() throws Exception + { + + folderEmailMessageHandler.setOverwriteDuplicates(true); + + logger.debug("Start testFromName"); + + String TEST_EMAIL = "buffy@sunnydale.high"; + + // TODO Investigate why setting PROP_EMAIL on createPerson does not work. + NodeRef person = personService.getPerson(TEST_USER); + if (person == null) + { + logger.debug("new person created"); + Map props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, TEST_USER); + props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); + person = personService.createPerson(props); + } + nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); + + Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); + if (!auths.contains(TEST_USER)) + { + authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); + } + + String companyHomePathInStore = "/app:company_home"; + String storePath = "workspace://SpacesStore"; + StoreRef storeRef = new StoreRef(storePath); + + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + assertNotNull("company home is null", companyHomeNodeRef); + String companyHomeDBID = ((Long) nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + String testUserDBID = ((Long) nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + NodeRef testUserHomeFolder = (NodeRef) nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); + assertNotNull("testUserHomeFolder is null", testUserHomeFolder); + String testUserHomeDBID = ((Long) nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + + /** + * Step 1 Negative test - send from "Bert" who does not exist. User will be mapped to anonymous who is not an email contributor. + */ + try + { + String from = "admin"; + String to = "bertie"; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress("Bert")); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("JavaMail APIs transport.java Test"); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + fail("anonymous user not rejected"); + } + catch (EmailMessageException e) + { + // Check the exception is for the anonymous user. + assertTrue("Message is not for anonymous", e.getMessage().contains("anonymous")); + } + + /** + * Step 2 + * + * Send From the test user TEST_EMAIL to the test user's home + */ + { + logger.debug("Step 2"); + + String from = TEST_EMAIL; + String to = testUserHomeDBID; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("JavaMail APIs transport.java Test"); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + } + + /** + * Step 3 + * + * message.from From with "name" < name@ domain > format SMTP.FROM="dummy" + * + * Send From the test user to the test user's home + */ + { + logger.debug("Step 3"); + + String from = " \"Joe Bloggs\" <" + TEST_EMAIL + ">"; + String to = testUserHomeDBID; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(from)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("JavaMail APIs transport.java Test"); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(System.out); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + + EmailDelivery delivery = new EmailDelivery(to, "dummy", null); + + emailService.importMessage(delivery, m); + } + + /** + * Step 4 + * + * From with "name" < name@ domain > format + * + * Send From the test user to the test user's home + */ + { + logger.debug("Step 4"); + + String from = " \"Joe Bloggs\" <" + TEST_EMAIL + ">"; + String to = testUserHomeDBID; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(from)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("JavaMail APIs transport.java Test"); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(System.out); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + + InternetAddress a = new InternetAddress(from); + String x = a.getAddress(); + + EmailDelivery delivery = new EmailDelivery(to, x, null); + + emailService.importMessage(delivery, m); + } + + // /** + // * Step 5 + // * + // * From with format + // * + // * RFC3696 + // * + // * Send From the test user to the test user's home + // */ + // { + // logger.debug("Step 4 format"); + // + // String from = "\"Joe Bloggs\" "; + // String to = testUserHomeDBID; + // String content = "hello world"; + // + // Session sess = Session.getDefaultInstance(new Properties()); + // assertNotNull("sess is null", sess); + // SMTPMessage msg = new SMTPMessage(sess); + // InternetAddress[] toa = { new InternetAddress(to) }; + // + // msg.setFrom(new InternetAddress(from)); + // msg.setRecipients(Message.RecipientType.TO, toa); + // msg.setSubject("JavaMail APIs transport.java Test"); + // msg.setContent(content, "text/plain"); + // + // StringBuffer sb = new StringBuffer(); + // ByteArrayOutputStream bos = new ByteArrayOutputStream(); + // msg.writeTo(System.out); + // msg.writeTo(bos); + // InputStream is = IOUtils.toInputStream(bos.toString()); + // assertNotNull("is is null", is); + // + // SubethaEmailMessage m = new SubethaEmailMessage(is); + // + // emailService.importMessage(m); + // } + } + + /** + * ALF-9544 ALF-751 + * + * Inbound email to a folder restricts file name to 86 characters or less. + * + * Also has tests for other variations of subject + */ + public void testFolderSubject() throws Exception + { + logger.debug("Start testFromName"); + + String TEST_EMAIL = "buffy@sunnydale.high"; + + folderEmailMessageHandler.setOverwriteDuplicates(true); + + // TODO Investigate why setting PROP_EMAIL on createPerson does not work. + NodeRef person = personService.getPerson(TEST_USER); + if (person == null) + { + logger.debug("new person created"); + Map props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, TEST_USER); + props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); + person = personService.createPerson(props); + } + nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); + + Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); + if (!auths.contains(TEST_USER)) + { + authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); + } + + String companyHomePathInStore = "/app:company_home"; + String storePath = "workspace://SpacesStore"; + StoreRef storeRef = new StoreRef(storePath); + + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + assertNotNull("company home is null", companyHomeNodeRef); + String companyHomeDBID = ((Long) nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + String testUserDBID = ((Long) nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + NodeRef testUserHomeFolder = (NodeRef) nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); + assertNotNull("testUserHomeFolder is null", testUserHomeFolder); + String testUserHomeDBID = ((Long) nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + + /** + * Send From the test user TEST_EMAIL to the test user's home + */ + String from = TEST_EMAIL; + String to = testUserHomeDBID; + String content = "hello world"; + + { + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("This is a very very long name in particular it is greater than eitghty six characters which was a problem explored in ALF-9544"); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + } + + // Check import with subject containing some "illegal chars" + { + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("Illegal<>!*/\\.txt"); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + } + + // Check with null subject + { + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + // msg.setSubject(); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + } + + // ALF-751 Email ends with period + { + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("Foobar."); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + } + + // ALF-751 Email ends with ... + { + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("Foobar..."); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + } + + // ALF-751 Email subject is blank " ... " + { + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject(" ... "); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + } + + // ALF-751 Email subject is a single . + { + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("."); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + } + + } + + /** + * ALF-1878 + * + * Duplicate incoming email Subjects over-write each other + */ + public void testMultipleMessagesToFolder() throws Exception + { + logger.debug("Start testFromName"); + + String TEST_EMAIL = "buffy@sunnydale.high"; + + String TEST_SUBJECT = "Practical Bee Keeping"; + + String TEST_LONG_SUBJECT = "This is a very very long name in particular it is greater than eitghty six characters which was a problem explored in ALF-9544"; + + // TODO Investigate why setting PROP_EMAIL on createPerson does not work. + NodeRef person = personService.getPerson(TEST_USER); + if (person == null) + { + logger.debug("new person created"); + Map props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, TEST_USER); + props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); + person = personService.createPerson(props); + } + nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); + + Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); + if (!auths.contains(TEST_USER)) + { + authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); + } + + String companyHomePathInStore = "/app:company_home"; + String storePath = "workspace://SpacesStore"; + StoreRef storeRef = new StoreRef(storePath); + + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + assertNotNull("company home is null", companyHomeNodeRef); + String companyHomeDBID = ((Long) nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + String testUserDBID = ((Long) nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + NodeRef testUserHomeFolder = (NodeRef) nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); + assertNotNull("testUserHomeFolder is null", testUserHomeFolder); + String testUserHomeDBID = ((Long) nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + + // Clean up old messages in test folder + List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + nodeService.deleteNode(assoc.getChildRef()); + } + + /** + * Send From the test user TEST_EMAIL to the test user's home + */ + String from = TEST_EMAIL; + String to = testUserHomeDBID; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject(TEST_SUBJECT); + msg.setContent(content, "text/plain"); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + + /** + * Turn on overwriteDuplicates + */ + logger.debug("Step 1: turn on Overwite Duplicates"); + folderEmailMessageHandler.setOverwriteDuplicates(true); + + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 1", 1, assocs.size()); + assertEquals("name of link not as expected", assocs.get(0).getQName(), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_SUBJECT)); + emailService.importMessage(delivery, m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 1", 1, assocs.size()); + + /** + * Turn off overwrite Duplicates + */ + logger.debug("Step 2: turn off Overwite Duplicates"); + folderEmailMessageHandler.setOverwriteDuplicates(false); + emailService.importMessage(delivery, m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 2", 2, assocs.size()); + emailService.importMessage(delivery, m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 3", 3, assocs.size()); + + /** + * Check assoc rename with long names. So truncation and rename need to work together. + */ + logger.debug("Step 3: turn off Overwite Duplicates with long subject name"); + msg.setSubject(TEST_LONG_SUBJECT); + ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); + msg.writeTo(bos2); + is = IOUtils.toInputStream(bos2.toString()); + assertNotNull("is is null", is); + m = new SubethaEmailMessage(is); + + folderEmailMessageHandler.setOverwriteDuplicates(false); + emailService.importMessage(delivery, m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 4", 4, assocs.size()); + emailService.importMessage(delivery, m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + assertEquals("assocs not 5", 5, assocs.size()); + + /** + * Check assoc rename with long names and an extension. So truncation and rename need to work together and not muck up a .extension. + */ + logger.debug("Step 4: turn off Overwite Duplicates with long subject name with extension"); + String EXT_NAME = "Blob.xls"; + + msg.setSubject(EXT_NAME); + ByteArrayOutputStream bos3 = new ByteArrayOutputStream(); + msg.writeTo(bos3); + is = IOUtils.toInputStream(bos3.toString()); + assertNotNull("is is null", is); + m = new SubethaEmailMessage(is); + folderEmailMessageHandler.setOverwriteDuplicates(false); + emailService.importMessage(delivery, m); + emailService.importMessage(delivery, m); + emailService.importMessage(delivery, m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + + List assocNames = new Vector(); + for (ChildAssociationRef assoc : assocs) + { + logger.debug("assocName: " + assoc.getQName()); + System.out.println(assoc.getQName()); + assocNames.add(assoc.getQName()); + } + assertTrue(EXT_NAME + "not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Blob.xls"))); + assertTrue("Blob(1).xls not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Blob(1).xls"))); + assertTrue("Blob(2).xls not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Blob(2).xls"))); + assertTrue(TEST_SUBJECT + "not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_SUBJECT))); + assertTrue(TEST_SUBJECT + "(1) not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Practical Bee Keeping(1)"))); + + /** + * Check concurrent deliver of the same message. Reuse message from the previous test. + */ + logger.debug("Step 5: turn off Overwite Duplicates and check concurrent deliver of the same message"); + folderEmailMessageHandler.setOverwriteDuplicates(false); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + int numBeforeConcurrentDeliver = assocs.size(); + deliverConcurrently(delivery, m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + int numAfterConcurrentDeliver = assocs.size(); + assertEquals("Two messages must be added", numBeforeConcurrentDeliver + 2, numAfterConcurrentDeliver); + } + + private void deliverConcurrently(final EmailDelivery delivery, final SubethaEmailMessage m) throws Exception + { + final CountDownLatch cdl = new CountDownLatch(1); + class ConcurrentMessageImporter implements Runnable + { + private Throwable throwable; + + @Override + public void run() + { + try + { + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { + public Void execute() throws Throwable + { + cdl.countDown(); + emailService.importMessage(delivery, m); + return null; + } + }, false, true); + } + catch (Throwable t) + { + throwable = t; + } + } + } + ConcurrentMessageImporter messageImporter = new ConcurrentMessageImporter(); + final Thread messageImporterThread = new Thread(messageImporter); + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { + public Void execute() throws Throwable + { + emailService.importMessage(delivery, m); + messageImporterThread.start(); + // wait until concurrent transaction has started + cdl.await(); + return null; + } + }, false, true); + messageImporterThread.join(); + if (null != messageImporter.throwable) + { + fail(messageImporter.throwable.getMessage()); + } + } + + /** + * MNT-9289 + * + * Change in case in email Subject causes DuplicateChildNodeNameException + */ + public void testCaseSensitivity() throws Exception + { + NodeRef person = personService.getPerson(TEST_USER); + String TEST_EMAIL = "buffy@sunnydale.high"; + NodeRef testUserHomeFolder = (NodeRef) nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); + if (person == null) + { + logger.debug("new person created"); + Map props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, TEST_USER); + props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); + person = personService.createPerson(props); + } + + nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); + + Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); + if (!auths.contains(TEST_USER)) + { + authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); + } + + String companyHomePathInStore = "/app:company_home"; + String storePath = "workspace://SpacesStore"; + StoreRef storeRef = new StoreRef(storePath); + + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + assertNotNull("company home is null", companyHomeNodeRef); + + String TEST_CASE_SENSITIVITY_SUBJECT = "Test (Mail)"; + String testUserHomeDBID = ((Long) nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + + String from = TEST_EMAIL; + String to = testUserHomeDBID; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + EmailDelivery delivery = new EmailDelivery(to, from, null); + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setContent(content, "text/plain"); + + msg.setSubject(TEST_CASE_SENSITIVITY_SUBJECT); + ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); + msg.writeTo(bos1); + InputStream is = IOUtils.toInputStream(bos1.toString()); + assertNotNull("is is null", is); + SubethaEmailMessage m = new SubethaEmailMessage(is); + folderEmailMessageHandler.setOverwriteDuplicates(false); + emailService.importMessage(delivery, m); + + QName safeQName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_CASE_SENSITIVITY_SUBJECT); + List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, safeQName); + assertEquals(1, assocs.size()); + + msg.setSubject(TEST_CASE_SENSITIVITY_SUBJECT.toUpperCase()); + ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); + msg.writeTo(bos2); + is = IOUtils.toInputStream(bos2.toString()); + assertNotNull("is is null", is); + m = new SubethaEmailMessage(is); + folderEmailMessageHandler.setOverwriteDuplicates(false); + emailService.importMessage(delivery, m); + + safeQName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_CASE_SENSITIVITY_SUBJECT.toUpperCase() + "(1)"); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, safeQName); + assertEquals(1, assocs.size()); + } + + /** + * ALF-12297 + * + * Test messages being sent to a cm:content node + */ + public void testMessagesToDocument() throws Exception + { + logger.debug("Start testMessagesToDocument"); + + String TEST_EMAIL = "buffy@sunnydale.high"; + + String TEST_SUBJECT = "Practical Bee Keeping"; + + String TEST_LONG_SUBJECT = "This is a very very long name in particular it is greater than eitghty six characters which was a problem explored in ALF-9544"; + + // TODO Investigate why setting PROP_EMAIL on createPerson does not work. + NodeRef person = personService.getPerson(TEST_USER); + if (person == null) + { + logger.debug("new person created"); + Map props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, TEST_USER); + props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); + person = personService.createPerson(props); + } + nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); + + Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); + if (!auths.contains(TEST_USER)) + { + authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); + } + + String companyHomePathInStore = "/app:company_home"; + String storePath = "workspace://SpacesStore"; + StoreRef storeRef = new StoreRef(storePath); + + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + assertNotNull("company home is null", companyHomeNodeRef); + String companyHomeDBID = ((Long) nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + // String testUserDBID = ((Long)nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + NodeRef testUserHomeFolder = (NodeRef) nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); + assertNotNull("testUserHomeFolder is null", testUserHomeFolder); + // String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + + // Clean up old messages in test folder + List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + nodeService.deleteNode(assoc.getChildRef()); + } + + Map properties = new HashMap(); + properties.put(ContentModel.PROP_NAME, "bees"); + properties.put(ContentModel.PROP_DESCRIPTION, "bees - test doc for email tests"); + ChildAssociationRef testDoc = nodeService.createNode(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "bees"), ContentModel.TYPE_CONTENT, properties); + NodeRef testDocNodeRef = testDoc.getChildRef(); + + String testDocDBID = ((Long) nodeService.getProperty(testDocNodeRef, ContentModel.PROP_NODE_DBID)).toString(); + + /** + * Send From the test user TEST_EMAIL to the test user's home + */ + String from = TEST_EMAIL; + String to = testDocDBID + "@alfresco.com"; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject(TEST_SUBJECT); + msg.setContent(content, "text/plain"); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + + /** + * Turn on overwriteDuplicates + */ + logger.debug("Step 1: send an email to a doc"); + + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + + assertTrue(nodeService.hasAspect(testDocNodeRef, ForumModel.ASPECT_DISCUSSABLE)); + + } // end of test sending to cm:content node + + /** + * ENH-560 - Inbound email server not working with custom types + */ + public void testMessagesToSubTypeOfDocument() throws Exception + { + logger.debug("Start testMessagesToSubTypesOfDocument"); + + String TEST_EMAIL = "buffy@sunnydale.high"; + + String TEST_SUBJECT = "Practical Bee Keeping"; + + String TEST_LONG_SUBJECT = "This is a very very long name in particular it is greater than eitghty six characters which was a problem explored in ALF-9544"; + + // TODO Investigate why setting PROP_EMAIL on createPerson does not work. + NodeRef person = personService.getPerson(TEST_USER); + if (person == null) + { + logger.debug("new person created"); + Map props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, TEST_USER); + props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); + person = personService.createPerson(props); + } + nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); + + Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); + if (!auths.contains(TEST_USER)) + { + authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); + } + + String companyHomePathInStore = "/app:company_home"; + String storePath = "workspace://SpacesStore"; + StoreRef storeRef = new StoreRef(storePath); + + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + assertNotNull("company home is null", companyHomeNodeRef); + String companyHomeDBID = ((Long) nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + // String testUserDBID = ((Long)nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + NodeRef testUserHomeFolder = (NodeRef) nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); + assertNotNull("testUserHomeFolder is null", testUserHomeFolder); + // String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + + // Clean up old messages in test folder + List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + nodeService.deleteNode(assoc.getChildRef()); + } + + Map properties = new HashMap(); + properties.put(ContentModel.PROP_NAME, "hamster"); + properties.put(ContentModel.PROP_DESCRIPTION, "syrian hamsters - test doc for email tests, sending to a subtype of cm:content"); + + // Transfer report is a subtype of cm:content + ChildAssociationRef testDoc = nodeService.createNode(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "bees"), TransferModel.TYPE_TRANSFER_REPORT, properties); + NodeRef testDocNodeRef = testDoc.getChildRef(); + + String testDocDBID = ((Long) nodeService.getProperty(testDocNodeRef, ContentModel.PROP_NODE_DBID)).toString(); + + /** + * Send From the test user TEST_EMAIL to the test user's home + */ + String from = TEST_EMAIL; + String to = testDocDBID + "@alfresco.com"; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject(TEST_SUBJECT); + msg.setContent(content, "text/plain"); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + + /** + * Turn on overwriteDuplicates + */ + logger.debug("Step 1: send an email to a transfer report"); + + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + + } // end of test sending to trx:transferReport + + /** + * The Email contributors authority controls who can add email. + * + * This test switches between the EMAIL_CONTRIBUTORS group and EVERYONE + */ + public void testEmailContributorsAuthority() throws Exception + { + EmailServiceImpl emailServiceImpl = (EmailServiceImpl) emailService; + + folderEmailMessageHandler.setOverwriteDuplicates(true); + + logger.debug("Start testEmailContributorsAuthority"); + + String TEST_EMAIL = "buffy@sunnydale.high"; + + // TODO Investigate why setting PROP_EMAIL on createPerson does not work. + NodeRef person = personService.getPerson(TEST_USER); + if (person == null) + { + logger.debug("new person created"); + Map props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, TEST_USER); + props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); + person = personService.createPerson(props); + } + nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); + + Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); + if (auths.contains(TEST_USER)) + { + authorityService.removeAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); + } + + String companyHomePathInStore = "/app:company_home"; + String storePath = "workspace://SpacesStore"; + StoreRef storeRef = new StoreRef(storePath); + + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + assertNotNull("company home is null", companyHomeNodeRef); + String companyHomeDBID = ((Long) nodeService.getProperty(companyHomeNodeRef, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + String testUserDBID = ((Long) nodeService.getProperty(person, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + NodeRef testUserHomeFolder = (NodeRef) nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); + assertNotNull("testUserHomeFolder is null", testUserHomeFolder); + String testUserHomeDBID = ((Long) nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + + /** + * Step 1 Set the email contributors authority to EVERYONE + * + * Test that TEST_USER is allowed to send email - so even though TEST_USER is not a contributor + */ + emailServiceImpl.setEmailContributorsAuthority("EVERYONE"); + + String from = "admin"; + String to = testUserHomeDBID; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = {new InternetAddress(to)}; + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setSubject("JavaMail APIs transport.java Test"); + msg.setContent(content, "text/plain"); + + StringBuffer sb = new StringBuffer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + msg.writeTo(bos); + InputStream is = IOUtils.toInputStream(bos.toString()); + assertNotNull("is is null", is); + + SubethaEmailMessage m = new SubethaEmailMessage(is); + + EmailDelivery delivery = new EmailDelivery(to, from, null); + + emailService.importMessage(delivery, m); + + /** + * Step 2 Negative test + * + * Send From the test user TEST_EMAIL to the test user's home + */ + try + { + logger.debug("Step 2"); + emailServiceImpl.setEmailContributorsAuthority("EMAIL_CONTRIBUTORS"); + emailService.importMessage(delivery, m); + fail("not thrown out"); + } + catch (EmailMessageException e) + { + // Check the exception is for the anonymous user. + // assertTrue(e.getMessage().contains("anonymous")); + } + } + +} // end of EmailServiceImplTest diff --git a/repository/src/test/java/org/alfresco/repo/imap/ImapMessageTest.java b/repository/src/test/java/org/alfresco/repo/imap/ImapMessageTest.java index 24f5c9739e..2ae7b74e6a 100644 --- a/repository/src/test/java/org/alfresco/repo/imap/ImapMessageTest.java +++ b/repository/src/test/java/org/alfresco/repo/imap/ImapMessageTest.java @@ -1,871 +1,866 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.repo.imap; - -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.SequenceInputStream; -import java.io.Serializable; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Properties; - -import jakarta.mail.Address; -import jakarta.mail.Folder; -import jakarta.mail.Message; -import jakarta.mail.MessagingException; -import jakarta.mail.Session; -import jakarta.mail.Store; -import jakarta.mail.internet.InternetAddress; -import jakarta.mail.internet.MimeMessage; -import jakarta.mail.internet.MimeMultipart; -import jakarta.mail.internet.MimeUtility; -import jakarta.transaction.UserTransaction; - -import junit.framework.TestCase; - -import org.alfresco.model.ContentModel; -import org.alfresco.model.ImapModel; -import org.alfresco.repo.importer.ACPImportPackageHandler; -import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; -import org.alfresco.repo.node.integrity.IntegrityChecker; -import org.alfresco.repo.search.QueryParameterDefImpl; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileFolderUtil; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.QueryParameterDefinition; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.MutableAuthenticationService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.cmr.view.ImporterService; -import org.alfresco.service.cmr.view.Location; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.test_category.OwnJVMTestsCategory; -import org.alfresco.util.ApplicationContextHelper; -import org.alfresco.util.GUID; -import org.alfresco.util.PropertyMap; -import org.alfresco.util.config.RepositoryFolderConfigBean; -import org.alfresco.util.testing.category.LuceneTests; -import org.alfresco.util.testing.category.RedundantTests; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.springframework.context.ApplicationContext; -import org.springframework.core.io.ClassPathResource; -import org.springframework.mail.javamail.MimeMessageHelper; - -import com.sun.mail.iap.ProtocolException; -import com.sun.mail.iap.Response; -import com.sun.mail.imap.IMAPFolder; -import com.sun.mail.imap.protocol.BODY; -import com.sun.mail.imap.protocol.FetchResponse; -import com.sun.mail.imap.protocol.IMAPProtocol; -import com.sun.mail.imap.protocol.IMAPResponse; -import com.sun.mail.imap.protocol.RFC822DATA; -import com.sun.mail.imap.protocol.UID; -import com.sun.mail.util.ASCIIUtility; - -import static org.alfresco.model.ContentModel.PROP_MODIFIED; - -@Category({OwnJVMTestsCategory.class, LuceneTests.class}) -public class ImapMessageTest extends TestCase -{ - private static Log logger = LogFactory.getLog(ImapMessageTest.class); - - // IMAP client settings - private static final String PROTOCOL = "imap"; - private static final String HOST = "localhost"; - private static final int PORT = 7143; - - private static final String ADMIN_USER_NAME = "admin"; - private static final String ADMIN_USER_PASSWORD = "admin"; - private static final String IMAP_FOLDER_NAME = "test"; - - private Session session = null; - private Store store = null; - private IMAPFolder folder = null; - - private ApplicationContext ctx; - private ServiceRegistry serviceRegistry; - private TransactionService transactionService; - private NodeService nodeService; - private ImporterService importerService; - private PersonService personService; - private SearchService searchService; - private NamespaceService namespaceService; - private FileFolderService fileFolderService; - private MutableAuthenticationService authenticationService; - private AlfrescoImapServer imapServer; - - String anotherUserName; - private NodeRef testImapFolderNodeRef; - private NodeRef storeRootNodeRef; - private final String storePath = "workspace://SpacesStore"; - private final String companyHomePathInStore = "/app:company_home"; - - private static final String TEST_FOLDER = "Alfresco IMAP/" + IMAP_FOLDER_NAME + "/___-___folder_a/" + "___-___folder_a_a"; - private static final String TEST_FILE = "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME + "/" + NamespaceService.CONTENT_MODEL_PREFIX - + ":___-___folder_a/" + NamespaceService.CONTENT_MODEL_PREFIX + ":___-___folder_a_a/" + NamespaceService.CONTENT_MODEL_PREFIX + ":___-___file_a_a"; - - @Override - public void setUp() throws Exception - { - ctx = ApplicationContextHelper.getApplicationContext(); - logger.debug("In SetUp"); - serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry"); - transactionService = serviceRegistry.getTransactionService(); - nodeService = serviceRegistry.getNodeService(); - importerService = serviceRegistry.getImporterService(); - personService = serviceRegistry.getPersonService(); - authenticationService = serviceRegistry.getAuthenticationService(); - searchService = serviceRegistry.getSearchService(); - namespaceService = serviceRegistry.getNamespaceService(); - fileFolderService = serviceRegistry.getFileFolderService(); - - - // start the transaction - UserTransaction txn = transactionService.getUserTransaction(); - txn.begin(); - authenticationService.authenticate(ADMIN_USER_NAME, ADMIN_USER_PASSWORD.toCharArray()); - - // downgrade integrity - IntegrityChecker.setWarnInTransaction(); - - anotherUserName = "user" + System.currentTimeMillis(); - - PropertyMap testUser = new PropertyMap(); - testUser.put(ContentModel.PROP_USERNAME, anotherUserName); - testUser.put(ContentModel.PROP_FIRSTNAME, anotherUserName); - testUser.put(ContentModel.PROP_LASTNAME, anotherUserName); - testUser.put(ContentModel.PROP_EMAIL, anotherUserName + "@alfresco.com"); - testUser.put(ContentModel.PROP_JOBTITLE, "jobTitle"); - - personService.createPerson(testUser); - - // create the ACEGI Authentication instance for the new user - authenticationService.createAuthentication(anotherUserName, anotherUserName.toCharArray()); - - StoreRef storeRef = new StoreRef(storePath); - storeRootNodeRef = nodeService.getRootNode(storeRef); - - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); - NodeRef companyHomeNodeRef = nodeRefs.get(0); - - nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null, - namespaceService, false); - if (nodeRefs != null && nodeRefs.size() > 0) - { - fileFolderService.delete(nodeRefs.get(0)); - } - - ChildApplicationContextFactory imap = (ChildApplicationContextFactory) ctx.getBean("imap"); - ApplicationContext imapCtx = imap.getApplicationContext(); - ImapServiceImpl imapServiceImpl = (ImapServiceImpl) imapCtx.getBean("imapService"); - imapServer = (AlfrescoImapServer) imapCtx.getBean("imapServer"); - - if(!imapServer.isImapServerEnabled()) - { - imapServer.setImapServerEnabled(true); - imapServer.setHost(HOST); - imapServer.setPort(PORT); - imapServer.startup(); - } - - // Creating IMAP test folder for IMAP root - LinkedList folders = new LinkedList(); - folders.add(IMAP_FOLDER_NAME); - FileFolderUtil.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER); - - // Setting IMAP root - RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean(); - imapHome.setStore(storePath); - imapHome.setRootPath(companyHomePathInStore); - imapHome.setFolderPath(NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME); - imapServiceImpl.setImapHome(imapHome); - - - // Starting IMAP - imapServiceImpl.startupInTxn(true); - - nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null, - namespaceService, false); - testImapFolderNodeRef = nodeRefs.get(0); - - /* - * Importing test folders: Test folder contains: "___-___folder_a" "___-___folder_a" contains: "___-___folder_a_a", "___-___file_a", "Message_485.eml" (this is IMAP - * Message) "___-___folder_a_a" contains: "____-____file_a_a" - */ - importInternal("imap/imapservice_test_folder_a.acp", testImapFolderNodeRef); - - txn.commit(); - - // Init mail client session - Properties props = new Properties(); - props.setProperty("mail.imap.partialfetch", "false"); - this.session = Session.getDefaultInstance(props, null); - - // Get the store - this.store = session.getStore(PROTOCOL); - //this.store.connect(HOST, PORT, anotherUserName, anotherUserName); - this.store.connect(imapServer.getHost(), imapServer.getPort(), anotherUserName, anotherUserName); - - // Get folder - folder = (IMAPFolder) store.getFolder(TEST_FOLDER); - folder.open(Folder.READ_ONLY); - - logger.debug("End SetUp"); - - } - - private void importInternal(String acpName, NodeRef space) throws IOException - { - // Importing IMAP test acp - ClassPathResource acpResource = new ClassPathResource(acpName); - ACPImportPackageHandler acpHandler = new ACPImportPackageHandler(acpResource.getFile(), null); - Location importLocation = new Location(space); - importerService.importView(acpHandler, importLocation, null, null); - } - - public void testMessageModifiedBetweenReads() throws Exception - { - // Get test message UID - final Long uid = getMessageUid(folder, 1); - - // Get unmodified message - BODY body = getMessageBody(folder, uid); - - // Parse the multipart MIME message - MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new BufferedInputStream(body.getByteArrayInputStream())); - - // Reading first part - should be successful - MimeMultipart content = (MimeMultipart) message.getContent(); - assertNotNull(content.getBodyPart(0).getContent()); - // Reading second part - should be successful - assertNotNull(content.getBodyPart(1).getContent()); - - // Modify message. The size of letter describing the node may change - // These changes should be committed because it should be visible from client - NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE); - UserTransaction txn = transactionService.getUserTransaction(); - txn.begin(); - ContentWriter writer = fileFolderService.getWriter(contentNode); - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < 2000; i++) - { - sb.append("test string"); - } - writer.putContent(sb.toString()); - txn.commit(); - - // Read updated message part - BODY bodyNew = getMessageBody(folder, uid); - - // The body should be updated - assertFalse(Arrays.equals(bodyNew.getByteArray().getBytes(), body.getByteArray().getBytes())); - - // Parse the multipart MIME message - message = new MimeMessage(Session.getDefaultInstance(new Properties()), new BufferedInputStream(bodyNew.getByteArrayInputStream())); - - // Reading first part - should be successful - content = (MimeMultipart) message.getContent(); - assertNotNull(content.getBodyPart(0).getContent()); - // Reading second part - should be successful - assertNotNull(content.getBodyPart(1).getContent()); - } - - public void testMessageRenamedBetweenReads() throws Exception - { - // Get test message UID - final Long uid = getMessageUid(folder, 1); - // Get Message size - final int count = getMessageSize(folder, uid); - - // Get first part - // Split the message into 2 part using a non multiple of 4 - 103 is a prime number - // as the BASE64Decoder may not throw the IOException - // see MNT-12995 - BODY body = getMessageBodyPart(folder, uid, 0, count - 103); - - // Rename message. The size of letter describing the node will change - // These changes should be committed because it should be visible from client - NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE); - UserTransaction txn = transactionService.getUserTransaction(); - txn.begin(); - fileFolderService.rename(contentNode, "testtesttesttesttesttesttesttesttesttest"); - txn.commit(); - - // Read second message part - BODY bodyRest = getMessageBodyPart(folder, uid, count - 103, 103); - - // Creating and parsing message from 2 parts - MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()), - new BufferedInputStream(bodyRest.getByteArrayInputStream()))); - - // Reading first part - should be successful - MimeMultipart content = (MimeMultipart) message.getContent(); - assertNotNull(content.getBodyPart(0).getContent()); - - try - { - // Reading second part cause error - content.getBodyPart(1).getContent(); - fail("Should raise an IOException"); - } - catch (IOException e) - { - } - } - - public void dontTestMessageCache() throws Exception - { - - // Create messages - NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE); - UserTransaction txn = transactionService.getUserTransaction(); - txn.begin(); - - // Create messages more than cache capacity - for (int i = 0; i < 51; i++) - { - FileInfo fi = fileFolderService.create(nodeService.getParentAssocs(contentNode).get(0).getParentRef(), "test" + i, ContentModel.TYPE_CONTENT); - ContentWriter writer = fileFolderService.getWriter(fi.getNodeRef()); - writer.putContent("test"); - } - - txn.commit(); - - // Reload folder - folder.close(false); - folder = (IMAPFolder) store.getFolder(TEST_FOLDER); - folder.open(Folder.READ_ONLY); - - // Read all messages - for (int i = 1; i < 51; i++) - { - // Get test message UID - final Long uid = getMessageUid(folder, i); - // Get Message size - final int count = getMessageSize(folder, uid); - - // Get first part - BODY body = getMessageBodyPart(folder, uid, 0, count - 100); - // Read second message part - BODY bodyRest = getMessageBodyPart(folder, uid, count - 100, 100); - - // Creating and parsing message from 2 parts - MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()), - new BufferedInputStream(bodyRest.getByteArrayInputStream()))); - - // Reading first part - should be successful - MimeMultipart content = (MimeMultipart) message.getContent(); - assertNotNull(content.getBodyPart(0).getContent()); - assertNotNull(content.getBodyPart(1).getContent()); - } - } - - @Test - public void testSearchTermWithNonEnglishLocale() throws Exception - { - folder = (IMAPFolder) store.getFolder(TEST_FOLDER); - folder.open(Folder.READ_ONLY); - - Locale defaultLocale = Locale.getDefault(); - try - { - Locale.setDefault(Locale.FRENCH); - String dateStr = "12-Jul-2020"; - final IMAPFolder.ProtocolCommand uid_search_since = new IMAPFolder.ProtocolCommand() - { - @Override - public Object doCommand(IMAPProtocol protocol) - { - return protocol.command("UID SEARCH SINCE " + dateStr, null); - } - }; - // UID SEARCH SINCE - Response[] ret = (Response[]) folder.doCommand(uid_search_since); - IMAPResponse response = (IMAPResponse) ret[0]; - assertEquals("* SEARCH ", response.toString()); - } - catch (MessagingException e) - { - fail("Date cannot be parsed"); - } - finally - { - Locale.setDefault(defaultLocale); - } - } - - @Test - public void testSearchTermWithNonEnglishLocaleFalsePositive() throws Exception - { - Locale defaultLocale = Locale.getDefault(); - try - { - folder = (IMAPFolder) store.getFolder(TEST_FOLDER); - folder.open(Folder.READ_ONLY); - - Locale.setDefault(Locale.FRENCH); - String dateStr = "12-juil.-2020"; - final IMAPFolder.ProtocolCommand uid_search_since = new IMAPFolder.ProtocolCommand() - { - @Override - public Object doCommand(IMAPProtocol protocol) - { - return protocol.command("UID SEARCH SINCE " + dateStr, null); - } - }; - // UID SEARCH SINCE - Response[] ret = (Response[]) folder.doCommand(uid_search_since); - assertEquals("java.io.IOException: Connection dropped by server?", ret[0].getException().toString()); - } - catch (MessagingException e) - { - // expected - } - finally - { - Locale.setDefault(defaultLocale); - } - } - - public void testUnmodifiedMessage() throws Exception - { - // Get test message UID - final Long uid = getMessageUid(folder, 1); - // Get Message size - final int count = getMessageSize(folder, uid); - - // Make multiple message reading - for (int i = 0; i < 100; i++) - { - // Get random offset - int n = (int) ((int) 100 * Math.random()); - - // Get first part - BODY body = getMessageBodyPart(folder, uid, 0, count - n); - // Read second message part - BODY bodyRest = getMessageBodyPart(folder, uid, count - n, n); - - // Creating and parsing message from 2 parts - MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()), - new BufferedInputStream(bodyRest.getByteArrayInputStream()))); - - MimeMultipart content = (MimeMultipart) message.getContent(); - // Reading first part - should be successful - assertNotNull(content.getBodyPart(0).getContent()); - // Reading second part - should be successful - assertNotNull(content.getBodyPart(1).getContent()); - } - } - - public void testEncodedFromToAddresses() throws Exception - { - // RFC1342 - String addressString = "ars.kov@gmail.com"; - String personalString = "�?р�?ений Ковальчук"; - InternetAddress address = new InternetAddress(addressString, personalString, "UTF-8"); - - // Following method returns the address with quoted personal aka <["�?р�?ений Ковальчук"] > - // NOTE! This should be coincided with RFC822MetadataExtracter. Would 'addresses' be quoted or not? - // String decodedAddress = address.toUnicodeString(); - // Starting from javax.mail 1.5.6 the address is folded at linear whitespace so that each line is no longer than 76 characters, if possible. - String decodedAddress = MimeUtility.decodeText(MimeUtility.fold(6, address.toString())); - - // InternetAddress.toString(new Address[] {address}) - is used in the RFC822MetadataExtracter - // So, compare with that - assertFalse("Non ASCII characters in the address should be encoded", decodedAddress.equals(InternetAddress.toString(new Address[] {address}))); - - MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties())); - - MimeMessageHelper messageHelper = new MimeMessageHelper(message, false, "UTF-8"); - - messageHelper.setText("This is a sample message for ALF-5647"); - messageHelper.setSubject("This is a sample message for ALF-5647"); - messageHelper.setFrom(address); - messageHelper.addTo(address); - messageHelper.addCc(address); - - // Creating the message node in the repository - UserTransaction txn = transactionService.getUserTransaction(); - txn.begin(); - String name = AlfrescoImapConst.MESSAGE_PREFIX + GUID.generate(); - FileInfo messageFile = fileFolderService.create(testImapFolderNodeRef, name, ContentModel.TYPE_CONTENT); - // Writing a content. - NodeRef nodeRef = messageFile.getNodeRef(); - Serializable origModified = getModified(nodeRef); - new IncomingImapMessage(messageFile, serviceRegistry, message); - txn.commit(); - - // Calls to new IncomingImapMessage(...) only takes place when a new nodeRef is being created. - // No other code will be changing the nodeRef. An ImapModel.ASPECT_IMAP_CONTENT is added, which - // triggers a metadata extract to take place in a post commit method. Previously this would have been a - // synchronous process. This is no longer true as it may now take place in a T-Engine. So, we need to wait - // for the extract to take place. There does not - long end = System.currentTimeMillis()+10000; - while (System.currentTimeMillis() <= end && origModified.equals(getModified(nodeRef))) - { - Thread.currentThread().sleep(1000); - } - - // Getting the transformed properties from the repository - // cm:originator, cm:addressee, cm:addressees, imap:messageFrom, imap:messageTo, imap:messageCc - Map properties = nodeService.getProperties(nodeRef); - String cmOriginator = (String) properties.get(ContentModel.PROP_ORIGINATOR); - String cmAddressee = (String) properties.get(ContentModel.PROP_ADDRESSEE); - @SuppressWarnings("unchecked") - List cmAddressees = (List) properties.get(ContentModel.PROP_ADDRESSEES); - String imapMessageFrom = (String) properties.get(ImapModel.PROP_MESSAGE_FROM); - String imapMessageTo = (String) properties.get(ImapModel.PROP_MESSAGE_TO); - String imapMessageCc = (String) properties.get(ImapModel.PROP_MESSAGE_CC); - - assertNotNull(cmOriginator); - assertEquals(decodedAddress, cmOriginator); - assertNotNull(cmAddressee); - assertEquals(decodedAddress, cmAddressee); - assertNotNull(cmAddressees); - assertEquals(1, cmAddressees.size()); - assertEquals(decodedAddress, cmAddressees.get(0)); - assertNotNull(imapMessageFrom); - assertEquals(decodedAddress, imapMessageFrom); - assertNotNull(imapMessageTo); - assertEquals(decodedAddress, imapMessageTo); - assertNotNull(imapMessageCc); - assertEquals(decodedAddress, imapMessageCc); - } - - private Serializable getModified(NodeRef nodeRef) - { - Map origProperties = nodeService.getProperties(nodeRef); - return origProperties.get(PROP_MODIFIED); - } - - @Category(RedundantTests.class) - public void testEightBitMessage() throws Exception - { - - Store lstore = session.getStore(PROTOCOL); - lstore.connect(imapServer.getHost(), imapServer.getPort(), ADMIN_USER_NAME, ADMIN_USER_PASSWORD); - - String folderName = "Alfresco IMAP/" + IMAP_FOLDER_NAME; - - IMAPFolder lfolder = (IMAPFolder) lstore.getFolder(folderName); - lfolder.open(Folder.READ_WRITE); - - InputStream messageFileInputStream1 = null; - InputStream messageFileInputStream2 = null; - try - { - ClassPathResource fileResource = new ClassPathResource("imap/test-8bit-message.eml"); - messageFileInputStream1 = new FileInputStream(fileResource.getFile()); - Message message = new MimeMessage(Session.getDefaultInstance(new Properties()), messageFileInputStream1); - String subject = message.getSubject(); - - // get original bytes for further comparation - messageFileInputStream2 = new FileInputStream(fileResource.getFile()); - byte[] original = ASCIIUtility.getBytes(messageFileInputStream2); - - Message[] messages = {message}; - - lfolder.appendMessages(messages); - - - - // The search is not implemented. - // SearchTerm term = new HeaderTerm("X-Alfresco-Unique", "test8bit"); - // messages = folder.search(term); - - // So wee need to get our test message's UID from the repo - - String messageXPath = companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME + "/*[like(@cm:title, $cm:title, true)]"; - - QueryParameterDefinition[] params = new QueryParameterDefinition[1]; - params[0] = new QueryParameterDefImpl( - ContentModel.PROP_TITLE, - serviceRegistry.getDictionaryService().getDataType(DataTypeDefinition.TEXT), - true, - subject); - - List nodeRefs = searchService.selectNodes(storeRootNodeRef, messageXPath, params, namespaceService, true); - - - // does the message exist - assertEquals(1, nodeRefs.size()); - - NodeRef messageNodeRef = nodeRefs.get(0); - - // get message UID - Long dbid = (Long) nodeService.getProperty(messageNodeRef, ContentModel.PROP_NODE_DBID); - - // fetch the massage - RFC822DATA data = getRFC822Message(lfolder, dbid); - - assertNotNull("Can't fetch a message from the repositiry", data); - - byte[] processed = ASCIIUtility.getBytes(data.getByteArrayInputStream()); - - assertTrue("Original message doesn't coincide to the message processed by the repository", Arrays.equals(original, processed)); - } - finally - { - if (messageFileInputStream1 != null) messageFileInputStream1.close(); - if (messageFileInputStream2 != null) messageFileInputStream2.close(); - } - - // close connection - lfolder.close(true); - lstore.close(); - - } - - - private static RFC822DATA getRFC822Message(final IMAPFolder folder, final long uid) throws MessagingException - { - return (RFC822DATA) folder.doCommand(new IMAPFolder.ProtocolCommand() - { - public Object doCommand(IMAPProtocol p) throws ProtocolException - { - Response[] r = p.command("UID FETCH " + uid + " (RFC822)", null); - logResponse(r); - Response response = r[r.length - 1]; - if (!response.isOK()) - { - throw new ProtocolException("Unable to retrieve message in RFC822 format"); - } - - FetchResponse fetchResponse = (FetchResponse) r[0]; - return fetchResponse.getItem(RFC822DATA.class); - } - }); - - } - - /** - * Returns BODY object containing desired message fragment - * - * @param folder Folder containing the message - * @param uid Message UID - * @param from starting byte - * @param count bytes to read - * @return BODY containing desired message fragment - * @throws MessagingException - */ - private static BODY getMessageBodyPart(IMAPFolder folder, final Long uid, final Integer from, final Integer count) throws MessagingException - { - return (BODY) folder.doCommand(new IMAPFolder.ProtocolCommand() - { - public Object doCommand(IMAPProtocol p) throws ProtocolException - { - Response[] r = p.command("UID FETCH " + uid + " (FLAGS BODY.PEEK[]<" + from + "." + count + ">)", null); - logResponse(r); - Response response = r[r.length - 1]; - - // Grab response - if (!response.isOK()) - { - throw new ProtocolException("Unable to retrieve message part <" + from + "." + count + ">"); - } - - FetchResponse fetchResponse = (FetchResponse) r[0]; - BODY body = (BODY) fetchResponse.getItem(com.sun.mail.imap.protocol.BODY.class); - return body; - } - }); - - } - - /** - * Finds node by its path - * - * @param path String - * @return NodeRef - */ - private NodeRef findNode(String path) - { - List nodeRefs = searchService.selectNodes(storeRootNodeRef, path, null, namespaceService, false); - return nodeRefs.size() > 0 ? nodeRefs.get(0) : null; - } - - /** - * Returns the UID of the first message in folder - * - * @param folder Folder containing the message - * @param msn message sequence number - * @return UID of the first message - * @throws MessagingException - */ - private static Long getMessageUid(IMAPFolder folder, final int msn) throws MessagingException - { - return (Long) folder.doCommand(new IMAPFolder.ProtocolCommand() - { - public Object doCommand(IMAPProtocol p) throws ProtocolException - { - String command = "FETCH " + msn + " (UID)"; - Response[] r = p.command(command, null); - logResponse(r); - Response response = r[r.length - 1]; - - // Grab response - if (!response.isOK()) - { - throw new ProtocolException("Unable to retrieve message UID"); - } - - for(int i = 0 ; i < r.length; i++) - { - if(r[i] instanceof FetchResponse) - { - FetchResponse fetchResponse = (FetchResponse) r[0]; - UID uid = (UID) fetchResponse.getItem(UID.class); - logger.debug("SECNUM=" + uid.seqnum + ", UID="+uid.uid); - return uid.uid; - } - } - - /** - * Uh-oh - this is where we would intermittently fall over with a class cast exception. - * The following code probes why we don't have a FetchResponse - */ - StringBuffer sb = new StringBuffer(); - sb.append("command="+command); - sb.append('\n'); - sb.append("resp length=" + r.length); - sb.append('\n'); - for(int i = 0 ; i < r.length; i++) - { - logger.error(r[i]); - sb.append("class=" + r[i].getClass().getName()); - IMAPResponse unexpected = (IMAPResponse)r[i]; - sb.append("key=" + unexpected.getKey()); - sb.append("number=" + unexpected.getNumber()); - sb.append("rest=" + unexpected.getRest()); - - sb.append("r[" + i + "]=" + r[i] + '\n'); - } - throw new ProtocolException("getMessageUid: "+ sb.toString()); - } - }); - } - - /** - * Returns size of the message - * - * @param folder Folder containing the message - * @param uid Message UID - * @return Returns size of the message - * @throws MessagingException - */ - private static Integer getMessageSize(IMAPFolder folder, final Long uid) throws MessagingException - { - return getMessageBody(folder, uid).getByteArray().getCount(); - } - - /** - * Returns a full message body - * - * @param folder Folder containing the message - * @param uid Message UID - * @return Returns size of the message - * @throws MessagingException - */ - private static BODY getMessageBody(IMAPFolder folder, final Long uid) throws MessagingException - { - return (BODY) folder.doCommand(new IMAPFolder.ProtocolCommand() - { - public Object doCommand(IMAPProtocol p) throws ProtocolException - { - Response[] r = p.command("UID FETCH " + uid + " (FLAGS BODY.PEEK[])", null); - logResponse(r); - Response response = r[r.length - 1]; - - // Grab response - if (!response.isOK()) - { - throw new ProtocolException("Unable to retrieve message size"); - } - FetchResponse fetchResponse = (FetchResponse) r[0]; - BODY body = (BODY) fetchResponse.getItem(BODY.class); - return body; - } - }); - } - - /** - * Simple util for logging response - * - * @param r response - */ - private static void logResponse(Response[] r) - { - for (int i = 0; i < r.length; i++) - { - logger.debug(r[i]); - } - } - - @Override - public void tearDown() throws Exception - { - // Deleting created test environment - logger.debug("tearDown "); - - UserTransaction txn = transactionService.getUserTransaction(); - txn.begin(); - - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null, - namespaceService, false); - if (nodeRefs != null && nodeRefs.size() > 0) - { - fileFolderService.delete(nodeRefs.get(0)); - } - - authenticationService.deleteAuthentication(anotherUserName); - personService.deletePerson(anotherUserName); - - txn.commit(); - - // Closing client connection - folder.forceClose(); - store.close(); - logger.debug("tearDown end"); - } - -} +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2025 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.imap; + +import static org.alfresco.model.ContentModel.PROP_MODIFIED; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.SequenceInputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import jakarta.mail.Address; +import jakarta.mail.Folder; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Session; +import jakarta.mail.Store; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; +import jakarta.mail.internet.MimeUtility; +import jakarta.transaction.UserTransaction; + +import junit.framework.TestCase; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.angus.mail.iap.ProtocolException; +import org.eclipse.angus.mail.iap.Response; +import org.eclipse.angus.mail.imap.IMAPFolder; +import org.eclipse.angus.mail.imap.protocol.BODY; +import org.eclipse.angus.mail.imap.protocol.FetchResponse; +import org.eclipse.angus.mail.imap.protocol.IMAPProtocol; +import org.eclipse.angus.mail.imap.protocol.IMAPResponse; +import org.eclipse.angus.mail.imap.protocol.RFC822DATA; +import org.eclipse.angus.mail.imap.protocol.UID; +import org.eclipse.angus.mail.util.ASCIIUtility; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.springframework.context.ApplicationContext; +import org.springframework.core.io.ClassPathResource; +import org.springframework.mail.javamail.MimeMessageHelper; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.ImapModel; +import org.alfresco.repo.importer.ACPImportPackageHandler; +import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.node.integrity.IntegrityChecker; +import org.alfresco.repo.search.QueryParameterDefImpl; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileFolderUtil; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.QueryParameterDefinition; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.cmr.view.Location; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.test_category.OwnJVMTestsCategory; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; +import org.alfresco.util.config.RepositoryFolderConfigBean; +import org.alfresco.util.testing.category.LuceneTests; +import org.alfresco.util.testing.category.RedundantTests; + +@Category({OwnJVMTestsCategory.class, LuceneTests.class}) +public class ImapMessageTest extends TestCase +{ + private static Log logger = LogFactory.getLog(ImapMessageTest.class); + + // IMAP client settings + private static final String PROTOCOL = "imap"; + private static final String HOST = "localhost"; + private static final int PORT = 7143; + + private static final String ADMIN_USER_NAME = "admin"; + private static final String ADMIN_USER_PASSWORD = "admin"; + private static final String IMAP_FOLDER_NAME = "test"; + + private Session session = null; + private Store store = null; + private IMAPFolder folder = null; + + private ApplicationContext ctx; + private ServiceRegistry serviceRegistry; + private TransactionService transactionService; + private NodeService nodeService; + private ImporterService importerService; + private PersonService personService; + private SearchService searchService; + private NamespaceService namespaceService; + private FileFolderService fileFolderService; + private MutableAuthenticationService authenticationService; + private AlfrescoImapServer imapServer; + + String anotherUserName; + private NodeRef testImapFolderNodeRef; + private NodeRef storeRootNodeRef; + private final String storePath = "workspace://SpacesStore"; + private final String companyHomePathInStore = "/app:company_home"; + + private static final String TEST_FOLDER = "Alfresco IMAP/" + IMAP_FOLDER_NAME + "/___-___folder_a/" + "___-___folder_a_a"; + private static final String TEST_FILE = "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME + "/" + NamespaceService.CONTENT_MODEL_PREFIX + + ":___-___folder_a/" + NamespaceService.CONTENT_MODEL_PREFIX + ":___-___folder_a_a/" + NamespaceService.CONTENT_MODEL_PREFIX + ":___-___file_a_a"; + + @Override + public void setUp() throws Exception + { + ctx = ApplicationContextHelper.getApplicationContext(); + logger.debug("In SetUp"); + serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry"); + transactionService = serviceRegistry.getTransactionService(); + nodeService = serviceRegistry.getNodeService(); + importerService = serviceRegistry.getImporterService(); + personService = serviceRegistry.getPersonService(); + authenticationService = serviceRegistry.getAuthenticationService(); + searchService = serviceRegistry.getSearchService(); + namespaceService = serviceRegistry.getNamespaceService(); + fileFolderService = serviceRegistry.getFileFolderService(); + + // start the transaction + UserTransaction txn = transactionService.getUserTransaction(); + txn.begin(); + authenticationService.authenticate(ADMIN_USER_NAME, ADMIN_USER_PASSWORD.toCharArray()); + + // downgrade integrity + IntegrityChecker.setWarnInTransaction(); + + anotherUserName = "user" + System.currentTimeMillis(); + + PropertyMap testUser = new PropertyMap(); + testUser.put(ContentModel.PROP_USERNAME, anotherUserName); + testUser.put(ContentModel.PROP_FIRSTNAME, anotherUserName); + testUser.put(ContentModel.PROP_LASTNAME, anotherUserName); + testUser.put(ContentModel.PROP_EMAIL, anotherUserName + "@alfresco.com"); + testUser.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + personService.createPerson(testUser); + + // create the ACEGI Authentication instance for the new user + authenticationService.createAuthentication(anotherUserName, anotherUserName.toCharArray()); + + StoreRef storeRef = new StoreRef(storePath); + storeRootNodeRef = nodeService.getRootNode(storeRef); + + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + + nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null, + namespaceService, false); + if (nodeRefs != null && nodeRefs.size() > 0) + { + fileFolderService.delete(nodeRefs.get(0)); + } + + ChildApplicationContextFactory imap = (ChildApplicationContextFactory) ctx.getBean("imap"); + ApplicationContext imapCtx = imap.getApplicationContext(); + ImapServiceImpl imapServiceImpl = (ImapServiceImpl) imapCtx.getBean("imapService"); + imapServer = (AlfrescoImapServer) imapCtx.getBean("imapServer"); + + if (!imapServer.isImapServerEnabled()) + { + imapServer.setImapServerEnabled(true); + imapServer.setHost(HOST); + imapServer.setPort(PORT); + imapServer.startup(); + } + + // Creating IMAP test folder for IMAP root + LinkedList folders = new LinkedList(); + folders.add(IMAP_FOLDER_NAME); + FileFolderUtil.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER); + + // Setting IMAP root + RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean(); + imapHome.setStore(storePath); + imapHome.setRootPath(companyHomePathInStore); + imapHome.setFolderPath(NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME); + imapServiceImpl.setImapHome(imapHome); + + // Starting IMAP + imapServiceImpl.startupInTxn(true); + + nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null, + namespaceService, false); + testImapFolderNodeRef = nodeRefs.get(0); + + /* Importing test folders: Test folder contains: "___-___folder_a" "___-___folder_a" contains: "___-___folder_a_a", "___-___file_a", "Message_485.eml" (this is IMAP Message) "___-___folder_a_a" contains: "____-____file_a_a" */ + importInternal("imap/imapservice_test_folder_a.acp", testImapFolderNodeRef); + + txn.commit(); + + // Init mail client session + Properties props = new Properties(); + props.setProperty("mail.imap.partialfetch", "false"); + this.session = Session.getDefaultInstance(props, null); + + // Get the store + this.store = session.getStore(PROTOCOL); + // this.store.connect(HOST, PORT, anotherUserName, anotherUserName); + this.store.connect(imapServer.getHost(), imapServer.getPort(), anotherUserName, anotherUserName); + + // Get folder + folder = (IMAPFolder) store.getFolder(TEST_FOLDER); + folder.open(Folder.READ_ONLY); + + logger.debug("End SetUp"); + + } + + private void importInternal(String acpName, NodeRef space) throws IOException + { + // Importing IMAP test acp + ClassPathResource acpResource = new ClassPathResource(acpName); + ACPImportPackageHandler acpHandler = new ACPImportPackageHandler(acpResource.getFile(), null); + Location importLocation = new Location(space); + importerService.importView(acpHandler, importLocation, null, null); + } + + public void testMessageModifiedBetweenReads() throws Exception + { + // Get test message UID + final Long uid = getMessageUid(folder, 1); + + // Get unmodified message + BODY body = getMessageBody(folder, uid); + + // Parse the multipart MIME message + MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new BufferedInputStream(body.getByteArrayInputStream())); + + // Reading first part - should be successful + MimeMultipart content = (MimeMultipart) message.getContent(); + assertNotNull(content.getBodyPart(0).getContent()); + // Reading second part - should be successful + assertNotNull(content.getBodyPart(1).getContent()); + + // Modify message. The size of letter describing the node may change + // These changes should be committed because it should be visible from client + NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE); + UserTransaction txn = transactionService.getUserTransaction(); + txn.begin(); + ContentWriter writer = fileFolderService.getWriter(contentNode); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 2000; i++) + { + sb.append("test string"); + } + writer.putContent(sb.toString()); + txn.commit(); + + // Read updated message part + BODY bodyNew = getMessageBody(folder, uid); + + // The body should be updated + assertFalse(Arrays.equals(bodyNew.getByteArray().getBytes(), body.getByteArray().getBytes())); + + // Parse the multipart MIME message + message = new MimeMessage(Session.getDefaultInstance(new Properties()), new BufferedInputStream(bodyNew.getByteArrayInputStream())); + + // Reading first part - should be successful + content = (MimeMultipart) message.getContent(); + assertNotNull(content.getBodyPart(0).getContent()); + // Reading second part - should be successful + assertNotNull(content.getBodyPart(1).getContent()); + } + + public void testMessageRenamedBetweenReads() throws Exception + { + // Get test message UID + final Long uid = getMessageUid(folder, 1); + // Get Message size + final int count = getMessageSize(folder, uid); + + // Get first part + // Split the message into 2 part using a non multiple of 4 - 103 is a prime number + // as the BASE64Decoder may not throw the IOException + // see MNT-12995 + BODY body = getMessageBodyPart(folder, uid, 0, count - 103); + + // Rename message. The size of letter describing the node will change + // These changes should be committed because it should be visible from client + NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE); + UserTransaction txn = transactionService.getUserTransaction(); + txn.begin(); + fileFolderService.rename(contentNode, "testtesttesttesttesttesttesttesttesttest"); + txn.commit(); + + // Read second message part + BODY bodyRest = getMessageBodyPart(folder, uid, count - 103, 103); + + // Creating and parsing message from 2 parts + MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()), + new BufferedInputStream(bodyRest.getByteArrayInputStream()))); + + // Reading first part - should be successful + MimeMultipart content = (MimeMultipart) message.getContent(); + assertNotNull(content.getBodyPart(0).getContent()); + + try + { + // Reading second part cause error + content.getBodyPart(1).getContent(); + fail("Should raise an IOException"); + } + catch (IOException e) + {} + } + + public void dontTestMessageCache() throws Exception + { + + // Create messages + NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE); + UserTransaction txn = transactionService.getUserTransaction(); + txn.begin(); + + // Create messages more than cache capacity + for (int i = 0; i < 51; i++) + { + FileInfo fi = fileFolderService.create(nodeService.getParentAssocs(contentNode).get(0).getParentRef(), "test" + i, ContentModel.TYPE_CONTENT); + ContentWriter writer = fileFolderService.getWriter(fi.getNodeRef()); + writer.putContent("test"); + } + + txn.commit(); + + // Reload folder + folder.close(false); + folder = (IMAPFolder) store.getFolder(TEST_FOLDER); + folder.open(Folder.READ_ONLY); + + // Read all messages + for (int i = 1; i < 51; i++) + { + // Get test message UID + final Long uid = getMessageUid(folder, i); + // Get Message size + final int count = getMessageSize(folder, uid); + + // Get first part + BODY body = getMessageBodyPart(folder, uid, 0, count - 100); + // Read second message part + BODY bodyRest = getMessageBodyPart(folder, uid, count - 100, 100); + + // Creating and parsing message from 2 parts + MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()), + new BufferedInputStream(bodyRest.getByteArrayInputStream()))); + + // Reading first part - should be successful + MimeMultipart content = (MimeMultipart) message.getContent(); + assertNotNull(content.getBodyPart(0).getContent()); + assertNotNull(content.getBodyPart(1).getContent()); + } + } + + @Test + public void testSearchTermWithNonEnglishLocale() throws Exception + { + folder = (IMAPFolder) store.getFolder(TEST_FOLDER); + folder.open(Folder.READ_ONLY); + + Locale defaultLocale = Locale.getDefault(); + try + { + Locale.setDefault(Locale.FRENCH); + String dateStr = "12-Jul-2020"; + final IMAPFolder.ProtocolCommand uid_search_since = new IMAPFolder.ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol protocol) + { + return protocol.command("UID SEARCH SINCE " + dateStr, null); + } + }; + // UID SEARCH SINCE + Response[] ret = (Response[]) folder.doCommand(uid_search_since); + IMAPResponse response = (IMAPResponse) ret[0]; + assertEquals("* SEARCH ", response.toString()); + } + catch (MessagingException e) + { + fail("Date cannot be parsed"); + } + finally + { + Locale.setDefault(defaultLocale); + } + } + + @Test + public void testSearchTermWithNonEnglishLocaleFalsePositive() throws Exception + { + Locale defaultLocale = Locale.getDefault(); + try + { + folder = (IMAPFolder) store.getFolder(TEST_FOLDER); + folder.open(Folder.READ_ONLY); + + Locale.setDefault(Locale.FRENCH); + String dateStr = "12-juil.-2020"; + final IMAPFolder.ProtocolCommand uid_search_since = new IMAPFolder.ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol protocol) + { + return protocol.command("UID SEARCH SINCE " + dateStr, null); + } + }; + // UID SEARCH SINCE + Response[] ret = (Response[]) folder.doCommand(uid_search_since); + assertEquals("java.io.IOException: Connection dropped by server?", ret[0].getException().toString()); + } + catch (MessagingException e) + { + // expected + } + finally + { + Locale.setDefault(defaultLocale); + } + } + + public void testUnmodifiedMessage() throws Exception + { + // Get test message UID + final Long uid = getMessageUid(folder, 1); + // Get Message size + final int count = getMessageSize(folder, uid); + + // Make multiple message reading + for (int i = 0; i < 100; i++) + { + // Get random offset + int n = (int) ((int) 100 * Math.random()); + + // Get first part + BODY body = getMessageBodyPart(folder, uid, 0, count - n); + // Read second message part + BODY bodyRest = getMessageBodyPart(folder, uid, count - n, n); + + // Creating and parsing message from 2 parts + MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()), + new BufferedInputStream(bodyRest.getByteArrayInputStream()))); + + MimeMultipart content = (MimeMultipart) message.getContent(); + // Reading first part - should be successful + assertNotNull(content.getBodyPart(0).getContent()); + // Reading second part - should be successful + assertNotNull(content.getBodyPart(1).getContent()); + } + } + + public void testEncodedFromToAddresses() throws Exception + { + // RFC1342 + String addressString = "ars.kov@gmail.com"; + String personalString = "�?р�?ений Ковальчук"; + InternetAddress address = new InternetAddress(addressString, personalString, "UTF-8"); + + // Following method returns the address with quoted personal aka <["�?р�?ений Ковальчук"] > + // NOTE! This should be coincided with RFC822MetadataExtracter. Would 'addresses' be quoted or not? + // String decodedAddress = address.toUnicodeString(); + // Starting from javax.mail 1.5.6 the address is folded at linear whitespace so that each line is no longer than 76 characters, if possible. + String decodedAddress = MimeUtility.decodeText(MimeUtility.fold(6, address.toString())); + + // InternetAddress.toString(new Address[] {address}) - is used in the RFC822MetadataExtracter + // So, compare with that + assertFalse("Non ASCII characters in the address should be encoded", decodedAddress.equals(InternetAddress.toString(new Address[]{address}))); + + MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties())); + + MimeMessageHelper messageHelper = new MimeMessageHelper(message, false, "UTF-8"); + + messageHelper.setText("This is a sample message for ALF-5647"); + messageHelper.setSubject("This is a sample message for ALF-5647"); + messageHelper.setFrom(address); + messageHelper.addTo(address); + messageHelper.addCc(address); + + // Creating the message node in the repository + UserTransaction txn = transactionService.getUserTransaction(); + txn.begin(); + String name = AlfrescoImapConst.MESSAGE_PREFIX + GUID.generate(); + FileInfo messageFile = fileFolderService.create(testImapFolderNodeRef, name, ContentModel.TYPE_CONTENT); + // Writing a content. + NodeRef nodeRef = messageFile.getNodeRef(); + Serializable origModified = getModified(nodeRef); + new IncomingImapMessage(messageFile, serviceRegistry, message); + txn.commit(); + + // Calls to new IncomingImapMessage(...) only takes place when a new nodeRef is being created. + // No other code will be changing the nodeRef. An ImapModel.ASPECT_IMAP_CONTENT is added, which + // triggers a metadata extract to take place in a post commit method. Previously this would have been a + // synchronous process. This is no longer true as it may now take place in a T-Engine. So, we need to wait + // for the extract to take place. There does not + long end = System.currentTimeMillis() + 10000; + while (System.currentTimeMillis() <= end && origModified.equals(getModified(nodeRef))) + { + Thread.currentThread().sleep(1000); + } + + // Getting the transformed properties from the repository + // cm:originator, cm:addressee, cm:addressees, imap:messageFrom, imap:messageTo, imap:messageCc + Map properties = nodeService.getProperties(nodeRef); + String cmOriginator = (String) properties.get(ContentModel.PROP_ORIGINATOR); + String cmAddressee = (String) properties.get(ContentModel.PROP_ADDRESSEE); + @SuppressWarnings("unchecked") + List cmAddressees = (List) properties.get(ContentModel.PROP_ADDRESSEES); + String imapMessageFrom = (String) properties.get(ImapModel.PROP_MESSAGE_FROM); + String imapMessageTo = (String) properties.get(ImapModel.PROP_MESSAGE_TO); + String imapMessageCc = (String) properties.get(ImapModel.PROP_MESSAGE_CC); + + assertNotNull(cmOriginator); + assertEquals(decodedAddress, cmOriginator); + assertNotNull(cmAddressee); + assertEquals(decodedAddress, cmAddressee); + assertNotNull(cmAddressees); + assertEquals(1, cmAddressees.size()); + assertEquals(decodedAddress, cmAddressees.get(0)); + assertNotNull(imapMessageFrom); + assertEquals(decodedAddress, imapMessageFrom); + assertNotNull(imapMessageTo); + assertEquals(decodedAddress, imapMessageTo); + assertNotNull(imapMessageCc); + assertEquals(decodedAddress, imapMessageCc); + } + + private Serializable getModified(NodeRef nodeRef) + { + Map origProperties = nodeService.getProperties(nodeRef); + return origProperties.get(PROP_MODIFIED); + } + + @Category(RedundantTests.class) + public void testEightBitMessage() throws Exception + { + + Store lstore = session.getStore(PROTOCOL); + lstore.connect(imapServer.getHost(), imapServer.getPort(), ADMIN_USER_NAME, ADMIN_USER_PASSWORD); + + String folderName = "Alfresco IMAP/" + IMAP_FOLDER_NAME; + + IMAPFolder lfolder = (IMAPFolder) lstore.getFolder(folderName); + lfolder.open(Folder.READ_WRITE); + + InputStream messageFileInputStream1 = null; + InputStream messageFileInputStream2 = null; + try + { + ClassPathResource fileResource = new ClassPathResource("imap/test-8bit-message.eml"); + messageFileInputStream1 = new FileInputStream(fileResource.getFile()); + Message message = new MimeMessage(Session.getDefaultInstance(new Properties()), messageFileInputStream1); + String subject = message.getSubject(); + + // get original bytes for further comparation + messageFileInputStream2 = new FileInputStream(fileResource.getFile()); + byte[] original = ASCIIUtility.getBytes(messageFileInputStream2); + + Message[] messages = {message}; + + lfolder.appendMessages(messages); + + // The search is not implemented. + // SearchTerm term = new HeaderTerm("X-Alfresco-Unique", "test8bit"); + // messages = folder.search(term); + + // So wee need to get our test message's UID from the repo + + String messageXPath = companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME + "/*[like(@cm:title, $cm:title, true)]"; + + QueryParameterDefinition[] params = new QueryParameterDefinition[1]; + params[0] = new QueryParameterDefImpl( + ContentModel.PROP_TITLE, + serviceRegistry.getDictionaryService().getDataType(DataTypeDefinition.TEXT), + true, + subject); + + List nodeRefs = searchService.selectNodes(storeRootNodeRef, messageXPath, params, namespaceService, true); + + // does the message exist + assertEquals(1, nodeRefs.size()); + + NodeRef messageNodeRef = nodeRefs.get(0); + + // get message UID + Long dbid = (Long) nodeService.getProperty(messageNodeRef, ContentModel.PROP_NODE_DBID); + + // fetch the massage + RFC822DATA data = getRFC822Message(lfolder, dbid); + + assertNotNull("Can't fetch a message from the repositiry", data); + + byte[] processed = ASCIIUtility.getBytes(data.getByteArrayInputStream()); + + assertTrue("Original message doesn't coincide to the message processed by the repository", Arrays.equals(original, processed)); + } + finally + { + if (messageFileInputStream1 != null) + messageFileInputStream1.close(); + if (messageFileInputStream2 != null) + messageFileInputStream2.close(); + } + + // close connection + lfolder.close(true); + lstore.close(); + + } + + private static RFC822DATA getRFC822Message(final IMAPFolder folder, final long uid) throws MessagingException + { + return (RFC822DATA) folder.doCommand(new IMAPFolder.ProtocolCommand() { + public Object doCommand(IMAPProtocol p) throws ProtocolException + { + Response[] r = p.command("UID FETCH " + uid + " (RFC822)", null); + logResponse(r); + Response response = r[r.length - 1]; + if (!response.isOK()) + { + throw new ProtocolException("Unable to retrieve message in RFC822 format"); + } + + FetchResponse fetchResponse = (FetchResponse) r[0]; + return fetchResponse.getItem(RFC822DATA.class); + } + }); + + } + + /** + * Returns BODY object containing desired message fragment + * + * @param folder + * Folder containing the message + * @param uid + * Message UID + * @param from + * starting byte + * @param count + * bytes to read + * @return BODY containing desired message fragment + * @throws MessagingException + */ + private static BODY getMessageBodyPart(IMAPFolder folder, final Long uid, final Integer from, final Integer count) throws MessagingException + { + return (BODY) folder.doCommand(new IMAPFolder.ProtocolCommand() { + public Object doCommand(IMAPProtocol p) throws ProtocolException + { + Response[] r = p.command("UID FETCH " + uid + " (FLAGS BODY.PEEK[]<" + from + "." + count + ">)", null); + logResponse(r); + Response response = r[r.length - 1]; + + // Grab response + if (!response.isOK()) + { + throw new ProtocolException("Unable to retrieve message part <" + from + "." + count + ">"); + } + + FetchResponse fetchResponse = (FetchResponse) r[0]; + BODY body = (BODY) fetchResponse.getItem(BODY.class); + return body; + } + }); + + } + + /** + * Finds node by its path + * + * @param path + * String + * @return NodeRef + */ + private NodeRef findNode(String path) + { + List nodeRefs = searchService.selectNodes(storeRootNodeRef, path, null, namespaceService, false); + return nodeRefs.size() > 0 ? nodeRefs.get(0) : null; + } + + /** + * Returns the UID of the first message in folder + * + * @param folder + * Folder containing the message + * @param msn + * message sequence number + * @return UID of the first message + * @throws MessagingException + */ + private static Long getMessageUid(IMAPFolder folder, final int msn) throws MessagingException + { + return (Long) folder.doCommand(new IMAPFolder.ProtocolCommand() { + public Object doCommand(IMAPProtocol p) throws ProtocolException + { + String command = "FETCH " + msn + " (UID)"; + Response[] r = p.command(command, null); + logResponse(r); + Response response = r[r.length - 1]; + + // Grab response + if (!response.isOK()) + { + throw new ProtocolException("Unable to retrieve message UID"); + } + + for (int i = 0; i < r.length; i++) + { + if (r[i] instanceof FetchResponse) + { + FetchResponse fetchResponse = (FetchResponse) r[0]; + UID uid = (UID) fetchResponse.getItem(UID.class); + logger.debug("SECNUM=" + uid.seqnum + ", UID=" + uid.uid); + return uid.uid; + } + } + + /** + * Uh-oh - this is where we would intermittently fall over with a class cast exception. The following code probes why we don't have a FetchResponse + */ + StringBuffer sb = new StringBuffer(); + sb.append("command=" + command); + sb.append('\n'); + sb.append("resp length=" + r.length); + sb.append('\n'); + for (int i = 0; i < r.length; i++) + { + logger.error(r[i]); + sb.append("class=" + r[i].getClass().getName()); + IMAPResponse unexpected = (IMAPResponse) r[i]; + sb.append("key=" + unexpected.getKey()); + sb.append("number=" + unexpected.getNumber()); + sb.append("rest=" + unexpected.getRest()); + + sb.append("r[" + i + "]=" + r[i] + '\n'); + } + throw new ProtocolException("getMessageUid: " + sb.toString()); + } + }); + } + + /** + * Returns size of the message + * + * @param folder + * Folder containing the message + * @param uid + * Message UID + * @return Returns size of the message + * @throws MessagingException + */ + private static Integer getMessageSize(IMAPFolder folder, final Long uid) throws MessagingException + { + return getMessageBody(folder, uid).getByteArray().getCount(); + } + + /** + * Returns a full message body + * + * @param folder + * Folder containing the message + * @param uid + * Message UID + * @return Returns size of the message + * @throws MessagingException + */ + private static BODY getMessageBody(IMAPFolder folder, final Long uid) throws MessagingException + { + return (BODY) folder.doCommand(new IMAPFolder.ProtocolCommand() { + public Object doCommand(IMAPProtocol p) throws ProtocolException + { + Response[] r = p.command("UID FETCH " + uid + " (FLAGS BODY.PEEK[])", null); + logResponse(r); + Response response = r[r.length - 1]; + + // Grab response + if (!response.isOK()) + { + throw new ProtocolException("Unable to retrieve message size"); + } + FetchResponse fetchResponse = (FetchResponse) r[0]; + BODY body = (BODY) fetchResponse.getItem(BODY.class); + return body; + } + }); + } + + /** + * Simple util for logging response + * + * @param r + * response + */ + private static void logResponse(Response[] r) + { + for (int i = 0; i < r.length; i++) + { + logger.debug(r[i]); + } + } + + @Override + public void tearDown() throws Exception + { + // Deleting created test environment + logger.debug("tearDown "); + + UserTransaction txn = transactionService.getUserTransaction(); + txn.begin(); + + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null, + namespaceService, false); + if (nodeRefs != null && nodeRefs.size() > 0) + { + fileFolderService.delete(nodeRefs.get(0)); + } + + authenticationService.deleteAuthentication(anotherUserName); + personService.deletePerson(anotherUserName); + + txn.commit(); + + // Closing client connection + folder.forceClose(); + store.close(); + logger.debug("tearDown end"); + } + +}