diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index a6e95dd079..e416e5f22d 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -414,30 +414,12 @@ - org.alfresco.repo.imap.config.ImapConfigMountPointsBean - - org.alfresco.repo.imap.config.ImapConfigBean - - - - - - ImapService - - - - org.alfresco.repo.imap.ImapService - - - - - diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index 2c2a8237b2..82670e0520 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -437,10 +437,6 @@ class="org.alfresco.repo.content.transform.MailContentTransformer" parent="baseContentTransformer" /> - - diff --git a/config/alfresco/extension/imap-bootsrap-context.xml.sample b/config/alfresco/extension/imap-bootsrap-context.xml.sample new file mode 100755 index 0000000000..307e8f2996 --- /dev/null +++ b/config/alfresco/extension/imap-bootsrap-context.xml.sample @@ -0,0 +1,40 @@ + + + + + + patch.imapFolders + patch.imapFolders.description + 0 + ${version.schema} + 10000 + + + + + + + + + + alfresco/templates/imap/imap_config_space.acp + alfresco/templates/imap/email_actions_space.acp + alfresco/templates/imap/command_processor_scripts.acp + + + + patch.imapUserFolders + patch.imapUserFolders.description + 0 + ${version.schema} + 10000 + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/imap/scripts/command-processor.js b/config/alfresco/imap/scripts/command-processor.js index 48844f53ec..963f147c10 100755 --- a/config/alfresco/imap/scripts/command-processor.js +++ b/config/alfresco/imap/scripts/command-processor.js @@ -44,7 +44,7 @@ function processCommand() { var message = "Unknown command: " + command; logger.log(message); - createEmail(message, message, message); + createEmail(message, message, message, false); return; } @@ -54,7 +54,7 @@ function processCommand() if (commandFolder == null) { var message = "Command Processor: wrong command=" + command; - createEmail(message, message, message); + createEmail(message, message, message, false); logger.log(message); return; } diff --git a/config/alfresco/imap/scripts/command-search.js b/config/alfresco/imap/scripts/command-search.js index 2f273d3eaa..5ffecaa983 100755 --- a/config/alfresco/imap/scripts/command-search.js +++ b/config/alfresco/imap/scripts/command-search.js @@ -75,17 +75,7 @@ function createResponseTextHtml(nodes) */ function createContentTextHtml(nodes) { - var content ="" + - "" + - "" + - "" + - "" + - "" + - "
" + "Command: " + title + "\n

\n"; + var content = "Command: " + title + "\n

\n"; content += "\n"; content += ""; content += ""; @@ -93,7 +83,7 @@ function createContentTextHtml(nodes) content += ""; content += ""; content += ""; - content += "\n" + ""; + content += "\n" for (var i = 0; i < nodes.length; i++) @@ -143,7 +133,7 @@ function commandSearch(params) if (query == null) { - createEmail(errorParameter, errorParameter, errorParameter); + createEmail(errorParameter, errorParameter, errorParameter, false); return; } @@ -164,7 +154,7 @@ function commandSearch(params) } else { - createEmail(errorXPathNotValid, errorXPathNotValid, errorXPathNotValid); + createEmail(errorXPathNotValid, errorXPathNotValid, errorXPathNotValid, false); return; } break; @@ -190,8 +180,8 @@ function commandSearch(params) createEmail(message, message, subject); return; } - /*createEmail(createContentTextPlain(nodes), createResponseTextHtml(nodes), subject);*/ - createEmail(createContentTextPlain(nodes), createContentTextHtml(nodes), subject); + /*createEmail(createResponseTextHtml(nodes), createContentTextPlain(nodes), subject, true);*/ + createEmail(createContentTextHtml(nodes), createContentTextPlain(nodes), subject, false); } /** * Decode subject @@ -234,13 +224,13 @@ function main() else { var message = unknownCommand + ": '" + title + "'"; - createEmail(message, message, message); + createEmail(message, message, message, false); } } else { var message = unknownCommand + ": '" + title + "'"; - createEmail(message, message, message); + createEmail(message, message, message, false); } document.remove(); diff --git a/config/alfresco/imap/scripts/command-utils.js b/config/alfresco/imap/scripts/command-utils.js index 513c394127..64039946f5 100755 --- a/config/alfresco/imap/scripts/command-utils.js +++ b/config/alfresco/imap/scripts/command-utils.js @@ -1,8 +1,9 @@ /** * Create e-mail -* contentEML (string) content message +* contentTextHtml (string) html content +* contentTextPlain (string) text content */ -function createEmail(messageTXT, messageHTML, subject) +function createEmail(contentTextHtml, contentTextPlain, subject, templateUsed) { var command = document.properties["cm:title"]; var userName = person.properties["cm:userName"]; @@ -14,42 +15,42 @@ function createEmail(messageTXT, messageHTML, subject) return; } - var response = inboxFolder.createNode("response" + Date.now() + ".eml", "cm:content"); + var nextMessageUID = inboxFolder.properties["imap:nextMessageUID"]; + inboxFolder.properties["imap:nextMessageUID"] = nextMessageUID + 1; + inboxFolder.save(); + + var response = inboxFolder.createNode("response" + Date.now(), "imap:imapContent"); response.properties["imap:messageFrom"] = "command@alfresco.com"; response.properties["imap:messageSubject"] = subject; response.properties["imap:messageTo"] = document.properties["cm:originator"]; response.properties["imap:messageCc"] = ""; - response.addAspect("imap:imapContent", null); + response.properties["imap:messageUID"] = nextMessageUID; - response.content = createRFC822Message("command@alfresco.com", document.properties["cm:originator"], subject, messageTXT, messageHTML); response.save(); - } -function createRFC822Message(from, to, subject, textPart, htmlPart) -{ - var id = new Number(Date.now()).toString(16); - var boundary = "----------" + id; - var date = new Date().toGMTString(); - var messageHeaders = "MIME-Version: 1.0\r\n" + - "Date: " + date + "\r\n" + - "From: " + from + "\r\n" + - "To: " + to + "\r\n" + - "Subject: " + subject + "\r\n" + - "Message-ID: " + id + "\r\n" + - "X-Priority: 3 (Normal)\r\n" + - "Content-Type: multipart/alternative; boundary=\"" + boundary + "\"\r\n\r\n"; - var messageBody = ""; - messageBody += messageHeaders; - messageBody += "--" + boundary + "\r\n"; - messageBody += "Content-Type: text/plain; charset=utf-8\r\n"; - //TODO Content-Transfer-Encoding - messageBody += "\r\n"; - messageBody += textPart + "\r\n\r\n"; - messageBody += "--" + boundary + "\r\n"; - messageBody += "Content-Type: text/html; charset=utf-8\r\n"; - //TODO Content-Transfer-Encoding - messageBody += "\r\n"; - messageBody += htmlPart + "\r\n\r\n"; - messageBody += "--" + boundary + "--\r\n\r\n"; - return messageBody; + var textBody = response.createNode("Body.txt", "imap:imapBody"); + textBody.content = contentTextPlain; + textBody.save(); + + var htmlBody = response.createNode("Body.html", "imap:imapBody"); + if (templateUsed == true) + { + htmlBody.content = contentTextHtml; + } + else + { + htmlBody.content = "" + + "" + + "" + + "" + + "" + + "" + + "
" + contentTextHtml + "
"; + } + htmlBody.save(); + } \ No newline at end of file diff --git a/config/alfresco/messages/imap-service.properties b/config/alfresco/messages/imap-service.properties index 4eeec7fd8e..fdfd12856a 100755 --- a/config/alfresco/messages/imap-service.properties +++ b/config/alfresco/messages/imap-service.properties @@ -7,8 +7,4 @@ imap.server.info.message_body_not_found = "The message body parts are not found. # Error messages. prefix 'imap.server.error' imap.server.error.properties_dont_exist = "Appropriate properties do not exist." -imap.server.error.permission_denied = "Cannot create folder - Permission denied." -imap.server.error.folder_already_exist = "Folder already exists." -imap.server.error.mailbox_name_is_mandatory = "Mailbox name is mandatory parameter." -imap.server.error.cannot_get_a_folder = "Cannot get a folder with name ''{0}''." diff --git a/config/alfresco/mimetype/mimetype-map.xml b/config/alfresco/mimetype/mimetype-map.xml index 7837cb7214..1e63fbff57 100644 --- a/config/alfresco/mimetype/mimetype-map.xml +++ b/config/alfresco/mimetype/mimetype-map.xml @@ -353,9 +353,6 @@ dwt - eml - - msg diff --git a/config/alfresco/model/imapModel.xml b/config/alfresco/model/imapModel.xml index f52ee75b8e..ee93e810b2 100755 --- a/config/alfresco/model/imapModel.xml +++ b/config/alfresco/model/imapModel.xml @@ -19,7 +19,7 @@ IMAP Folder cm:folder - + d:boolean @@ -30,6 +30,29 @@ + + IMAP File + cm:folder + + + + d:text + + + d:text + + + d:text + + + d:text + + + + imap:flaggable + + + Attachment to the IMAP message cm:content @@ -46,63 +69,11 @@ - - - IMAP File - imap:flaggable - - - - d:text - - - d:text - - - d:text - - - d:text - - - d:text - - - d:text - - - - - Attachment - - false - false - - - cm:cmobject - false - true - - - - Attachments Folder - - false - false - - - cm:cmobject - false - false - - - - - - - + + + - d:boolean + d:boolean d:boolean @@ -119,10 +90,11 @@ d:boolean + - - - + + + \ No newline at end of file diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index e141bf8f53..9c25c30fda 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -1808,40 +1808,4 @@ - - patch.imapFolders - patch.imapFolders.description - 0 - 2012 - 2013 - - - - - - - - - - alfresco/templates/imap/imap_config_space.acp - alfresco/templates/imap/email_actions_space.acp - alfresco/templates/imap/command_processor_scripts.acp - - - - patch.imapUserFolders - patch.imapUserFolders.description - 0 - 2012 - 2013 - - - - - - - - - - diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 1dbf8cfb7b..efc9ddd4c0 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -338,22 +338,14 @@ nfs.user.mappings.default.gid=0 # IMAP imap.server.enabled=false imap.server.port=143 -imap.server.host=localhost -imap.server.attachments.extraction.enabled=true # Default IMAP mount points -imap.server.mountPoints=Repository_virtual,Repository_archive,Repository_mixed +imap.server.mountPoints=Repository_virtual,Repository_archive imap.server.mountPoints.default.store=${spaces.store} imap.server.mountPoints.default.rootPath=/${spaces.company_home.childname} imap.server.mountPoints.default.mode=virtual imap.server.mountPoints.value.Repository_virtual.mode=virtual imap.server.mountPoints.value.Repository_archive.mode=archive -imap.server.mountPoints.value.Repository_mixed.mode=mixed - -# Folders that will be excluded from the automatic extraction capability -#imap.ignore.extraction=adminInbox -#imap.ignore.extraction.value.adminInbox.store=${spaces.store} -#imap.ignore.extraction.value.adminInbox.rootPath=/${spaces.company_home.childname}/imap:imap_home/cm:admin/cm:INBOX # Activity feed max size and max age (eg. 44640 mins = 31 days) activities.feed.max.size=100 diff --git a/config/alfresco/subsystems/imap/default/imap-server-context.xml b/config/alfresco/subsystems/imap/default/imap-server-context.xml index 3e39ce89db..471428e36c 100755 --- a/config/alfresco/subsystems/imap/default/imap-server-context.xml +++ b/config/alfresco/subsystems/imap/default/imap-server-context.xml @@ -11,10 +11,41 @@ - - - ${imap.server.host} + + + + + + + + + + + + + + + + + + ${mail.from.default} + + + + ${web.application.context.url} + + + + ${spaces.store}/${spaces.company_home.childname}/${spaces.imap_home.childname} + + + + ${spaces.store}/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.imapConfig.childname}/${spaces.imap_templates.childname} + + + + ${imap.server.port} @@ -27,13 +58,16 @@ + + + - + virtual @@ -44,7 +78,7 @@ /${spaces.company_home.childname} - + archive @@ -59,101 +93,17 @@ - - - - - - - ${spaces.store} - - - /${spaces.company_home.childname} - - - - - - - - - - - - - ${server.transaction.mode.readOnly} - ${server.transaction.mode.default} - ${server.transaction.mode.default} - ${server.transaction.mode.readOnly} - ${server.transaction.mode.default} - - - - - - - - - - - - - - - - - - - - - - - - ${spaces.store}/${spaces.company_home.childname}/${spaces.imap_home.childname} - - - ${mail.from.default} - - - ${web.application.context.url} - - - ${spaces.store}/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.imapConfig.childname}/${spaces.imap_templates.childname} - - - ${imap.server.attachments.extraction.enabled} - - - - - - - - - org.alfresco.repo.imap.ImapService - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/config/alfresco/subsystems/imap/default/imap-server.properties b/config/alfresco/subsystems/imap/default/imap-server.properties index d7b69af91e..42d301e12e 100755 --- a/config/alfresco/subsystems/imap/default/imap-server.properties +++ b/config/alfresco/subsystems/imap/default/imap-server.properties @@ -1,5 +1,4 @@ imap.server.enabled=false -imap.server.host=localhost imap.server.port=143 #imap.server.web.application.context.url=http://localhost:8080/alfresco diff --git a/config/alfresco/templates/imap/command_processor_scripts.acp b/config/alfresco/templates/imap/command_processor_scripts.acp index 9ed2769b74..dda4f430b3 100755 Binary files a/config/alfresco/templates/imap/command_processor_scripts.acp and b/config/alfresco/templates/imap/command_processor_scripts.acp differ diff --git a/config/alfresco/templates/imap/imap_config_space.acp b/config/alfresco/templates/imap/imap_config_space.acp index 31b4c744ee..d2dee2323c 100755 Binary files a/config/alfresco/templates/imap/imap_config_space.acp and b/config/alfresco/templates/imap/imap_config_space.acp differ diff --git a/config/alfresco/templates/imap/imap_message_text_html.ftl b/config/alfresco/templates/imap/imap_message_text_html.ftl index 41c0433af7..5af0c44148 100755 --- a/config/alfresco/templates/imap/imap_message_text_html.ftl +++ b/config/alfresco/templates/imap/imap_message_text_html.ftl @@ -84,7 +84,7 @@ Content links
UrlDownload Url
- + @@ -101,7 +101,7 @@ Start Workflow diff --git a/config/alfresco/templates/imap/imap_message_text_plain.ftl b/config/alfresco/templates/imap/imap_message_text_plain.ftl index 9bd374f831..b8edc2195e 100755 --- a/config/alfresco/templates/imap/imap_message_text_plain.ftl +++ b/config/alfresco/templates/imap/imap_message_text_plain.ftl @@ -21,7 +21,7 @@ Size: ${document.size / 1024} Kb CONTENT LINKS -Content folder: ${contextUrl}/navigate/browse${document.displayPath} +Content folder: ${contextUrl}${document.displayPath} Content URL: ${contextUrl}${document.url} Download URL: ${contextUrl}${document.downloadUrl} WebDAV URL: ${contextUrl}${document.webdavUrl} diff --git a/source/java/org/alfresco/email/server/handler/FolderEmailMessageHandler.java b/source/java/org/alfresco/email/server/handler/FolderEmailMessageHandler.java index e82d4a00f3..152d302634 100644 --- a/source/java/org/alfresco/email/server/handler/FolderEmailMessageHandler.java +++ b/source/java/org/alfresco/email/server/handler/FolderEmailMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Alfresco Software Limited. + * Copyright (C) 2005-2007 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/source/java/org/alfresco/email/server/impl/subetha/SubethaEmailMessage.java b/source/java/org/alfresco/email/server/impl/subetha/SubethaEmailMessage.java index 02fc47edda..91c344c1a5 100644 --- a/source/java/org/alfresco/email/server/impl/subetha/SubethaEmailMessage.java +++ b/source/java/org/alfresco/email/server/impl/subetha/SubethaEmailMessage.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Alfresco Software Limited. + * Copyright (C) 2005-2007 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,8 +49,6 @@ import org.apache.commons.logging.LogFactory; /** * Concrete representation of an email message as implemented for the SubEtha mail server. * - * @author Mike Shavnev - * * @since 2.2 */ public class SubethaEmailMessage implements EmailMessage diff --git a/source/java/org/alfresco/model/ImapModel.java b/source/java/org/alfresco/model/ImapModel.java index 3dfce73713..f918e53452 100755 --- a/source/java/org/alfresco/model/ImapModel.java +++ b/source/java/org/alfresco/model/ImapModel.java @@ -35,18 +35,14 @@ public interface ImapModel { static final String IMAP_MODEL_1_0_URI = "http://www.alfresco.org/model/imap/1.0"; - static final QName ASPECT_IMAP_FOLDER_NONSUBSCRIBED = QName.createQName(IMAP_MODEL_1_0_URI, "nonSubscribed"); + static final QName ASPECT_IMAP_FOLDER_SUBSCRIBED = QName.createQName(IMAP_MODEL_1_0_URI, "subscribed"); static final QName ASPECT_IMAP_FOLDER_NONSELECTABLE = QName.createQName(IMAP_MODEL_1_0_URI, "nonselectable"); - static final QName ASPECT_IMAP_CONTENT = QName.createQName(IMAP_MODEL_1_0_URI, "imapContent"); + static final QName TYPE_IMAP_CONTENT = QName.createQName(IMAP_MODEL_1_0_URI, "imapContent"); static final QName PROP_MESSAGE_FROM = QName.createQName(IMAP_MODEL_1_0_URI, "messageFrom"); static final QName PROP_MESSAGE_TO = QName.createQName(IMAP_MODEL_1_0_URI, "messageTo"); static final QName PROP_MESSAGE_CC = QName.createQName(IMAP_MODEL_1_0_URI, "messageCc"); static final QName PROP_MESSAGE_SUBJECT = QName.createQName(IMAP_MODEL_1_0_URI, "messageSubject"); - static final QName PROP_MESSAGE_ID = QName.createQName(IMAP_MODEL_1_0_URI, "messageId"); - static final QName PROP_THREAD_INDEX = QName.createQName(IMAP_MODEL_1_0_URI, "threadIndex"); - static final QName ASSOC_IMAP_ATTACHMENT = QName.createQName(IMAP_MODEL_1_0_URI, "attachment"); - static final QName ASSOC_IMAP_ATTACHMENTS_FOLDER = QName.createQName(IMAP_MODEL_1_0_URI, "attachmentsFolder"); static final QName ASPECT_FLAGGABLE = QName.createQName(IMAP_MODEL_1_0_URI, "flaggable"); static final QName PROP_FLAG_ANSWERED = QName.createQName(IMAP_MODEL_1_0_URI, "flagAnswered"); diff --git a/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java index 4d09fbc653..e82dd044a2 100644 --- a/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Alfresco Software Limited. + * Copyright (C) 2005-2007 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java index 18043d0dc0..787d4c7a7f 100755 --- a/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java @@ -41,7 +41,6 @@ import org.alfresco.service.cmr.view.ImporterService; import org.alfresco.service.cmr.view.Location; import org.springframework.context.MessageSource; import org.springframework.core.io.ClassPathResource; - /** * Builds folders tree necessary for IMAP functionality and imports email action scripts. * @@ -177,7 +176,6 @@ public class ImapFoldersPatch extends AbstractPatch throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs); } this.dictionaryNodeRef = nodeRefs.get(0); - sb.append("/").append(scriptsChildName); xpath = sb.toString(); nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false); @@ -190,7 +188,6 @@ public class ImapFoldersPatch extends AbstractPatch throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs); } this.scriptsNodeRef = nodeRefs.get(0); - // get the ImapConfig node sb.delete((sb.length() - (scriptsChildName.length() + 1)), sb.length()); sb.append("/").append(imapConfigChildName); diff --git a/source/java/org/alfresco/repo/content/MimetypeMap.java b/source/java/org/alfresco/repo/content/MimetypeMap.java index 2d33dc2f82..ec80768e98 100644 --- a/source/java/org/alfresco/repo/content/MimetypeMap.java +++ b/source/java/org/alfresco/repo/content/MimetypeMap.java @@ -79,7 +79,6 @@ public class MimetypeMap implements MimetypeService public static final String MIMETYPE_ATOM = "application/atom+xml"; public static final String MIMETYPE_RSS = "application/rss+xml"; public static final String MIMETYPE_RFC822 = "message/rfc822"; - public static final String MIMETYPE_OUTLOOK_MSG = "application/vnd.ms-outlook"; // Open Document public static final String MIMETYPE_OPENDOCUMENT_TEXT = "application/vnd.oasis.opendocument.text"; public static final String MIMETYPE_OPENDOCUMENT_TEXT_TEMPLATE = "application/vnd.oasis.opendocument.text-template"; diff --git a/source/java/org/alfresco/repo/content/transform/EMLTransformer.java b/source/java/org/alfresco/repo/content/transform/EMLTransformer.java deleted file mode 100755 index c0edd16798..0000000000 --- a/source/java/org/alfresco/repo/content/transform/EMLTransformer.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.content.transform; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -import javax.mail.Multipart; -import javax.mail.Part; -import javax.mail.Session; -import javax.mail.internet.MimeMessage; - -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.TransformationOptions; - -public class EMLTransformer extends AbstractContentTransformer2 -{ - public boolean isTransformable(String sourceMimetype, String targetMimetype, TransformationOptions options) - { - if (!MimetypeMap.MIMETYPE_RFC822.equals(sourceMimetype) || !MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(targetMimetype)) - { - // only support RFC822 -> TEXT - return false; - } - else - { - return true; - } - } - - @Override - protected void transformInternal(ContentReader reader, ContentWriter writer, TransformationOptions options) throws Exception - { - InputStream is = null; - try - { - is = reader.getContentInputStream(); - - MimeMessage mimeMessage = new MimeMessage(Session.getDefaultInstance(new Properties()), is); - - final StringBuilder sb = new StringBuilder(); - Object content = mimeMessage.getContent(); - if (content instanceof Multipart) - { - Multipart multipart = (Multipart) content; - Part part = multipart.getBodyPart(0); - - if (part.getContent() instanceof Multipart) - { - multipart = (Multipart) part.getContent(); - for (int i = 0, n = multipart.getCount(); i < n; i++) - { - part = multipart.getBodyPart(i); - if (part.getContentType().contains("text")) - { - sb.append(part.getContent().toString()).append("\n"); - - } - - } - - } - else if (part.getContentType().contains("text")) - { - sb.append(part.getContent().toString()); - - } - - } - else - { - sb.append(content.toString()); - } - - writer.putContent(sb.toString()); - } - finally - { - if (is != null) - { - try - { - is.close(); - } - catch (IOException e) - { - e.printStackTrace(); - } - } - } - } - -} diff --git a/source/java/org/alfresco/repo/content/transform/MailContentTransformer.java b/source/java/org/alfresco/repo/content/transform/MailContentTransformer.java index 4edad6fe64..d49e284eec 100644 --- a/source/java/org/alfresco/repo/content/transform/MailContentTransformer.java +++ b/source/java/org/alfresco/repo/content/transform/MailContentTransformer.java @@ -56,7 +56,7 @@ public class MailContentTransformer extends AbstractContentTransformer2 */ public boolean isTransformable(String sourceMimetype, String targetMimetype, TransformationOptions options) { - if (!MimetypeMap.MIMETYPE_OUTLOOK_MSG.equals(sourceMimetype) || + if (!MimetypeMap.MIMETYPE_RFC822.equals(sourceMimetype) || !MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(targetMimetype)) { // only support MSG -> TEXT diff --git a/source/java/org/alfresco/repo/imap/AbstractImapFolder.java b/source/java/org/alfresco/repo/imap/AbstractImapFolder.java deleted file mode 100755 index 8d35173b69..0000000000 --- a/source/java/org/alfresco/repo/imap/AbstractImapFolder.java +++ /dev/null @@ -1,694 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -import javax.mail.Flags; -import javax.mail.internet.MimeMessage; -import javax.mail.search.SearchTerm; - -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.ServiceRegistry; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.icegreen.greenmail.foedus.util.MsgRangeFilter; -import com.icegreen.greenmail.mail.MovingMessage; -import com.icegreen.greenmail.store.FolderException; -import com.icegreen.greenmail.store.FolderListener; -import com.icegreen.greenmail.store.MailFolder; -import com.icegreen.greenmail.store.SimpleStoredMessage; - -/** - * Implementation of greenmail MailFolder. It represents an Alfresco content folder and handles appendMessage, copyMessage, expunge (delete), getMessages, getMessage and so - * requests. - * - * @author Ivan Rybnikov - */ -public abstract class AbstractImapFolder implements MailFolder -{ - - private static Log logger = LogFactory.getLog(AbstractImapFolder.class); - - private List listeners = new LinkedList(); - - protected ServiceRegistry serviceRegistry; - protected static int MAX_RETRIES = 1; - - - public AbstractImapFolder(ServiceRegistry serviceRegistry) - { - this.serviceRegistry = serviceRegistry; - } - - /** - * Method that checks mandatory parameter. - * @param The parameter instance to check. - * @param The name of the parameter. - */ - protected void checkParameter(Object parameter, String name) - { - if (parameter == null) - { - throw new IllegalArgumentException(name + " parameter is null."); - } - } - - - /** - * Appends message to the folder. - * - * @param message - message. - * @param flags - message flags. - * @param internalDate - not used. Current date used instead. - * @return - */ - public long appendMessage(final MimeMessage message, final Flags flags, final Date internalDate) throws FolderException - { - if (isReadOnly()) - { - throw new FolderException("Can't append message - Permission denied"); - } - - CommandCallback command = new CommandCallback() - { - public Long command() throws Throwable - { - return appendMessageInternal(message, flags, internalDate); - } - }; - return command.runFeedback(); - } - - - /** - * Copies message with the given UID to the specified {@link MailFolder}. - * - * @param uid - UID of the message - * @param toFolder - reference to the destination folder. - */ - public void copyMessage(final long uid, final MailFolder toFolder) throws FolderException - { - AbstractImapFolder toImapMailFolder = (AbstractImapFolder) toFolder; - - if (toImapMailFolder.isReadOnly()) - { - throw new FolderException("Can't create folder - Permission denied"); - } - - CommandCallback command = new CommandCallback() - { - public Object command() throws Throwable - { - copyMessageInternal(uid, toFolder); - return null; - } - }; - command.runFeedback(); - } - - /** - * Marks all messages in the folder as deleted using {@link Flags.Flag#DELETED} flag. - */ - public void deleteAllMessages() throws FolderException - { - CommandCallback command = new CommandCallback() - { - public Object command() throws Throwable - { - deleteAllMessagesInternal(); - return null; - } - }; - command.runFeedback(); - } - - - - /** - * Deletes messages marked with {@link Flags.Flag#DELETED}. Note that this message deletes all messages with this flag. - */ - public void expunge() throws FolderException - { - if (isReadOnly()) - { - throw new FolderException("Can't expunge - Permission denied"); - } - CommandCallback command = new CommandCallback() - { - public Object command() throws Throwable - { - expungeInternal(); - return null; - } - }; - command.runFeedback(); - } - - - /** - * Returns the number of the first unseen message. - * - * @return Number of the first unseen message. - */ - public int getFirstUnseen() - { - return getFirstUnseenInternal(); - } - - - /** - * Returns full name of the folder with namespace and full path delimited with the hierarchy delimiter (see {@link AlfrescoImapConst#HIERARCHY_DELIMITER})

E.g.:

- * #mail.admin."Repository_archive.Data Dictionary.Space Templates.Software Engineering Project"

This is required by GreenMail implementation. - */ - public String getFullName() - { - CommandCallback command = new CommandCallback() - { - public String command() throws Throwable - { - return getFullNameInternal(); - } - }; - return command.run(); - } - - - /** - * Returns message by its UID. - * - * @param uid - UID of the message. - * @return message. - */ - public SimpleStoredMessage getMessage(final long uid) - { - CommandCallback command = new CommandCallback() - { - public SimpleStoredMessage command() throws Throwable - { - return getMessageInternal(uid); - } - }; - return command.run(); - } - - /** - * Returns count of the messages in the folder. - * - * @return Count of the messages. - */ - public int getMessageCount() - { - CommandCallback command = new CommandCallback() - { - public Integer command() throws Throwable - { - return getMessageCountInternal(); - } - }; - return command.run(); - } - - /** - * Returns list of all messages in the folder. - * - * @return list of {@link SimpleStoredMessage} objects. - */ - public List getMessages() - { - CommandCallback> command = new CommandCallback>() - { - public List command() throws Throwable - { - return getMessagesInternal(); - } - }; - return command.run(); - } - - /** - * Returns list of messages by filter. - * - * @param msgRangeFilter - {@link MsgRangeFilter} object representing filter. - * @return list of filtered messages. - */ - public List getMessages(final MsgRangeFilter msgRangeFilter) - { - CommandCallback > command = new CommandCallback >() - { - public List command() throws Throwable - { - return getMessagesInternal(msgRangeFilter); - } - }; - return command.run(); - } - - /** - * Returns message sequence number in the folder by its UID. - * - * @param uid - message UID. - * @return message sequence number. - * @throws FolderException if no message with given UID. - */ - public int getMsn(final long uid) throws FolderException - { - CommandCallback command = new CommandCallback() - { - public Integer command() throws Throwable - { - return getMsnInternal(uid); - } - }; - return command.runFeedback(true); - } - - /** - * Returns folder name. - * - * @return folder name. - */ - public String getName() - { - CommandCallback command = new CommandCallback() - { - public String command() throws Throwable - { - return getNameInternal(); - } - }; - return command.run(); - } - - /** - * Returns UIDs of all messages in the folder. - * - * @return UIDS of the messages. - */ - public long[] getMessageUids() - { - CommandCallback command = new CommandCallback() - { - public Object command() throws Throwable - { - return getMessageUidsInternal(); - } - }; - return (long[])command.run(); - } - - /** - * Returns the list of messages that have no {@link Flags.Flag#DELETED} flag set for current user. - * - * @return the list of non-deleted messages. - */ - public List getNonDeletedMessages() - { - CommandCallback > command = new CommandCallback>() - { - public List command() throws Throwable - { - return getNonDeletedMessagesInternal(); - } - }; - List result = (List)command.run(); - return result; - } - - /** - * Returns permanent flags. - * - * @return {@link Flags} object containing flags. - */ - public Flags getPermanentFlags() - { - CommandCallback command = new CommandCallback() - { - public Flags command() throws Throwable - { - return getPermanentFlagsInternal(); - } - }; - return command.run(true); - } - - /** - * Returns count of messages with {@link Flags.Flag#RECENT} flag. If {@code reset} parameter is {@code true} - removes {@link Flags.Flag#RECENT} flag from the message for - * current user. - * - * @param reset - if true the {@link Flags.Flag#RECENT} will be deleted for current user if exists. - * @return returns count of recent messages. - */ - public int getRecentCount(final boolean reset) - { - CommandCallback command = new CommandCallback() - { - public Integer command() throws Throwable - { - return getRecentCountInternal(reset); - } - }; - return command.run(true); - } - - /** - * Returns UIDNEXT value of the folder. - * - * @return UIDNEXT value. - */ - public long getUidNext() - { - CommandCallback command = new CommandCallback() - { - public Long command() throws Throwable - { - return getUidNextInternal(); - } - }; - return command.run(true); - } - - /** - * Returns UIDVALIDITY value of the folder. - * - * @return UIDVALIDITY value. - */ - public long getUidValidity() - { - CommandCallback command = new CommandCallback() - { - public Long command() throws Throwable - { - return getUidValidityInternal(); - } - }; - return command.run(true); - } - - /** - * Returns count of the messages with {@link Flags.Flag#SEEN} in the folder for the current user. - * - * @return Count of the unseen messages for current user. - */ - public int getUnseenCount() - { - CommandCallback command = new CommandCallback() - { - public Integer command() throws Throwable - { - return getUnseenCountInternal(); - } - }; - return command.run(); - } - - /** - * Whether the folder is selectable. - * - * @return {@code boolean}. - */ - public boolean isSelectable() - { - CommandCallback command = new CommandCallback() - { - public Boolean command() throws Throwable - { - return isSelectableInternal(); - } - }; - return command.run(true); - } - - /** - * Replaces flags for the message with the given UID. If {@code addUid} is set to {@code true} {@link FolderListener} objects defined for this folder will be notified. - * {@code silentListener} can be provided - this listener wouldn't be notified. - * - * @param flags - new flags. - * @param uid - message UID. - * @param silentListener - listener that shouldn't be notified. - * @param addUid - defines whether or not listeners be notified. - */ - public void replaceFlags(final Flags flags, final long uid, final FolderListener silentListener, final boolean addUid) throws FolderException - { - CommandCallback command = new CommandCallback() - { - public Object command() throws Throwable - { - replaceFlagsInternal(flags, uid, silentListener, addUid); - return null; - } - }; - command.runFeedback(true); - } - - - /** - * Simply returns UIDs of all messages in the folder. - * - * @param searchTerm - not used - * @return UIDs of the messages - */ - public long[] search(SearchTerm searchTerm) - { - return getMessageUids(); - } - - /** - * Sets flags for the message with the given UID. If {@code addUid} is set to {@code true} {@link FolderListener} objects defined for this folder will be notified. - * {@code silentListener} can be provided - this listener wouldn't be notified. - * - * @param flags - new flags. - * @param value - flags value. - * @param uid - message UID. - * @param silentListener - listener that shouldn't be notified. - * @param addUid - defines whether or not listeners be notified. - */ - public void setFlags(final Flags flags, final boolean value, final long uid, final FolderListener silentListener, final boolean addUid) throws FolderException - { - CommandCallback command = new CommandCallback() - { - public Object command() throws Throwable - { - setFlagsInternal(flags, value, uid, silentListener, addUid); - return null; - } - }; - command.runFeedback(); - } - - - - /** - * Not supported. Added to implement {@link MailFolder#store(MovingMessage)}. - */ - public void store(MovingMessage mail) throws Exception - { - throw new UnsupportedOperationException("Method store(MovingMessage) is not suppoted."); - } - - /** - * Not supported. Added to implement {@link MailFolder#store(MimeMessage)}. - */ - public void store(MimeMessage message) throws Exception - { - throw new UnsupportedOperationException("Method store(MimeMessage) is not suppoted."); - } - - - /** - * Adds {@link FolderListener} to the folder. - * - * @param listener - new listener. - */ - public void addListener(FolderListener listener) - { - listeners.add(listener); - } - - - - /** - * Removes {@link FolderListener} from the folder. - * - * @param listener - Listener to remove. - */ - public void removeListener(FolderListener listener) - { - listeners.remove(listener); - } - - /** - * Method is called before the deletion of the folder. Notifies {@link FolderListener} objects with {@link FolderListener#mailboxDeleted()} method calls. - */ - public void signalDeletion() - { - synchronized (listeners) - { - for (int i = 0; i < listeners.size(); i++) - { - FolderListener listener = (FolderListener) listeners.get(i); - listener.mailboxDeleted(); - } - } - } - - - protected void notifyFlagUpdate(int msn, Flags flags, Long uidNotification, FolderListener silentListener) - { - synchronized (listeners) - { - for (FolderListener listener : listeners) - { - if (listener == silentListener) - { - continue; - } - - listener.flagsUpdated(msn, flags, uidNotification); - } - } - } - - - protected abstract boolean isReadOnly(); - - protected abstract long appendMessageInternal(MimeMessage message, Flags flags, Date internalDate) throws Exception; - - protected abstract void copyMessageInternal(long uid, MailFolder toFolder) throws Exception; - - protected abstract void deleteAllMessagesInternal() throws Exception; - - protected abstract void expungeInternal() throws Exception; - - protected abstract int getFirstUnseenInternal(); - - protected abstract String getFullNameInternal() throws Exception; - - protected abstract SimpleStoredMessage getMessageInternal(long uid) throws Exception; - - protected abstract int getMessageCountInternal(); - - protected abstract List getMessagesInternal(); - - protected abstract List getMessagesInternal(MsgRangeFilter msgRangeFilter); - - protected abstract int getMsnInternal(long uid) throws Exception; - - protected abstract String getNameInternal(); - - protected abstract long[] getMessageUidsInternal(); - - protected abstract List getNonDeletedMessagesInternal(); - - protected abstract Flags getPermanentFlagsInternal(); - - protected abstract int getRecentCountInternal(boolean reset); - - protected abstract long getUidNextInternal(); - - protected abstract long getUidValidityInternal(); - - protected abstract int getUnseenCountInternal(); - - protected abstract boolean isSelectableInternal(); - - protected abstract void replaceFlagsInternal(Flags flags, long uid, FolderListener silentListener, boolean addUid) throws Exception; - - protected abstract void setFlagsInternal(Flags flags, boolean value, long uid, FolderListener silentListener, boolean addUid) throws Exception; - - - - - protected abstract class CommandCallback - { - public abstract T command() throws Throwable; - - public T runFeedback() throws FolderException - { - return this.runFeedback(false); - } - - public T runFeedback(boolean readOnly) throws FolderException - { - try - { - RetryingTransactionHelper txHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); - txHelper.setMaxRetries(MAX_RETRIES); - txHelper.setReadOnly(readOnly); - T result = txHelper.doInTransaction(new RetryingTransactionCallback() { - public T execute() throws Throwable - { - return command(); - } - }, readOnly); - - return result; - } - catch (Exception e) - { - Throwable cause = e.getCause(); - String message; - if (cause != null) - { - message = cause.getMessage(); - } - else - { - message = e.getMessage(); - } - throw new FolderException(message); - } - } - - - public T run() - { - return this.run(false); - } - - - public T run(boolean readOnly) - { - RetryingTransactionHelper txHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); - txHelper.setMaxRetries(MAX_RETRIES); - txHelper.setReadOnly(readOnly); - T result = txHelper.doInTransaction(new RetryingTransactionCallback() { - public T execute() throws Throwable - { - return command(); - } - }, readOnly); - - return result; - } - - } - - -} diff --git a/source/java/org/alfresco/repo/imap/AbstractMimeMessage.java b/source/java/org/alfresco/repo/imap/AbstractMimeMessage.java deleted file mode 100755 index a2d90ad509..0000000000 --- a/source/java/org/alfresco/repo/imap/AbstractMimeMessage.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_HTML_TEMPLATE; -import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_PLAIN_TEMPLATE; -import static org.alfresco.repo.imap.AlfrescoImapConst.DICTIONARY_TEMPLATE_PREFIX; -import static org.alfresco.repo.imap.AlfrescoImapConst.MIME_VERSION; -import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_NODEREF_ID; - -import java.io.Serializable; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import javax.mail.Address; -import javax.mail.Flags; -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.internet.AddressException; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; - -import org.alfresco.i18n.I18NUtil; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.template.TemplateNode; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.namespace.QName; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * @author Arseny Kovalchuk - */ -public abstract class AbstractMimeMessage extends MimeMessage -{ - /** Used if imapHelper.getDefaultFromAddress is not set */ - protected static final String DEFAULT_EMAIL_FROM = "alfresco@alfresco.org"; - protected static final String DEFAULT_EMAIL_TO = DEFAULT_EMAIL_FROM; - protected static final String KOI8R_CHARSET = "koi8-r"; - - protected static int MAX_RETRIES = 1; - - private Log logger = LogFactory.getLog(AbstractMimeMessage.class); - - protected boolean generateBody = true; - - protected ServiceRegistry serviceRegistry; - protected ImapService imapService; - protected FileInfo messageFileInfo; - protected MimeMessage wrappedMessage; - - protected AbstractMimeMessage(Session session) - { - super(session); - } - - protected AbstractMimeMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, boolean generateBody) throws MessagingException - { - super(Session.getDefaultInstance(new Properties())); - this.generateBody = generateBody; - buildMessage(fileInfo, serviceRegistry); - } - - public static enum EmailBodyType - { - TEXT_PLAIN, TEXT_HTML; - - public String getSubtype() - { - return name().toLowerCase().substring(5); - } - - public String getTypeSubtype() - { - return name().toLowerCase().replaceAll("_", ""); - } - - public String getMimeType() - { - return name().toLowerCase().replaceAll("_", "/"); - } - - } - - protected void buildMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry) throws MessagingException - { - checkParameter(serviceRegistry, "ServiceRegistry"); - this.content = null; - this.serviceRegistry = serviceRegistry; - this.imapService = serviceRegistry.getImapService(); - this.messageFileInfo = fileInfo; - RetryingTransactionHelper txHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); - txHelper.setMaxRetries(MAX_RETRIES); - txHelper.doInTransaction(new RetryingTransactionCallback() { - public Object execute() throws Throwable - { - buildMessageInternal(); - return null; - } - }); - } - - /** - * Method must be implemented in subclasses. It usually should be used to generate message body. - * - * @throws MessagingException - */ - public abstract void buildMessageInternal() throws MessagingException; - - /** - * Method that checks mandatory parameter. - * @param The parameter instance to check. - * @param The name of the parameter. - */ - protected void checkParameter(Object parameter, String name) - { - if (parameter == null) - { - throw new IllegalArgumentException(name + " parameter is null."); - } - } - - protected void setMessageHeaders() throws MessagingException - { - setHeader(MIME_VERSION, "1.0"); - // Optional headers for further implementation of multiple Alfresco server support. - setHeader(X_ALF_NODEREF_ID, messageFileInfo.getNodeRef().getId()); - // setHeader(X_ALF_SERVER_UID, imapService.getAlfrescoServerUID()); - } - - /** - * Builds the InternetAddress from the Content Author name if provided. If name not specified, it takes Content Creator name. If content creator does not exists, the default - * from address will be returned. - * - * @param contentAuthor The content author full name. - * @return Generated InternetAddress[] array. - * @throws AddressException - */ - protected InternetAddress[] buildSenderFromAddress() throws AddressException - { - // Generate FROM address (Content author) - InternetAddress[] addressList = null; - Map properties = messageFileInfo.getProperties(); - String prop = (String) properties.get(ContentModel.PROP_AUTHOR); - String defaultFromAddress = imapService.getDefaultFromAddress(); - defaultFromAddress = defaultFromAddress == null ? DEFAULT_EMAIL_FROM : defaultFromAddress; - try - { - - if (prop != null) - { - StringBuilder contentAuthor = new StringBuilder(); - contentAuthor.append("\"").append(prop).append("\" <").append(defaultFromAddress).append(">"); - addressList = InternetAddress.parse(contentAuthor.toString()); - } - else - { - prop = (String) properties.get(ContentModel.PROP_CREATOR); - if (prop != null) - { - StringBuilder creator = new StringBuilder(); - creator.append("\"").append(prop).append("\" <").append(defaultFromAddress).append(">"); - addressList = InternetAddress.parse(creator.toString()); - } - else - { - throw new AddressException(I18NUtil.getMessage("imap.server.error.properties_dont_exist")); - } - } - } - catch (AddressException e) - { - addressList = InternetAddress.parse(DEFAULT_EMAIL_FROM); - } - return addressList; - } - - /** - * Returns {@link FileInfo} object representing message in Alfresco. - * - * @return reference to the {@link FileInfo} object. - */ - public FileInfo getMessageInfo() - { - return messageFileInfo; - } - - /** - * Returns message flags. - * - * @return {@link Flags} - */ - @Override - public Flags getFlags() - { - return imapService.getFlags(messageFileInfo); - } - - - /** - * Sets message flags. - * - * @param flags - {@link Flags} object. - * @param value - flags value. - */ - @Override - public void setFlags(Flags flags, boolean value) throws MessagingException - { - imapService.setFlags(messageFileInfo, flags, value); - } - - /** - * Returns the text representing email body for ContentModel node. - * - * @param nodeRef NodeRef of the target content. - * @param type The type of the returned body. May be the one of {@link EmailBodyType}. - * @return Text representing email body for ContentModel node. - */ - public String getEmailBodyText(EmailBodyType type) - { - return serviceRegistry.getTemplateService().processTemplate(getDefaultEmailBodyTemplate(type), createEmailTemplateModel(messageFileInfo.getNodeRef())); - } - - /** - * TODO USE CASE 2: "The To/addressee will be the first email alias found in the parent folders or a default one (TBD)". It seems to be more informative as alike - * {@code @}... - * - * @return Generated TO address {@code @} - * @throws AddressException - */ - protected InternetAddress[] buildRecipientToAddress() throws AddressException - { - InternetAddress[] result = null; - String defaultEmailTo = null; - final String escapedUserName = AuthenticationUtil.getFullyAuthenticatedUser().replaceAll("[/,\\,@]", "."); - final String userDomain = DEFAULT_EMAIL_TO.split("@")[1]; - defaultEmailTo = escapedUserName + "@" + userDomain; - try - { - result = InternetAddress.parse(defaultEmailTo); - } - catch (AddressException e) - { - logger.error(String.format("Wrong email address '%s'.", defaultEmailTo), e); - result = InternetAddress.parse(DEFAULT_EMAIL_TO); - } - return result; - } - - protected void addFromInternal(String addressesString) throws MessagingException - { - if (addressesString != null) - { - addFrom(InternetAddress.parse(addressesString)); - } - else - { - addFrom(new Address[] { new InternetAddress(DEFAULT_EMAIL_FROM) }); - } - } - - /** - * Returns default email body template. This method trying to find a template on the path in the repository first e.g. {@code "Data Dictionary > IMAP Templates >"}. This path - * should be set as the property of the "imapHelper" bean. In this case it returns {@code NodeRef.toString()} of the template. If there are no template in the repository it - * returns a default template on the classpath. - * - * @param type One of the {@link EmailBodyType}. - * @return String representing template classpath path or NodeRef.toString(). - */ - private String getDefaultEmailBodyTemplate(EmailBodyType type) - { - String result = null; - switch (type) - { - case TEXT_HTML: - result = CLASSPATH_TEXT_HTML_TEMPLATE; - break; - case TEXT_PLAIN: - result = CLASSPATH_TEXT_PLAIN_TEMPLATE; - break; - } - final StringBuilder templateName = new StringBuilder(DICTIONARY_TEMPLATE_PREFIX).append("-").append(type.getTypeSubtype()).append(".ftl"); - final String repositoryTemplatePath = imapService.getRepositoryTemplatePath(); - int indexOfStoreDelim = repositoryTemplatePath.indexOf(StoreRef.URI_FILLER); - if (indexOfStoreDelim == -1) - { - logger.error("Bad path format, " + StoreRef.URI_FILLER + " not found"); - return result; - } - indexOfStoreDelim += StoreRef.URI_FILLER.length(); - int indexOfPathDelim = repositoryTemplatePath.indexOf("/", indexOfStoreDelim); - if (indexOfPathDelim == -1) - { - logger.error("Bad path format, / not found"); - return result; - } - final String storePath = repositoryTemplatePath.substring(0, indexOfPathDelim); - final String rootPathInStore = repositoryTemplatePath.substring(indexOfPathDelim); - final String query = String.format("+PATH:\"%1$s/*\" +@cm\\:name:\"%2$s\"", rootPathInStore, templateName.toString()); - if (logger.isDebugEnabled()) - { - logger.debug("Using template path :" + repositoryTemplatePath + "/" + templateName); - logger.debug("Query: " + query); - } - StoreRef storeRef = new StoreRef(storePath); - ResultSet resultSet = serviceRegistry.getSearchService().query(storeRef, "lucene", query); - if (resultSet == null || resultSet.length() == 0) - { - logger.error(String.format("IMAP message template '%1$s' does not exist in the path '%2$s'.", templateName, repositoryTemplatePath)); - return result; - } - result = resultSet.getNodeRef(0).toString(); - return result; - } - - /** - * Builds default email template model for TemplateProcessor - * - * @param ref NodeRef of the target content. - * @return Map that includes template model objects. - */ - private Map createEmailTemplateModel(NodeRef ref) - { - Map model = new HashMap(8, 1.0f); - TemplateNode tn = new TemplateNode(ref, serviceRegistry, null); - model.put("document", tn); - NodeRef parent = serviceRegistry.getNodeService().getPrimaryParent(ref).getParentRef(); - model.put("space", new TemplateNode(parent, serviceRegistry, null)); - model.put("date", new Date()); - model.put("contextUrl", new String(imapService.getWebApplicationContextUrl())); - model.put("alfTicket", new String(serviceRegistry.getAuthenticationService().getCurrentTicket())); - return model; - } - - - -} diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapConst.java b/source/java/org/alfresco/repo/imap/AlfrescoImapConst.java index f6507227e2..6fa28f2150 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapConst.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapConst.java @@ -30,7 +30,7 @@ package org.alfresco.repo.imap; public interface AlfrescoImapConst { - public static final char HIERARCHY_DELIMITER = '/'; + public static final char HIERARCHY_DELIMITER = '.'; public static final String NAMESPACE_PREFIX = "#"; public static final String USER_NAMESPACE = "#mail"; public static final String INBOX_NAME = "INBOX"; @@ -38,23 +38,17 @@ public interface AlfrescoImapConst public static final String BODY_TEXT_PLAIN_NAME = "Body.txt"; public static final String BODY_TEXT_HTML_NAME = "Body.html"; public static final String MESSAGE_PREFIX = "Message_"; - public static final String EML_EXTENSION = ".eml"; // Separator for user enties in flag and subscribe properties public static final String USER_SEPARATOR = ";"; /** - * Defines {@link AlfrescoImapFolder} view mode as archive mode. Used for Email Archive View. + * Defines {@link AlfrescoImapMailFolder} view mode as archive mode. Used for Email Archive View. */ public static final String MODE_ARCHIVE = "archive"; /** - * Defines {@link AlfrescoImapFolder} view mode as virtual mode. Used for IMAP Virtualised View. + * Defines {@link AlfrescoImapMailFolder} view mode as virtual mode. Used for IMAP Virtualised View. */ public static final String MODE_VIRTUAL = "virtual"; - /** - * Defines {@link AlfrescoImapFolder} view mode as mixed mode. Used for IMAP Mixed View. - */ - public static final String MODE_MIXED = "mixed"; - // Default content model email message templates public static final String CLASSPATH_TEXT_PLAIN_TEMPLATE = "/alfresco/templates/imap/imap_message_text_plain.ftl"; diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapHostManager.java b/source/java/org/alfresco/repo/imap/AlfrescoImapHostManager.java index 996206740f..d158c1bf4a 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapHostManager.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapHostManager.java @@ -24,11 +24,27 @@ */ package org.alfresco.repo.imap; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; -import org.alfresco.service.transaction.TransactionService; +import org.alfresco.model.ContentModel; +import org.alfresco.model.ImapModel; +import org.alfresco.repo.imap.config.ImapConfigBean; +import org.alfresco.repo.imap.exception.AlfrescoImapFolderException; +import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -37,6 +53,7 @@ import com.icegreen.greenmail.imap.ImapHostManager; import com.icegreen.greenmail.store.FolderException; import com.icegreen.greenmail.store.MailFolder; import com.icegreen.greenmail.user.GreenMailUser; +import com.icegreen.greenmail.util.GreenMailUtil; /** * @author Mike Shavnev @@ -46,8 +63,11 @@ public class AlfrescoImapHostManager implements ImapHostManager private Log logger = LogFactory.getLog(AlfrescoImapHostManager.class); - private ImapService imapService; - private TransactionService transactionService; + private ServiceRegistry serviceRegistry; + + private NodeService nodeService; + private FileFolderService fileFolderService; + private ImapHelper imapHelper; /** * Returns the hierarchy delimiter for mailboxes on this host. @@ -70,14 +90,18 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public Collection listMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException { - try + mailboxPattern = GreenMailUtil.convertFromUtf7(mailboxPattern); + + if (logger.isDebugEnabled()) { - return new ArrayList(imapService.listMailboxes(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxPattern)); + logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern); } - catch (Throwable e) + mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern); + if (logger.isDebugEnabled()) { - throw new FolderException(e.getMessage()); + logger.debug("Listing mailboxes: mailboxPattern in alfresco=" + mailboxPattern); } + return listMailboxes(user, mailboxPattern, false); } /** @@ -92,14 +116,251 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public Collection listSubscribedMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException { - try + mailboxPattern = GreenMailUtil.convertFromUtf7(mailboxPattern); + + if (logger.isDebugEnabled()) { - return new ArrayList(imapService.listSubscribedMailboxes(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxPattern)); + logger.debug("Listing subscribed mailboxes: mailboxPattern=" + mailboxPattern); } - catch (Throwable e) + mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern); + if (logger.isDebugEnabled()) { - throw new FolderException(e.getMessage()); + logger.debug("Listing subscribed mailboxes: mailboxPattern in alfresco=" + mailboxPattern); } + + return listMailboxes(user, mailboxPattern, true); + } + + /** + * Depend on listSubscribed param, list Mailboxes or list subscribed Mailboxes + */ + private Collection listMailboxes(GreenMailUser user, String mailboxPattern, boolean listSubscribed) throws FolderException + { + Collection result = new LinkedList(); + + Map mountPoints = imapHelper.getMountPoints(); + Map imapConfigs = imapHelper.getImapConfig(); + + NodeRef mountPoint; + + // List mailboxes that are in mount points + for (String mountPointName : mountPoints.keySet()) + { + + mountPoint = mountPoints.get(mountPointName); + FileInfo mountPointFileInfo = imapHelper.getFileFolderService().getFileInfo(mountPoint); + NodeRef mountParent = imapHelper.getNodeService().getParentAssocs(mountPoint).get(0).getParentRef(); + String viewMode = imapConfigs.get(mountPointName).getMode(); + + if (!mailboxPattern.equals("*")) + { + mountPoint = mountParent; + } + + boolean isVirtualView = imapConfigs.get(mountPointName).getMode().equals(AlfrescoImapConst.MODE_VIRTUAL); + Collection folders = listFolder(mountPoint, mountPoint, user, mailboxPattern, listSubscribed, isVirtualView); + if (folders != null) + { + for (MailFolder mailFolder : folders) + { + AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) mailFolder; + folder.setMountPointName(mountPointName); + folder.setViewMode(viewMode); + folder.setMountParent(mountParent); + } + result.addAll(folders); + } + + // Add mount point to the result list + if (mailboxPattern.equals("*")) + { + if ((listSubscribed && isSubscribed(mountPointFileInfo, user.getLogin())) || (!listSubscribed)) + { + result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, imapHelper)); + } + // \NoSelect + else if (listSubscribed && hasSubscribedChild(mountPointFileInfo, user.getLogin(), isVirtualView)) + { + result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, imapHelper, + false)); + } + } + + } + + // List mailboxes that are in user IMAP Home + NodeRef root = imapHelper.getUserImapHomeRef(user.getLogin()); + Collection imapFolders = listFolder(root, root, user, mailboxPattern, listSubscribed, false); + + if (imapFolders != null) + { + for (MailFolder mailFolder : imapFolders) + { + AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) mailFolder; + folder.setViewMode(AlfrescoImapConst.MODE_ARCHIVE); + folder.setMountParent(root); + } + result.addAll(imapFolders); + } + + return result; + + } + + private Collection listFolder(NodeRef mailboxRoot, NodeRef root, GreenMailUser user, String mailboxPattern, boolean listSubscribed, boolean isVirtualView) + throws FolderException + { + if (logger.isDebugEnabled()) + { + logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern); + } + + int index = mailboxPattern.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); + + String name = null; + String remainName = null; + + if (index < 0) + { + name = mailboxPattern; + } + else + { + name = mailboxPattern.substring(0, index); + remainName = mailboxPattern.substring(index + 1); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Listing mailboxes: name=" + name); + } + + if (index < 0) + { + if ("*".equals(name)) + { + Collection list = imapHelper.searchFolders(root, name, true, isVirtualView); + if (listSubscribed) + { + list = getSubscribed(list, user.getLogin()); + } + + if (list.size() > 0) + { + return createMailFolderList(user, list, mailboxRoot); + } + return null; + } + else if (name.endsWith("*")) + { + List fullList = new LinkedList(); + List list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView); + Collection subscribedList = list; + if (listSubscribed) + { + subscribedList = getSubscribed(list, user.getLogin()); + } + + if (list.size() > 0) + { + fullList.addAll(subscribedList); + for (FileInfo fileInfo : list) + { + List childList = imapHelper.searchFolders(fileInfo.getNodeRef(), "*", true, isVirtualView); + if (listSubscribed) + { + fullList.addAll(getSubscribed(childList, user.getLogin())); + } + else + { + fullList.addAll(childList); + } + } + return createMailFolderList(user, fullList, mailboxRoot); + } + return null; + } + else if ("%".equals(name)) + { + List list = imapHelper.searchFolders(root, "*", false, isVirtualView); + LinkedList subscribedList = new LinkedList(); + + if (listSubscribed) + { + for (FileInfo fileInfo : list) + { + if (isSubscribed(fileInfo, user.getLogin())) + { + // folderName, viewMode, mountPointName will be setted in listMailboxes() method + subscribedList.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), fileInfo, null, null, mailboxRoot, null, imapHelper)); + } + // \NoSelect + else if (hasSubscribedChild(fileInfo, user.getLogin(), isVirtualView)) + { + // folderName, viewMode, mountPointName will be setted in listMailboxes() method + subscribedList.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), fileInfo, null, null, mailboxRoot, null, imapHelper, false)); + } + } + } + else + { + return createMailFolderList(user, list, mailboxRoot); + } + + return subscribedList; + } + else if (name.contains("%") || name.contains("*")) + { + List list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView); + Collection subscribedList = list; + if (listSubscribed) + { + subscribedList = getSubscribed(list, user.getLogin()); + } + + if (subscribedList.size() > 0) + { + return createMailFolderList(user, subscribedList, mailboxRoot); + } + return null; + } + else + { + List list = imapHelper.searchFolders(root, name, false, isVirtualView); + Collection subscribedList = list; + if (listSubscribed) + { + subscribedList = getSubscribed(list, user.getLogin()); + } + + if (subscribedList.size() > 0) + { + return createMailFolderList(user, subscribedList, mailboxRoot); + } + return null; + } + } + + // If (index != -1) this is not the last level + Collection result = new LinkedList(); + + List list = imapHelper.searchFolders(root, name.replace('%', '*'), false, isVirtualView); + for (FileInfo folder : list) + { + Collection childFolders = listFolder(mailboxRoot, folder.getNodeRef(), user, remainName, listSubscribed, isVirtualView); + + if (childFolders != null) + { + result.addAll(childFolders); + } + } + + if (result.isEmpty()) + { + return null; + } + + return result; } /** @@ -114,16 +375,97 @@ public class AlfrescoImapHostManager implements ImapHostManager * @throws com.icegreen.greenmail.store.FolderException if an existing folder with the new name. * @throws AlfrescoImapFolderException if user does not have rights to create the new mailbox. */ + public void renameMailbox(GreenMailUser user, String oldMailboxName, String newMailboxName) throws FolderException, AuthorizationException { - try + oldMailboxName = GreenMailUtil.convertFromUtf7(oldMailboxName); + newMailboxName = GreenMailUtil.convertFromUtf7(newMailboxName); + if (logger.isDebugEnabled()) { - imapService.renameMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), oldMailboxName, newMailboxName); + logger.debug("Renaming folder: oldMailboxName=" + oldMailboxName + " newMailboxName=" + newMailboxName); } - catch (Throwable e) + + AlfrescoImapMailFolder sourceNode = (AlfrescoImapMailFolder) getFolder(user, GreenMailUtil.convertInUtf7(oldMailboxName)); + + NodeRef root = imapHelper.getMailboxRootRef(oldMailboxName, user.getLogin()); + String mailboxRepoName = imapHelper.getMailPathInRepo(newMailboxName); + + StringTokenizer tokenizer = new StringTokenizer(mailboxRepoName, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER)); + + NodeRef parentNodeRef = root; + while (tokenizer.hasMoreTokens()) { - throw new FolderException(e.getMessage()); + String folderName = tokenizer.nextToken(); + + if (!tokenizer.hasMoreTokens()) + { + try + { + if (oldMailboxName.equalsIgnoreCase(AlfrescoImapConst.INBOX_NAME)) + { + // If you trying to rename INBOX + // - just copy it to another folder with new name + // and leave INBOX (with children) intact. + fileFolderService.copy(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName); + List itemsForRemove = fileFolderService.list(sourceNode.getFolderInfo().getNodeRef()); + for (FileInfo fileInfo : itemsForRemove) + { + fileFolderService.delete(fileInfo.getNodeRef()); + } + + } + else + { + fileFolderService.move(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName); + } + return; + } + catch (FileExistsException e) + { + throw new FolderException(FolderException.ALREADY_EXISTS_LOCALLY); + } + catch (FileNotFoundException e) + { + if (logger.isDebugEnabled()) + { + logger.error(e); + } + } + + } + else + { + List folders = imapHelper.searchFolders(parentNodeRef, folderName, false, true); + + if (folders.size() == 0) + { + AccessStatus status = imapHelper.hasPermission(parentNodeRef, PermissionService.WRITE); + if (status == AccessStatus.DENIED) + { + if (logger.isDebugEnabled()) + { + logger.debug("Creating folder: Cant't create folder - Permission denied"); + } + throw new AlfrescoImapFolderException(AlfrescoImapFolderException.PERMISSION_DENIED); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Create mailBox: " + folderName); + } + FileFolderServiceImpl.makeFolders(fileFolderService, parentNodeRef, Arrays.asList(folderName), ContentModel.TYPE_FOLDER); + } + else + { + parentNodeRef = folders.get(0).getNodeRef(); + if (logger.isDebugEnabled()) + { + logger.debug("MailBox: " + folderName + " already exists"); + } + } + } } + } /** @@ -138,14 +480,58 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public MailFolder createMailbox(GreenMailUser user, String mailboxName) throws AuthorizationException, FolderException { - try + mailboxName = GreenMailUtil.convertFromUtf7(mailboxName); + if (logger.isDebugEnabled()) { - return imapService.createMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName); + logger.debug("Creating folder: " + mailboxName); } - catch (Throwable e) + + NodeRef root = imapHelper.getMailboxRootRef(mailboxName, user.getLogin()); + + String mountPointName = imapHelper.getMountPointName(mailboxName); + String mailboxRepoNam = imapHelper.getMailPathInRepo(mailboxName); + StringTokenizer tokenizer = new StringTokenizer(mailboxRepoNam, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER)); + + NodeRef parentNodeRef = root; + + while (tokenizer.hasMoreTokens()) { - throw new FolderException(e.getMessage()); + String folderName = tokenizer.nextToken(); + + List folders = imapHelper.searchFolders(parentNodeRef, folderName, false, true); + + if (folders.size() == 0) + { + AccessStatus status = imapHelper.hasPermission(parentNodeRef, PermissionService.WRITE); + if (status == AccessStatus.DENIED) + { + if (logger.isDebugEnabled()) + { + logger.debug("Creating folder: Cant't create folder - Permission denied"); + } + throw new AlfrescoImapFolderException(AlfrescoImapFolderException.PERMISSION_DENIED); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Create mailBox: " + mailboxName); + } + FileInfo mailFolder = FileFolderServiceImpl.makeFolders(fileFolderService, parentNodeRef, Arrays.asList(folderName), ContentModel.TYPE_FOLDER); + + return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mailFolder, folderName, imapHelper.getViewMode(mailboxName), root, mountPointName, imapHelper); + + } + else + { + parentNodeRef = folders.get(0).getNodeRef(); + if (logger.isDebugEnabled()) + { + logger.debug("MailBox: " + folderName + " already exists"); + } + } } + + throw new FolderException(FolderException.ALREADY_EXISTS_LOCALLY); } /** @@ -158,13 +544,34 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public void deleteMailbox(GreenMailUser user, String mailboxName) throws FolderException, AuthorizationException { - try + AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) getFolder(user, mailboxName); + NodeRef nodeRef = folder.getFolderInfo().getNodeRef(); + + List childFolders = imapHelper.searchFolders(nodeRef, "*", false, false); + + if (childFolders.isEmpty()) { - imapService.deleteMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName); + folder.signalDeletion(); + // Delete child folders and messages + fileFolderService.delete(nodeRef); } - catch (Throwable e) + else { - throw new FolderException(e.getMessage()); + if (folder.isSelectable()) + { + // Delete all messages for this folder + // Don't delete subfolders and their messages + List messages = imapHelper.searchFiles(nodeRef, "*", ImapModel.TYPE_IMAP_CONTENT, false); + for (FileInfo message : messages) + { + fileFolderService.delete(message.getNodeRef()); + } + nodeService.addAspect(nodeRef, ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE, null); + } + else + { + throw new FolderException(mailboxName + " - Can't delete a non-selectable store with children."); + } } } @@ -179,22 +586,80 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public MailFolder getFolder(GreenMailUser user, String mailboxName) { - return imapService.getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName); + mailboxName = GreenMailUtil.convertFromUtf7(mailboxName); + if (logger.isDebugEnabled()) + { + logger.debug("Getting folder: " + mailboxName); + } + + // If MailFolder object is used to obtain hierarchy delimiter by LIST command: + // Example: + // C: 2 list "" "" + // S: * LIST () "." "" + // S: 2 OK LIST completed. + if ("".equals(mailboxName)) + { + if (logger.isDebugEnabled()) + { + logger.debug("Request for the hierarchy delimiter"); + } + return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), null, null, null, null, null, null); + } + + NodeRef root = imapHelper.getMailboxRootRef(mailboxName, user.getLogin()); + String mountPointName = imapHelper.getMountPointName(mailboxName); + String mailboxRepoName = imapHelper.getMailPathInRepo(mailboxName); + + StringTokenizer tokenizer = new StringTokenizer(mailboxRepoName, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER)); + int count = tokenizer.countTokens(); + NodeRef nodeRef = root; + + while (tokenizer.hasMoreTokens()) + { + String t = tokenizer.nextToken(); + if (logger.isDebugEnabled()) + { + logger.debug("token=" + t); + } + count--; + + List list = imapHelper.searchFolders(nodeRef, t, false, true); + + if (count == 0) + { + if (!list.isEmpty()) + { + + return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), list.get(0), list.get(0).getName(), imapHelper.getViewMode(mailboxName), root, + mountPointName, imapHelper); + } + else + { + return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), null, null, null, null, null, null); + } + } + else + { + if (!list.isEmpty()) + { + nodeRef = list.get(0).getNodeRef(); + } + else + { + return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), null, null, null, null, null, null); + } + } + } + + throw new IllegalStateException("Error state"); } /** * Simply calls {@link #getFolder(GreenMailUser, String)}.

Added to implement {@link ImapHostManager}. */ - public MailFolder getFolder(final GreenMailUser user, final String mailboxName, boolean mustExist) throws FolderException + public MailFolder getFolder(GreenMailUser user, String mailboxName, boolean mustExist) throws FolderException { - try - { - return getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName); - } - catch (Throwable e) - { - throw new FolderException(e.getMessage()); - } + return getFolder(user, mailboxName); } /** @@ -205,14 +670,7 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public MailFolder getInbox(GreenMailUser user) throws FolderException { - try - { - return getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), AlfrescoImapConst.INBOX_NAME); - } - catch (Throwable e) - { - throw new FolderException(e.getMessage()); - } + return getFolder(user, AlfrescoImapConst.INBOX_NAME); } /** @@ -229,16 +687,39 @@ public class AlfrescoImapHostManager implements ImapHostManager * @param user User making the request * @param mailbox String representation of a mailbox name. */ - public void subscribe(GreenMailUser user, String mailbox) throws FolderException + public void subscribe(final GreenMailUser user, final String mailbox) throws FolderException { - try + if (logger.isDebugEnabled()) { - imapService.subscribe(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailbox); - } - catch (Throwable e) - { - throw new FolderException(e.getMessage()); + logger.debug("Subscribing: " + mailbox); } + AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox); + nodeService.addAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED, null); +// This is a multiuser support. Commented due new requirements +// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() +// { +// public Void doWork() throws Exception +// { +// AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox); +// FileInfo fileInfo = mailFolder.getFolderInfo(); +// if (fileInfo != null) +// { +// String subscribedList = (String) nodeService.getProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED); +// if (subscribedList == null) +// { +// subscribedList = ""; +// } +// subscribedList = subscribedList.replaceAll(imapHelper.formatUserEntry(user.getLogin()), ""); +// subscribedList += imapHelper.formatUserEntry(user.getLogin()); +// nodeService.setProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED, subscribedList); +// } +// else +// { +// logger.debug("MailBox: " + mailbox + "doesn't exsist. Maybe it was deleted earlier."); +// } +// return null; +// } +// }, AuthenticationUtil.getSystemUserName()); } /** @@ -247,46 +728,137 @@ public class AlfrescoImapHostManager implements ImapHostManager * @param user User making the request * @param mailbox String representation of a mailbox name. */ - public void unsubscribe(GreenMailUser user, String mailbox) throws FolderException + public void unsubscribe(final GreenMailUser user, final String mailbox) throws FolderException { - try + if (logger.isDebugEnabled()) { - imapService.unsubscribe(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailbox); - } - catch (Throwable e) - { - throw new FolderException(e.getMessage()); + logger.debug("Unsubscribing: " + mailbox); } + AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox); + nodeService.removeAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED); + +// This is a multiuser support. Commented due new requirements +// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() +// { +// public Void doWork() throws Exception +// { +// AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox); +// if (mailFolder.getFolderInfo() != null) +// { +// FileInfo fileInfo = mailFolder.getFolderInfo(); +// String subscribedList = (String) nodeService.getProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED); +// if (subscribedList == null) +// { +// subscribedList = ""; +// } +// subscribedList = subscribedList.replaceAll(imapHelper.formatUserEntry(user.getLogin()), ""); +// nodeService.setProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED, subscribedList); +// } +// else +// { +// logger.debug("MailBox: " + mailbox + " doesn't exsist. Maybe it was deleted earlier."); +// } +// +// return null; +// } +// }, AuthenticationUtil.getSystemUserName()); } /** * Not supported. Used by GreenMail class. */ - public List getAllMessages() + public List getAllMessages() { throw new UnsupportedOperationException(); } + private boolean isSubscribed(FileInfo fileInfo, String userName) + { + return nodeService.hasAspect(fileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED); +// This is a multiuser support. Commented due new requirements + +// Map properties = fileInfo.getProperties(); +// String subscribedList = (String) properties.get(ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED); +// if (subscribedList == null) +// { +// return false; +// } +// else +// { +// return subscribedList.contains(imapHelper.formatUserEntry(userName)); +// } + + } + + private Collection getSubscribed(Collection list, String userName) + { + Collection result = new LinkedList(); + + for (FileInfo folderInfo : list) + { + if (isSubscribed(folderInfo, userName)) + { + result.add(folderInfo); + } + } + + return result; + } + + private boolean hasSubscribedChild(FileInfo parent, String userName, boolean isVirtualView) + { + List list = imapHelper.searchFolders(parent.getNodeRef(), "*", true, isVirtualView); + + for (FileInfo fileInfo : list) + { + if (isSubscribed(fileInfo, userName)) + { + return true; + } + } + + return false; + } + + private Collection createMailFolderList(GreenMailUser user, Collection list, NodeRef imapUserHomeRef) + { + Collection result = new LinkedList(); + + for (FileInfo folderInfo : list) + { + // folderName, viewMode, mountPointName will be setted in listSubscribedMailboxes() method + result.add(new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), folderInfo, null, null, imapUserHomeRef, null, imapHelper)); + } + + return result; + + } + // ----------------------Getters and Setters---------------------------- - public ImapService getImapService() + public void setServiceRegistry(ServiceRegistry serviceRegistry) { - return imapService; + this.serviceRegistry = serviceRegistry; } - public void setImapService(ImapService imapService) + public ServiceRegistry getServiceRegistry() { - this.imapService = imapService; + return serviceRegistry; } - public TransactionService getTransactionService() + public void setNodeService(NodeService nodeService) { - return transactionService; + this.nodeService = nodeService; } - public void setTransactionService(TransactionService transactionService) + public void setFileFolderService(FileFolderService fileFolderService) { - this.transactionService = transactionService; + this.fileFolderService = fileFolderService; + } + + public void setImapHelper(ImapHelper imapHelper) + { + this.imapHelper = imapHelper; } } diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java b/source/java/org/alfresco/repo/imap/AlfrescoImapMailFolder.java similarity index 50% rename from source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java rename to source/java/org/alfresco/repo/imap/AlfrescoImapMailFolder.java index 631c3c3ac3..3537733ec6 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapMailFolder.java @@ -24,53 +24,58 @@ */ package org.alfresco.repo.imap; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; +import java.io.OutputStreamWriter; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import javax.mail.BodyPart; import javax.mail.Flags; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.internet.ContentType; +import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeUtility; +import javax.mail.search.SearchTerm; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.model.ImapModel; -import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.model.FileExistsException; -import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.ContentIOException; 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.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; -import org.alfresco.util.Utf7; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.util.FileCopyUtils; import com.icegreen.greenmail.foedus.util.MsgRangeFilter; import com.icegreen.greenmail.imap.ImapConstants; +import com.icegreen.greenmail.mail.MovingMessage; import com.icegreen.greenmail.store.FolderException; import com.icegreen.greenmail.store.FolderListener; import com.icegreen.greenmail.store.MailFolder; import com.icegreen.greenmail.store.MessageFlags; import com.icegreen.greenmail.store.SimpleStoredMessage; +import com.icegreen.greenmail.util.GreenMailUtil; /** * Implementation of greenmail MailFolder. It represents an Alfresco content folder and handles appendMessage, copyMessage, expunge (delete), getMessages, getMessage and so @@ -78,10 +83,10 @@ import com.icegreen.greenmail.store.SimpleStoredMessage; * * @author Mike Shavnev */ -public class AlfrescoImapFolder extends AbstractImapFolder +public class AlfrescoImapMailFolder implements MailFolder { - private static Log logger = LogFactory.getLog(AlfrescoImapFolder.class); + private static Log logger = LogFactory.getLog(AlfrescoImapMailFolder.class); /** * Reference to the {@link FileInfo} object representing the folder. @@ -114,27 +119,28 @@ public class AlfrescoImapFolder extends AbstractImapFolder private String mountPointName; /** - * Reference to the {@link ImapService} object. + * Reference to the {@link ImapHelper} object. */ - private ImapService imapService; + private ImapHelper imapHelper; /** * Defines whether the folder is selectable or not. */ - private boolean selectable; + private Boolean selectable; /** * Defines whether the folder is read-only for user or not. */ private Boolean readOnly; - private boolean extractAttachmentsEnabled; - + private Map messages = new TreeMap(); - private Map msnCache = new HashMap(); + private boolean isBodyGenerated = false; private static final Flags PERMANENT_FLAGS = new Flags(); + private List listeners = new LinkedList(); + static { PERMANENT_FLAGS.add(Flags.Flag.ANSWERED); @@ -143,19 +149,9 @@ public class AlfrescoImapFolder extends AbstractImapFolder PERMANENT_FLAGS.add(Flags.Flag.FLAGGED); PERMANENT_FLAGS.add(Flags.Flag.SEEN); } - - public boolean isExtractAttachmentsEnabled() - { - return extractAttachmentsEnabled; - } - /*package*/ AlfrescoImapFolder(String qualifiedMailboxName, ServiceRegistry serviceRegistry) - { - this(qualifiedMailboxName, null, null, null, null, null, false, serviceRegistry); - } - /** - * Constructs {@link AlfrescoImapFolder} object. + * Constructs {@link AlfrescoImapMailFolder} object. * * @param qualifiedMailboxName - name of the mailbox (e.g. "admin" for admin user). * @param folderInfo - reference to the {@link FileInfo} object representing the folder. @@ -165,14 +161,14 @@ public class AlfrescoImapFolder extends AbstractImapFolder * @param mountPointName - name of the mount point. * @param imapHelper - reference to the {@link ImapHelper} object. */ - public AlfrescoImapFolder(String qualifiedMailboxName, FileInfo folderInfo, String folderName, String viewMode, NodeRef rootNodeRef, String mountPointName, - boolean extractAttachmentsEnabled, ServiceRegistry serviceRegistry) + public AlfrescoImapMailFolder(String qualifiedMailboxName, FileInfo folderInfo, String folderName, String viewMode, NodeRef rootNodeRef, String mountPointName, + ImapHelper imapHelper) { - this(qualifiedMailboxName, folderInfo, folderName, viewMode, rootNodeRef, mountPointName, serviceRegistry, null, extractAttachmentsEnabled); + this(qualifiedMailboxName, folderInfo, folderName, viewMode, rootNodeRef, mountPointName, imapHelper, null); } /** - * Constructs {@link AlfrescoImapFolder} object. + * Constructs {@link AlfrescoImapMailFolder} object. * * @param qualifiedMailboxName - name of the mailbox (e.g. "admin" for admin user). * @param folderInfo - reference to the {@link FileInfo} object representing the folder. @@ -180,25 +176,19 @@ public class AlfrescoImapFolder extends AbstractImapFolder * @param viewMode - defines view mode. Can be one of the following: {@link AlfrescoImapConst#MODE_ARCHIVE} or {@link AlfrescoImapConst#MODE_VIRTUAL}. * @param rootNodeRef - reference to the root node of the store where folder is placed. * @param mountPointName - name of the mount point. - * @param imapService - reference to the {@link ImapHelper} object. + * @param imapHelper - reference to the {@link ImapHelper} object. * @param selectable - defines whether the folder is selectable or not. */ - public AlfrescoImapFolder(String qualifiedMailboxName, FileInfo folderInfo, String folderName, String viewMode, NodeRef rootNodeRef, String mountPointName, - ServiceRegistry serviceRegistry, Boolean selectable, boolean extractAttachmentsEnabled) + public AlfrescoImapMailFolder(String qualifiedMailboxName, FileInfo folderInfo, String folderName, String viewMode, NodeRef rootNodeRef, String mountPointName, + ImapHelper imapHelper, Boolean selectable) { - super(serviceRegistry); this.qualifiedMailboxName = qualifiedMailboxName; this.folderInfo = folderInfo; this.rootNodeRef = rootNodeRef; + this.imapHelper = imapHelper; this.folderName = folderName != null ? folderName : (folderInfo != null ? folderInfo.getName() : null); this.viewMode = viewMode != null ? viewMode : AlfrescoImapConst.MODE_ARCHIVE; this.mountPointName = mountPointName; - this.extractAttachmentsEnabled = extractAttachmentsEnabled; - - if (serviceRegistry != null) - { - this.imapService = serviceRegistry.getImapService(); - } // MailFolder object can be null if it is used to obtain hierarchy delimiter by LIST command: // Example: @@ -210,7 +200,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder if (selectable == null) { // isSelectable(); - Boolean storedSelectable = !serviceRegistry.getNodeService().hasAspect(folderInfo.getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE); + Boolean storedSelectable = !imapHelper.getNodeService().hasAspect(folderInfo.getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE); if (storedSelectable == null) { setSelectable(true); @@ -225,7 +215,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder setSelectable(selectable); } - AccessStatus status = serviceRegistry.getPermissionService().hasPermission(folderInfo.getNodeRef(), PermissionService.WRITE); + AccessStatus status = imapHelper.hasPermission(folderInfo.getNodeRef(), PermissionService.WRITE); if (status == AccessStatus.DENIED) { readOnly = true; @@ -238,11 +228,88 @@ public class AlfrescoImapFolder extends AbstractImapFolder } else { - setSelectable(true); + setSelectable(false); } } + /** + * Adds {@link FolderListener} to the folder. + * + * @param listener - new listener. + */ + public void addListener(FolderListener listener) + { + listeners.add(listener); + + } + + protected void processTextMessage(MimeMessage message, FileInfo messageHome) throws MessagingException, ContentIOException, IOException + { + FileInfo messageBody = imapHelper.getFileFolderService().create(messageHome.getNodeRef(), AlfrescoImapConst.BODY_TEXT_PLAIN_NAME, ImapModel.TYPE_IMAP_BODY); + ContentWriter writer = imapHelper.getFileFolderService().getWriter(messageBody.getNodeRef()); + writer.setMimetype(message.getContentType()); + writer.setEncoding("UTF-8"); + writer.putContent(message.getInputStream()); + } + + protected void processTextMessage(BodyPart part, FileInfo messageHome, boolean isBody) throws MessagingException, ContentIOException, IOException + { + FileInfo messageBody = null; + ContentType ct = new ContentType(part.getContentType()); + ContentWriter writer = null; + if (isBody) + { + if ("plain".equalsIgnoreCase(ct.getSubType())) + { + messageBody = imapHelper.getFileFolderService().create(messageHome.getNodeRef(), AlfrescoImapConst.BODY_TEXT_PLAIN_NAME, ImapModel.TYPE_IMAP_BODY); + writer = imapHelper.getFileFolderService().getWriter(messageBody.getNodeRef()); + writer.setEncoding(MimeUtility.javaCharset(ct.getParameter("charset"))); + writer.setMimetype(ct.toString()); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + IOUtils.copy(part.getInputStream(), outputStream); + OutputStreamWriter outputWriter = new OutputStreamWriter(writer.getContentOutputStream()); + outputWriter.write(outputStream.toString()); + outputWriter.flush(); + outputWriter.close(); + } + else if ("html".equalsIgnoreCase(ct.getSubType())) + { + messageBody = imapHelper.getFileFolderService().create(messageHome.getNodeRef(), AlfrescoImapConst.BODY_TEXT_HTML_NAME, ImapModel.TYPE_IMAP_BODY); + writer = imapHelper.getFileFolderService().getWriter(messageBody.getNodeRef()); + writer.setMimetype(ct.toString()); + String javaCharset = MimeUtility.javaCharset(ct.getParameter("charset")); + writer.setEncoding(javaCharset); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + IOUtils.copy(part.getInputStream(), os); + OutputStreamWriter cosw = new OutputStreamWriter(writer.getContentOutputStream()); + cosw.write(os.toString()); + cosw.flush(); + cosw.close(); + } + } + else + { + saveAttachment(part, messageHome); + } + } + + protected void saveAttachment(BodyPart part, FileInfo messageHome) throws FileExistsException, MessagingException, ContentIOException, IOException + { + FileInfo messageBody = imapHelper.getFileFolderService().create(messageHome.getNodeRef(), MimeUtility.decodeText(part.getFileName()), ImapModel.TYPE_IMAP_ATTACH); + ContentWriter writer = imapHelper.getFileFolderService().getWriter(messageBody.getNodeRef()); + writer.setMimetype(part.getContentType()); + writer.setEncoding("UTF-8"); + writer.putContent(part.getInputStream()); + + String[] attachId = part.getHeader("Content-ID"); + if (attachId != null && attachId.length > 0) + { + imapHelper.getNodeService().setProperty(messageBody.getNodeRef(), ImapModel.PROP_ATTACH_ID, attachId[0]); + } + } + /** * Appends message to the folder. * @@ -250,24 +317,82 @@ public class AlfrescoImapFolder extends AbstractImapFolder * @param flags - message flags. * @param internalDate - not used. Current date used instead. * @return - * @throws MessagingException - * @throws IOException - * @throws FileNotFoundException - * @throws FileExistsException - * @throws MessagingException */ - @Override - protected long appendMessageInternal(MimeMessage message, Flags flags, Date internalDate) throws FileExistsException, FileNotFoundException, IOException, MessagingException + public long appendMessage(MimeMessage message, Flags flags, Date internalDate) throws FolderException { - AbstractMimeMessage internalMessage = createMimeMessageInFolder(this.folderInfo, message); - long newMessageUid = (Long) internalMessage.getMessageInfo().getProperties().get(ContentModel.PROP_NODE_DBID); - SimpleStoredMessage storedMessage = new SimpleStoredMessage(internalMessage, new Date(), newMessageUid); - messages.put(newMessageUid, storedMessage); - - // Saving message sequence number to cache - msnCache.put(newMessageUid, messages.size()); + if (this.readOnly) + { + throw new FolderException("Can't append message - Permission denied"); + } + + //TODO FILE EXIST + String name = AlfrescoImapConst.MESSAGE_PREFIX + GUID.generate(); + FileInfo messageHome = imapHelper.getFileFolderService().create(folderInfo.getNodeRef(), name, ImapModel.TYPE_IMAP_CONTENT); + final long newMessageUid = (Long) messageHome.getProperties().get(ContentModel.PROP_NODE_DBID); + + try + { + name = AlfrescoImapConst.MESSAGE_PREFIX + newMessageUid; + imapHelper.getFileFolderService().rename(messageHome.getNodeRef(), name); + + Object content = message.getContent(); + if (content instanceof Multipart) + { + Multipart multipart = (Multipart) content; + + for (int i = 0, n = multipart.getCount(); i < n; i++) + { + Part part = multipart.getBodyPart(i); + createMessageFiles(messageHome, (MimeBodyPart) part); + + } + } + else + { + processTextMessage(message, messageHome); + } + + imapHelper.setFlags(messageHome, flags, true); + SimpleStoredMessage storedMessage = new SimpleStoredMessage(new AlfrescoImapMessage(messageHome, imapHelper, message), new Date(), newMessageUid); + messages.put(newMessageUid, storedMessage); + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Internal error", e); + } return newMessageUid; + + } + + private void createMessageFiles(FileInfo messageHome, MimeBodyPart part) throws IOException, MessagingException + { + + Object content = part.getContent(); + + if (content instanceof MimeMultipart) + { + int count = ((MimeMultipart) content).getCount(); + for (int i = 0; i < count; i++) + { + createMessageFiles(messageHome, (MimeBodyPart) ((MimeMultipart) content).getBodyPart(i)); + } + } + else + { + + String partName = part.getFileName(); + if (partName == null) + { + processTextMessage(part, messageHome, true); + } + else + { + processTextMessage(part, messageHome, false); + } + + } + } /** @@ -275,38 +400,77 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @param uid - UID of the message * @param toFolder - reference to the destination folder. - * @throws MessagingException - * @throws IOException - * @throws FileNotFoundException - * @throws FileExistsException */ - @Override - protected void copyMessageInternal(long uid, MailFolder toFolder) throws MessagingException, FileExistsException, FileNotFoundException, IOException + public void copyMessage(long uid, MailFolder toFolder) throws FolderException { - AlfrescoImapFolder toImapMailFolder = (AlfrescoImapFolder) toFolder; + AlfrescoImapMailFolder toImapMailFolder = (AlfrescoImapMailFolder) toFolder; - NodeRef destFolderNodeRef = toImapMailFolder.getFolderInfo().getNodeRef(); + if (toImapMailFolder.isReadOnly()) + { + throw new FolderException("Can't create folder - Permission denied"); + } + + NodeRef toNodeRef = toImapMailFolder.getFolderInfo().getNodeRef(); SimpleStoredMessage message = messages.get(uid); - FileInfo sourceMessageFileInfo = ((AbstractMimeMessage) message.getMimeMessage()).getMessageInfo(); + FileInfo copyMess = ((AlfrescoImapMessage) message.getMimeMessage()).getMessageInfo(); - if (serviceRegistry.getNodeService().hasAspect(sourceMessageFileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_CONTENT)) + List fis = new LinkedList(); + + if (imapHelper.getNodeService().getType(copyMess.getNodeRef()).equals(ImapModel.TYPE_IMAP_CONTENT)) { - //Generate body of message - MimeMessage newMessage = new ImapModelMessage(sourceMessageFileInfo, serviceRegistry, true); - toImapMailFolder.appendMessageInternal(newMessage, message.getFlags(), new Date()); + + //TODO FILE EXIST + NodeRef messageFolder = imapHelper.getFileFolderService().create(toNodeRef, AlfrescoImapConst.MESSAGE_PREFIX + GUID.generate(), ImapModel.TYPE_IMAP_CONTENT).getNodeRef(); + + final long nextUid = (Long) imapHelper.getNodeService().getProperty(messageFolder, ContentModel.PROP_NODE_DBID); + + String name = AlfrescoImapConst.MESSAGE_PREFIX + nextUid; + try + { + imapHelper.getFileFolderService().rename(messageFolder, name); + } + catch (FileNotFoundException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + Map srcMesProps = imapHelper.getNodeService().getProperties(copyMess.getNodeRef()); + Map dstMessProps = imapHelper.getNodeService().getProperties(messageFolder); + srcMesProps.putAll(dstMessProps); + imapHelper.getNodeService().setProperties(messageFolder, srcMesProps); + + + fis = imapHelper.getFileFolderService().search(copyMess.getNodeRef(), "*", false); + toNodeRef = messageFolder; } else { - serviceRegistry.getFileFolderService().copy(sourceMessageFileInfo.getNodeRef(), destFolderNodeRef, null); + fis.add(copyMess); + } + for (FileInfo fi : fis) + { + try + { + imapHelper.getFileFolderService().copy(fi.getNodeRef(), toNodeRef, null); + } + catch (FileExistsException e) + { + logger.error(e); + } + catch (FileNotFoundException e) + { + logger.error(e); + } } } /** * Marks all messages in the folder as deleted using {@link Flags.Flag#DELETED} flag. */ - @Override - public void deleteAllMessagesInternal() throws FolderException + public void deleteAllMessages() throws FolderException { if (this.readOnly) { @@ -315,21 +479,19 @@ public class AlfrescoImapFolder extends AbstractImapFolder for (SimpleStoredMessage mess : messages.values()) { - AbstractMimeMessage message = (AbstractMimeMessage) mess.getMimeMessage(); + AlfrescoImapMessage message = (AlfrescoImapMessage) mess.getMimeMessage(); FileInfo fileInfo = message.getMessageInfo(); - imapService.setFlag(fileInfo, Flags.Flag.DELETED, true); + imapHelper.setFlag(fileInfo, Flags.Flag.DELETED, true); // comment out to physically remove content. // fileFolderService.delete(fileInfo.getNodeRef()); messages.remove(mess.getUid()); - msnCache.remove(mess.getUid()); } } /** * Deletes messages marked with {@link Flags.Flag#DELETED}. Note that this message deletes all messages with this flag. */ - @Override - protected void expungeInternal() throws FolderException + public void expunge() throws FolderException { if (this.readOnly) { @@ -343,8 +505,8 @@ public class AlfrescoImapFolder extends AbstractImapFolder Flags flags = getFlags(mess); if (flags.contains(Flags.Flag.DELETED)) { - NodeRef nodeRef = ((AbstractMimeMessage) mess.getMimeMessage()).getMessageInfo().getNodeRef(); - serviceRegistry.getFileFolderService().delete(nodeRef); + NodeRef nodeRef = ((AlfrescoImapMessage) mess.getMimeMessage()).getMessageInfo().getNodeRef(); + imapHelper.getFileFolderService().delete(nodeRef); } } } @@ -354,8 +516,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return Number of the first unseen message. */ - @Override - protected int getFirstUnseenInternal() + public int getFirstUnseen() { return 0; } @@ -363,11 +524,8 @@ public class AlfrescoImapFolder extends AbstractImapFolder /** * Returns full name of the folder with namespace and full path delimited with the hierarchy delimiter (see {@link AlfrescoImapConst#HIERARCHY_DELIMITER})

E.g.:

* #mail.admin."Repository_archive.Data Dictionary.Space Templates.Software Engineering Project"

This is required by GreenMail implementation. - * - * @throws FileNotFoundException */ - @Override - protected String getFullNameInternal() throws FileNotFoundException + public String getFullName() { // If MailFolder object is used to obtain hierarchy delimiter by LIST command: @@ -383,37 +541,44 @@ public class AlfrescoImapFolder extends AbstractImapFolder StringBuilder fullName = new StringBuilder(); List pathList; - pathList = serviceRegistry.getFileFolderService().getNamePath(rootNodeRef, folderInfo.getNodeRef()); - fullName.append(ImapConstants.USER_NAMESPACE).append(AlfrescoImapConst.HIERARCHY_DELIMITER).append(qualifiedMailboxName); - - boolean isFirst = true; - for (FileInfo path : pathList) + try { - fullName.append(AlfrescoImapConst.HIERARCHY_DELIMITER); - if (isFirst) + pathList = imapHelper.getFileFolderService().getNamePath(rootNodeRef, folderInfo.getNodeRef()); + fullName.append(ImapConstants.USER_NAMESPACE).append(AlfrescoImapConst.HIERARCHY_DELIMITER).append(qualifiedMailboxName); + + boolean isFirst = true; + for (FileInfo path : pathList) { - fullName.append("\""); - isFirst = false; - if (mountPointName != null) + fullName.append(AlfrescoImapConst.HIERARCHY_DELIMITER); + if (isFirst) { - fullName.append(mountPointName); + fullName.append("\""); + isFirst = false; + if (mountPointName != null) + { + fullName.append(mountPointName); + } + else + { + fullName.append(path.getName()); + } } else { fullName.append(path.getName()); } } - else - { - fullName.append(path.getName()); - } + fullName.append("\""); + } + catch (FileNotFoundException e) + { + logger.error(e); } - fullName.append("\""); if (logger.isDebugEnabled()) { logger.debug("fullName: " + fullName); } - return Utf7.encode(fullName.toString(), Utf7.UTF7_MODIFIED); + return GreenMailUtil.convertInUtf7(fullName.toString()); } /** @@ -421,14 +586,15 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @param uid - UID of the message. * @return message. - * @throws MessagingException */ - @Override - protected SimpleStoredMessage getMessageInternal(long uid) throws MessagingException + public SimpleStoredMessage getMessage(long uid) + { + if (!isBodyGenerated) { - AbstractMimeMessage mes = (AbstractMimeMessage) messages.get(uid).getMimeMessage(); - FileInfo mesInfo = mes.getMessageInfo(); - return createImapMessage(mesInfo, uid, true); + // regenerate messages list and include message body into result + getMessages(); + } + return messages.get(uid); } /** @@ -436,13 +602,12 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return Count of the messages. */ - @Override - protected int getMessageCountInternal() + public int getMessageCount() { if (messages.size() == 0) { - List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos); + List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos, false); } if (logger.isDebugEnabled()) { @@ -456,13 +621,12 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return UIDS of the messages. */ - @Override - protected long[] getMessageUidsInternal() + public long[] getMessageUids() { if (messages == null || messages.size() == 0) { - List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos); + List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos, false); } int len = messages.size(); long[] uids = new long[len]; @@ -480,32 +644,28 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return list of {@link SimpleStoredMessage} objects. */ - @Override - protected List getMessagesInternal() + public List getMessages() { - List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - return getMessages(fileInfos); + List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + return getMessages(fileInfos, true); } - private List getMessages(List fileInfos) + private List getMessages(List fileInfos, boolean generateBody) { + isBodyGenerated = generateBody; if (fileInfos == null || fileInfos.size() == 0) { - return Collections.emptyList(); + messages = Collections.emptyMap(); } - if (fileInfos.size() != messages.size()) + if (fileInfos.size() != messages.size() || generateBody) { for (FileInfo fileInfo : fileInfos) { try { Long key = getMessageUid(fileInfo); - SimpleStoredMessage message = createImapMessage(fileInfo, key, false); + SimpleStoredMessage message = new SimpleStoredMessage(new AlfrescoImapMessage(fileInfo, imapHelper, generateBody), new Date(), key); messages.put(key, message); - - // Saving message sequence number to cache - msnCache.put(key, messages.size()); - if (logger.isDebugEnabled()) { logger.debug("Message added: " + fileInfo.getName()); @@ -520,31 +680,18 @@ public class AlfrescoImapFolder extends AbstractImapFolder return new LinkedList(messages.values()); } - protected SimpleStoredMessage createImapMessage(FileInfo fileInfo, Long key, boolean generateBody) throws MessagingException - { - if (serviceRegistry.getNodeService().hasAspect(fileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_CONTENT)) - { - return new SimpleStoredMessage(new ImapModelMessage(fileInfo, serviceRegistry, generateBody), new Date(), key); - } - else - { - return new SimpleStoredMessage(new ContentModelMessage(fileInfo, serviceRegistry, generateBody), new Date(), key); - } - } - /** * Returns list of messages by filter. * * @param msgRangeFilter - {@link MsgRangeFilter} object representing filter. * @return list of filtered messages. */ - @Override - protected List getMessagesInternal(MsgRangeFilter msgRangeFilter) + public List getMessages(MsgRangeFilter msgRangeFilter) { - if (messages == null || messages.size() == 0) + if (messages == null || messages.size() == 0 || !isBodyGenerated) { - List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos); + List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos, true); } List ret = new ArrayList(); for (int i = 0; i < messages.size(); i++) @@ -565,13 +712,20 @@ public class AlfrescoImapFolder extends AbstractImapFolder * @return message sequence number. * @throws FolderException if no message with given UID. */ - @Override - protected int getMsnInternal(long uid) throws FolderException + public int getMsn(long uid) throws FolderException { - Integer msn = msnCache.get(uid); - if (msn != null) + // Um not sure in this because the getMsn is not documented... + // Implemented alike GreenMail implementation. + Set keys = messages.keySet(); + int msn = 0; + for (Long key : keys) { - return msn; + // "==" is legal with primitives and autoboxing + if (key == uid) + { + return msn + 1; + } + msn++; } throw new FolderException("No such message."); } @@ -581,8 +735,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return folder name. */ - @Override - protected String getNameInternal() + public String getName() { return folderName; } @@ -592,15 +745,14 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return the list of non-deleted messages. */ - @Override - protected List getNonDeletedMessagesInternal() + public List getNonDeletedMessages() { List result = new ArrayList(); - if (messages.size() == 0) + if (messages.size() == 0 || !isBodyGenerated) { - List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos); + List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos, true); } Collection values = messages.values(); @@ -624,8 +776,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return {@link Flags} object containing flags. */ - @Override - protected Flags getPermanentFlagsInternal() + public Flags getPermanentFlags() { return PERMANENT_FLAGS; } @@ -637,13 +788,12 @@ public class AlfrescoImapFolder extends AbstractImapFolder * @param reset - if true the {@link Flags.Flag#RECENT} will be deleted for current user if exists. * @return returns count of recent messages. */ - @Override - protected int getRecentCountInternal(boolean reset) + public int getRecentCount(boolean reset) { if (messages.size() == 0) { - List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos); + List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos, false); } int count = 0; @@ -655,7 +805,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder count++; if (reset) { - imapService.setFlag(((AbstractMimeMessage) message.getMimeMessage()).getMessageInfo(), Flags.Flag.RECENT, false); + imapHelper.setFlag(((AlfrescoImapMessage) message.getMimeMessage()).getMessageInfo(), Flags.Flag.RECENT, false); } } @@ -673,8 +823,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return UIDNEXT value. */ - @Override - protected long getUidNextInternal() + public long getUidNext() { return getUidValidity(); } @@ -684,10 +833,9 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return UIDVALIDITY value. */ - @Override - protected long getUidValidityInternal() + public long getUidValidity() { - return ((Date) serviceRegistry.getNodeService().getProperty(folderInfo.getNodeRef(), ContentModel.PROP_MODIFIED)).getTime(); + return ((Date) imapHelper.getNodeService().getProperty(folderInfo.getNodeRef(), ContentModel.PROP_MODIFIED)).getTime(); } /** @@ -695,13 +843,12 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return Count of the unseen messages for current user. */ - @Override - protected int getUnseenCountInternal() + public int getUnseenCount() { if (messages.size() == 0) { - List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos); + List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos, false); } int count = 0; @@ -721,6 +868,16 @@ public class AlfrescoImapFolder extends AbstractImapFolder return count; } + /** + * Removes {@link FolderListener} from the folder. + * + * @param listener - Listener to remove. + */ + public void removeListener(FolderListener listener) + { + listeners.remove(listener); + } + /** * Replaces flags for the message with the given UID. If {@code addUid} is set to {@code true} {@link FolderListener} objects defined for this folder will be notified. * {@code silentListener} can be provided - this listener wouldn't be notified. @@ -729,24 +886,55 @@ public class AlfrescoImapFolder extends AbstractImapFolder * @param uid - message UID. * @param silentListener - listener that shouldn't be notified. * @param addUid - defines whether or not listeners be notified. - * @throws FolderException - * @throws MessagingException */ - @Override - protected void replaceFlagsInternal(Flags flags, long uid, FolderListener silentListener, boolean addUid) throws FolderException, MessagingException + public void replaceFlags(Flags flags, long uid, FolderListener silentListener, boolean addUid) throws FolderException { int msn = getMsn(uid); SimpleStoredMessage message = messages.get(uid); - FileInfo fileInfo = ((AbstractMimeMessage) message.getMimeMessage()).getMessageInfo(); - imapService.setFlags(fileInfo, MessageFlags.ALL_FLAGS, false); - imapService.setFlags(fileInfo, flags, true); - message = new SimpleStoredMessage(message.getMimeMessage(), message.getInternalDate(), uid); - messages.put(uid, message); + FileInfo fileInfo = ((AlfrescoImapMessage) message.getMimeMessage()).getMessageInfo(); + try + { + imapHelper.setFlags(fileInfo, MessageFlags.ALL_FLAGS, false); + imapHelper.setFlags(fileInfo, flags, true); + message = new SimpleStoredMessage(message.getMimeMessage(), message.getInternalDate(), uid); + messages.put(uid, message); + } + catch (MessagingException e) + { + logger.warn("Can't set flags due to an error:", e); + } Long uidNotification = addUid ? uid : null; notifyFlagUpdate(msn, message.getFlags(), uidNotification, silentListener); } + private void notifyFlagUpdate(int msn, Flags flags, Long uidNotification, FolderListener silentListener) + { + synchronized (listeners) + { + for (FolderListener listener : listeners) + { + if (listener == silentListener) + { + continue; + } + + listener.flagsUpdated(msn, flags, uidNotification); + } + } + } + + /** + * Simply returns UIDs of all messages in the folder. + * + * @param searchTerm - not used + * @return UIDs of the messages + */ + public long[] search(SearchTerm searchTerm) + { + return getMessageUids(); + } + /** * Sets flags for the message with the given UID. If {@code addUid} is set to {@code true} {@link FolderListener} objects defined for this folder will be notified. * {@code silentListener} can be provided - this listener wouldn't be notified. @@ -756,18 +944,22 @@ public class AlfrescoImapFolder extends AbstractImapFolder * @param uid - message UID. * @param silentListener - listener that shouldn't be notified. * @param addUid - defines whether or not listeners be notified. - * @throws MessagingException - * @throws FolderException */ - @Override - protected void setFlagsInternal(Flags flags, boolean value, long uid, FolderListener silentListener, boolean addUid) throws MessagingException, FolderException + public void setFlags(Flags flags, boolean value, long uid, FolderListener silentListener, boolean addUid) throws FolderException { int msn = getMsn(uid); SimpleStoredMessage message = (SimpleStoredMessage) messages.get(uid); - imapService.setFlags(((AbstractMimeMessage) message.getMimeMessage()).getMessageInfo(), flags, value); - message = new SimpleStoredMessage(message.getMimeMessage(), message.getInternalDate(), uid); - messages.put(uid, message); + try + { + imapHelper.setFlags(((AlfrescoImapMessage) message.getMimeMessage()).getMessageInfo(), flags, value); + message = new SimpleStoredMessage(message.getMimeMessage(), message.getInternalDate(), uid); + messages.put(uid, message); + } + catch (MessagingException e) + { + logger.warn("Can't set flags due to an error:", e); + } Long uidNotification = null; if (addUid) @@ -778,23 +970,54 @@ public class AlfrescoImapFolder extends AbstractImapFolder } + /** + * Method is called before the deletion of the folder. Notifies {@link FolderListener} objects with {@link FolderListener#mailboxDeleted()} method calls. + */ + public void signalDeletion() + { + synchronized (listeners) + { + for (int i = 0; i < listeners.size(); i++) + { + FolderListener listener = (FolderListener) listeners.get(i); + listener.mailboxDeleted(); + } + } + } + + /** + * Not supported. Added to implement {@link MailFolder#store(MovingMessage)}. + */ + public void store(MovingMessage mail) throws Exception + { + throw new UnsupportedOperationException("Method store(MovingMessage) is not suppoted."); + } + + /** + * Not supported. Added to implement {@link MailFolder#store(MimeMessage)}. + */ + public void store(MimeMessage message) throws Exception + { + throw new UnsupportedOperationException("Method store(MimeMessage) is not suppoted."); + } + /** * @param fileInfo - {@link FileInfo} representing message. * @return UID of the message. */ private long getMessageUid(FileInfo fileInfo) { - if (serviceRegistry.getNodeService().getType(fileInfo.getNodeRef()).equals(ContentModel.TYPE_FOLDER)) + if (imapHelper.getNodeService().getType(fileInfo.getNodeRef()).equals(ContentModel.TYPE_FOLDER)) { - return ((Date) serviceRegistry.getNodeService().getProperty(fileInfo.getNodeRef(), ContentModel.PROP_MODIFIED)).getTime(); + return ((Date) imapHelper.getNodeService().getProperty(fileInfo.getNodeRef(), ContentModel.PROP_MODIFIED)).getTime(); } - return (Long) serviceRegistry.getNodeService().getProperty(fileInfo.getNodeRef(), ContentModel.PROP_NODE_DBID); + return (Long) imapHelper.getNodeService().getProperty(fileInfo.getNodeRef(), ContentModel.PROP_NODE_DBID); } private Flags getFlags(SimpleStoredMessage mess) { - return ((AbstractMimeMessage) mess.getMimeMessage()).getFlags(); + return ((AlfrescoImapMessage) mess.getMimeMessage()).getFlags(); } // ----------------------Getters and Setters---------------------------- @@ -829,8 +1052,7 @@ public class AlfrescoImapFolder extends AbstractImapFolder * * @return {@code boolean}. */ - @Override - protected boolean isSelectableInternal() + public boolean isSelectable() { return this.selectable; @@ -849,121 +1071,14 @@ public class AlfrescoImapFolder extends AbstractImapFolder // imapHelper.setProperties(folderInfo, properties); } + /** * Whether the folder is read-only for user. - * * @return {@code boolean} */ - @Override - protected boolean isReadOnly() + public boolean isReadOnly() { return readOnly; } - public String getViewMode() - { - return viewMode; - } - - /** - * Creates the EML message in the specified folder. - * - * @param folderFileInfo The folder to create message in. - * @param message The original MimeMessage. - * @return Wrapped AbstractMimeMessage which was created. - * @throws FileNotFoundException - * @throws FileExistsException - * @throws MessagingException - * @throws IOException - */ - private AbstractMimeMessage createMimeMessageInFolder(FileInfo folderFileInfo, MimeMessage message) throws FileExistsException, FileNotFoundException, IOException, MessagingException - { - String name = AlfrescoImapConst.MESSAGE_PREFIX + GUID.generate(); - FileFolderService fileFolderService = serviceRegistry.getFileFolderService(); - FileInfo messageFile = fileFolderService.create(folderFileInfo.getNodeRef(), name, ContentModel.TYPE_CONTENT); - final long newMessageUid = (Long) messageFile.getProperties().get(ContentModel.PROP_NODE_DBID); - name = AlfrescoImapConst.MESSAGE_PREFIX + newMessageUid + AlfrescoImapConst.EML_EXTENSION; - fileFolderService.rename(messageFile.getNodeRef(), name); - - if (extractAttachmentsEnabled) - { - extractAttachments(folderFileInfo, messageFile, message); - } - return new IncomingImapMessage(messageFile, serviceRegistry, message); - } - - private void extractAttachments(FileInfo parentFolder, FileInfo messageFile, MimeMessage originalMessage) throws IOException, MessagingException - { - NodeService nodeService = serviceRegistry.getNodeService(); - FileFolderService fileFolderService = serviceRegistry.getFileFolderService(); - - String messageName = (String)nodeService.getProperty(messageFile.getNodeRef(), ContentModel.PROP_NAME); - String attachmentsFolderName = messageName + "-attachments"; - FileInfo attachmentsFolderFileInfo = null; - Object content = originalMessage.getContent(); - if (content instanceof Multipart) - { - Multipart multipart = (Multipart) content; - - for (int i = 0, n = multipart.getCount(); i < n; i++) - { - Part part = multipart.getBodyPart(i); - if ("attachment".equalsIgnoreCase(part.getDisposition())) - { - if (attachmentsFolderFileInfo == null) - { - attachmentsFolderFileInfo = fileFolderService.create(parentFolder.getNodeRef(), attachmentsFolderName, ContentModel.TYPE_FOLDER); - serviceRegistry.getNodeService().createAssociation(messageFile.getNodeRef(), attachmentsFolderFileInfo.getNodeRef(), - ImapModel.ASSOC_IMAP_ATTACHMENTS_FOLDER); - } - createAttachment(messageFile, attachmentsFolderFileInfo, part); - } - } - } - - } - - private void createAttachment(FileInfo messageFile, FileInfo attachmentsFolderFileInfo, Part part) throws MessagingException, IOException - { - String fileName = part.getFileName(); - try - { - fileName = MimeUtility.decodeText(fileName); - } - catch (UnsupportedEncodingException e) - { - if (logger.isWarnEnabled()) - { - logger.warn("Cannot decode file name '" + fileName + "'", e); - } - } - - ContentType contentType = new ContentType(part.getContentType()); - FileFolderService fileFolderService = serviceRegistry.getFileFolderService(); - List result = fileFolderService.search(attachmentsFolderFileInfo.getNodeRef(), fileName, false); - // The one possible behaviour - /* - if (result.size() > 0) - { - for (FileInfo fi : result) - { - fileFolderService.delete(fi.getNodeRef()); - } - } - */ - // And another one behaviour which will overwrite the content of the existing file. It is performance preferable. - FileInfo attachmentFile = null; - if (result.size() == 0) - { - FileInfo createdFile = fileFolderService.create(attachmentsFolderFileInfo.getNodeRef(), fileName, ContentModel.TYPE_CONTENT); - serviceRegistry.getNodeService().createAssociation(messageFile.getNodeRef(), createdFile.getNodeRef(), ImapModel.ASSOC_IMAP_ATTACHMENT); - result.add(createdFile); - } - attachmentFile = result.get(0); - ContentWriter writer = fileFolderService.getWriter(attachmentFile.getNodeRef()); - writer.setMimetype(contentType.getBaseType()); - OutputStream os = writer.getContentOutputStream(); - FileCopyUtils.copy(part.getInputStream(), os); - } - } diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapMessage.java b/source/java/org/alfresco/repo/imap/AlfrescoImapMessage.java new file mode 100755 index 0000000000..1d2155586a --- /dev/null +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapMessage.java @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.imap; + +import static org.alfresco.repo.imap.AlfrescoImapConst.BASE_64_ENCODING; +import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_ID; +import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TRANSFER_ENCODING; +import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TYPE; +import static org.alfresco.repo.imap.AlfrescoImapConst.MIME_VERSION; +import static org.alfresco.repo.imap.AlfrescoImapConst.UTF_8; +import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_NODEREF_ID; +import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_SERVER_UID; + +import java.io.IOException; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.activation.DataHandler; +import javax.activation.DataSource; +import javax.mail.Address; +import javax.mail.Flags; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.Session; +import javax.mail.internet.AddressException; +import javax.mail.internet.ContentType; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; +import javax.mail.internet.MimeUtility; +import javax.mail.util.ByteArrayDataSource; + +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; +import org.alfresco.model.ImapModel; +import org.alfresco.repo.imap.ImapHelper.EmailBodyType; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Extended MimeMessage to represent a content stored in the Alfresco repository. + * + * @author Arseny Kovalchuk + */ +public class AlfrescoImapMessage extends MimeMessage +{ + /** Used if imapHelper.getDefaultFromAddress is not set */ + private static final String DEFAULT_EMAIL_FROM = "alfresco@alfresco.org"; + private static final String DEFAULT_EMAIL_TO = DEFAULT_EMAIL_FROM; + private static final String KOI8R_CHARSET = "koi8-r"; + + private static Log logger = LogFactory.getLog(AlfrescoImapMessage.class); + + private ImapHelper imapHelper; + private FileInfo messageInfo; + + /** + * Constructs {@link AlfrescoImapMessage} object. + * + * @param fileInfo - reference to the {@link FileInfo} object representing the message. + * @param imapHelper - reference to the {@link ImapHelper} object. + * @param generateBody - if {@code true} message body will be generated. + * + * @throws MessagingException if generation of the body fails. + */ + public AlfrescoImapMessage(FileInfo fileInfo, ImapHelper imapHelper, boolean generateBody) throws MessagingException + { + super(Session.getDefaultInstance(new Properties())); + this.messageInfo = fileInfo; + this.imapHelper = imapHelper; + if (generateBody) + { + setMessageHeaders(); + buildMessage(); + } + } + + /** + * Constructs {@link AlfrescoImapMessage} object. + * + * @param fileInfo - reference to the {@link FileInfo} object representing the message. + * @param imapHelper - reference to the {@link ImapHelper} object. + * @param message - {@link MimeMessage} + * @throws MessagingException + */ + public AlfrescoImapMessage(FileInfo fileInfo, ImapHelper imapHelper, MimeMessage message) throws MessagingException + { + super(message); + this.messageInfo = fileInfo; + this.imapHelper = imapHelper; + + setMessageHeaders(); + final NodeRef nodeRef = fileInfo.getNodeRef(); + Map props = new HashMap(); + props.put(ImapModel.PROP_MESSAGE_FROM, InternetAddress.toString(message.getFrom())); + props.put(ImapModel.PROP_MESSAGE_TO, InternetAddress.toString(message.getRecipients(RecipientType.TO))); + props.put(ImapModel.PROP_MESSAGE_CC, InternetAddress.toString(message.getRecipients(RecipientType.CC))); + + String[] subj = message.getHeader("Subject"); + if (subj.length > 0) + { + props.put(ImapModel.PROP_MESSAGE_SUBJECT, subj[0]); + imapHelper.getNodeService().setProperty(nodeRef, ContentModel.PROP_DESCRIPTION, subj[0]); + } + + Map allprops = imapHelper.getNodeService().getProperties(fileInfo.getNodeRef()); + allprops.putAll(props); + imapHelper.getNodeService().setProperties(nodeRef, allprops); + // setContent(buildMultipart(fileInfo)); - disabled for better performance. + } + + /** + * Returns message flags. + * + * @return {@link Flags} + */ + @Override + public synchronized Flags getFlags() + { + return imapHelper.getFlags(messageInfo); + } + + + /** + * Sets message flags. + * + * @param flags - {@link Flags} object. + * @param value - flags value. + */ + @Override + public synchronized void setFlags(Flags flags, boolean value) throws MessagingException + { + imapHelper.setFlags(messageInfo, flags, value); + } + + + /** + * Returns {@link FileInfo} object representing message in Alfresco. + * + * @return reference to the {@link FileInfo} object. + */ + public FileInfo getMessageInfo() + { + return messageInfo; + } + + private void setMessageHeaders() throws MessagingException + { + setHeader(MIME_VERSION, "1.0"); + // Optional headers for further implementation of multiple Alfresco server support. + setHeader(X_ALF_NODEREF_ID, messageInfo.getNodeRef().getId()); + setHeader(X_ALF_SERVER_UID, imapHelper.getAlfrescoServerUID()); + } + + /** + * This method builds MimeMessage based on either ImapModel or ContentModel type. + * + * @param fileInfo - Source file information {@link FileInfo} + * @throws MessagingException + */ + private void buildMessage() throws MessagingException + { + final NodeRef nodeRef = messageInfo.getNodeRef(); + if (ImapModel.TYPE_IMAP_CONTENT.equals(imapHelper.getNodeService().getType(nodeRef))) + { + buildImapModelMessage(); + } + else + { + buildContentModelMessage(); + } + } + + /** + * This method builds MimeMessage based on {@link ImapModel} + * + * @param fileInfo - Source file information {@link FileInfo} + * @throws MessagingException + */ + private void buildImapModelMessage() throws MessagingException + { + Map properties = messageInfo.getProperties(); + setSentDate(messageInfo.getModifiedDate()); + String prop = (String) properties.get(ImapModel.PROP_MESSAGE_FROM); + addFromInternal(prop); + prop = (String) properties.get(ImapModel.PROP_MESSAGE_TO); + + if (prop != null && prop.length() > 0) + { + addRecipients(RecipientType.TO, InternetAddress.parse(prop)); + } + else + { + addRecipients(RecipientType.TO, DEFAULT_EMAIL_TO); + } + + prop = (String) properties.get(ImapModel.PROP_MESSAGE_CC); + if (prop != null && prop.length() > 0) + { + addRecipients(RecipientType.CC, InternetAddress.parse(prop)); + } + + prop = (String) properties.get(ImapModel.PROP_MESSAGE_SUBJECT); + setSubject(prop == null ? messageInfo.getName() : prop); + + setContent(buildImapModelMultipart()); + + } + + /** + * This method builds {@link MimeMessage} based on {@link ContentModel} + * + * @param fileInfo - Source file information {@link FileInfo} + * @throws MessagingException + */ + private void buildContentModelMessage() throws MessagingException + { + Map properties = messageInfo.getProperties(); + String prop = null; + setSentDate(messageInfo.getModifiedDate()); + // Add FROM address + Address[] addressList = buildSenderFromAddress(properties); + addFrom(addressList); + // Add TO address + addressList = buildRecipientToAddress(); + addRecipients(RecipientType.TO, addressList); + prop = (String) properties.get(ContentModel.PROP_TITLE); + try + { + prop = (prop == null) ? MimeUtility.encodeText(messageInfo.getName(), KOI8R_CHARSET, null) : MimeUtility.encodeText(prop, KOI8R_CHARSET, null); + } + catch (UnsupportedEncodingException e) + { + // ignore + } + setSubject(prop); + setContent(buildContentModelMultipart()); + } + + /** + * This method builds {@link Multipart} based on {@link ContentModel} + * + * @param fileInfo - Source file information {@link FileInfo} + * @throws MessagingException + */ + private Multipart buildContentModelMultipart() throws MessagingException + { + MimeMultipart rootMultipart = new MimeMultipart("alternative"); + // Cite MOB-395: "email agent will be used to select an appropriate template" - we are not able to + // detect an email agent so we use a default template for all messages. + // See AlfrescoImapConst to see the possible templates to use. + String bodyTxt = imapHelper.getEmailBodyText(messageInfo.getNodeRef(), EmailBodyType.TEXT_PLAIN); + rootMultipart.addBodyPart(getTextBodyPart(bodyTxt, EmailBodyType.TEXT_PLAIN.getSubtype())); + String bodyHtml = imapHelper.getEmailBodyText(messageInfo.getNodeRef(), EmailBodyType.TEXT_HTML); + rootMultipart.addBodyPart(getTextBodyPart(bodyHtml, EmailBodyType.TEXT_HTML.getSubtype())); + return rootMultipart; + } + + private MimeBodyPart getTextBodyPart(String bodyText, String subtype) throws MessagingException + { + MimeBodyPart result = new MimeBodyPart(); + result.setText(bodyText, UTF_8, subtype); + result.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING); + return result; + } + + /** + * This method builds {@link Multipart} based on {@link ImapModel} + * + * @param fileInfo - Source file information {@link FileInfo} + * @throws MessagingException + */ + private Multipart buildImapModelMultipart() throws MessagingException + { + DataSource source = null; + String errorMessage = null; + + // Root multipart - multipart/mixed + MimeMultipart rootMultipart = new MimeMultipart("mixed"); + // Message body - multipart/alternative - consists of two parts: text/plain and text/html + MimeMultipart messageBody = new MimeMultipart("alternative"); + // <------------------------ text html body part ------------------------> + List bodyHtmls = imapHelper.searchFiles(messageInfo.getNodeRef(), "*.html", ImapModel.TYPE_IMAP_BODY, false); + ContentType contentType = null; + MimeBodyPart textHtmlBodyPart = null; + if (bodyHtmls != null && bodyHtmls.size() > 0) + { + textHtmlBodyPart = new MimeBodyPart(); + FileInfo bodyHtml = bodyHtmls.get(0); + contentType = new ContentType(bodyHtml.getContentData().getMimetype()); + ContentReader reader = imapHelper.getFileFolderService().getReader(bodyHtml.getNodeRef()); + try + { + source = new ByteArrayDataSource(reader.getContentInputStream(), contentType.toString()); + } + catch (IOException e) + { + logger.error(e); + errorMessage = e.getMessage(); + } + if (source != null) + { + textHtmlBodyPart.setDataHandler(new DataHandler(source)); + textHtmlBodyPart.addHeader(CONTENT_TYPE, bodyHtml.getContentData().getMimetype()); + // textHtmlBodyPart.addHeader(CONTENT_TRANSFER_ENCODING, EIGHT_BIT_ENCODING); + textHtmlBodyPart.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING); + } + else + { + textHtmlBodyPart.setText(errorMessage, UTF_8); + } + messageBody.addBodyPart(textHtmlBodyPart); + } + // + // <------------------------ text plain body part ------------------------> + List results = imapHelper.searchFiles(messageInfo.getNodeRef(), "*.txt", ImapModel.TYPE_IMAP_BODY, false); + MimeBodyPart textPlainBodyPart = null; + String text = null; + if (results != null && results.size() > 0) + { + textPlainBodyPart = new MimeBodyPart(); + FileInfo bodyTxt = results.get(0); + text = imapHelper.getFileFolderService().getReader(bodyTxt.getNodeRef()).getContentString(); + contentType = new ContentType(bodyTxt.getContentData().getMimetype()); + } + else if (textHtmlBodyPart == null) + { + text = I18NUtil.getMessage("imap.server.info.message_body_not_found"); + contentType = new ContentType(EmailBodyType.TEXT_PLAIN.getMimeType() + "; charset=UTF-8"); + } + + textPlainBodyPart.setText(text, contentType.getParameter("charset"), contentType.getSubType()); + textPlainBodyPart.addHeader(CONTENT_TYPE, contentType.toString()); + messageBody.addBodyPart(textPlainBodyPart); + // + + // Body part for multipart/alternative + MimeBodyPart messageBodyPart = new MimeBodyPart(); + messageBodyPart.setContent(messageBody); + // Add multipart/alternative into root multipart/mixed... + rootMultipart.addBodyPart(messageBodyPart); + + // Process attachments + List attaches = imapHelper.searchFiles(messageInfo.getNodeRef(), "*", ImapModel.TYPE_IMAP_ATTACH, false); + + for (FileInfo attach : attaches) + { + try + { + + errorMessage = null; + messageBodyPart = new MimeBodyPart(); + ContentReader reader = imapHelper.getFileFolderService().getReader(attach.getNodeRef()); + source = new ByteArrayDataSource(reader.getContentInputStream(), attach.getContentData().getMimetype()); + } + catch (IOException e) + { + logger.error(e); + errorMessage = e.getMessage(); + } + if (source != null) + { + String attachID = (String) imapHelper.getNodeService().getProperty(attach.getNodeRef(), ImapModel.PROP_ATTACH_ID); + if (attachID != null) + { + messageBodyPart.addHeader(CONTENT_ID, attachID); + } + StringBuilder ct = new StringBuilder(attach.getContentData().getMimetype()).append("; name=\"").append(attach.getName()).append("\""); + messageBodyPart.addHeader(CONTENT_TYPE, ct.toString()); + messageBodyPart.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING); + messageBodyPart.setDataHandler(new DataHandler(source)); + try + { + messageBodyPart.setFileName(MimeUtility.encodeText(attach.getName(), KOI8R_CHARSET, null)); + } + catch (UnsupportedEncodingException e) + { + // ignore + } + } + else + { + messageBodyPart.setText(errorMessage, UTF_8); + } + rootMultipart.addBodyPart(messageBodyPart); + } + return rootMultipart; + } + + private void addFromInternal(String addressesString) throws MessagingException + { + if (addressesString != null) + { + addFrom(InternetAddress.parse(addressesString)); + } + else + { + addFrom(new Address[] { new InternetAddress(DEFAULT_EMAIL_FROM) }); + } + } + + /** + * TODO USE CASE 2: "The To/addressee will be the first email alias found in the parent folders or a default one (TBD)". It seems to be more informative as alike + * {@code @}... + * + * @return Generated TO address {@code @} + * @throws AddressException + */ + private InternetAddress[] buildRecipientToAddress() throws AddressException + { + InternetAddress[] result = null; + String defaultEmailTo = null; + // TODO : search first email alias found in the parent folders + // if (found) defaultEmailTo = foundAlias + // else + final String escapedUserName = imapHelper.getCurrentUser().replaceAll("[/,\\,@]", "."); + final String userDomain = DEFAULT_EMAIL_TO.split("@")[1]; + defaultEmailTo = escapedUserName + "@" + userDomain; + try + { + result = InternetAddress.parse(defaultEmailTo); + } + catch (AddressException e) + { + logger.error(String.format("Wrong email address '%s'.", defaultEmailTo), e); + result = InternetAddress.parse(DEFAULT_EMAIL_TO); + } + return result; + } + + /** + * Builds the InternetAddress from the Content Author name if provided. If name not specified, it takes Content Creator name. If content creator does not exists, the default + * from address will be returned. + * + * @param contentAuthor The content author full name. + * @return Generated InternetAddress[] array. + * @throws AddressException + */ + private InternetAddress[] buildSenderFromAddress(Map properties) throws AddressException + { + // Generate FROM address (Content author) + InternetAddress[] addressList = null; + String prop = (String) properties.get(ContentModel.PROP_AUTHOR); + String defaultFromAddress = imapHelper.getDefaultFromAddress(); + defaultFromAddress = defaultFromAddress == null ? DEFAULT_EMAIL_FROM : defaultFromAddress; + try + { + + if (prop != null) + { + StringBuilder contentAuthor = new StringBuilder(); + contentAuthor.append("\"").append(prop).append("\" <").append(defaultFromAddress).append(">"); + addressList = InternetAddress.parse(contentAuthor.toString()); + } + else + { + prop = (String) properties.get(ContentModel.PROP_CREATOR); + if (prop != null) + { + StringBuilder creator = new StringBuilder(); + creator.append("\"").append(prop).append("\" <").append(defaultFromAddress).append(">"); + addressList = InternetAddress.parse(creator.toString()); + } + else + { + throw new AddressException(I18NUtil.getMessage("imap.server.error.properties_dont_exist")); + } + } + } + catch (AddressException e) + { + addressList = InternetAddress.parse(DEFAULT_EMAIL_FROM); + } + return addressList; + } + +} diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java b/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java index 49d3d2467d..1efdeb32fa 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java @@ -47,14 +47,14 @@ public class AlfrescoImapServer extends AbstractLifecycleBean private int port = 143; - private String host = "localhost"; - private ImapHostManager imapHostManager; private UserManager imapUserManager; private boolean imapServerEnabled; + private ImapHelper imapHelper; + public void setImapServerEnabled(boolean imapServerEnabled) { this.imapServerEnabled = imapServerEnabled; @@ -65,11 +65,6 @@ public class AlfrescoImapServer extends AbstractLifecycleBean this.port = port; } - public void setHost(String host) - { - this.host = host; - } - public void setImapHostManager(ImapHostManager imapHostManager) { this.imapHostManager = imapHostManager; @@ -80,9 +75,14 @@ public class AlfrescoImapServer extends AbstractLifecycleBean this.imapUserManager = imapUserManager; } + public void setImapHelper(ImapHelper imapHelper) + { + this.imapHelper = imapHelper; + } + protected void onBootstrap(ApplicationEvent event) { - if (imapServerEnabled) + if (imapServerEnabled && imapHelper.isPatchApplied()) { Managers imapManagers = new Managers() { @@ -96,11 +96,11 @@ public class AlfrescoImapServer extends AbstractLifecycleBean return imapUserManager; } }; - serverImpl = new ImapServer(new ServerSetup(port, host, ServerSetup.PROTOCOL_IMAP), imapManagers); + serverImpl = new ImapServer(new ServerSetup(port, null, ServerSetup.PROTOCOL_IMAP), imapManagers); serverImpl.startService(null); if (logger.isInfoEnabled()) { - logger.info("IMAP service started on host:port " + this.host + ":" + this.port + "."); + logger.info("IMAP service started on port " + this.port + "."); } } else diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapUser.java b/source/java/org/alfresco/repo/imap/AlfrescoImapUser.java index c86f7428b1..ed5d31aa71 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapUser.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapUser.java @@ -26,6 +26,7 @@ package org.alfresco.repo.imap; import javax.mail.internet.MimeMessage; +import com.icegreen.greenmail.imap.ImapHostManager; import com.icegreen.greenmail.mail.MovingMessage; import com.icegreen.greenmail.user.GreenMailUser; import com.icegreen.greenmail.user.UserException; @@ -41,11 +42,14 @@ public class AlfrescoImapUser implements GreenMailUser private char[] password; private String email; - public AlfrescoImapUser(String email, String login, String password) + private ImapHostManager imapHostManager; + + public AlfrescoImapUser(String email, String login, String password, ImapHostManager imapHostManager) { this.email = email; this.userName = login; this.password = password.toCharArray(); + this.imapHostManager = imapHostManager; } public void authenticate(String password) throws UserException @@ -67,12 +71,12 @@ public class AlfrescoImapUser implements GreenMailUser public void deliver(MovingMessage msg) throws UserException { - throw new UnsupportedOperationException(); + } public void deliver(MimeMessage msg) throws UserException { - throw new UnsupportedOperationException(); + } public String getEmail() diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java b/source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java index 7d1377af4c..eb938cb728 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java @@ -37,6 +37,7 @@ import org.alfresco.service.cmr.security.PersonService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import com.icegreen.greenmail.imap.ImapHostManager; import com.icegreen.greenmail.user.GreenMailUser; import com.icegreen.greenmail.user.UserException; import com.icegreen.greenmail.user.UserManager; @@ -49,6 +50,7 @@ public class AlfrescoImapUserManager extends UserManager private Log logger = LogFactory.getLog(AlfrescoImapUserManager.class); protected Map userMap = Collections.synchronizedMap(new HashMap()); + protected ImapHostManager imapHostManager; protected AuthenticationService authenticationService; protected PersonService personService; @@ -59,11 +61,17 @@ public class AlfrescoImapUserManager extends UserManager super(null); } + public AlfrescoImapUserManager(ImapHostManager imapHostManager) + { + this(); + this.imapHostManager = imapHostManager; + } + public GreenMailUser createUser(String email, String login, String password) throws UserException { // TODO: User creation/addition code should be implemented here (in the AlfrescoImapUserManager). // Following code is not need and not used in the current implementation. - GreenMailUser user = new AlfrescoImapUser(email, login, password); + GreenMailUser user = new AlfrescoImapUser(email, login, password, imapHostManager); user.create(); addUser(user); return user; @@ -121,7 +129,7 @@ public class AlfrescoImapUserManager extends UserManager NodeRef personNodeRef = personService.getPerson(userid); email = (String) nodeService.getProperty(personNodeRef, ContentModel.PROP_EMAIL); } - GreenMailUser user = new AlfrescoImapUser(email, userid, password); + GreenMailUser user = new AlfrescoImapUser(email, userid, password, imapHostManager); addUser(user); } catch (AuthenticationException ex) @@ -132,6 +140,16 @@ public class AlfrescoImapUserManager extends UserManager return true; } + public ImapHostManager getImapHostManager() + { + return this.imapHostManager; + } + + public void setImapHostManager(ImapHostManager imapHostManager) + { + this.imapHostManager = imapHostManager; + } + public void setAuthenticationService(AuthenticationService authenticationService) { this.authenticationService = authenticationService; diff --git a/source/java/org/alfresco/repo/imap/ContentModelMessage.java b/source/java/org/alfresco/repo/imap/ContentModelMessage.java deleted file mode 100755 index be0931426f..0000000000 --- a/source/java/org/alfresco/repo/imap/ContentModelMessage.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.alfresco.repo.imap; - -import static org.alfresco.repo.imap.AlfrescoImapConst.BASE_64_ENCODING; -import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TRANSFER_ENCODING; -import static org.alfresco.repo.imap.AlfrescoImapConst.UTF_8; - -import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.util.Map; - -import javax.mail.Address; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import javax.mail.internet.MimeUtility; - -import org.alfresco.model.ContentModel; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.namespace.QName; - -public class ContentModelMessage extends AbstractMimeMessage -{ - - public ContentModelMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, boolean generateBody) throws MessagingException - { - super(fileInfo, serviceRegistry, generateBody); - } - - @Override - public void buildMessageInternal() throws MessagingException - { - if (generateBody != false) - { - setMessageHeaders(); - buildContentModelMessage(); - } - } - - /** - * This method builds {@link MimeMessage} based on {@link ContentModel} - * - * @param fileInfo - Source file information {@link FileInfo} - * @throws MessagingException - */ - private void buildContentModelMessage() throws MessagingException - { - Map properties = messageFileInfo.getProperties(); - String prop = null; - setSentDate(messageFileInfo.getModifiedDate()); - // Add FROM address - Address[] addressList = buildSenderFromAddress(); - addFrom(addressList); - // Add TO address - addressList = buildRecipientToAddress(); - addRecipients(RecipientType.TO, addressList); - prop = (String) properties.get(ContentModel.PROP_TITLE); - try - { - prop = (prop == null) ? MimeUtility.encodeText(messageFileInfo.getName(), KOI8R_CHARSET, null) : MimeUtility.encodeText(prop, KOI8R_CHARSET, null); - } - catch (UnsupportedEncodingException e) - { - // ignore - } - setSubject(prop); - setContent(buildContentModelMultipart()); - } - - /** - * This method builds {@link Multipart} based on {@link ContentModel} - * - * @param fileInfo - Source file information {@link FileInfo} - * @throws MessagingException - */ - private Multipart buildContentModelMultipart() throws MessagingException - { - MimeMultipart rootMultipart = new MimeMultipart("alternative"); - // Cite MOB-395: "email agent will be used to select an appropriate template" - we are not able to - // detect an email agent so we use a default template for all messages. - // See AlfrescoImapConst to see the possible templates to use. - String bodyTxt = getEmailBodyText(EmailBodyType.TEXT_PLAIN); - rootMultipart.addBodyPart(getTextBodyPart(bodyTxt, EmailBodyType.TEXT_PLAIN.getSubtype())); - String bodyHtml = getEmailBodyText(EmailBodyType.TEXT_HTML); - rootMultipart.addBodyPart(getTextBodyPart(bodyHtml, EmailBodyType.TEXT_HTML.getSubtype())); - return rootMultipart; - } - - private MimeBodyPart getTextBodyPart(String bodyText, String subtype) throws MessagingException - { - MimeBodyPart result = new MimeBodyPart(); - result.setText(bodyText, UTF_8, subtype); - result.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING); - return result; - } - -} diff --git a/source/java/org/alfresco/repo/imap/ImapHelper.java b/source/java/org/alfresco/repo/imap/ImapHelper.java new file mode 100755 index 0000000000..7e493e825a --- /dev/null +++ b/source/java/org/alfresco/repo/imap/ImapHelper.java @@ -0,0 +1,994 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.imap; + +import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_HTML_TEMPLATE; +import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_PLAIN_TEMPLATE; +import static org.alfresco.repo.imap.AlfrescoImapConst.DICTIONARY_TEMPLATE_PREFIX; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.mail.Flags; +import javax.mail.Flags.Flag; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.model.ImapModel; +import org.alfresco.repo.admin.patch.PatchInfo; +import org.alfresco.repo.admin.patch.PatchService; +import org.alfresco.repo.imap.config.ImapConfigBean; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.template.TemplateNode; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.preference.PreferenceService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.TemplateService; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.AbstractLifecycleBean; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; + +/** + * Helper class to access repository services by IMAP components. Also contains a common helper methods to search and + * manage IMAP content and other usefull methods. Configured as {@code } in the {@code imap-server-context.xml} file. + * + * @author Dmitry Vaserin + */ +/*package*/class ImapHelper extends AbstractLifecycleBean +{ + private static Log logger = LogFactory.getLog(ImapHelper.class); + + private static String PATCH_ID = "patch.imapFolders"; + + private NodeService nodeService; + private SearchService searchService; + private FileFolderService fileFolderService; + private TemplateService templateService; + private NamespaceService namespaceService; + private PermissionService permissionService; + private DictionaryService dictionaryService; + private PreferenceService preferenceService; + private SiteService siteService; + + private ServiceRegistry serviceRegistry; + + private PatchService patchService; + + private String defaultFromAddress; + private String webApplicationContextUrl = "http://localhost:8080/alfresco"; + private String repositoryTemplatePath; + private String imapRoot; + private NodeRef spacesStoreNodeRef; + private NodeRef companyHomeNodeRef; + private NodeRef imapRootNodeRef; + + private boolean patchApplied = false; + + private final static Map qNameToFlag; + private final static Map flagToQname; + + private Map imapConfigBeans = Collections.emptyMap(); + + static + { + qNameToFlag = new HashMap(); + qNameToFlag.put(ImapModel.PROP_FLAG_ANSWERED, Flags.Flag.ANSWERED); + qNameToFlag.put(ImapModel.PROP_FLAG_DELETED, Flags.Flag.DELETED); + qNameToFlag.put(ImapModel.PROP_FLAG_DRAFT, Flags.Flag.DRAFT); + qNameToFlag.put(ImapModel.PROP_FLAG_SEEN, Flags.Flag.SEEN); + qNameToFlag.put(ImapModel.PROP_FLAG_RECENT, Flags.Flag.RECENT); + qNameToFlag.put(ImapModel.PROP_FLAG_FLAGGED, Flags.Flag.FLAGGED); + + flagToQname = new HashMap(); + flagToQname.put(Flags.Flag.ANSWERED, ImapModel.PROP_FLAG_ANSWERED); + flagToQname.put(Flags.Flag.DELETED, ImapModel.PROP_FLAG_DELETED); + flagToQname.put(Flags.Flag.DRAFT, ImapModel.PROP_FLAG_DRAFT); + flagToQname.put(Flags.Flag.SEEN, ImapModel.PROP_FLAG_SEEN); + flagToQname.put(Flags.Flag.RECENT, ImapModel.PROP_FLAG_RECENT); + flagToQname.put(Flags.Flag.FLAGGED, ImapModel.PROP_FLAG_FLAGGED); + } + + public static enum EmailBodyType + { + TEXT_PLAIN, TEXT_HTML; + + public String getSubtype() + { + return name().toLowerCase().substring(5); + } + + public String getTypeSubtype() + { + return name().toLowerCase().replaceAll("_", ""); + } + + public String getMimeType() + { + return name().toLowerCase().replaceAll("_", "/"); + } + + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // Do nothing + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Void doWork() throws Exception + { + List patches = getPatchService().getPatches(null, null); + for (PatchInfo patch : patches) + { + if (patch.getId().equals(PATCH_ID)) + { + patchApplied = true; + break; + } + } + + if (!patchApplied) + { + return null; + } + + int indexOfStoreDelim = imapRoot.indexOf(StoreRef.URI_FILLER); + + if (indexOfStoreDelim == -1) + { + throw new RuntimeException("Bad path format, " + StoreRef.URI_FILLER + " not found"); + } + + indexOfStoreDelim += StoreRef.URI_FILLER.length(); + + int indexOfPathDelim = imapRoot.indexOf("/", indexOfStoreDelim); + + if (indexOfPathDelim == -1) + { + throw new java.lang.RuntimeException("Bad path format, / not found"); + } + + String storePath = imapRoot.substring(0, indexOfPathDelim); + String rootPathInStore = imapRoot.substring(indexOfPathDelim); + + StoreRef storeRef = new StoreRef(storePath); + + if (nodeService.exists(storeRef) == false) + { + throw new RuntimeException("No store for path: " + storeRef); + } + + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + + List nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPathInStore, null, namespaceService, false); + + if (nodeRefs.size() > 1) + { + throw new RuntimeException("Multiple possible roots for : \n" + " root path: " + rootPathInStore + "\n" + " results: " + nodeRefs); + } + else if (nodeRefs.size() == 0) + { + throw new RuntimeException("No root found for : \n" + " root path: " + rootPathInStore); + } + + imapRootNodeRef = nodeRefs.get(0); + + // Get "Company Home" node reference + StoreRef store = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + ResultSet rs = searchService.query(store, SearchService.LANGUAGE_XPATH, "/app:company_home"); + try + { + if (rs.length() == 0) + { + throw new AlfrescoRuntimeException("'Company Home' space doesn't exists."); + } + companyHomeNodeRef = rs.getNodeRef(0); + } + finally + { + rs.close(); + } + + spacesStoreNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * Search for files in specified context + * + * @param contextNodeRef context folder for search + * @param namePattern name pattern for search + * @param searchType type for search + * @param includeSubFolders include SubFolders + * @return list of files with specifed type + */ + public List searchFiles(NodeRef contextNodeRef, String namePattern, QName searchType, boolean includeSubFolders) + { + return search(contextNodeRef, namePattern, searchType, true, false, includeSubFolders); + } + + /** + * Search for mailboxes in specified context + * + * @param contextNodeRef context folder for search + * @param namePattern name pattern for search + * @param includeSubFolders include SubFolders + * @param isVirtualView is folder in "Virtual" View + * @return list of mailboxes + */ + public List searchFolders(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders, boolean isVirtualView) + { + QName searchType = ContentModel.TYPE_FOLDER; + if (isVirtualView) + { + searchType = null; + } + + List result = search(contextNodeRef, namePattern, searchType, false, true, includeSubFolders); + if (isVirtualView) + { + List nonFavSites = getNonFavouriteSites(getCurrentUser()); + for (SiteInfo siteInfo : nonFavSites) + { + FileInfo nonFavSite = fileFolderService.getFileInfo(siteInfo.getNodeRef()); + List siteChilds = search(nonFavSite.getNodeRef(), namePattern, null, false, true, true); + result.removeAll(siteChilds); + result.remove(nonFavSite); + } + + } + else + { + // Remove folders from Sites + List sites = siteService.listSites(getCurrentUser()); + for (SiteInfo siteInfo : sites) + { + List siteChilds = search(siteInfo.getNodeRef(), namePattern, null, false, true, true); + result.removeAll(siteChilds); + } + + } + return result; + } + + /** + * Search for emails in specified folder depend on view mode. + * + * @param contextNodeRef context folder for search + * @param namePattern name pattern for search + * @param viewMode context folder view mode + * @param includeSubFolders includeSubFolders + * @return list of emails that context folder contains. + */ + public List searchMails(NodeRef contextNodeRef, String namePattern, String viewMode, boolean includeSubFolders) + { + + List result = new LinkedList(); + if (viewMode.equals(AlfrescoImapConst.MODE_ARCHIVE)) + { + result = search(contextNodeRef, namePattern, ImapModel.TYPE_IMAP_CONTENT, false, true, includeSubFolders); + } + else + { + if (viewMode.equals(AlfrescoImapConst.MODE_VIRTUAL)) + { + result = search(contextNodeRef, namePattern, null, true, false, includeSubFolders); + } + } + + return result; + } + + private List search(NodeRef contextNodeRef, String namePattern, QName searchType, boolean fileSearch, boolean folderSearch, boolean includeSubFolders) + { + List result = new LinkedList(); + List searchResult = fileFolderService.search(contextNodeRef, namePattern, fileSearch, folderSearch, includeSubFolders); + + if (searchType == null) + { + return searchResult; + } + + for (FileInfo fileInfo : searchResult) + { + if (nodeService.getType(fileInfo.getNodeRef()).equals(searchType)) + { + result.add(fileInfo); + } + } + + return result; + } + + /** + * Get root reference for the specified mailbox + * + * @param mailboxName mailbox name in IMAP client. + * @param userName + * @return + */ + public NodeRef getMailboxRootRef(String mailboxName, String userName) + { + String rootFolder; + int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); + if (index > 0) + { + rootFolder = mailboxName.substring(0, index); + } + else + { + rootFolder = mailboxName; + } + + Map imapConfigs = getImapConfig(); + if (imapConfigs.keySet().contains(rootFolder)) + { + Map mountPoints = getMountPoints(); + NodeRef mountRef = mountPoints.get(rootFolder); + return nodeService.getParentAssocs(mountRef).get(0).getParentRef(); + } + else + { + return getUserImapHomeRef(userName); + } + } + + /** + * @param userName user name + * @return user IMAP home reference and create it if it doesn't exist. + */ + public NodeRef getUserImapHomeRef(final String userName) + { + NodeRef userHome = fileFolderService.searchSimple(imapRootNodeRef, userName); + if (userHome == null) + { + // create user home + userHome = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public NodeRef doWork() throws Exception + { + NodeRef result = fileFolderService.create(imapRootNodeRef, userName, ContentModel.TYPE_FOLDER).getNodeRef(); + nodeService.setProperty(result, ContentModel.PROP_DESCRIPTION, userName); + // create inbox + fileFolderService.create(result, AlfrescoImapConst.INBOX_NAME, ContentModel.TYPE_FOLDER); + return result; + } + }, AuthenticationUtil.getSystemUserName()); + } + return userHome; + } + + public String getCurrentUser() + { + return AuthenticationUtil.getFullyAuthenticatedUser(); + } + + public String getUserImapHomeId(String userName) + { + return getUserImapHomeRef(userName).getId(); + } + + public NodeRef getImapRootNodeRef() + { + return imapRootNodeRef; + } + + public NodeRef getCompanyHomeNodeRef() + { + return companyHomeNodeRef; + } + + public NodeRef getSpacesStoreNodeRef() + { + return spacesStoreNodeRef; + } + + public void setImapRoot(String imapRoot) + { + this.imapRoot = imapRoot; + } + + public String getDefaultFromAddress() + { + return defaultFromAddress; + } + + public void setDefaultFromAddress(String defaultFromAddress) + { + this.defaultFromAddress = defaultFromAddress; + } + + public String getWebApplicationContextUrl() + { + return this.webApplicationContextUrl; + } + + public void setWebApplicationContextUrl(String webApplicationContextUrl) + { + this.webApplicationContextUrl = webApplicationContextUrl; + } + + public String getRepositoryTemplatePath() + { + return repositoryTemplatePath; + } + + public void setRepositoryTemplatePath(String repositoryTemplatePath) + { + this.repositoryTemplatePath = repositoryTemplatePath; + } + + /** + * Return flags that belong to the specified imap folder. + * + * @param messageInfo imap folder info. + * @return flags. + */ + public Flags getFlags(FileInfo messageInfo) + { + Flags flags = new Flags(); + checkForFlaggableAspect(messageInfo.getNodeRef()); + Map props = nodeService.getProperties(messageInfo.getNodeRef()); + + for (QName key : qNameToFlag.keySet()) + { + Boolean value = (Boolean) props.get(key); + if (value != null && value) + { + flags.add(qNameToFlag.get(key)); + } + } + // This is a multiuser flag support. Commented due new requirements + // for (QName key : qNameToFlag.keySet()) + // { + // if (key.equals(ImapModel.PROP_FLAG_DELETED)) + // { + // Boolean value = (Boolean) props.get(key); + // if (value != null && value) + // { + // flags.add(qNameToFlag.get(key)); + // } + // } + // else + // { + // String users = (String) props.get(key); + // + // if (users != null && users.indexOf(formatUserEntry(getCurrentUser())) >= 0) + // { + // flags.add(qNameToFlag.get(key)); + // } + // } + // } + + return flags; + } + + /** + * Set flags to the specified imapFolder. + * + * @param messageInfo FileInfo of imap Folder. + * @param flags flags to set. + * @param value value to set. + */ + public void setFlags(FileInfo messageInfo, Flags flags, boolean value) + { + checkForFlaggableAspect(messageInfo.getNodeRef()); + for (Flags.Flag flag : flags.getSystemFlags()) + { + setFlag(messageInfo, flag, value); + } + } + + /** + * Set flags to the specified imapFolder. + * + * @param messageInfo FileInfo of imap Folder + * @param flag flag to set. + * @param value value value to set. + */ + public void setFlag(final FileInfo messageInfo, final Flag flag, final boolean value) + { + checkForFlaggableAspect(messageInfo.getNodeRef()); + nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), value); + + // This is a multiuser flag support. Commented due new requirements + // if (flagToQname.get(flag).equals(ImapModel.PROP_FLAG_DELETED)) + // { + // nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), value); + // } + // else + // { + // AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + // { + // public Void doWork() throws Exception + // { + // + // String users = (String) nodeService.getProperty(messageInfo.getNodeRef(), flagToQname.get(flag)); + // if (value) + // { + // if (users == null) + // { + // users = ""; + // } + // users += formatUserEntry(getCurrentUser()); + // + // } + // else if (users != null) + // { + // users = users.replace(formatUserEntry(getCurrentUser()), ""); + // + // } + // nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), users); + // return null; + // } + // }, AuthenticationUtil.getSystemUserName()); + // } + + } + + /** + * Check that the given authentication has a particular permission for the given node. + * + * @param nodeRef nodeRef of the node + * @param permission permission for check + * @return the access status + */ + public AccessStatus hasPermission(NodeRef nodeRef, String permission) + { + return permissionService.hasPermission(nodeRef, permission); + } + + /** + * Change userName into following format ;userName; + * + * @param userName + * @return + */ + public String formatUserEntry(String userName) + { + return AlfrescoImapConst.USER_SEPARATOR + userName + AlfrescoImapConst.USER_SEPARATOR; + } + + /** + * This method should returns a unique identifier of Alfresco server. The possible UID may be calculated based on IP address, Server port, MAC address, Web Application context. + * This UID should be parseable into initial components. This necessary for the implementation of the following case: If the message being copied (e.g. drag-and-drop) between + * two different Alfresco accounts in the IMAP client, we must unambiguously identify from which Alfresco server this message being copied. The message itself does not contain + * content data, so we must download it from the initial server (e.g. using download content servlet) and save it into destination repository. + * + * @return String representation of unique identifier of Alfresco server + */ + public String getAlfrescoServerUID() + { + // TODO Implement as javadoc says. + return "Not-Implemented"; + } + + /** + * Map of mount points. Name of mount point == key in the map. + * + * @return Map of mount points. + */ + public Map getMountPoints() + { + Map imapConfigs = getImapConfig(); + Map mountPoints = new HashMap(); + + for (ImapConfigBean config : imapConfigs.values()) + { + // Get node reference + StoreRef store = new StoreRef(config.getStore()); + ResultSet rs = searchService.query(store, SearchService.LANGUAGE_XPATH, config.getRootPath()); + if (rs.length() == 0) + { + logger.warn("Didn't find " + config.getName()); + } + else + { + NodeRef nodeRef = rs.getNodeRef(0); + mountPoints.put(config.getName(), nodeRef); + } + rs.close(); + } + return mountPoints; + } + + public void setImapConfigBeans(ImapConfigBean[] imapConfigBeans) + { + this.imapConfigBeans = new LinkedHashMap(imapConfigBeans.length * 2); + for (ImapConfigBean bean : imapConfigBeans) + { + this.imapConfigBeans.put(bean.getName(), bean); + } + } + + /** + * Return map of imap configs. Name of config == key in the map + * + * @return map of imap configs. + */ + public Map getImapConfig() + { + return this.imapConfigBeans; + } + + /** + * Return view mode ("virtual" or "archive") for specified mailbox. + * + * @param mailboxName name of the mailbox in IMAP client. + * @return view mode of the specified mailbox. + */ + public String getViewMode(String mailboxName) + { + String rootFolder; + int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); + if (index > 0) + { + rootFolder = mailboxName.substring(0, index); + } + else + { + rootFolder = mailboxName; + } + Map imapConfigs = getImapConfig(); + if (imapConfigs.keySet().contains(rootFolder)) + { + return imapConfigs.get(rootFolder).getMode(); + } + else + { + return AlfrescoImapConst.MODE_ARCHIVE; + } + } + + /** + * Return mount point name, which was specified in imap-config.xml for the current mailbox. + * + * @param mailboxName mailbox name in IMAP client. + * @return mount point name or null. + */ + public String getMountPointName(String mailboxName) + { + String rootFolder; + int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); + if (index > 0) + { + rootFolder = mailboxName.substring(0, index); + } + else + { + rootFolder = mailboxName; + } + Map imapConfigs = getImapConfig(); + if (imapConfigs.keySet().contains(rootFolder)) + { + return rootFolder; + } + else + { + return null; + } + + } + + /** + * Convert mailpath from IMAP client representation to the alfresco representation view. (e.g. with default settings "getMailPathInRepo(Repository_virtual.Imap Home)" will + * return "Company Home.Imap Home") + * + * @param mailPath mailbox path in IMAP client + * @return mailbox path in alfresco + */ + public String getMailPathInRepo(String mailPath) + { + String rootFolder; + String remain = ""; + int index = mailPath.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); + if (index > 0) + { + rootFolder = mailPath.substring(0, index); + remain = mailPath.substring(index); + } + else + { + rootFolder = mailPath; + } + Map imapConfigs = getImapConfig(); + if (imapConfigs.keySet().contains(rootFolder)) + { + Map mountPoints = getMountPoints(); + NodeRef rootRef = mountPoints.get(rootFolder); + String rootName = nodeService.getProperty(rootRef, ContentModel.PROP_NAME).toString(); + + return rootName + remain; + } + else + { + return mailPath; + } + } + + /** + * Return list of sites, that belong to the specified user and not marked as "Imap favourite" + * + * @param userName name of user + * @return List of nonFavourite sites. + */ + public List getNonFavouriteSites(String userName) + { + List nonFavSites = new LinkedList(); + Map prefs = preferenceService.getPreferences(userName, AlfrescoImapConst.PREF_IMAP_FAVOURITE_SITES); + List sites = siteService.listSites(userName); + for (SiteInfo siteInfo : sites) + { + String key = AlfrescoImapConst.PREF_IMAP_FAVOURITE_SITES + "." + siteInfo.getShortName(); + Boolean isImapFavourite = (Boolean) prefs.get(key); + if (isImapFavourite == null || !isImapFavourite) + { + nonFavSites.add(siteInfo); + } + } + + return nonFavSites; + } + + /** + * Returns the text representing email body for ContentModel node. + * + * @param nodeRef NodeRef of the target content. + * @param type The type of the returned body. May be the one of {@link EmailBodyType}. + * @return Text representing email body for ContentModel node. + */ + public String getEmailBodyText(NodeRef nodeRef, EmailBodyType type) + { + return templateService.processTemplate(getDefaultEmailBodyTemplate(type), createEmailTemplateModel(nodeRef)); + } + + /** + * Returns default email body template. This method trying to find a template on the path in the repository first e.g. {@code "Data Dictionary > IMAP Templates >"}. This path + * should be set as the property of the "imapHelper" bean. In this case it returns {@code NodeRef.toString()} of the template. If there are no template in the repository it + * returns a default template on the classpath. + * + * @param type One of the {@link EmailBodyType}. + * @return String representing template classpath path or NodeRef.toString(). + */ + public String getDefaultEmailBodyTemplate(EmailBodyType type) + { + String result = null; + switch (type) + { + case TEXT_HTML: + result = CLASSPATH_TEXT_HTML_TEMPLATE; + break; + case TEXT_PLAIN: + result = CLASSPATH_TEXT_PLAIN_TEMPLATE; + break; + } + final StringBuilder templateName = new StringBuilder(DICTIONARY_TEMPLATE_PREFIX).append("-").append(type.getTypeSubtype()).append(".ftl"); + int indexOfStoreDelim = repositoryTemplatePath.indexOf(StoreRef.URI_FILLER); + if (indexOfStoreDelim == -1) + { + logger.error("Bad path format, " + StoreRef.URI_FILLER + " not found"); + return result; + } + indexOfStoreDelim += StoreRef.URI_FILLER.length(); + int indexOfPathDelim = repositoryTemplatePath.indexOf("/", indexOfStoreDelim); + if (indexOfPathDelim == -1) + { + logger.error("Bad path format, / not found"); + return result; + } + final String storePath = repositoryTemplatePath.substring(0, indexOfPathDelim); + final String rootPathInStore = repositoryTemplatePath.substring(indexOfPathDelim); + final String query = String.format("+PATH:\"%1$s/*\" +@cm\\:name:\"%2$s\"", rootPathInStore, templateName.toString()); + if (logger.isDebugEnabled()) + { + logger.debug("Using template path :" + repositoryTemplatePath + "/" + templateName); + logger.debug("Query: " + query); + } + StoreRef storeRef = new StoreRef(storePath); + ResultSet resultSet = searchService.query(storeRef, "lucene", query); + if (resultSet == null || resultSet.length() == 0) + { + logger.error(String.format("IMAP message template '%1$s' does not exist in the path '%2$s'.", templateName, repositoryTemplatePath)); + return result; + } + result = resultSet.getNodeRef(0).toString(); + return result; + } + + /** + * Builds default email template model for TemplateProcessor + * + * @param ref NodeRef of the target content. + * @return Map that includes template model objects. + */ + private Map createEmailTemplateModel(NodeRef ref) + { + Map model = new HashMap(8, 1.0f); + TemplateNode tn = new TemplateNode(ref, serviceRegistry, null); + model.put("document", tn); + NodeRef parent = nodeService.getPrimaryParent(ref).getParentRef(); + model.put("space", new TemplateNode(parent, serviceRegistry, null)); + model.put("date", new Date()); + model.put("contextUrl", new String(getWebApplicationContextUrl())); + model.put("alfTicket", new String(serviceRegistry.getAuthenticationService().getCurrentTicket())); + return model; + } + + private void checkForFlaggableAspect(NodeRef nodeRef) + { + if (!nodeService.hasAspect(nodeRef, ImapModel.ASPECT_FLAGGABLE)) + { + Map aspectProperties = new HashMap(); + nodeService.addAspect(nodeRef, ImapModel.ASPECT_FLAGGABLE, aspectProperties); + } + } + + public boolean isPatchApplied() + { + return patchApplied; + } + + // ----------------------Getters and Setters---------------------------- + + public NodeService getNodeService() + { + return nodeService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public SearchService getSearchService() + { + return searchService; + } + + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + public FileFolderService getFileFolderService() + { + return fileFolderService; + } + + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + public TemplateService getTemplateService() + { + return templateService; + } + + public void setTemplateService(TemplateService templateService) + { + this.templateService = templateService; + } + + public NamespaceService getNamespaceService() + { + return namespaceService; + } + + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public PermissionService getPermissionService() + { + return permissionService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + public DictionaryService getDictionaryService() + { + return dictionaryService; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public PreferenceService getPreferenceService() + { + return preferenceService; + } + + public void setPreferenceService(PreferenceService preferenceService) + { + this.preferenceService = preferenceService; + } + + public SiteService getSiteService() + { + return siteService; + } + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + public ServiceRegistry getServiceRegistry() + { + return serviceRegistry; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + public PatchService getPatchService() + { + return patchService; + } + + public void setPatchService(PatchService patchService) + { + this.patchService = patchService; + } + +} diff --git a/source/java/org/alfresco/repo/imap/ImapModelMessage.java b/source/java/org/alfresco/repo/imap/ImapModelMessage.java deleted file mode 100755 index c4a56d5e03..0000000000 --- a/source/java/org/alfresco/repo/imap/ImapModelMessage.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import java.io.IOException; -import java.io.InputStream; - -import javax.mail.MessagingException; -import javax.mail.util.SharedByteArrayInputStream; - -import org.alfresco.model.ContentModel; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.ContentIOException; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentService; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Extended MimeMessage to represent a content stored in the Alfresco repository. - * - * @author Arseny Kovalchuk - */ -public class ImapModelMessage extends AbstractMimeMessage -{ - private static Log logger = LogFactory.getLog(ImapModelMessage.class); - - /** - * Constructs {@link ImapModelMessage} object. - * - * @param fileInfo - reference to the {@link FileInfo} object representing the message. - * @param imapHelper - reference to the {@link ImapHelper} object. - * @param generateBody - if {@code true} message body will be generated. - * - * @throws MessagingException if generation of the body fails. - */ - public ImapModelMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, boolean generateBody) throws MessagingException - { - super(fileInfo, serviceRegistry, generateBody); - } - - @Override - public void buildMessageInternal() throws MessagingException - { - if (generateBody != false) - { - setMessageHeaders(); - buildImapMessage(); - } - } - - /** - * This method builds MimeMessage based on either ImapModel or ContentModel type. - * - * @param fileInfo - Source file information {@link FileInfo} - * @throws MessagingException - */ - private void buildImapMessage() throws MessagingException - { - modified = false; - saved = false; - buildRFC822Message(); - saved = true; - } - - private void buildRFC822Message() throws MessagingException - { - ContentService contentService = serviceRegistry.getContentService(); - ContentReader reader = contentService.getReader(messageFileInfo.getNodeRef(), ContentModel.PROP_CONTENT); - try - { - InputStream is = reader.getContentInputStream(); - this.parse(is); - is.close(); - is = null; - } - catch (ContentIOException e) - { - //logger.error(e); - throw new MessagingException("The error occured during message creation from content stream.", e); - } - catch (IOException e) - { - //logger.error(e); - throw new MessagingException("The error occured during message creation from content stream.", e); - } - } - - @Override - protected InputStream getContentStream() throws MessagingException - { - try - { - if (this.contentStream == null) - { - if (content != null) - { - return new SharedByteArrayInputStream(content); - } - else - { - throw new MessagingException("No content"); - } - } - return this.contentStream; - } - catch (Exception e) - { - throw new MessagingException(e.getMessage(),e); - } - } - - /* - protected void parse(InputStream inputstream) throws MessagingException - { - headers = createInternetHeaders(inputstream); - contentStream = inputstream; - } - */ - -} diff --git a/source/java/org/alfresco/repo/imap/ImapService.java b/source/java/org/alfresco/repo/imap/ImapService.java deleted file mode 100755 index 60113841f7..0000000000 --- a/source/java/org/alfresco/repo/imap/ImapService.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import java.util.List; - -import javax.mail.Flags; -import javax.mail.Flags.Flag; - -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.NodeRef; - - -/** - * @author Arseny Kovalchuk - */ -public interface ImapService -{ - public List listMailboxes(AlfrescoImapUser user, String mailboxPattern); - - public List listSubscribedMailboxes(AlfrescoImapUser user, String mailboxPattern); - - public AlfrescoImapFolder createMailbox(AlfrescoImapUser user, String mailboxName); - - public void deleteMailbox(AlfrescoImapUser user, String mailboxName); - - public void renameMailbox(AlfrescoImapUser user, String oldMailboxName, String newMailboxName); - - public AlfrescoImapFolder getFolder(AlfrescoImapUser user, String mailboxName); - - public void subscribe(AlfrescoImapUser user, String mailbox); - - public void unsubscribe(AlfrescoImapUser user, String mailbox); - - public List searchFiles(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders); - - public List searchFolders(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders, String viewMode); - - public List searchMails(NodeRef contextNodeRef, String namePattern, String viewMode, boolean includeSubFolders); - - public Flags getFlags(FileInfo messageFileInfo); - - public void setFlags(FileInfo messageFileInfo, Flags flags, boolean value); - - public void setFlag(FileInfo messageFileInfo, Flag flag, boolean value); - - public String getDefaultFromAddress(); - - public String getRepositoryTemplatePath(); - - public String getWebApplicationContextUrl(); - -} diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java deleted file mode 100755 index 697705df35..0000000000 --- a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java +++ /dev/null @@ -1,1284 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import java.io.Serializable; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.mail.Flags; -import javax.mail.Flags.Flag; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.i18n.I18NUtil; -import org.alfresco.model.ContentModel; -import org.alfresco.model.ImapModel; -import org.alfresco.repo.imap.config.ImapConfigBean; -import org.alfresco.repo.imap.config.ImapConfigMountPointsBean; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.preference.PreferenceService; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AccessStatus; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.site.SiteInfo; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.Utf7; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - - -/** - * @author Dmitry Vaserin - * @author Arseny Kovalchuk - */ -public class ImapServiceImpl implements ImapService -{ - - - private Log logger = LogFactory.getLog(ImapServiceImpl.class); - - private static final String ERROR_PERMISSION_DENIED = "imap.server.error.permission_denied"; - private static final String ERROR_FOLDER_ALREADY_EXISTS = "imap.server.error.folder_already_exist"; - private static final String ERROR_MAILBOX_NAME_IS_MANDATORY = "imap.server.error.mailbox_name_is_mandatory"; - private static final String ERROR_CANNOT_GET_A_FOLDER = "imap.server.error.cannot_get_a_folder"; - - private FileFolderService fileFolderService; - private NodeService nodeService; - private ServiceRegistry serviceRegistry; - - private Map imapConfigMountPointsBeans = Collections.emptyMap(); - private Set ignoreExtractionFolders = Collections.emptySet(); - - private String imapRoot; - private String defaultFromAddress; - private String webApplicationContextUrl = "http://localhost:8080/alfresco"; - private String repositoryTemplatePath; - private boolean extractAttachmentsEnabled = true; - - private NodeRef imapRootNodeRef; - - private final static Map qNameToFlag; - private final static Map flagToQname; - - static - { - qNameToFlag = new HashMap(); - qNameToFlag.put(ImapModel.PROP_FLAG_ANSWERED, Flags.Flag.ANSWERED); - qNameToFlag.put(ImapModel.PROP_FLAG_DELETED, Flags.Flag.DELETED); - qNameToFlag.put(ImapModel.PROP_FLAG_DRAFT, Flags.Flag.DRAFT); - qNameToFlag.put(ImapModel.PROP_FLAG_SEEN, Flags.Flag.SEEN); - qNameToFlag.put(ImapModel.PROP_FLAG_RECENT, Flags.Flag.RECENT); - qNameToFlag.put(ImapModel.PROP_FLAG_FLAGGED, Flags.Flag.FLAGGED); - - flagToQname = new HashMap(); - flagToQname.put(Flags.Flag.ANSWERED, ImapModel.PROP_FLAG_ANSWERED); - flagToQname.put(Flags.Flag.DELETED, ImapModel.PROP_FLAG_DELETED); - flagToQname.put(Flags.Flag.DRAFT, ImapModel.PROP_FLAG_DRAFT); - flagToQname.put(Flags.Flag.SEEN, ImapModel.PROP_FLAG_SEEN); - flagToQname.put(Flags.Flag.RECENT, ImapModel.PROP_FLAG_RECENT); - flagToQname.put(Flags.Flag.FLAGGED, ImapModel.PROP_FLAG_FLAGGED); - } - - - public FileFolderService getFileFolderService() - { - return fileFolderService; - } - - public void setFileFolderService(FileFolderService fileFolderService) - { - this.fileFolderService = fileFolderService; - } - - public NodeService getNodeService() - { - return nodeService; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public ServiceRegistry getServiceRegistry() - { - return serviceRegistry; - } - - public void setServiceRegistry(ServiceRegistry serviceRegistry) - { - this.serviceRegistry = serviceRegistry; - } - - public String getImapRoot() - { - return imapRoot; - } - - public void setImapRoot(String imapRoot) - { - this.imapRoot = imapRoot; - } - - public String getDefaultFromAddress() - { - return defaultFromAddress; - } - - public void setDefaultFromAddress(String defaultFromAddress) - { - this.defaultFromAddress = defaultFromAddress; - } - - public String getWebApplicationContextUrl() - { - return webApplicationContextUrl; - } - - public void setWebApplicationContextUrl(String webApplicationContextUrl) - { - this.webApplicationContextUrl = webApplicationContextUrl; - } - - public String getRepositoryTemplatePath() - { - return repositoryTemplatePath; - } - - public void setRepositoryTemplatePath(String repositoryTemplatePath) - { - this.repositoryTemplatePath = repositoryTemplatePath; - } - - public void setImapConfigMountPointsBeans(ImapConfigMountPointsBean[] imapConfigMountPointsBeans) - { - this.imapConfigMountPointsBeans = new LinkedHashMap(imapConfigMountPointsBeans.length * 2); - for (ImapConfigMountPointsBean bean : imapConfigMountPointsBeans) - { - this.imapConfigMountPointsBeans.put(bean.getName(), bean); - } - } - - /** - * Return map of imap configs. Name of config == key in the map - * - * @return map of imap configs. - */ - public Map getImapConfigMountPoints() - { - return this.imapConfigMountPointsBeans; - } - - public void setIgnoreExtractionFolders(final ImapConfigBean[] ignoreExtractionFolders) - { - this.ignoreExtractionFolders = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() - { - public Set doWork() throws Exception - { - Set result = new HashSet(ignoreExtractionFolders.length * 2); - - for (ImapConfigBean bean : ignoreExtractionFolders) - { - StoreRef storeRef = new StoreRef(bean.getStore()); - - if (nodeService.exists(storeRef) == false) - { - throw new RuntimeException("No store for path: " + storeRef); - } - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - - NamespaceService namespaceService = serviceRegistry.getNamespaceService(); - String rootPathInStore = bean.getRootPath(); - - List nodeRefs = serviceRegistry.getSearchService().selectNodes(storeRootNodeRef, rootPathInStore, null, namespaceService, false); - - if (nodeRefs.size() > 1) - { - throw new RuntimeException("Multiple possible roots for : \n" + " root path: " + rootPathInStore + "\n" + " results: " + nodeRefs); - } - else if (nodeRefs.size() == 0) - { - throw new RuntimeException("No root found for : \n" + " root path: " + rootPathInStore); - } - - result.add(nodeRefs.get(0)); - } - - return result; - } - }, AuthenticationUtil.getSystemUserName()); - } - - public boolean getExtractAttachmentsEnabled() - { - return extractAttachmentsEnabled; - } - - public void setExtractAttachmentsEnabled(boolean extractAttachmentsEnabled) - { - this.extractAttachmentsEnabled = extractAttachmentsEnabled; - } - - // ---------------------- Service Methods -------------------------------- - - public void init() - { - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public Void doWork() throws Exception - { - int indexOfStoreDelim = imapRoot.indexOf(StoreRef.URI_FILLER); - - if (indexOfStoreDelim == -1) - { - throw new RuntimeException("Bad path format, " + StoreRef.URI_FILLER + " not found"); - } - - indexOfStoreDelim += StoreRef.URI_FILLER.length(); - - int indexOfPathDelim = imapRoot.indexOf("/", indexOfStoreDelim); - - if (indexOfPathDelim == -1) - { - throw new RuntimeException("Bad path format, / not found"); - } - - String storePath = imapRoot.substring(0, indexOfPathDelim); - String rootPathInStore = imapRoot.substring(indexOfPathDelim); - - StoreRef storeRef = new StoreRef(storePath); - - if (nodeService.exists(storeRef) == false) - { - throw new RuntimeException("No store for path: " + storeRef); - } - - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - - SearchService searchService = serviceRegistry.getSearchService(); - NamespaceService namespaceService = serviceRegistry.getNamespaceService(); - - List nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPathInStore, null, namespaceService, false); - - if (nodeRefs.size() > 1) - { - throw new RuntimeException("Multiple possible roots for : \n" + " root path: " + rootPathInStore + "\n" + " results: " + nodeRefs); - } - else if (nodeRefs.size() == 0) - { - throw new RuntimeException("No root found for : \n" + " root path: " + rootPathInStore); - } - - imapRootNodeRef = nodeRefs.get(0); - return null; - } - }, AuthenticationUtil.getSystemUserName()); - } - - public List listSubscribedMailboxes(AlfrescoImapUser user, String mailboxPattern) - { - mailboxPattern = Utf7.decode(mailboxPattern, Utf7.UTF7_MODIFIED); - - if (logger.isDebugEnabled()) - { - logger.debug("Listing subscribed mailboxes: mailboxPattern=" + mailboxPattern); - } - mailboxPattern = getMailPathInRepo(mailboxPattern); - if (logger.isDebugEnabled()) - { - logger.debug("Listing subscribed mailboxes: mailboxPattern in alfresco=" + mailboxPattern); - } - return listMailboxes(user, mailboxPattern, true); - } - - public List listMailboxes(AlfrescoImapUser user, String mailboxPattern) - { - mailboxPattern = Utf7.decode(mailboxPattern, Utf7.UTF7_MODIFIED); - - if (logger.isDebugEnabled()) - { - logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern); - } - mailboxPattern = getMailPathInRepo(mailboxPattern); - if (logger.isDebugEnabled()) - { - logger.debug("Listing mailboxes: mailboxPattern in alfresco=" + mailboxPattern); - } - - return listMailboxes(user, mailboxPattern, false); - } - - public AlfrescoImapFolder createMailbox(AlfrescoImapUser user, String mailboxName) - { - if (mailboxName == null) - { - throw new IllegalArgumentException(I18NUtil.getMessage(ERROR_MAILBOX_NAME_IS_MANDATORY)); - } - mailboxName = Utf7.decode(mailboxName, Utf7.UTF7_MODIFIED); - if (logger.isDebugEnabled()) - { - logger.debug("Creating folder: " + mailboxName); - } - NodeRef root = getMailboxRootRef(mailboxName, user.getLogin()); - NodeRef parentNodeRef = root; // it is used for hierarhy deep search. - for (String folderName : getMailPathInRepo(mailboxName).split(String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER))) - { - List folders = searchFolders(parentNodeRef, folderName, false, AlfrescoImapConst.MODE_MIXED); - if (logger.isDebugEnabled()) - { - logger.debug("Trying to create folder '" + folderName + "'"); - } - if (folders.size() == 0) - { - // folder doesn't exist - AccessStatus status = serviceRegistry.getPermissionService().hasPermission(parentNodeRef, PermissionService.CREATE_CHILDREN); - if (status == AccessStatus.DENIED) - { - throw new AlfrescoRuntimeException(ERROR_PERMISSION_DENIED); - } - FileInfo mailFolder = serviceRegistry.getFileFolderService().create(parentNodeRef, folderName, ContentModel.TYPE_FOLDER); - return new AlfrescoImapFolder(user.getQualifiedMailboxName(), - mailFolder, - folderName, - getViewMode(mailboxName), - root, - getMountPointName(mailboxName), - isExtractionEnabled(mailFolder.getNodeRef()), - serviceRegistry); - } - else - { - // folder already exists - if (logger.isDebugEnabled()) - { - logger.debug("Folder '" + folderName + "' already exists"); - } - // next search from new parent - parentNodeRef = folders.get(0).getNodeRef(); - } - } - throw new AlfrescoRuntimeException(ERROR_FOLDER_ALREADY_EXISTS); - } - - public void deleteMailbox(AlfrescoImapUser user, String mailboxName) - { - if (mailboxName == null) - { - throw new IllegalArgumentException(I18NUtil.getMessage(ERROR_MAILBOX_NAME_IS_MANDATORY)); - } - if (logger.isDebugEnabled()) - { - logger.debug("Deleting folder: mailboxName=" + mailboxName); - } - - AlfrescoImapFolder folder = getFolder(user, mailboxName); - NodeRef nodeRef = folder.getFolderInfo().getNodeRef(); - - List childFolders = searchFolders(nodeRef, "*", false, folder.getViewMode()); - - if (childFolders.isEmpty()) - { - folder.signalDeletion(); - // Delete child folders and messages - fileFolderService.delete(nodeRef); - } - else - { - if (folder.isSelectable()) - { - // Delete all messages for this folder - // Don't delete subfolders and their messages - List messages = searchFiles(nodeRef, "*", false); - for (FileInfo message : messages) - { - fileFolderService.delete(message.getNodeRef()); - } - nodeService.addAspect(nodeRef, ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE, null); - } - else - { - throw new AlfrescoRuntimeException(mailboxName + " - Can't delete a non-selectable store with children."); - } - } - } - - public void renameMailbox(AlfrescoImapUser user, String oldMailboxName, String newMailboxName) - { - if (oldMailboxName == null || newMailboxName == null) - { - throw new IllegalArgumentException(ERROR_MAILBOX_NAME_IS_MANDATORY); - } - oldMailboxName = Utf7.decode(oldMailboxName, Utf7.UTF7_MODIFIED); - newMailboxName = Utf7.decode(newMailboxName, Utf7.UTF7_MODIFIED); - if (logger.isDebugEnabled()) - { - logger.debug("Renaming folder oldMailboxName=" + oldMailboxName + " newMailboxName=" + newMailboxName); - } - - AlfrescoImapFolder sourceNode = getFolder(user, oldMailboxName); - - NodeRef root = getMailboxRootRef(oldMailboxName, user.getLogin()); - String[] folderNames = getMailPathInRepo(newMailboxName).split(String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER)); - String folderName = null; - NodeRef parentNodeRef = root; // initial root for search - try - { - for (int i=0; i < folderNames.length; i++) - { - folderName = folderNames[i]; - if (i == (folderNames.length - 1)) // is it the last element - { - if (oldMailboxName.equalsIgnoreCase(AlfrescoImapConst.INBOX_NAME)) - { - // If you trying to rename INBOX - // - just copy it to another folder with new name - // and leave INBOX (with children) intact. - fileFolderService.copy(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName); - } - else - { - fileFolderService.move(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName); - } - } - else // not last element than checks if it exists and creates if doesn't - { - List folders = searchFolders(parentNodeRef, folderName, false, sourceNode.getViewMode()); - if (folders.size() == 0) - { - // check creation permission - AccessStatus status = serviceRegistry.getPermissionService().hasPermission(parentNodeRef, PermissionService.CREATE_CHILDREN); - if (status == AccessStatus.DENIED) - { - throw new AlfrescoRuntimeException(ERROR_PERMISSION_DENIED); - } - - if (logger.isDebugEnabled()) - { - logger.debug("Creating folder '" + folderName + "'"); - } - serviceRegistry.getFileFolderService().create(parentNodeRef, folderName, ContentModel.TYPE_FOLDER); - } - else - { - parentNodeRef = folders.get(0).getNodeRef(); - if (logger.isDebugEnabled()) - { - logger.debug("Folder '" + folderName + "' already exists"); - } - } - } - } - } - catch (Exception e) - { - if (e instanceof AlfrescoRuntimeException) - { - throw (AlfrescoRuntimeException)e; - } - else - { - throw new AlfrescoRuntimeException(e.getMessage(), e); - } - } - } - - public AlfrescoImapFolder getFolder(AlfrescoImapUser user, String mailboxName) - { - mailboxName = Utf7.decode(mailboxName, Utf7.UTF7_MODIFIED); - if (logger.isDebugEnabled()) - { - logger.debug("Getting folder '" + mailboxName + "'"); - } - // If MailFolder object is used to obtain hierarchy delimiter by LIST command: - // Example: - // C: 2 list "" "" - // S: * LIST () "." "" - // S: 2 OK LIST completed. - if ("".equals(mailboxName)) - { - if (logger.isDebugEnabled()) - { - logger.debug("Request for the hierarchy delimiter"); - } - return new AlfrescoImapFolder(user.getQualifiedMailboxName(), serviceRegistry); - } - - NodeRef root = getMailboxRootRef(mailboxName, user.getLogin()); - String mountPointName = getMountPointName(mailboxName); - NodeRef nodeRef = root; // initial top folder - String viewMode = getViewMode(mailboxName); - - String[] folderNames = getMailPathInRepo(mailboxName).split(String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER)); - - for (int i = 0; i < folderNames.length; i++) - { - if (logger.isDebugEnabled()) - { - logger.debug("Processing of " + folderNames[i]); - } - List folderList = searchFolders(nodeRef, folderNames[i], false, viewMode); - if (folderList.isEmpty()) - { - return new AlfrescoImapFolder(user.getQualifiedMailboxName(), serviceRegistry); - } - FileInfo folderFileInfo = folderList.get(0); // we need the only one - if (i == (folderNames.length - 1)) // is last - { - return new AlfrescoImapFolder(user.getQualifiedMailboxName(), - folderFileInfo, - folderFileInfo.getName(), - viewMode, - root, - mountPointName, - isExtractionEnabled(folderFileInfo.getNodeRef()), - serviceRegistry); - } - else - { - nodeRef = folderFileInfo.getNodeRef(); // next parent - } - } - - throw new AlfrescoRuntimeException(ERROR_CANNOT_GET_A_FOLDER, new String[]{mailboxName}); - } - - /** - * Search for mailboxes in specified context - * - * @param contextNodeRef context folder for search - * @param namePattern name pattern for search - * @param includeSubFolders include SubFolders - * @param isVirtualView is folder in "Virtual" View - * @return list of mailboxes - */ - public List searchFolders(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders, String viewMode) - { - List result = fileFolderService.search(contextNodeRef, namePattern, false, true, includeSubFolders); - if (viewMode.equals(AlfrescoImapConst.MODE_VIRTUAL) || viewMode.equals(AlfrescoImapConst.MODE_MIXED)) - { - List nonFavSites = getNonFavouriteSites(getCurrentUser()); - for (SiteInfo siteInfo : nonFavSites) - { - FileInfo nonFavSite = fileFolderService.getFileInfo(siteInfo.getNodeRef()); - List siteChilds = fileFolderService.search(nonFavSite.getNodeRef(), namePattern, false, true, true); - result.removeAll(siteChilds); - result.remove(nonFavSite); - } - - } - else - { - // Remove folders from Sites - List sites = serviceRegistry.getSiteService().listSites(getCurrentUser()); - for (SiteInfo siteInfo : sites) - { - List siteChilds = fileFolderService.search(siteInfo.getNodeRef(), namePattern, false, true, true); - result.removeAll(siteChilds); - //remove site - result.remove(fileFolderService.getFileInfo(siteInfo.getNodeRef())); - } - - } - return result; - } - - /** - * Search for files in specified context - * - * @param contextNodeRef context folder for search - * @param namePattern name pattern for search - * @param searchType type for search - * @param includeSubFolders include SubFolders - * @return list of files with specifed type - */ - public List searchFiles(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders) - { - return fileFolderService.search(contextNodeRef, namePattern, true, false, includeSubFolders); - } - - /** - * Search for emails in specified folder depend on view mode. - * - * @param contextNodeRef context folder for search - * @param namePattern name pattern for search - * @param viewMode context folder view mode - * @param includeSubFolders includeSubFolders - * @return list of emails that context folder contains. - */ - public List searchMails(NodeRef contextNodeRef, String namePattern, String viewMode, boolean includeSubFolders) - { - - List result = new LinkedList(); - List searchResult = fileFolderService.search(contextNodeRef, namePattern, true, false, includeSubFolders); - if (viewMode.equals(AlfrescoImapConst.MODE_MIXED)) - { - return searchResult; - } - else if (viewMode.equals(AlfrescoImapConst.MODE_ARCHIVE)) - { - for (FileInfo fileInfo : searchResult) - { - if (nodeService.hasAspect(fileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_CONTENT)) - { - result.add(fileInfo); - } - - } - } - else if (viewMode.equals(AlfrescoImapConst.MODE_VIRTUAL)) - { - for (FileInfo fileInfo : searchResult) - { - if (!nodeService.hasAspect(fileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_CONTENT)) - { - result.add(fileInfo); - } - - } - - } - - return result; - } - - public void subscribe(AlfrescoImapUser user, String mailbox) - { - if (logger.isDebugEnabled()) - { - logger.debug("Subscribing: " + mailbox); - } - AlfrescoImapFolder mailFolder = getFolder(user, mailbox); - nodeService.removeAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_NONSUBSCRIBED); - } - - public void unsubscribe(AlfrescoImapUser user, String mailbox) - { - if (logger.isDebugEnabled()) - { - logger.debug("Unsubscribing: " + mailbox); - } - AlfrescoImapFolder mailFolder = getFolder(user, mailbox); - nodeService.addAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_NONSUBSCRIBED, null); - } - - /** - * Return flags that belong to the specified imap folder. - * - * @param messageInfo imap folder info. - * @return flags. - */ - public synchronized Flags getFlags(FileInfo messageInfo) - { - Flags flags = new Flags(); - checkForFlaggableAspect(messageInfo.getNodeRef()); - Map props = nodeService.getProperties(messageInfo.getNodeRef()); - - for (QName key : qNameToFlag.keySet()) - { - Boolean value = (Boolean) props.get(key); - if (value != null && value) - { - flags.add(qNameToFlag.get(key)); - } - } - return flags; - } - - /** - * Set flags to the specified imapFolder. - * - * @param messageInfo FileInfo of imap Folder. - * @param flags flags to set. - * @param value value to set. - */ - public synchronized void setFlags(FileInfo messageInfo, Flags flags, boolean value) - { - checkForFlaggableAspect(messageInfo.getNodeRef()); - for (Flags.Flag flag : flags.getSystemFlags()) - { - setFlag(messageInfo, flag, value); - } - } - - /** - * Set flags to the specified imapFolder. - * - * @param messageInfo FileInfo of imap Folder - * @param flag flag to set. - * @param value value value to set. - */ - public void setFlag(FileInfo messageInfo, Flag flag, boolean value) - { - checkForFlaggableAspect(messageInfo.getNodeRef()); - nodeService.setProperty(messageInfo.getNodeRef(), flagToQname.get(flag), value); - } - - /** - * Depend on listSubscribed param, list Mailboxes or list subscribed Mailboxes - */ - private List listMailboxes(AlfrescoImapUser user, String mailboxPattern, boolean listSubscribed) - { - List result = new LinkedList(); - - Map mountPoints = getMountPoints(); - Map imapConfigs = getImapConfigMountPoints(); - - NodeRef mountPoint; - - // List mailboxes that are in mount points - for (String mountPointName : mountPoints.keySet()) - { - - mountPoint = mountPoints.get(mountPointName); - FileInfo mountPointFileInfo = fileFolderService.getFileInfo(mountPoint); - NodeRef mountParent = nodeService.getParentAssocs(mountPoint).get(0).getParentRef(); - String viewMode = imapConfigs.get(mountPointName).getMode(); - - if (!mailboxPattern.equals("*")) - { - mountPoint = mountParent; - } - - List folders = listFolder(mountPoint, mountPoint, user, mailboxPattern, listSubscribed, viewMode); - if (folders != null) - { - for (AlfrescoImapFolder mailFolder : folders) - { - AlfrescoImapFolder folder = (AlfrescoImapFolder) mailFolder; - folder.setMountPointName(mountPointName); - folder.setViewMode(viewMode); - folder.setMountParent(mountParent); - } - result.addAll(folders); - } - - // Add mount point to the result list - if (mailboxPattern.equals("*")) - { - if ((listSubscribed && isSubscribed(mountPointFileInfo, user.getLogin())) || (!listSubscribed)) - { - result.add(new AlfrescoImapFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, isExtractionEnabled(mountPointFileInfo.getNodeRef()), serviceRegistry)); - } - // \NoSelect - else if (listSubscribed && hasSubscribedChild(mountPointFileInfo, user.getLogin(), viewMode)) - { - result.add(new AlfrescoImapFolder(user.getQualifiedMailboxName(), mountPointFileInfo, mountPointName, viewMode, mountParent, mountPointName, serviceRegistry, false, isExtractionEnabled(mountPointFileInfo.getNodeRef()))); - } - } - - } - - // List mailboxes that are in user IMAP Home - NodeRef root = getUserImapHomeRef(user.getLogin()); - List imapFolders = listFolder(root, root, user, mailboxPattern, listSubscribed, AlfrescoImapConst.MODE_ARCHIVE); - - if (imapFolders != null) - { - for (AlfrescoImapFolder mailFolder : imapFolders) - { - AlfrescoImapFolder folder = (AlfrescoImapFolder) mailFolder; - folder.setViewMode(AlfrescoImapConst.MODE_ARCHIVE); - folder.setMountParent(root); - } - result.addAll(imapFolders); - } - - return result; - - } - - private List listFolder(NodeRef mailboxRoot, NodeRef root, AlfrescoImapUser user, String mailboxPattern, boolean listSubscribed, String viewMode) - { - if (logger.isDebugEnabled()) - { - logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern); - } - - int index = mailboxPattern.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); - - String name = null; - String remainName = null; - - if (index < 0) - { - name = mailboxPattern; - } - else - { - name = mailboxPattern.substring(0, index); - remainName = mailboxPattern.substring(index + 1); - } - - if (logger.isDebugEnabled()) - { - logger.debug("Listing mailboxes: name=" + name); - } - - if (index < 0) - { - if ("*".equals(name)) - { - Collection list = searchFolders(root, name, true, viewMode); - if (listSubscribed) - { - list = getSubscribed(list, user.getLogin()); - } - - if (list.size() > 0) - { - return createMailFolderList(user, list, mailboxRoot); - } - return null; - } - else if (name.endsWith("*")) - { - List fullList = new LinkedList(); - List list = searchFolders(root, name.replace('%', '*'), false, viewMode); - Collection subscribedList = list; - if (listSubscribed) - { - subscribedList = getSubscribed(list, user.getLogin()); - } - - if (list.size() > 0) - { - fullList.addAll(subscribedList); - for (FileInfo fileInfo : list) - { - List childList = searchFolders(fileInfo.getNodeRef(), "*", true, viewMode); - if (listSubscribed) - { - fullList.addAll(getSubscribed(childList, user.getLogin())); - } - else - { - fullList.addAll(childList); - } - } - return createMailFolderList(user, fullList, mailboxRoot); - } - return null; - } - else if ("%".equals(name)) - { - List list = searchFolders(root, "*", false, viewMode); - LinkedList subscribedList = new LinkedList(); - - if (listSubscribed) - { - for (FileInfo fileInfo : list) - { - if (isSubscribed(fileInfo, user.getLogin())) - { - // folderName, viewMode, mountPointName will be setted in listMailboxes() method - subscribedList.add(new AlfrescoImapFolder(user.getQualifiedMailboxName(), fileInfo, null, null, mailboxRoot, null, isExtractionEnabled(fileInfo.getNodeRef()), serviceRegistry)); - } - // \NoSelect - else if (hasSubscribedChild(fileInfo, user.getLogin(), viewMode)) - { - // folderName, viewMode, mountPointName will be setted in listMailboxes() method - subscribedList.add(new AlfrescoImapFolder(user.getQualifiedMailboxName(), fileInfo, null, null, mailboxRoot, null, serviceRegistry, false, isExtractionEnabled(fileInfo.getNodeRef()))); - } - } - } - else - { - return createMailFolderList(user, list, mailboxRoot); - } - - return subscribedList; - } - else if (name.contains("%") || name.contains("*")) - { - List list = searchFolders(root, name.replace('%', '*'), false, viewMode); - Collection subscribedList = list; - if (listSubscribed) - { - subscribedList = getSubscribed(list, user.getLogin()); - } - - if (subscribedList.size() > 0) - { - return createMailFolderList(user, subscribedList, mailboxRoot); - } - return null; - } - else - { - List list = searchFolders(root, name, false, viewMode); - Collection subscribedList = list; - if (listSubscribed) - { - subscribedList = getSubscribed(list, user.getLogin()); - } - - if (subscribedList.size() > 0) - { - return createMailFolderList(user, subscribedList, mailboxRoot); - } - return null; - } - } - - // If (index != -1) this is not the last level - List result = new LinkedList(); - - List list = searchFolders(root, name.replace('%', '*'), false, viewMode); - for (FileInfo folder : list) - { - Collection childFolders = listFolder(mailboxRoot, folder.getNodeRef(), user, remainName, listSubscribed, viewMode); - - if (childFolders != null) - { - result.addAll(childFolders); - } - } - - if (result.isEmpty()) - { - return null; - } - - return result; - } - - /** - * Convert mailpath from IMAP client representation to the alfresco representation view. (e.g. with default settings "getMailPathInRepo(Repository_virtual.Imap Home)" will - * return "Company Home.Imap Home") - * - * @param mailPath mailbox path in IMAP client - * @return mailbox path in alfresco - */ - private String getMailPathInRepo(String mailPath) - { - String rootFolder; - String remain = ""; - int index = mailPath.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); - if (index > 0) - { - rootFolder = mailPath.substring(0, index); - remain = mailPath.substring(index); - } - else - { - rootFolder = mailPath; - } - Map imapConfigs = getImapConfigMountPoints(); - if (imapConfigs.keySet().contains(rootFolder)) - { - Map mountPoints = getMountPoints(); - NodeRef rootRef = mountPoints.get(rootFolder); - String rootName = nodeService.getProperty(rootRef, ContentModel.PROP_NAME).toString(); - - return rootName + remain; - } - else - { - return mailPath; - } - } - - /** - * Return mount point name, which was specified in imap-config.xml for the current mailbox. - * - * @param mailboxName mailbox name in IMAP client. - * @return mount point name or null. - */ - private String getMountPointName(String mailboxName) - { - String rootFolder; - int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); - if (index > 0) - { - rootFolder = mailboxName.substring(0, index); - } - else - { - rootFolder = mailboxName; - } - Map imapConfigs = getImapConfigMountPoints(); - if (imapConfigs.keySet().contains(rootFolder)) - { - return rootFolder; - } - else - { - return null; - } - - } - - /** - * Map of mount points. Name of mount point == key in the map. - * - * @return Map of mount points. - */ - private Map getMountPoints() - { - Map imapConfigs = getImapConfigMountPoints(); - Map mountPoints = new HashMap(); - SearchService searchService = serviceRegistry.getSearchService(); - for (ImapConfigMountPointsBean config : imapConfigs.values()) - { - // Get node reference - StoreRef store = new StoreRef(config.getStore()); - ResultSet rs = searchService.query(store, SearchService.LANGUAGE_XPATH, config.getRootPath()); - if (rs.length() == 0) - { - logger.warn("Didn't find " + config.getName()); - } - else - { - NodeRef nodeRef = rs.getNodeRef(0); - mountPoints.put(config.getName(), nodeRef); - } - rs.close(); - } - return mountPoints; - } - - /** - * Get root reference for the specified mailbox - * - * @param mailboxName mailbox name in IMAP client. - * @param userName - * @return - */ - private NodeRef getMailboxRootRef(String mailboxName, String userName) - { - String rootFolder; - int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); - if (index > 0) - { - rootFolder = mailboxName.substring(0, index); - } - else - { - rootFolder = mailboxName; - } - - Map imapConfigs = getImapConfigMountPoints(); - if (imapConfigs.keySet().contains(rootFolder)) - { - Map mountPoints = getMountPoints(); - NodeRef mountRef = mountPoints.get(rootFolder); - return nodeService.getParentAssocs(mountRef).get(0).getParentRef(); - } - else - { - return getUserImapHomeRef(userName); - } - } - - /** - * @param userName user name - * @return user IMAP home reference and create it if it doesn't exist. - */ - private NodeRef getUserImapHomeRef(final String userName) - { - NodeRef userHome = fileFolderService.searchSimple(imapRootNodeRef, userName); - if (userHome == null) - { - // create user home - userHome = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public NodeRef doWork() throws Exception - { - NodeRef result = fileFolderService.create(imapRootNodeRef, userName, ContentModel.TYPE_FOLDER).getNodeRef(); - nodeService.setProperty(result, ContentModel.PROP_DESCRIPTION, userName); - // create inbox - fileFolderService.create(result, AlfrescoImapConst.INBOX_NAME, ContentModel.TYPE_FOLDER); - return result; - } - }, AuthenticationUtil.getSystemUserName()); - } - return userHome; - } - - private boolean isSubscribed(FileInfo fileInfo, String userName) - { - return !nodeService.hasAspect(fileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_NONSUBSCRIBED); - // This is a multiuser support. Commented due new requirements - - // Map properties = fileInfo.getProperties(); - // String subscribedList = (String) properties.get(ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED); - // if (subscribedList == null) - // { - // return false; - // } - // else - // { - // return subscribedList.contains(imapHelper.formatUserEntry(userName)); - // } - - } - - private Collection getSubscribed(Collection list, String userName) - { - Collection result = new LinkedList(); - - for (FileInfo folderInfo : list) - { - if (isSubscribed(folderInfo, userName)) - { - result.add(folderInfo); - } - } - - return result; - } - - private boolean hasSubscribedChild(FileInfo parent, String userName, String viewMode) - { - List list = searchFolders(parent.getNodeRef(), "*", true, viewMode); - - for (FileInfo fileInfo : list) - { - if (isSubscribed(fileInfo, userName)) - { - return true; - } - } - - return false; - } - - private List createMailFolderList(AlfrescoImapUser user, Collection list, NodeRef imapUserHomeRef) - { - List result = new LinkedList(); - - for (FileInfo folderInfo : list) - { - // folderName, viewMode, mountPointName will be setted in listSubscribedMailboxes() method - result.add(new AlfrescoImapFolder(user.getQualifiedMailboxName(), folderInfo, null, null, imapUserHomeRef, null, isExtractionEnabled(folderInfo.getNodeRef()), serviceRegistry)); - } - - return result; - - } - - /** - * Return view mode ("virtual", "archive" or "mixed") for specified mailbox. - * - * @param mailboxName name of the mailbox in IMAP client. - * @return view mode of the specified mailbox. - */ - private String getViewMode(String mailboxName) - { - String rootFolder; - int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); - if (index > 0) - { - rootFolder = mailboxName.substring(0, index); - } - else - { - rootFolder = mailboxName; - } - Map imapConfigs = getImapConfigMountPoints(); - if (imapConfigs.keySet().contains(rootFolder)) - { - return imapConfigs.get(rootFolder).getMode(); - } - else - { - return AlfrescoImapConst.MODE_ARCHIVE; - } - } - - private String getCurrentUser() - { - return AuthenticationUtil.getFullyAuthenticatedUser(); - } - - /** - * Return list of sites, that belong to the specified user and not marked as "Imap favourite" - * - * @param userName name of user - * @return List of nonFavourite sites. - */ - private List getNonFavouriteSites(String userName) - { - List nonFavSites = new LinkedList(); - PreferenceService preferenceService = (PreferenceService) serviceRegistry.getService(QName.createQName(NamespaceService.ALFRESCO_URI, "PreferenceService")); - Map prefs = preferenceService.getPreferences(userName, AlfrescoImapConst.PREF_IMAP_FAVOURITE_SITES); - List sites = serviceRegistry.getSiteService().listSites(userName); - for (SiteInfo siteInfo : sites) - { - String key = AlfrescoImapConst.PREF_IMAP_FAVOURITE_SITES + "." + siteInfo.getShortName(); - Boolean isImapFavourite = (Boolean) prefs.get(key); - if (isImapFavourite == null || !isImapFavourite) - { - nonFavSites.add(siteInfo); - } - } - - return nonFavSites; - } - - private void checkForFlaggableAspect(NodeRef nodeRef) - { - if (!nodeService.hasAspect(nodeRef, ImapModel.ASPECT_FLAGGABLE)) - { - Map aspectProperties = new HashMap(); - nodeService.addAspect(nodeRef, ImapModel.ASPECT_FLAGGABLE, aspectProperties); - } - } - - private boolean isExtractionEnabled(NodeRef nodeRef) - { - return extractAttachmentsEnabled && !ignoreExtractionFolders.contains(nodeRef); - } - - /** - * This method should returns a unique identifier of Alfresco server. The possible UID may be calculated based on IP address, Server port, MAC address, Web Application context. - * This UID should be parseable into initial components. This necessary for the implementation of the following case: If the message being copied (e.g. drag-and-drop) between - * two different Alfresco accounts in the IMAP client, we must unambiguously identify from which Alfresco server this message being copied. The message itself does not contain - * content data, so we must download it from the initial server (e.g. using download content servlet) and save it into destination repository. - * - * @return String representation of unique identifier of Alfresco server - */ - public String getAlfrescoServerUID() - { - // TODO Implement as javadoc says. - return "Not-Implemented"; - } - -} diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java b/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java deleted file mode 100755 index 06a9dfe96e..0000000000 --- a/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java +++ /dev/null @@ -1,417 +0,0 @@ -package org.alfresco.repo.imap; - -import java.io.IOException; -import java.io.Serializable; -import java.util.List; -import java.util.Map; - -import javax.mail.Flags; -import javax.transaction.UserTransaction; - -import junit.framework.TestCase; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.model.ImapModel; -import org.alfresco.repo.importer.ACPImportPackageHandler; -import org.alfresco.repo.node.integrity.IntegrityChecker; -import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AuthenticationService; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.cmr.view.ImporterService; -import org.alfresco.service.cmr.view.Location; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.ApplicationContextHelper; -import org.alfresco.util.PropertyMap; -import org.springframework.context.ApplicationContext; -import org.springframework.core.io.ClassPathResource; - -public class ImapServiceImplTest extends TestCase -{ - - private static final String USER_NAME = "admin"; - private static final String USER_PASSWORD = "admin"; - - private static final String MAILBOX_NAME_A = "mailbox_a"; - private static final String MAILBOX_NAME_B = "mailbox_b"; - private static final String MAILBOX_PATTERN = "mailbox*"; - private static final String FOLDER_PATTERN = "___-___folder*"; - private static final String FILE_PATTERN = "___-___file*"; - - private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); - private TransactionService transactionService; - private NodeService nodeService; - private ImporterService importerService; - private PersonService personService; - private AuthenticationService authenticationService; - private PermissionService permissionService; - private SearchService searchService; - private NamespaceService namespaceService; - - private AlfrescoImapUser user; - private ImapService imapService; - private UserTransaction txn; - - private NodeRef companyHomeNodeRef; - - private Flags flags; - - String anotherUserName; - - @Override - public void setUp() throws Exception - { - ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry"); - transactionService = serviceRegistry.getTransactionService(); - nodeService = serviceRegistry.getNodeService(); - importerService = serviceRegistry.getImporterService(); - personService = serviceRegistry.getPersonService(); - authenticationService = serviceRegistry.getAuthenticationService(); - permissionService = serviceRegistry.getPermissionService(); - imapService = serviceRegistry.getImapService(); - searchService = serviceRegistry.getSearchService(); - namespaceService = serviceRegistry.getNamespaceService(); - - flags = new Flags(); - flags.add(Flags.Flag.SEEN); - flags.add(Flags.Flag.FLAGGED); - flags.add(Flags.Flag.ANSWERED); - flags.add(Flags.Flag.DELETED); - - // start the transaction - txn = transactionService.getUserTransaction(); - txn.begin(); - authenticationService.authenticate(USER_NAME, USER_PASSWORD.toCharArray()); - - // downgrade integrity - IntegrityChecker.setWarnInTransaction(); - - anotherUserName = "user" + System.currentTimeMillis(); - - PropertyMap testUser = new PropertyMap(); - testUser.put(ContentModel.PROP_USERNAME, anotherUserName); - testUser.put(ContentModel.PROP_FIRSTNAME, anotherUserName); - testUser.put(ContentModel.PROP_LASTNAME, anotherUserName); - testUser.put(ContentModel.PROP_EMAIL, anotherUserName + "@alfresco.com"); - testUser.put(ContentModel.PROP_JOBTITLE, "jobTitle"); - - personService.createPerson(testUser); - - // create the ACEGI Authentication instance for the new user - authenticationService.createAuthentication(anotherUserName, anotherUserName.toCharArray()); - - user = new AlfrescoImapUser(anotherUserName + "@alfresco.com", anotherUserName, anotherUserName); - - String storePath = "workspace://SpacesStore"; - String companyHomePathInStore = "/app:company_home"; - - StoreRef storeRef = new StoreRef(storePath); - - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - - List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); - companyHomeNodeRef = nodeRefs.get(0); - - /* - * Importing test folders: - * - * "Company Home" contains: "___-___folder_a" - * - * "___-___folder_a" contains: "___-___folder_a_a", - * "___-___file_a", - * "Message_485.eml" (this is IMAP Message) - * - * "___-___folder_a_a" contains: "____-____file_a_a" - * - */ - importInternal("test-resources/imapservice_test_folder_a.acp", companyHomeNodeRef); - - reauthenticate(anotherUserName, anotherUserName); - } - - public void tearDown() throws Exception - { - try - { - txn.rollback(); - } - catch (Throwable e) - { - e.printStackTrace(); - } - } - - private void importInternal(String acpName, NodeRef space) - throws IOException - { - ClassPathResource acpResource = new ClassPathResource(acpName); - ACPImportPackageHandler acpHandler = new ACPImportPackageHandler(acpResource.getFile(), null); - Location importLocation = new Location(space); - importerService.importView(acpHandler, importLocation, null, null); - } - - private boolean checkMailbox(AlfrescoImapUser user, String mailboxName) - { - AlfrescoImapFolder mailFolder = (AlfrescoImapFolder)imapService.getFolder(user, mailboxName); - - if (mailFolder.getFolderInfo() == null) - { - return false; - } - return true; - } - - private boolean checkSubscribedMailbox(AlfrescoImapUser user, String mailboxName) - { - List aifs = imapService.listSubscribedMailboxes(user, mailboxName); - boolean present = false; - for (AlfrescoImapFolder aif : aifs) - { - if (aif.getName().equals(mailboxName)) - { - present = true; - break; - } - } - return present; - } - - private void reauthenticate(String name, String password) - { - authenticationService.invalidateTicket(authenticationService.getCurrentTicket()); - authenticationService.clearCurrentSecurityContext(); - authenticationService.authenticate(name, password.toCharArray()); - } - - public void testGetFolder() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_A); - assertTrue(checkMailbox(user, MAILBOX_NAME_A)); - } - - public void testListMailbox() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_A); - imapService.createMailbox(user, MAILBOX_NAME_B); - List mf = imapService.listMailboxes(user, MAILBOX_PATTERN); - assertEquals(mf.size(), 2); - } - - public void testListSubscribedMailbox() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_A); - imapService.createMailbox(user, MAILBOX_NAME_B); - imapService.subscribe(user, MAILBOX_NAME_A); - imapService.subscribe(user, MAILBOX_NAME_B); - List aif = imapService.listSubscribedMailboxes(user, MAILBOX_PATTERN); - assertEquals(aif.size(), 2); - } - - public void testCreateMailbox() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_A); - assertTrue("Mailbox isn't created", checkMailbox(user, MAILBOX_NAME_A)); - } - - public void testDuplicateMailboxes() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_A); - try - { - imapService.createMailbox(user, MAILBOX_NAME_A); - fail("Duplicate Mailbox was created"); - } - catch (AlfrescoRuntimeException e) - { - // expected - } - - } - - public void testRenameMailbox() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_A); - imapService.renameMailbox(user, MAILBOX_NAME_A, MAILBOX_NAME_B); - assertFalse("Can't rename mailbox", checkMailbox(user, MAILBOX_NAME_A)); - assertTrue("Can't rename mailbox", checkMailbox(user, MAILBOX_NAME_B)); - } - - public void testRenameMailboxDuplicate() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_A); - imapService.createMailbox(user, MAILBOX_NAME_B); - try - { - imapService.renameMailbox(user, MAILBOX_NAME_A, MAILBOX_NAME_B); - fail("Mailbox was renamed to existing one but shouldn't"); - } - catch (AlfrescoRuntimeException e) - { - // expected - } - } - - public void testDeleteMailbox() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_B); - imapService.deleteMailbox(user, MAILBOX_NAME_B); - assertFalse("Can't delete mailbox", checkMailbox(user, MAILBOX_NAME_B)); - } - - public void testSearchFoldersInArchive() throws Exception - { - List fi = imapService.searchFolders(companyHomeNodeRef, FOLDER_PATTERN, true, AlfrescoImapConst.MODE_ARCHIVE); - assertNotNull("Can't find folders in Archive Mode", fi); - assertEquals("Can't find folders in Archive Mode", fi.size(), 2); - - fi = imapService.searchFolders(companyHomeNodeRef, FOLDER_PATTERN, false, AlfrescoImapConst.MODE_ARCHIVE); - assertNotNull("Can't find folders in Archive Mode", fi); - assertEquals("Can't find folders in Archive Mode", fi.size(), 1); - } - - public void testSearchFoldersInVirtual() throws Exception - { - List fi = imapService.searchFolders(companyHomeNodeRef, FOLDER_PATTERN, true, AlfrescoImapConst.MODE_VIRTUAL); - assertNotNull("Can't find folders in Virtual Mode", fi); - assertEquals("Can't find folders in Virtual Mode", fi.size(), 2); - - fi = imapService.searchFolders(companyHomeNodeRef, FOLDER_PATTERN, false, AlfrescoImapConst.MODE_VIRTUAL); - assertNotNull("Can't find folders in Virtual Mode", fi); - assertEquals("Can't find folders in Virtual Mode", fi.size(), 1); - } - - public void testSearchFoldersInMixed() throws Exception - { - List fi = imapService.searchFolders(companyHomeNodeRef, FOLDER_PATTERN, true, AlfrescoImapConst.MODE_MIXED); - assertNotNull("Can't find folders in Mixed Mode", fi); - assertEquals("Can't find folders in Mixed Mode", fi.size(), 2); - - fi = imapService.searchFolders(companyHomeNodeRef, FOLDER_PATTERN, false, AlfrescoImapConst.MODE_MIXED); - assertNotNull("Can't find folders in Mixed Mode", fi); - assertEquals("Can't find folders in Mixed Mode", fi.size(), 1); - } - - public void testSearchFiles() throws Exception - { - List fi = imapService.searchFiles(companyHomeNodeRef, FILE_PATTERN, true); - assertNotNull(fi); - assertTrue(fi.size() > 0); - } - - public void testSearchMails() throws Exception - { - List fi = imapService.searchMails(companyHomeNodeRef, "*", AlfrescoImapConst.MODE_MIXED, true); - assertNotNull(fi); - assertTrue(fi.size() > 0); - } - - public void testSubscribe() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_A); - - imapService.subscribe(user, MAILBOX_NAME_A); - assertTrue("Can't subscribe mailbox", checkSubscribedMailbox(user, MAILBOX_NAME_A)); - } - - public void testUnsubscribe() throws Exception - { - imapService.createMailbox(user, MAILBOX_NAME_A); - imapService.subscribe(user, MAILBOX_NAME_A); - imapService.unsubscribe(user, MAILBOX_NAME_A); - assertFalse("Can't unsubscribe mailbox", checkSubscribedMailbox(user, MAILBOX_NAME_A)); - } - - private void setFlags(FileInfo messageFileInfo) throws Exception - { - imapService.setFlags(messageFileInfo, flags, true); - NodeRef messageNodeRef = messageFileInfo.getNodeRef(); - Map props = nodeService.getProperties(messageNodeRef); - - assertTrue("Can't set SEEN flag", props.containsKey(ImapModel.PROP_FLAG_SEEN)); - assertTrue("Can't set FLAGGED flag", props.containsKey(ImapModel.PROP_FLAG_FLAGGED)); - assertTrue("Can't set ANSWERED flag", props.containsKey(ImapModel.PROP_FLAG_ANSWERED)); - assertTrue("Can't set DELETED flag", props.containsKey(ImapModel.PROP_FLAG_DELETED)); - } - - public void testSetFlags() throws Exception - { - List fis = imapService.searchMails(companyHomeNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true); - if (fis != null && fis.size() > 0) - { - FileInfo messageFileInfo = fis.get(0); - try - { - setFlags(messageFileInfo); - fail("Can't set flags"); - } - catch (Exception e) - { - if (e instanceof AccessDeniedException) - { - // expected - } - else - { - throw e; - } - } - - reauthenticate(USER_NAME, USER_PASSWORD); - - permissionService.setPermission(companyHomeNodeRef, anotherUserName, PermissionService.WRITE, true); - - reauthenticate(anotherUserName, anotherUserName); - - setFlags(messageFileInfo); - } - } - - public void testSetFlag() throws Exception - { - List fis = imapService.searchMails(companyHomeNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true); - if (fis != null && fis.size() > 0) - { - FileInfo messageFileInfo = fis.get(0); - - reauthenticate(USER_NAME, USER_PASSWORD); - - permissionService.setPermission(companyHomeNodeRef, anotherUserName, PermissionService.WRITE, true); - - reauthenticate(anotherUserName, anotherUserName); - - imapService.setFlag(messageFileInfo, Flags.Flag.RECENT, true); - - Serializable prop = nodeService.getProperty(messageFileInfo.getNodeRef(), ImapModel.PROP_FLAG_RECENT); - assertNotNull("Can't set RECENT flag", prop); - } - } - - public void testGetFlags() throws Exception - { - List fis = imapService.searchMails(companyHomeNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true); - if (fis != null && fis.size() > 0) - { - FileInfo messageFileInfo = fis.get(0); - - reauthenticate(USER_NAME, USER_PASSWORD); - - permissionService.setPermission(companyHomeNodeRef, anotherUserName, PermissionService.WRITE, true); - - imapService.setFlags(messageFileInfo, flags, true); - - reauthenticate(anotherUserName, anotherUserName); - - Flags fl = imapService.getFlags(messageFileInfo); - assertTrue(fl.contains(flags)); - } - } -} diff --git a/source/java/org/alfresco/repo/imap/IncomingImapMessage.java b/source/java/org/alfresco/repo/imap/IncomingImapMessage.java deleted file mode 100755 index aa15c53ef9..0000000000 --- a/source/java/org/alfresco/repo/imap/IncomingImapMessage.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeUtility; - -import org.alfresco.model.ContentModel; -import org.alfresco.model.ImapModel; -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.ContentIOException; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.QName; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.util.FileCopyUtils; - -/** - * This class is used to serve incoming IMAP message. E.g. when message is copied /moved into some IMAP older. - * - * @author Arseny Kovalchuk - */ -public class IncomingImapMessage extends AbstractMimeMessage -{ - private Log logger = LogFactory.getLog(IncomingImapMessage.class); - private ContentReader contentReader; - /** - * Constructs {@link IncomingImapMessage} object based on {@link MimeMessage} - * - * @param fileInfo - reference to the {@link FileInfo} object representing the message. - * @param imapHelper - reference to the {@link ImapHelper} object. - * @param message - {@link MimeMessage} - * @throws MessagingException - */ - public IncomingImapMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, MimeMessage message) throws MessagingException - { - super(Session.getDefaultInstance(new Properties())); - this.wrappedMessage = message; // temporary save it and then destroyed in writeContent() (to avoid memory leak with byte[] MimeMessage.content field) - this.buildMessage(fileInfo, serviceRegistry); - } - - @Override - public void buildMessageInternal() throws MessagingException - { - setMessageHeaders(); - // Add Imap Content Aspect with properties - NodeService nodeService = serviceRegistry.getNodeService(); - nodeService.addAspect(this.messageFileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_CONTENT, null); - // Write content - writeContent(); - imapService.setFlags(messageFileInfo, flags, true); - - final NodeRef nodeRef = messageFileInfo.getNodeRef(); - Map newProperties = new HashMap(); - newProperties.put(ImapModel.PROP_MESSAGE_FROM, InternetAddress.toString(this.getFrom())); - newProperties.put(ImapModel.PROP_MESSAGE_TO, InternetAddress.toString(this.getRecipients(RecipientType.TO))); - newProperties.put(ImapModel.PROP_MESSAGE_CC, InternetAddress.toString(this.getRecipients(RecipientType.CC))); - newProperties.put(ImapModel.PROP_MESSAGE_ID, this.getMessageID()); - String[] threadIndexes = this.getHeader("Thread-Index"); - String threadIndex = (threadIndexes == null || threadIndexes.length == 0) ? null : threadIndexes[0]; - newProperties.put(ImapModel.PROP_THREAD_INDEX, threadIndex); - - String[] subj = this.getHeader("Subject"); - if (subj != null && subj.length > 0) - { - String decodedSubject = subj[0]; - try - { - decodedSubject = MimeUtility.decodeText(decodedSubject); - } - catch (UnsupportedEncodingException e) - { - logger.warn(e.toString()); - } - newProperties.put(ImapModel.PROP_MESSAGE_SUBJECT, decodedSubject); - newProperties.put(ContentModel.PROP_TITLE, decodedSubject); - newProperties.put(ContentModel.PROP_DESCRIPTION, decodedSubject); - } - - serviceRegistry.getNodeService().addProperties(nodeRef, newProperties); - } - - /** - * Writes the content of incoming message into Alfresco repository. - * - * @throws MessagingException - */ - private void writeContent() throws MessagingException - { - ContentWriter writer = serviceRegistry.getContentService().getWriter(messageFileInfo.getNodeRef(), ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_RFC822); - try - { - OutputStream outputStream = writer.getContentOutputStream(); - wrappedMessage.writeTo(outputStream); - outputStream.close(); - wrappedMessage = null; // it is not used any more and it is available to GC (to avoid memory leak with byte[] MimeMessage.content field) - this.contentReader = serviceRegistry.getContentService().getReader(messageFileInfo.getNodeRef(), ContentModel.PROP_CONTENT); - } - catch (ContentIOException e) - { - throw new MessagingException(e.getMessage(), e); - } - catch (IOException e) - { - throw new MessagingException(e.getMessage(), e); - } - } - - @Override - protected InputStream getContentStream() throws MessagingException - { - try - { - if (this.contentStream == null) - { - this.contentStream = this.contentReader.getContentInputStream(); - } - return this.contentStream; - } - catch (Exception e) - { - throw new MessagingException(e.getMessage(),e); - } - } - -} diff --git a/source/java/org/alfresco/repo/imap/LoadTest.java b/source/java/org/alfresco/repo/imap/LoadTest.java deleted file mode 100755 index a392e716c8..0000000000 --- a/source/java/org/alfresco/repo/imap/LoadTest.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.alfresco.repo.imap; - -import java.io.IOException; -import java.util.Date; -import java.util.List; - -import javax.mail.Flags; - -import org.alfresco.repo.importer.ACPImportPackageHandler; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AuthenticationService; -import org.alfresco.service.cmr.view.ImporterService; -import org.alfresco.service.cmr.view.Location; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.util.ApplicationContextHelper; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.core.io.ClassPathResource; - -import com.icegreen.greenmail.imap.ImapConstants; -import com.icegreen.greenmail.store.SimpleStoredMessage; - -import junit.framework.TestCase; - -public class LoadTest extends TestCase -{ - private Log logger = LogFactory.getLog(LoadTest.class); - - - private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); - - private ImapService imapService; - private ImporterService importerService; - - private AlfrescoImapUser user; - private static final String USER_NAME = "admin"; - private static final String USER_PASSWORD = "admin"; - private static final String TEST_DATA_FOLDER_NAME = "test_data"; - private static final String TEST_FOLDER_NAME = "test_imap3"; - private static final long MESSAGE_QUANTITY = 3; - - - - @Override - public void setUp() throws Exception - { - ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry"); - AuthenticationService authenticationService = serviceRegistry.getAuthenticationService(); - imapService = serviceRegistry.getImapService(); - importerService = serviceRegistry.getImporterService(); - NodeService nodeService = serviceRegistry.getNodeService(); - SearchService searchService = serviceRegistry.getSearchService(); - NamespaceService namespaceService = serviceRegistry.getNamespaceService(); - - user = new AlfrescoImapUser(USER_NAME + "@alfresco.com", USER_NAME, USER_PASSWORD); - - authenticationService.authenticate(USER_NAME, USER_PASSWORD.toCharArray()); - - StoreRef storeRef = new StoreRef("workspace://SpacesStore"); - NodeRef rootRef = nodeService.getRootNode(storeRef); - - // Delete test folder - List nodeRefs = searchService.selectNodes(rootRef, "/app:company_home/imap:imap_home/cm:admin/cm:" + TEST_FOLDER_NAME, null, namespaceService, false); - if (nodeRefs.size() == 1) - { - NodeRef ch = nodeRefs.get(0); - nodeService.deleteNode(ch); - } - // Delete test data folder - nodeRefs = searchService.selectNodes(rootRef, "/app:company_home/imap:imap_home/cm:admin/cm:" + TEST_DATA_FOLDER_NAME, null, namespaceService, false); - if (nodeRefs.size() == 1) - { - NodeRef ch = nodeRefs.get(0); - nodeService.deleteNode(ch); - } - - NodeRef adminNodeRef = searchService.selectNodes(rootRef, "/app:company_home/imap:imap_home/cm:admin", null, namespaceService, false).get(0); - importTestData("test-resources/load_test_data.acp", adminNodeRef); - - - AlfrescoImapFolder testDataFolder = imapService.getFolder(user, TEST_DATA_FOLDER_NAME); -// FileInfo fileInfo = imapService.searchMails(drafts.getFolderInfo().getNodeRef(), "*", AlfrescoImapConst.MODE_ARCHIVE, false).get(0); -// SimpleStoredMessage m = drafts.createImapMessage(fileInfo, (long)0, true); - -// imapService.setExtractAttachmentsEnabled(false); - SimpleStoredMessage m = testDataFolder.getMessages().get(0); - m = testDataFolder.getMessage(m.getUid()); - - - - - AlfrescoImapFolder folder = imapService.createMailbox(user, TEST_FOLDER_NAME); - - logger.info("Creating folders..."); - long t = System.currentTimeMillis(); - - try - { - for (int i = 0; i < MESSAGE_QUANTITY; i++) - { - System.out.println("i = " + i); -// folder.appendMessageInternal(message, new Flags(), new Date()); - folder.appendMessage(m.getMimeMessage(), new Flags(), new Date()); - } - - } - catch (Exception e) - { - logger.error(e, e); - } - - t = System.currentTimeMillis() - t; - logger.info("Create time: " + t + " ms (" + t/1000 + " s (" + t/60000 + " min))"); - - } - - - - public void tearDown() throws Exception - { - } - - - public void testList() - { - logger.info("Listing folders..."); - - long t = System.currentTimeMillis(); - List list = imapService.listMailboxes(user, TEST_FOLDER_NAME + "*"); - t = System.currentTimeMillis() - t; - - logger.info("List time: " + t + " ms (" + t/1000 + " s)"); - logger.info("List size: " + list.size()); - - } - - - private void importTestData(String acpName, NodeRef space) throws IOException - { - ClassPathResource acpResource = new ClassPathResource(acpName); - ACPImportPackageHandler acpHandler = new ACPImportPackageHandler(acpResource.getFile(), null); - Location importLocation = new Location(space); - importerService.importView(acpHandler, importLocation, null, null); - } - - - -} diff --git a/source/java/org/alfresco/repo/imap/RemoteLoadTest.java b/source/java/org/alfresco/repo/imap/RemoteLoadTest.java deleted file mode 100755 index 7a6c85afe6..0000000000 --- a/source/java/org/alfresco/repo/imap/RemoteLoadTest.java +++ /dev/null @@ -1,161 +0,0 @@ -package org.alfresco.repo.imap; - -import java.io.BufferedInputStream; -import java.io.FilterInputStream; -import java.util.Properties; - -import javax.mail.Address; -import javax.mail.BodyPart; -import javax.mail.Folder; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Part; -import javax.mail.Session; -import javax.mail.Store; -import javax.mail.internet.MimeMultipart; - -import junit.framework.TestCase; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.sun.mail.util.BASE64DecoderStream; - -public class RemoteLoadTest extends TestCase -{ - - private Log logger = LogFactory.getLog(LoadTest.class); - - private static final String USER_NAME = "admin"; - private static final String USER_PASSWORD = "admin"; - private static final String TEST_FOLDER_NAME = "test_imap1000"; - - @Override - public void setUp() throws Exception - { - } - - public void tearDown() throws Exception - { - - } - - - public void testMailbox() - { - logger.info("Getting folder..."); - long t = System.currentTimeMillis(); - - String host = "localhost"; - - // Create empty properties - Properties props = new Properties(); - - // Get session - Session session = Session.getDefaultInstance(props, null); - - Store store = null; - Folder folder = null; - try - { - // Get the store - store = session.getStore("imap"); - store.connect(host, USER_NAME, USER_PASSWORD); - - // Get folder - folder = store.getFolder(TEST_FOLDER_NAME); - folder.open(Folder.READ_ONLY); - - // Get directory - Message message[] = folder.getMessages(); - - for (int i = 0, n = message.length; i < n; i++) - { - message[i].getAllHeaders(); - - Address[] from = message[i].getFrom(); - System.out.print(i + ": "); - if (from != null) - { - System.out.print(message[i].getFrom()[0] + "\t"); - } - System.out.println(message[i].getSubject()); - - Object content = message[i].getContent(); - if (content instanceof MimeMultipart) - { - for (int j = 0, m = ((MimeMultipart)content).getCount(); j < m; j++) - { - BodyPart part = ((MimeMultipart)content).getBodyPart(j); - Object partContent = part.getContent(); - - if (partContent instanceof String) - { - String body = (String)partContent; - } - else if (partContent instanceof FilterInputStream) - { - FilterInputStream fis = (FilterInputStream)partContent; - BufferedInputStream bis = new BufferedInputStream(fis); - - /* while (bis.available() > 0) - { - bis.read(); - }*/ - byte[] bytes = new byte[524288]; - while (bis.read(bytes) != -1) - { - } - bis.close(); - } - } - } - - int nn = 0; - - } - - - - t = System.currentTimeMillis() - t; - logger.info("Time: " + t + " ms (" + t/1000 + " s)"); - logger.info("Length: " + message.length); - - } - catch (Exception e) - { - logger.error(e.getMessage(), e); - fail(e.getMessage()); - } - finally - { - // Close connection - try - { - if (folder != null) - { - folder.close(false); - } - } - catch (MessagingException e) - { - logger.error(e.getMessage(), e); - fail(e.getMessage()); - } - try - { - if (store != null) - { - store.close(); - } - } - catch (MessagingException e) - { - logger.error(e.getMessage(), e); - fail(e.getMessage()); - } - } - - } - - -} diff --git a/source/java/org/alfresco/repo/imap/SimpleImapMessage.java b/source/java/org/alfresco/repo/imap/SimpleImapMessage.java deleted file mode 100755 index 52e6c9b7f7..0000000000 --- a/source/java/org/alfresco/repo/imap/SimpleImapMessage.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import static org.alfresco.repo.imap.AlfrescoImapConst.BASE_64_ENCODING; -import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TRANSFER_ENCODING; -import static org.alfresco.repo.imap.AlfrescoImapConst.UTF_8; - -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.util.Map; - -import javax.mail.Address; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import javax.mail.internet.MimeUtility; - -import org.alfresco.model.ContentModel; -import org.alfresco.model.ImapModel; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.ContentIOException; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Extended MimeMessage to represent a content stored in the Alfresco repository. - * - * @author Arseny Kovalchuk - */ -public class SimpleImapMessage extends AbstractMimeMessage -{ - private static Log logger = LogFactory.getLog(SimpleImapMessage.class); - - /** - * Constructs {@link SimpleImapMessage} object. - * - * @param fileInfo - reference to the {@link FileInfo} object representing the message. - * @param imapHelper - reference to the {@link ImapHelper} object. - * @param generateBody - if {@code true} message body will be generated. - * - * @throws MessagingException if generation of the body fails. - */ - public SimpleImapMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, boolean generateBody) throws MessagingException - { - super(fileInfo, serviceRegistry, generateBody); - } - - @Override - public void buildMessageInternal() throws MessagingException - { - if (generateBody != false) - { - setMessageHeaders(); - buildImapMessage(); - } - } - - /** - * This method builds MimeMessage based on either ImapModel or ContentModel type. - * - * @param fileInfo - Source file information {@link FileInfo} - * @throws MessagingException - */ - private void buildImapMessage() throws MessagingException - { - final NodeRef nodeRef = messageFileInfo.getNodeRef(); - if (serviceRegistry.getNodeService().hasAspect(nodeRef, ImapModel.ASPECT_IMAP_CONTENT)) - { - buildRFC822Message(); - } - else - { - buildContentModelMessage(); - } - } - - private void buildRFC822Message() throws MessagingException - { - ContentService contentService = serviceRegistry.getContentService(); - ContentReader reader = contentService.getReader(messageFileInfo.getNodeRef(), ContentModel.PROP_CONTENT); - try - { - InputStream inputStream = reader.getContentInputStream(); - this.parse(inputStream); - inputStream.close(); - } - catch (ContentIOException e) - { - //logger.error(e); - throw new MessagingException("The error occured during message creation from content stream.", e); - } - catch (IOException e) - { - //logger.error(e); - throw new MessagingException("The error occured during message creation from content stream.", e); - } - } - - /** - * This method builds {@link MimeMessage} based on {@link ContentModel} - * - * @param fileInfo - Source file information {@link FileInfo} - * @throws MessagingException - */ - private void buildContentModelMessage() throws MessagingException - { - Map properties = messageFileInfo.getProperties(); - String prop = null; - setSentDate(messageFileInfo.getModifiedDate()); - // Add FROM address - Address[] addressList = buildSenderFromAddress(); - addFrom(addressList); - // Add TO address - addressList = buildRecipientToAddress(); - addRecipients(RecipientType.TO, addressList); - prop = (String) properties.get(ContentModel.PROP_TITLE); - try - { - prop = (prop == null) ? MimeUtility.encodeText(messageFileInfo.getName(), KOI8R_CHARSET, null) : MimeUtility.encodeText(prop, KOI8R_CHARSET, null); - } - catch (UnsupportedEncodingException e) - { - // ignore - } - setSubject(prop); - setContent(buildContentModelMultipart()); - } - - /** - * This method builds {@link Multipart} based on {@link ContentModel} - * - * @param fileInfo - Source file information {@link FileInfo} - * @throws MessagingException - */ - private Multipart buildContentModelMultipart() throws MessagingException - { - MimeMultipart rootMultipart = new MimeMultipart("alternative"); - // Cite MOB-395: "email agent will be used to select an appropriate template" - we are not able to - // detect an email agent so we use a default template for all messages. - // See AlfrescoImapConst to see the possible templates to use. - String bodyTxt = getEmailBodyText(EmailBodyType.TEXT_PLAIN); - rootMultipart.addBodyPart(getTextBodyPart(bodyTxt, EmailBodyType.TEXT_PLAIN.getSubtype())); - String bodyHtml = getEmailBodyText(EmailBodyType.TEXT_HTML); - rootMultipart.addBodyPart(getTextBodyPart(bodyHtml, EmailBodyType.TEXT_HTML.getSubtype())); - return rootMultipart; - } - - - private MimeBodyPart getTextBodyPart(String bodyText, String subtype) throws MessagingException - { - MimeBodyPart result = new MimeBodyPart(); - result.setText(bodyText, UTF_8, subtype); - result.addHeader(CONTENT_TRANSFER_ENCODING, BASE_64_ENCODING); - return result; - } - -} diff --git a/source/java/org/alfresco/repo/imap/config/ImapConfigBean.java b/source/java/org/alfresco/repo/imap/config/ImapConfigBean.java index 8fd3b43938..4301e247da 100755 --- a/source/java/org/alfresco/repo/imap/config/ImapConfigBean.java +++ b/source/java/org/alfresco/repo/imap/config/ImapConfigBean.java @@ -27,7 +27,7 @@ package org.alfresco.repo.imap.config; import org.springframework.beans.factory.BeanNameAware; /** - * Standard ImapConfig bean. + * Provides the parameters for an IMAP mount point (a mapping from an Alfresco node path to an IMAP folder name). */ public class ImapConfigBean implements BeanNameAware { @@ -35,6 +35,9 @@ public class ImapConfigBean implements BeanNameAware /** The IMAP folder name. */ private String name; + /** The mode (virtual or archive). */ + private String mode; + /** The Alfresco store name. */ private String store; @@ -60,6 +63,27 @@ public class ImapConfigBean implements BeanNameAware this.name = name; } + /** + * Gets the mode. + * + * @return the mode (virtual or archive) + */ + public String getMode() + { + return this.mode; + } + + /** + * Sets the mode. + * + * @param mode + * the new mode (virtual or archive) + */ + public void setMode(String mode) + { + this.mode = mode; + } + /** * Gets the Alfresco store name. * diff --git a/source/java/org/alfresco/repo/imap/config/ImapConfigMountPointsBean.java b/source/java/org/alfresco/repo/imap/config/ImapConfigMountPointsBean.java deleted file mode 100755 index 25bc5eec4f..0000000000 --- a/source/java/org/alfresco/repo/imap/config/ImapConfigMountPointsBean.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have received a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap.config; - - -/** - * Provides the parameters for an IMAP mount point (a mapping from an Alfresco node path to an IMAP folder name). - */ -public class ImapConfigMountPointsBean extends ImapConfigBean -{ - - - /** The mode (virtual, archive or mixed). */ - private String mode; - - /** - * Gets the mode. - * - * @return the mode (virtual or archive) - */ - public String getMode() - { - return this.mode; - } - - /** - * Sets the mode. - * - * @param mode - * the new mode (virtual or archive) - */ - public void setMode(String mode) - { - this.mode = mode; - } - -} diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index 73c9d9d31c..b6d62dd6c9 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -31,7 +31,6 @@ import org.alfresco.cmis.CMISQueryService; import org.alfresco.cmis.CMISServices; import org.alfresco.mbeans.VirtServerRegistry; import org.alfresco.repo.forms.FormService; -import org.alfresco.repo.imap.ImapService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.ActionService; @@ -545,13 +544,4 @@ public class ServiceDescriptorRegistry { return (CMISQueryService)getService(CMIS_QUERY_SERVICE); } - - /* - * (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCMISQueryService() - */ - public ImapService getImapService() - { - return (ImapService)getService(IMAP_SERVICE); - } } diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index 323de1f22d..73a87f4d25 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -31,7 +31,6 @@ import org.alfresco.cmis.CMISQueryService; import org.alfresco.cmis.CMISServices; import org.alfresco.mbeans.VirtServerRegistry; import org.alfresco.repo.forms.FormService; -import org.alfresco.repo.imap.ImapService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.attributes.AttributeService; @@ -150,7 +149,6 @@ public interface ServiceRegistry static final QName CMIS_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CMISService"); static final QName CMIS_DICTIONARY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CMISDictionaryService"); static final QName CMIS_QUERY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CMISQueryService"); - static final QName IMAP_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ImapService"); /** @@ -508,11 +506,4 @@ public interface ServiceRegistry */ @NotAuditable CMISQueryService getCMISQueryService(); - - /** - * Get the IMAP service (or null if one is not provided) - * @return the IMAP service - */ - @NotAuditable - ImapService getImapService(); } diff --git a/source/java/org/alfresco/util/Utf7.java b/source/java/org/alfresco/util/Utf7.java deleted file mode 100755 index 2b6ff0fc73..0000000000 --- a/source/java/org/alfresco/util/Utf7.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.util; - -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; - -import com.beetstra.jutf7.CharsetProvider; - -/** - * - * @author Mike Shavnev - * - */ -public class Utf7 -{ - public static final String UTF7 = "UTF-7"; - public static final String UTF7_OPTIONAL = "X-UTF-7-OPTIONAL"; - public static final String UTF7_MODIFIED = "X-MODIFIED-UTF-7"; - - - - /** - * Convert string from UTF-7 characters - * - * @param string Input string for decoding - * @return Decoded string - */ - public static String decode(String string, String charsetName) - { - if (string.length() <= 1) - { - return string; - } - CharsetProvider provider = new CharsetProvider(); - Charset charset = provider.charsetForName(charsetName); - CharBuffer charBuffer = charset.decode(ByteBuffer.wrap(string.getBytes())); - return charBuffer.toString(); - } - - /** - * Convert string to UTF-7 characters - * - * @param string Input string for decoding - * @return Encoded string - */ - public static String encode(String string, String charsetName) - { - if (string.length() <= 1) - { - return string; - } - CharsetProvider provider = new CharsetProvider(); - Charset charset = provider.charsetForName(charsetName); - ByteBuffer byteBuffer = charset.encode(string); - return new String(byteBuffer.array()).substring(0, byteBuffer.limit()); - } -} diff --git a/source/test-resources/imapservice_test_folder_a.acp b/source/test-resources/imapservice_test_folder_a.acp deleted file mode 100755 index 11ba385da2..0000000000 Binary files a/source/test-resources/imapservice_test_folder_a.acp and /dev/null differ diff --git a/source/test-resources/load_test_data.acp b/source/test-resources/load_test_data.acp deleted file mode 100755 index 1e34899c40..0000000000 Binary files a/source/test-resources/load_test_data.acp and /dev/null differ