diff --git a/.externalToolBuilders/JibX.launch b/.externalToolBuilders/JibX.launch index 7ef90b1e04..fd24eb5fe2 100644 --- a/.externalToolBuilders/JibX.launch +++ b/.externalToolBuilders/JibX.launch @@ -1,4 +1,4 @@ - + @@ -21,11 +21,12 @@ - + + - + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 40d333eb1b..3d2ac26355 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -417,9 +417,12 @@ - - org.alfresco.repo.imap.config.ImapConfigBean - + + org.alfresco.repo.imap.config.ImapConfigMountPointsBean + + + org.alfresco.repo.imap.config.ImapConfigBean + diff --git a/config/alfresco/bootstrap/imapScripts.xml b/config/alfresco/bootstrap/imapScripts.xml new file mode 100755 index 0000000000..62591a26cc --- /dev/null +++ b/config/alfresco/bootstrap/imapScripts.xml @@ -0,0 +1,27 @@ + + + + + ${imap.command_search.name} + ${imap.command_search.title} + ${imap.command_search.description} + contentUrl=classpath:alfresco/imap/scripts/command-search.js|mimetype=application/x-javascript|size=|encoding=UTF-8 + + + + ${imap.command_processor.name} + ${imap.command_processor.title} + ${imap.command_processor.description} + contentUrl=classpath:alfresco/imap/scripts/command-processor.js|mimetype=application/x-javascript|size=|encoding=UTF-8 + + + + ${imap.command_utils.name} + ${imap.command_utils.title} + ${imap.command_utils.description} + contentUrl=classpath:alfresco/imap/scripts/command-utils.js|mimetype=application/x-javascript|size=|encoding=UTF-8 + + + \ No newline at end of file diff --git a/config/alfresco/bootstrap/imapSpaces.xml b/config/alfresco/bootstrap/imapSpaces.xml new file mode 100755 index 0000000000..583ae99a82 --- /dev/null +++ b/config/alfresco/bootstrap/imapSpaces.xml @@ -0,0 +1,336 @@ + + + + + ${spaces.imapConfig.name} + space-icon-default + ${spaces.imapConfig.name} + ${spaces.imapConfig.description} + + + + ${spaces.imap_templates.name} + space-icon-default + ${spaces.imap_templates.name} + ${spaces.imap_templates.description} + + + + + + + + + true + Email template used to generate the "multipart/alternative" IMAP message body ("text/plain" part). + contentUrl=classpath:alfresco/templates/imap/imap_message_text_plain.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=en_US_ + emailbody-textplain.ftl + + emailbody-textplain.ftl + + + + + + + + + + + true + Email template used to generate the "multipart/alternative" IMAP message body ("text/html" part) + contentUrl=classpath:alfresco/templates/imap/imap_message_text_html.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=en_US_ + emailbody-texthtml.ftl + + emailbody-texthtml.ftl + + + + + + + + + + + + + + + + + + + + space-icon-default + ${spaces.emailActions.description} + command + ${spaces.emailActions.name} + + + + + + + + + + 6b256b76-fb79-429e-b773-e33fc0588f34 + + + + + + + + + + + + Start Command Processor + false + false + + + inbound + + + false + cd446c77-d031-49ed-b26b-2efa9612ee38 + + + + + + + + + + false + + + + + + + composite-action + 1631059f-02a3-462f-9344-52e1a1f6507e + + + + + + + + + + no-condition + false + eed3c378-2351-4735-ac72-6cfb7cfd52a1 + + + + + + + + + + + false + + + + + + + script + bed70283-5f54-4122-8d2e-1f8330d43970 + + + + + + + + + + + /app:company_home/app:dictionary/app:scripts/cm:command-processor.js + + script-ref + 3afcb3ef-ad28-412d-b7d8-04aeb9c20174 + + + + + + + + + + + + + + + + + + + + + + + + + + + + space-icon-default + ${spaces.searchAction.description} + ${spaces.searchAction.name} + + + + + + + + + + 879a6234-e586-4fc2-af5c-0c7b0839b85a + + + + + + + + + + + + Process Command + false + false + + + inbound + + + false + 18f42dda-060b-40d5-9cf1-32a6713f7a4c + + + + + + + + + + false + + + + + + + composite-action + 9b4b8128-4066-4772-857f-d798976e8a76 + + + + + + + + + + no-condition + false + ff5e3091-c1b3-4054-9215-bc42ca636661 + + + + + + + + + + + + false + + + + + + + script + df4f46b9-b031-4518-bfcb-65fb5dee3b12 + + + + + + + + + + + /app:company_home/app:dictionary/app:scripts/cm:command-search.js + + script-ref + d7d7eb0e-e1b9-4503-928f-fa84c9fe412f + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index 95718ae244..18a564d227 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -38,10 +38,10 @@ - + - + @@ -288,7 +288,7 @@ parent="baseContentTransformer" > - + application/pdf text/plain @@ -306,15 +306,15 @@ parent="baseContentTransformer" > - + application/pdf image/jpeg - + application/pdf image/png - + application/pdf image/gif @@ -362,7 +362,7 @@ - @@ -451,6 +451,10 @@ 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 deleted file mode 100755 index 307e8f2996..0000000000 --- a/config/alfresco/extension/imap-bootsrap-context.xml.sample +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - 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 963f147c10..48844f53ec 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, false); + createEmail(message, message, message); return; } @@ -54,7 +54,7 @@ function processCommand() if (commandFolder == null) { var message = "Command Processor: wrong command=" + command; - createEmail(message, message, message, false); + createEmail(message, message, message); logger.log(message); return; } diff --git a/config/alfresco/imap/scripts/command-search.js b/config/alfresco/imap/scripts/command-search.js index 5ffecaa983..2f273d3eaa 100755 --- a/config/alfresco/imap/scripts/command-search.js +++ b/config/alfresco/imap/scripts/command-search.js @@ -75,7 +75,17 @@ function createResponseTextHtml(nodes) */ function createContentTextHtml(nodes) { - var content = "Command: " + title + "\n

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

\n"; content += "\n"; content += ""; content += ""; @@ -83,7 +93,7 @@ function createContentTextHtml(nodes) content += ""; content += ""; content += ""; - content += "\n" + content += "\n" + ""; for (var i = 0; i < nodes.length; i++) @@ -133,7 +143,7 @@ function commandSearch(params) if (query == null) { - createEmail(errorParameter, errorParameter, errorParameter, false); + createEmail(errorParameter, errorParameter, errorParameter); return; } @@ -154,7 +164,7 @@ function commandSearch(params) } else { - createEmail(errorXPathNotValid, errorXPathNotValid, errorXPathNotValid, false); + createEmail(errorXPathNotValid, errorXPathNotValid, errorXPathNotValid); return; } break; @@ -180,8 +190,8 @@ function commandSearch(params) createEmail(message, message, subject); return; } - /*createEmail(createResponseTextHtml(nodes), createContentTextPlain(nodes), subject, true);*/ - createEmail(createContentTextHtml(nodes), createContentTextPlain(nodes), subject, false); + /*createEmail(createContentTextPlain(nodes), createResponseTextHtml(nodes), subject);*/ + createEmail(createContentTextPlain(nodes), createContentTextHtml(nodes), subject); } /** * Decode subject @@ -224,13 +234,13 @@ function main() else { var message = unknownCommand + ": '" + title + "'"; - createEmail(message, message, message, false); + createEmail(message, message, message); } } else { var message = unknownCommand + ": '" + title + "'"; - createEmail(message, message, message, false); + createEmail(message, message, message); } document.remove(); diff --git a/config/alfresco/imap/scripts/command-utils.js b/config/alfresco/imap/scripts/command-utils.js index 64039946f5..0ef5704ec5 100755 --- a/config/alfresco/imap/scripts/command-utils.js +++ b/config/alfresco/imap/scripts/command-utils.js @@ -1,56 +1,57 @@ /** * Create e-mail -* contentTextHtml (string) html content -* contentTextPlain (string) text content +* contentEML (string) content message */ -function createEmail(contentTextHtml, contentTextPlain, subject, templateUsed) +function createEmail(messageTXT, messageHTML, subject) { var command = document.properties["cm:title"]; var userName = person.properties["cm:userName"]; - var inboxFolder = companyhome.childByNamePath("IMAP Home/" + userName + "/INBOX"); + var imapRoot = imap.getImapHomeRef("INBOX", userName); + var inboxFolder = imapRoot.childByNamePath("INBOX"); + if (inboxFolder == null) { logger.log("Command Processor: INBOX folder does't exists."); return; } - var nextMessageUID = inboxFolder.properties["imap:nextMessageUID"]; - inboxFolder.properties["imap:nextMessageUID"] = nextMessageUID + 1; - inboxFolder.save(); - - var response = inboxFolder.createNode("response" + Date.now(), "imap:imapContent"); + var response = inboxFolder.createNode("response" + Date.now() + ".eml", "cm:content"); response.properties["imap:messageFrom"] = "command@alfresco.com"; response.properties["imap:messageSubject"] = subject; response.properties["imap:messageTo"] = document.properties["cm:originator"]; response.properties["imap:messageCc"] = ""; - response.properties["imap:messageUID"] = nextMessageUID; + response.addAspect("imap:imapContent", null); + response.content = createRFC822Message("command@alfresco.com", document.properties["cm:originator"], subject, messageTXT, messageHTML); response.save(); - - var textBody = response.createNode("Body.txt", "imap:imapBody"); - textBody.content = contentTextPlain; - textBody.save(); - - var htmlBody = response.createNode("Body.html", "imap:imapBody"); - if (templateUsed == true) - { - htmlBody.content = contentTextHtml; } - else - { - htmlBody.content = "" + - "" + - "" + - "" + - "" + - "" + - "
" + contentTextHtml + "
"; - } - htmlBody.save(); +function createRFC822Message(from, to, subject, textPart, htmlPart) +{ + var id = new Number(Date.now()).toString(16); + var boundary = "----------" + id; + var date = new Date().toGMTString(); + var messageHeaders = "MIME-Version: 1.0\r\n" + + "Date: " + date + "\r\n" + + "From: " + from + "\r\n" + + "To: " + to + "\r\n" + + "Subject: " + subject + "\r\n" + + "Message-ID: " + id + "\r\n" + + "X-Priority: 3 (Normal)\r\n" + + "Content-Type: multipart/alternative; boundary=\"" + boundary + "\"\r\n\r\n"; + var messageBody = ""; + messageBody += messageHeaders; + messageBody += "--" + boundary + "\r\n"; + messageBody += "Content-Type: text/plain; charset=utf-8\r\n"; + //TODO Content-Transfer-Encoding + messageBody += "\r\n"; + messageBody += textPart + "\r\n\r\n"; + messageBody += "--" + boundary + "\r\n"; + messageBody += "Content-Type: text/html; charset=utf-8\r\n"; + //TODO Content-Transfer-Encoding + messageBody += "\r\n"; + messageBody += htmlPart + "\r\n\r\n"; + messageBody += "--" + boundary + "--\r\n\r\n"; + return messageBody; } \ No newline at end of file diff --git a/config/alfresco/import-export-context.xml b/config/alfresco/import-export-context.xml index d2bf98e1da..8d23604302 100644 --- a/config/alfresco/import-export-context.xml +++ b/config/alfresco/import-export-context.xml @@ -35,13 +35,13 @@ - + - + - + @@ -53,7 +53,7 @@ - + @@ -76,13 +76,13 @@ - + - + - + @@ -94,7 +94,7 @@ - + @@ -125,23 +125,23 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + - + system://system @@ -169,8 +169,8 @@ versions2 - - + + @@ -207,7 +207,7 @@ - + @@ -233,16 +233,16 @@ en_GB --> - + - - - - + + + + ${alfresco_user_store.store} @@ -327,9 +327,10 @@ ${spaces.user_homes.childname} ${spaces.sites.childname} ${spaces.templates.email.invite.childname} - ${spaces.imap_home.childname} ${spaces.imapConfig.childname} ${spaces.imap_templates.childname} + ${spaces.emailActions.childname} + ${spaces.searchAction.childname} @@ -509,6 +510,18 @@ /${system.system_container.childname} alfresco/bootstrap/alfrescoAuthorityStore.xml + + + /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.scripts.childname} + alfresco/bootstrap/imapScripts.xml + alfresco/messages/bootstrap-imapScripts + + + + /${spaces.company_home.childname}/${spaces.dictionary.childname} + alfresco/bootstrap/imapSpaces.xml + alfresco/messages/bootstrap-spaces + diff --git a/config/alfresco/messages/bootstrap-spaces.properties b/config/alfresco/messages/bootstrap-spaces.properties index dabd16aa08..289b89d3db 100644 --- a/config/alfresco/messages/bootstrap-spaces.properties +++ b/config/alfresco/messages/bootstrap-spaces.properties @@ -12,6 +12,12 @@ spaces.imapConfig.description=Imap Configs spaces.imap_templates.name=Templates spaces.imap_templates.description=Templates for IMAP generated messages +spaces.emailActions.name=Email Actions +spaces.emailActions.description=Email Actions + +spaces.searchAction.name=search +spaces.searchAction.description=search + spaces.templates.name=Space Templates spaces.templates.description=Space folder templates diff --git a/config/alfresco/messages/imap-service.properties b/config/alfresco/messages/imap-service.properties index fdfd12856a..ce2e6c5a1e 100755 --- a/config/alfresco/messages/imap-service.properties +++ b/config/alfresco/messages/imap-service.properties @@ -1,10 +1,14 @@ -# -# Imap I18N messages -# - -# Information messages. prefix 'imap.server.info' -imap.server.info.message_body_not_found = "The message body parts are not found." - -# Error messages. prefix 'imap.server.error' -imap.server.error.properties_dont_exist = "Appropriate properties do not exist." - +# +# Imap I18N messages +# + +# Information messages. prefix 'imap.server.info' +imap.server.info.message_body_not_found = "The message body parts are not found." + +# Error messages. prefix 'imap.server.error' +imap.server.error.properties_dont_exist = "Appropriate properties do not exist." +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/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index 258235791c..f6594c093b 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -266,10 +266,6 @@ patch.imapFolders.description=Creates folders tree necessary for IMAP functional patch.imapFolders.result.exists=The 'Imap Configs' folder already exists patch.imapFolders.result.created=The 'Imap Configs' folder was successfully created -patch.imapUserFolders.description=Creates folders tree necessary for IMAP functionality -patch.imapUserFolders.result.exists=The 'IMAP Home' folder already exists -patch.imapUserFolders.result.created=The 'IMAP Home' folder was successfully created - patch.zonedAuthorities.description=Adds the remodelled cm:authority container to the spaces store patch.authorityMigration.description=Copies any old authorities from the user store to the spaces store. diff --git a/config/alfresco/mimetype/mimetype-map.xml b/config/alfresco/mimetype/mimetype-map.xml index 1e63fbff57..025d356983 100644 --- a/config/alfresco/mimetype/mimetype-map.xml +++ b/config/alfresco/mimetype/mimetype-map.xml @@ -1,44 +1,44 @@ - + - - - eps - - - - jp2 - jpx - jpm - jpc - j2k - jpf - - - - mpp - - - - psd - - - - fm - - - - pmd - pm6 - p65 - pm - - - - prn - - + + + eps + + + + jp2 + jpx + jpm + jpc + j2k + jpf + + + + mpp + + + + psd + + + + fm + + + + pmd + pm6 + p65 + pm + + + + prn + + txt csv @@ -65,7 +65,7 @@ xhtml - + ai ps @@ -78,12 +78,12 @@ acp - - vsd - - - xdp - + + vsd + + + xdp + au snd @@ -295,7 +295,9 @@ swf -            flv         + +            flv +                 tar @@ -353,6 +355,9 @@ dwt + eml + + msg diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml index 9159902644..05f52e88ca 100644 --- a/config/alfresco/model-specific-services-context.xml +++ b/config/alfresco/model-specific-services-context.xml @@ -118,4 +118,18 @@ + + + + + + ImapService + + + + org.alfresco.repo.imap.ImapService + + + + diff --git a/config/alfresco/model/imapModel.xml b/config/alfresco/model/imapModel.xml index ee93e810b2..57dab7b258 100755 --- a/config/alfresco/model/imapModel.xml +++ b/config/alfresco/model/imapModel.xml @@ -19,7 +19,7 @@ IMAP Folder cm:folder - + d:boolean @@ -30,29 +30,6 @@ - - IMAP File - cm:folder - - - - d:text - - - d:text - - - d:text - - - d:text - - - - imap:flaggable - - - Attachment to the IMAP message cm:content @@ -69,7 +46,59 @@ - + + + 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 + + + + + @@ -90,10 +119,9 @@ d:boolean - - + diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index c39850342f..9ac3ad18f4 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -1943,5 +1943,25 @@ classpath:alfresco/dbscripts/upgrade/3.2/${db.script.dialect}/jbpm-upgrade.sql + + + patch.imapFolders + patch.imapFolders.description + 0 + 2018 + 2019 + + + + + + + + + + alfresco/templates/imap/imap_config_space.acp + alfresco/templates/imap/email_actions_space.acp + alfresco/templates/imap/command_processor_scripts.acp + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index d86f406568..b39d7312bf 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -63,7 +63,7 @@ index.tracking.minRecordPurgeAgeDays=30 system.bootstrap.config_check.strict=true # The name of the cluster -# Leave this empty to disable cluster entry +# Leave this empty to disable cluster entry alfresco.cluster.name= # JGroups configuration (http://www.jgroups.org) @@ -231,6 +231,8 @@ spaces.dictionary.childname=app:dictionary spaces.templates.childname=app:space_templates spaces.imapConfig.childname=app:imap_configs spaces.imap_templates.childname=app:imap_templates +spaces.emailActions.childname=app:email_actions +spaces.searchAction.childname=cm:search spaces.templates.content.childname=app:content_templates spaces.templates.email.childname=app:email_templates spaces.templates.rss.childname=app:rss_templates @@ -240,7 +242,6 @@ spaces.wcm.childname=app:wcm spaces.wcm_content_forms.childname=app:wcm_forms spaces.content_forms.childname=app:forms spaces.user_homes.childname=app:user_homes -spaces.imap_home.childname=imap:imap_home spaces.sites.childname=st:sites spaces.templates.email.invite.childname=cm:invite @@ -274,10 +275,10 @@ avm.remote.idlestream.timeout=30000 # Note: Link Validation is disabled by default (as per poll interval = 0) # # linkvalidation.pollInterval - Poll interval to check getLatestSnapshotID (in milliseconds), eg. 5000 for 5 sec interval -# If pollInterval is 0, link validation is disabled. +# If pollInterval is 0, link validation is disabled. # # linkvalidation.retryInterval - Retry interval (Virtualization server is not accessible or an error has occurred -# during link validation. +# during link validation. # # linkvalidation.disableOnFail - If set to TRUE link validation service will be terminated if an error will be occurred. @@ -337,14 +338,22 @@ nfs.user.mappings.default.gid=0 # IMAP imap.server.enabled=false imap.server.port=143 +imap.server.host=localhost +imap.server.attachments.extraction.enabled=true # Default IMAP mount points -imap.server.mountPoints=Repository_virtual,Repository_archive +imap.server.mountPoints=Repository_virtual,Repository_archive,Repository_mixed imap.server.mountPoints.default.store=${spaces.store} imap.server.mountPoints.default.rootPath=/${spaces.company_home.childname} imap.server.mountPoints.default.mode=virtual imap.server.mountPoints.value.Repository_virtual.mode=virtual imap.server.mountPoints.value.Repository_archive.mode=archive +imap.server.mountPoints.value.Repository_mixed.mode=mixed + +# Folders that will be excluded from the automatic extraction capability +#imap.ignore.extraction=adminInbox +#imap.ignore.extraction.value.adminInbox.store=${spaces.store} +#imap.ignore.extraction.value.adminInbox.rootPath=/${spaces.company_home.childname}/imap:imap_home/cm:admin/cm:INBOX # Activity feed max size and max age (eg. 44640 mins = 31 days) activities.feed.max.size=100 diff --git a/config/alfresco/script-services-context.xml b/config/alfresco/script-services-context.xml index bb85f19507..876a767b68 100644 --- a/config/alfresco/script-services-context.xml +++ b/config/alfresco/script-services-context.xml @@ -3,8 +3,8 @@ - - + + javascript @@ -13,7 +13,7 @@ - + javascript @@ -71,6 +71,21 @@ + + + imap + + + + + + + + + ${spaces.store} + + + search @@ -79,7 +94,7 @@ - + ${spaces.store} diff --git a/config/alfresco/subsystems/imap/default/imap-server-context.xml b/config/alfresco/subsystems/imap/default/imap-server-context.xml index 471428e36c..d6abcf447d 100755 --- a/config/alfresco/subsystems/imap/default/imap-server-context.xml +++ b/config/alfresco/subsystems/imap/default/imap-server-context.xml @@ -3,49 +3,19 @@ - - - - alfresco.messages.imap-service - + + + - - - - - - - - - - - - - - - - - - - - - ${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.enabled} + + ${imap.server.host} + ${imap.server.port} @@ -58,8 +28,13 @@ - - + + + + + + alfresco.messages.imap-service + @@ -67,7 +42,7 @@ - + virtual @@ -78,7 +53,7 @@ /${spaces.company_home.childname} - + archive @@ -93,17 +68,101 @@ + + + + + + + ${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} + + + ${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 42d301e12e..d7b69af91e 100755 --- a/config/alfresco/subsystems/imap/default/imap-server.properties +++ b/config/alfresco/subsystems/imap/default/imap-server.properties @@ -1,4 +1,5 @@ imap.server.enabled=false +imap.server.host=localhost imap.server.port=143 #imap.server.web.application.context.url=http://localhost:8080/alfresco diff --git a/config/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml b/config/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml index c212ccca4b..0b44032a2e 100644 --- a/config/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml +++ b/config/alfresco/subsystems/thirdparty/default/imagemagick-transform-context.xml @@ -20,7 +20,7 @@ - + ${img.root} diff --git a/config/alfresco/templates/imap/command_processor_scripts.acp b/config/alfresco/templates/imap/command_processor_scripts.acp index dda4f430b3..f1427d193d 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/email_actions_space.acp b/config/alfresco/templates/imap/email_actions_space.acp index f54d9069c0..60718ddd8a 100755 Binary files a/config/alfresco/templates/imap/email_actions_space.acp and b/config/alfresco/templates/imap/email_actions_space.acp differ diff --git a/config/alfresco/templates/imap/imap_config_space.acp b/config/alfresco/templates/imap/imap_config_space.acp index d2dee2323c..c27fb173c3 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 5af0c44148..41c0433af7 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 b8edc2195e..9bd374f831 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}${document.displayPath} +Content folder: ${contextUrl}/navigate/browse${document.displayPath} Content URL: ${contextUrl}${document.url} Download URL: ${contextUrl}${document.downloadUrl} WebDAV URL: ${contextUrl}${document.webdavUrl} diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index e48473c0b3..bc547521aa 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -19,4 +19,4 @@ version.build=@build-number@ # Schema number -version.schema=2018 +version.schema=2019 diff --git a/source/java/org/alfresco/model/ImapModel.java b/source/java/org/alfresco/model/ImapModel.java index f918e53452..3dfce73713 100755 --- a/source/java/org/alfresco/model/ImapModel.java +++ b/source/java/org/alfresco/model/ImapModel.java @@ -35,14 +35,18 @@ public interface ImapModel { static final String IMAP_MODEL_1_0_URI = "http://www.alfresco.org/model/imap/1.0"; - static final QName ASPECT_IMAP_FOLDER_SUBSCRIBED = QName.createQName(IMAP_MODEL_1_0_URI, "subscribed"); + static final QName ASPECT_IMAP_FOLDER_NONSUBSCRIBED = QName.createQName(IMAP_MODEL_1_0_URI, "nonSubscribed"); static final QName ASPECT_IMAP_FOLDER_NONSELECTABLE = QName.createQName(IMAP_MODEL_1_0_URI, "nonselectable"); - static final QName TYPE_IMAP_CONTENT = QName.createQName(IMAP_MODEL_1_0_URI, "imapContent"); + static final QName ASPECT_IMAP_CONTENT = QName.createQName(IMAP_MODEL_1_0_URI, "imapContent"); static final QName PROP_MESSAGE_FROM = QName.createQName(IMAP_MODEL_1_0_URI, "messageFrom"); static final QName PROP_MESSAGE_TO = QName.createQName(IMAP_MODEL_1_0_URI, "messageTo"); static final QName PROP_MESSAGE_CC = QName.createQName(IMAP_MODEL_1_0_URI, "messageCc"); static final QName PROP_MESSAGE_SUBJECT = QName.createQName(IMAP_MODEL_1_0_URI, "messageSubject"); + static final QName PROP_MESSAGE_ID = QName.createQName(IMAP_MODEL_1_0_URI, "messageId"); + static final QName PROP_THREAD_INDEX = QName.createQName(IMAP_MODEL_1_0_URI, "threadIndex"); + static final QName ASSOC_IMAP_ATTACHMENT = QName.createQName(IMAP_MODEL_1_0_URI, "attachment"); + static final QName ASSOC_IMAP_ATTACHMENTS_FOLDER = QName.createQName(IMAP_MODEL_1_0_URI, "attachmentsFolder"); static final QName ASPECT_FLAGGABLE = QName.createQName(IMAP_MODEL_1_0_URI, "flaggable"); static final QName PROP_FLAG_ANSWERED = QName.createQName(IMAP_MODEL_1_0_URI, "flagAnswered"); diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ImapUsersPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ImapUsersPatch.java deleted file mode 100755 index 699ffa3e8d..0000000000 --- a/source/java/org/alfresco/repo/admin/patch/impl/ImapUsersPatch.java +++ /dev/null @@ -1,257 +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.admin.patch.impl; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import org.alfresco.i18n.I18NUtil; -import org.alfresco.model.ApplicationModel; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.admin.patch.AbstractPatch; -import org.alfresco.repo.importer.ImporterBootstrap; -import org.alfresco.service.cmr.admin.PatchException; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.springframework.context.MessageSource; - -/** - * @author Dmitry Vaserin - */ -public class ImapUsersPatch extends AbstractPatch -{ - // messages' ids - private static final String MSG_EXISTS = "patch.imapUserFolders.result.exists"; - private static final String MSG_CREATED = "patch.imapUserFolders.result.created"; - - // folders' names for path building - private static final String PROPERTY_COMPANY_HOME_CHILDNAME = "spaces.company_home.childname"; - private static final String PROPERTY_IMAP_HOME_FOLDER_CHILDNAME = "spaces.imap_home.childname"; - - private static final String PROPERTY_ICON = "space-icon-default"; - private static final String MSG_IMAP_HOME_FOLDER_NAME = "spaces.imap_home.name"; - private static final String MSG_IMAP_HOME_FOLDER_DESCRIPTION = "spaces.imap_home.description"; - - private static final String INBOX_NAME = "INBOX"; - private static final String INBOX_DECSRIPTION = "INBOX mail box"; - - private ImporterBootstrap importerBootstrap; - private MessageSource messageSource; - - protected NodeRef companyHomeNodeRef; - protected Properties configuration; - protected NodeRef imapHomeNodeRef; - - public void setImporterBootstrap(ImporterBootstrap importerBootstrap) - { - this.importerBootstrap = importerBootstrap; - } - - public void setMessageSource(MessageSource messageSource) - { - this.messageSource = messageSource; - } - - /** - * Ensure that required common properties have been set - */ - protected void checkCommonProperties() throws Exception - { - checkPropertyNotNull(importerBootstrap, "importerBootstrap"); - checkPropertyNotNull(namespaceService, "namespaceService"); - checkPropertyNotNull(searchService, "searchService"); - checkPropertyNotNull(nodeService, "nodeService"); - checkPropertyNotNull(messageSource, "messageSource"); - } - - /** - * Extracts pertinent references and properties that are common to execution of this and derived patches. - */ - protected void setUp() throws Exception - { - // get the node store that we must work against - StoreRef storeRef = importerBootstrap.getStoreRef(); - if (storeRef == null) - { - throw new PatchException("Bootstrap store has not been set"); - } - NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); - - this.configuration = importerBootstrap.getConfiguration(); - // get the association names that form the path - String companyHomeChildName = configuration.getProperty(PROPERTY_COMPANY_HOME_CHILDNAME); - if (companyHomeChildName == null || companyHomeChildName.length() == 0) - { - throw new PatchException("Bootstrap property '" + PROPERTY_COMPANY_HOME_CHILDNAME + "' is not present"); - } - String imapHomeChildName = configuration.getProperty(PROPERTY_IMAP_HOME_FOLDER_CHILDNAME); - if (imapHomeChildName == null || imapHomeChildName.length() == 0) - { - throw new PatchException("Bootstrap property '" + PROPERTY_IMAP_HOME_FOLDER_CHILDNAME + "' is not present"); - } - - // build the search string to get the company home node - StringBuilder sb = new StringBuilder(256); - sb.append("/").append(companyHomeChildName); - String xpath = sb.toString(); - // get the company home - List nodeRefs = searchService.selectNodes(storeRootNodeRef, xpath, null, namespaceService, false); - if (nodeRefs.size() == 0) - { - throw new PatchException("XPath didn't return any results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath); - } - else if (nodeRefs.size() > 1) - { - throw new PatchException("XPath returned too many results: \n" + " root: " + storeRootNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " + nodeRefs); - } - this.companyHomeNodeRef = nodeRefs.get(0); - - xpath = imapHomeChildName; - nodeRefs = searchService.selectNodes(companyHomeNodeRef, xpath, null, namespaceService, false); - if (nodeRefs.size() > 1) - { - throw new PatchException("XPath returned too many results: \n" + " dictionary node: " + companyHomeNodeRef + "\n" + " xpath: " + xpath + "\n" + " results: " - + nodeRefs); - } - else if (nodeRefs.size() == 0) - { - // the node does not exist - this.imapHomeNodeRef = null; - } - else - { - this.imapHomeNodeRef = nodeRefs.get(0); - } - } - - @Override - protected String applyInternal() throws Exception - { - // properties must be set - checkCommonProperties(); - // get useful values - setUp(); - - String msg = null; - - if (imapHomeNodeRef == null) - { - // create it - createImapHomeFolders(); - msg = I18NUtil.getMessage(MSG_CREATED, imapHomeNodeRef); - } - else - { - // it already exists - msg = I18NUtil.getMessage(MSG_EXISTS, imapHomeNodeRef); - } - - // done - return msg; - } - - private void createImapHomeFolders() - { - // get required properties - String imapHomeChildName = configuration.getProperty(PROPERTY_IMAP_HOME_FOLDER_CHILDNAME); - if (imapHomeChildName == null) - { - throw new PatchException("Bootstrap property '" + PROPERTY_IMAP_HOME_FOLDER_CHILDNAME + "' is not present"); - } - - String name = messageSource.getMessage(MSG_IMAP_HOME_FOLDER_NAME, null, I18NUtil.getLocale()); - if (name == null || name.length() == 0) - { - throw new PatchException("Bootstrap property '" + MSG_IMAP_HOME_FOLDER_NAME + "' is not present"); - } - - String description = messageSource.getMessage(MSG_IMAP_HOME_FOLDER_DESCRIPTION, null, I18NUtil.getLocale()); - if (description == null || description.length() == 0) - { - throw new PatchException("Bootstrap property '" + MSG_IMAP_HOME_FOLDER_DESCRIPTION + "' is not present"); - } - - imapHomeNodeRef = createSpace(companyHomeNodeRef, name, description, imapHomeChildName); - - // Create IMAP Home and "INBOX" for each user - createImapUserHomes(); - - } - - private void createImapUserHomes() - { - StringBuilder query = new StringBuilder(128); - query.append("@").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:userName:*"); - - SearchParameters params = new SearchParameters(); - params.setLanguage(SearchService.LANGUAGE_LUCENE); - params.addStore(importerBootstrap.getStoreRef()); - params.setQuery(query.toString()); - - ResultSet results = searchService.query(params); - List people; - try - { - people = results.getNodeRefs(); - } - finally - { - results.close(); - } - - for (NodeRef nodeRef : people) - { - String userName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME); - String desc = userName + " " + messageSource.getMessage(MSG_IMAP_HOME_FOLDER_NAME, null, I18NUtil.getLocale()); - NodeRef userHome = createSpace(imapHomeNodeRef, userName, desc, userName); - // Create Inbox - createSpace(userHome, INBOX_NAME, INBOX_DECSRIPTION, INBOX_NAME); - - } - - } - - private NodeRef createSpace(NodeRef parent, String name, String desc, String childName) - { - Map properties = new HashMap(7); - properties.put(ContentModel.PROP_NAME, name); - properties.put(ContentModel.PROP_TITLE, name); - properties.put(ContentModel.PROP_DESCRIPTION, desc); - properties.put(ApplicationModel.PROP_ICON, PROPERTY_ICON); - // create the node - ChildAssociationRef childAssocRef = nodeService.createNode(parent, ContentModel.ASSOC_CONTAINS, QName.resolveToQName(namespaceService, childName), - ContentModel.TYPE_FOLDER, properties); - NodeRef result = childAssocRef.getChildRef(); - - // add the required aspects - nodeService.addAspect(result, ApplicationModel.ASPECT_UIFACETS, null); - return result; - } -} diff --git a/source/java/org/alfresco/repo/content/MimetypeMap.java b/source/java/org/alfresco/repo/content/MimetypeMap.java index ec80768e98..2d33dc2f82 100644 --- a/source/java/org/alfresco/repo/content/MimetypeMap.java +++ b/source/java/org/alfresco/repo/content/MimetypeMap.java @@ -79,6 +79,7 @@ public class MimetypeMap implements MimetypeService public static final String MIMETYPE_ATOM = "application/atom+xml"; public static final String MIMETYPE_RSS = "application/rss+xml"; public static final String MIMETYPE_RFC822 = "message/rfc822"; + public static final String MIMETYPE_OUTLOOK_MSG = "application/vnd.ms-outlook"; // Open Document public static final String MIMETYPE_OPENDOCUMENT_TEXT = "application/vnd.oasis.opendocument.text"; public static final String MIMETYPE_OPENDOCUMENT_TEXT_TEMPLATE = "application/vnd.oasis.opendocument.text-template"; diff --git a/source/java/org/alfresco/repo/content/transform/EMLTransformer.java b/source/java/org/alfresco/repo/content/transform/EMLTransformer.java new file mode 100755 index 0000000000..c0edd16798 --- /dev/null +++ b/source/java/org/alfresco/repo/content/transform/EMLTransformer.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.content.transform; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import javax.mail.Multipart; +import javax.mail.Part; +import javax.mail.Session; +import javax.mail.internet.MimeMessage; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.TransformationOptions; + +public class EMLTransformer extends AbstractContentTransformer2 +{ + public boolean isTransformable(String sourceMimetype, String targetMimetype, TransformationOptions options) + { + if (!MimetypeMap.MIMETYPE_RFC822.equals(sourceMimetype) || !MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(targetMimetype)) + { + // only support RFC822 -> TEXT + return false; + } + else + { + return true; + } + } + + @Override + protected void transformInternal(ContentReader reader, ContentWriter writer, TransformationOptions options) throws Exception + { + InputStream is = null; + try + { + is = reader.getContentInputStream(); + + MimeMessage mimeMessage = new MimeMessage(Session.getDefaultInstance(new Properties()), is); + + final StringBuilder sb = new StringBuilder(); + Object content = mimeMessage.getContent(); + if (content instanceof Multipart) + { + Multipart multipart = (Multipart) content; + Part part = multipart.getBodyPart(0); + + if (part.getContent() instanceof Multipart) + { + multipart = (Multipart) part.getContent(); + for (int i = 0, n = multipart.getCount(); i < n; i++) + { + part = multipart.getBodyPart(i); + if (part.getContentType().contains("text")) + { + sb.append(part.getContent().toString()).append("\n"); + + } + + } + + } + else if (part.getContentType().contains("text")) + { + sb.append(part.getContent().toString()); + + } + + } + else + { + sb.append(content.toString()); + } + + writer.putContent(sb.toString()); + } + finally + { + if (is != null) + { + try + { + is.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } + } + +} diff --git a/source/java/org/alfresco/repo/content/transform/MailContentTransformer.java b/source/java/org/alfresco/repo/content/transform/MailContentTransformer.java index d49e284eec..4edad6fe64 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_RFC822.equals(sourceMimetype) || + if (!MimetypeMap.MIMETYPE_OUTLOOK_MSG.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 new file mode 100755 index 0000000000..f03215c47e --- /dev/null +++ b/source/java/org/alfresco/repo/imap/AbstractImapFolder.java @@ -0,0 +1,695 @@ +/* + * 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 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 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 new file mode 100755 index 0000000000..de5bba509f --- /dev/null +++ b/source/java/org/alfresco/repo/imap/AbstractMimeMessage.java @@ -0,0 +1,371 @@ +/* + * 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 4455c0d665..855bae4403 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,17 +38,23 @@ public interface AlfrescoImapConst public static final String BODY_TEXT_PLAIN_NAME = "Body.txt"; public static final String BODY_TEXT_HTML_NAME = "Body.html"; public static final String MESSAGE_PREFIX = "Message_"; + public static final String EML_EXTENSION = ".eml"; // Separator for user enties in flag and subscribe properties public static final String USER_SEPARATOR = ";"; /** - * Defines {@link AlfrescoImapMailFolder} view mode as archive mode. Used for Email Archive View. + * Defines {@link AlfrescoImapFolder} view mode as archive mode. Used for Email Archive View. */ public static final String MODE_ARCHIVE = "archive"; /** - * Defines {@link AlfrescoImapMailFolder} view mode as virtual mode. Used for IMAP Virtualised View. + * Defines {@link AlfrescoImapFolder} view mode as virtual mode. Used for IMAP Virtualised View. */ public static final String MODE_VIRTUAL = "virtual"; + /** + * Defines {@link AlfrescoImapFolder} view mode as mixed mode. Used for IMAP Mixed View. + */ + public static final String MODE_MIXED = "mixed"; + // Default content model email message templates public static final String CLASSPATH_TEXT_PLAIN_TEMPLATE = "/alfresco/templates/imap/imap_message_text_plain.ftl"; diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java b/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java new file mode 100755 index 0000000000..f1fc0829fa --- /dev/null +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapFolder.java @@ -0,0 +1,1016 @@ +/* + * 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.OutputStream; +import java.io.UnsupportedEncodingException; +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.Flags; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.Part; +import javax.mail.internet.ContentType; +import javax.mail.internet.MimeMessage; +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.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.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.util.GUID; +import org.alfresco.util.Utf7; +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.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; + +/** + * Implementation of greenmail MailFolder. It represents an Alfresco content folder and handles + * appendMessage, copyMessage, expunge (delete), getMessages, getMessage and so requests. + * + * @author Mike Shavnev + */ +public class AlfrescoImapFolder extends AbstractImapFolder +{ + + private static Log logger = LogFactory.getLog(AlfrescoImapFolder.class); + + /** + * Reference to the {@link FileInfo} object representing the folder. + */ + private FileInfo folderInfo; + + /** + * Reference to the root node of the store where folder is placed. + */ + private NodeRef rootNodeRef; + + /** + * Name of the mailbox (e.g. "admin" for admin user). + */ + private String qualifiedMailboxName; + + /** + * Name of the folder. + */ + private String folderName; + + /** + * Defines view mode. Can be one of the following: {@link AlfrescoImapConst#MODE_ARCHIVE} or {@link AlfrescoImapConst#MODE_VIRTUAL}. + */ + private String viewMode; + + /** + * Name of the mount point. + */ + private String mountPointName; + + /** + * Reference to the {@link ImapService} object. + */ + private ImapService imapService; + + /** + * Defines whether the folder is selectable or not. + */ + 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 static final Flags PERMANENT_FLAGS = new Flags(); + + static + { + PERMANENT_FLAGS.add(Flags.Flag.ANSWERED); + PERMANENT_FLAGS.add(Flags.Flag.DELETED); + PERMANENT_FLAGS.add(Flags.Flag.DRAFT); + 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. + * + * @param qualifiedMailboxName - name of the mailbox (e.g. "admin" for admin user). + * @param folderInfo - reference to the {@link FileInfo} object representing the folder. + * @param folderName - name of the folder. + * @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. + */ + public AlfrescoImapFolder( + String qualifiedMailboxName, + FileInfo folderInfo, + String folderName, + String viewMode, + NodeRef rootNodeRef, + String mountPointName, + boolean extractAttachmentsEnabled, + ServiceRegistry serviceRegistry) + { + this(qualifiedMailboxName, folderInfo, folderName, viewMode, rootNodeRef, mountPointName, serviceRegistry, null, extractAttachmentsEnabled); + } + + /** + * Constructs {@link AlfrescoImapFolder} object. + * + * @param qualifiedMailboxName - name of the mailbox (e.g. "admin" for admin user). + * @param folderInfo - reference to the {@link FileInfo} object representing the folder. + * @param folderName - name of the folder. + * @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 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) + { + super(serviceRegistry); + this.qualifiedMailboxName = qualifiedMailboxName; + this.folderInfo = folderInfo; + this.rootNodeRef = rootNodeRef; + 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: + // C: 2 list "" "" + // S: * LIST () "." "" + // S: 2 OK LIST completed. + if (folderInfo != null) + { + if (selectable == null) + { + // isSelectable(); + Boolean storedSelectable = !serviceRegistry.getNodeService().hasAspect(folderInfo.getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE); + if (storedSelectable == null) + { + setSelectable(true); + } + else + { + setSelectable(storedSelectable); + } + } + else + { + setSelectable(selectable); + } + + AccessStatus status = serviceRegistry.getPermissionService().hasPermission(folderInfo.getNodeRef(), PermissionService.WRITE); + if (status == AccessStatus.DENIED) + { + readOnly = true; + } + else + { + readOnly = false; + } + + } + else + { + setSelectable(true); + } + } + + /** + * Appends message to the folder. + * + * @param message - message. + * @param flags - message flags. + * @param internalDate - not used. Current date used instead. + */ + @Override + protected long appendMessageInternal( + MimeMessage message, + Flags flags, + Date internalDate) + throws FileExistsException, FileNotFoundException, IOException, MessagingException + { + 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()); + + return newMessageUid; + } + + /** + * Copies message with the given UID to the specified {@link MailFolder}. + * + * @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 + { + AlfrescoImapFolder toImapMailFolder = (AlfrescoImapFolder) toFolder; + + NodeRef destFolderNodeRef = toImapMailFolder.getFolderInfo().getNodeRef(); + + SimpleStoredMessage message = messages.get(uid); + FileInfo sourceMessageFileInfo = ((AbstractMimeMessage) message.getMimeMessage()).getMessageInfo(); + + if (serviceRegistry.getNodeService().hasAspect(sourceMessageFileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_CONTENT)) + { + //Generate body of message + MimeMessage newMessage = new ImapModelMessage(sourceMessageFileInfo, serviceRegistry, true); + toImapMailFolder.appendMessageInternal(newMessage, message.getFlags(), new Date()); + } + else + { + serviceRegistry.getFileFolderService().copy(sourceMessageFileInfo.getNodeRef(), destFolderNodeRef, null); + } + } + + /** + * Marks all messages in the folder as deleted using {@link Flags.Flag#DELETED} flag. + */ + @Override + public void deleteAllMessagesInternal() throws FolderException + { + if (this.readOnly) + { + throw new FolderException("Can't delete all - Permission denied"); + } + + for (SimpleStoredMessage mess : messages.values()) + { + AbstractMimeMessage message = (AbstractMimeMessage) mess.getMimeMessage(); + FileInfo fileInfo = message.getMessageInfo(); + imapService.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 + { + if (this.readOnly) + { + throw new FolderException("Can't expunge - Permission denied"); + } + + Collection listMess = messages.values(); + for (SimpleStoredMessage mess : listMess) + { + + Flags flags = getFlags(mess); + if (flags.contains(Flags.Flag.DELETED)) + { + NodeRef nodeRef = ((AbstractMimeMessage) mess.getMimeMessage()).getMessageInfo().getNodeRef(); + serviceRegistry.getFileFolderService().delete(nodeRef); + } + } + } + + /** + * Returns the number of the first unseen message. + * + * @return Number of the first unseen message. + */ + @Override + protected int getFirstUnseenInternal() + { + return 0; + } + + /** + * 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 + { + // If MailFolder object is used to obtain hierarchy delimiter by LIST command: + // Example: + // C: 2 list "" "" + // S: * LIST () "." "" + // S: 2 OK LIST completed. + + if (rootNodeRef == null) + { + return ""; + } + + 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) + { + fullName.append(AlfrescoImapConst.HIERARCHY_DELIMITER); + if (isFirst) + { + fullName.append("\""); + isFirst = false; + if (mountPointName != null) + { + fullName.append(mountPointName); + } + else + { + fullName.append(path.getName()); + } + } + else + { + fullName.append(path.getName()); + } + } + fullName.append("\""); + if (logger.isDebugEnabled()) + { + logger.debug("fullName: " + fullName); + } + return Utf7.encode(fullName.toString(), Utf7.UTF7_MODIFIED); + } + + /** + * Returns message by its UID. + * + * @param uid - UID of the message. + * @return message. + * @throws MessagingException + */ + @Override + protected SimpleStoredMessage getMessageInternal(long uid) throws MessagingException + { + AbstractMimeMessage mes = (AbstractMimeMessage) messages.get(uid).getMimeMessage(); + FileInfo mesInfo = mes.getMessageInfo(); + return createImapMessage(mesInfo, uid, true); + } + + /** + * Returns count of the messages in the folder. + * + * @return Count of the messages. + */ + @Override + protected int getMessageCountInternal() + { + if (messages.size() == 0) + { + List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos); + } + if (logger.isDebugEnabled()) + { + logger.debug(folderInfo.getName() + " - Messages count:" + messages.size()); + } + return messages.size(); + } + + /** + * Returns UIDs of all messages in the folder. + * + * @return UIDS of the messages. + */ + @Override + protected long[] getMessageUidsInternal() + { + if (messages == null || messages.size() == 0) + { + List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos); + } + int len = messages.size(); + long[] uids = new long[len]; + Set keys = messages.keySet(); + int i = 0; + for (Long key : keys) + { + uids[i++] = key; + } + return uids; + } + + /** + * Returns list of all messages in the folder. + * + * @return list of {@link SimpleStoredMessage} objects. + */ + @Override + protected List getMessagesInternal() + { + List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + return getMessages(fileInfos); + } + + private List getMessages(List fileInfos) + { + if (fileInfos == null || fileInfos.size() == 0) + { + return Collections.emptyList(); + } + if (fileInfos.size() != messages.size()) + { + for (FileInfo fileInfo : fileInfos) + { + try + { + Long key = getMessageUid(fileInfo); + SimpleStoredMessage message = createImapMessage(fileInfo, key, false); + messages.put(key, message); + + // Saving message sequence number to cache + msnCache.put(key, messages.size()); + + if (logger.isDebugEnabled()) + { + logger.debug("Message added: " + fileInfo.getName()); + } + } + catch (MessagingException e) + { + logger.warn("Invalid message! File name:" + fileInfo.getName(), e); + } + } + } + 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) + { + if (messages == null || messages.size() == 0) + { + List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos); + } + List ret = new ArrayList(); + for (int i = 0; i < messages.size(); i++) + { + if (msgRangeFilter.includes(i + 1)) + { + ret.add(messages.get(i)); + } + } + + return ret; + } + + /** + * 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. + */ + @Override + protected int getMsnInternal(long uid) throws FolderException + { + Integer msn = msnCache.get(uid); + if (msn != null) + { + return msn; + } + throw new FolderException("No such message."); + } + + /** + * Returns folder name. + * + * @return folder name. + */ + @Override + protected String getNameInternal() + { + return folderName; + } + + /** + * Returns the list of messages that have no {@link Flags.Flag#DELETED} flag set for current user. + * + * @return the list of non-deleted messages. + */ + @Override + protected List getNonDeletedMessagesInternal() + { + List result = new ArrayList(); + + if (messages.size() == 0) + { + List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos); + } + + Collection values = messages.values(); + for (SimpleStoredMessage message : values) + { + if (!getFlags(message).contains(Flags.Flag.DELETED)) + { + result.add(message); + } + + } + if (logger.isDebugEnabled()) + { + logger.debug(folderInfo.getName() + " - Non deleted messages count:" + result.size()); + } + return result; + } + + /** + * Returns permanent flags. + * + * @return {@link Flags} object containing flags. + */ + @Override + protected Flags getPermanentFlagsInternal() + { + return PERMANENT_FLAGS; + } + + /** + * 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. + */ + @Override + protected int getRecentCountInternal(boolean reset) + { + if (messages.size() == 0) + { + List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos); + } + + int count = 0; + Collection values = messages.values(); + for (SimpleStoredMessage message : values) + { + if (getFlags(message).contains(Flags.Flag.RECENT)) + { + count++; + if (reset) + { + imapService.setFlag(((AbstractMimeMessage) message.getMimeMessage()).getMessageInfo(), Flags.Flag.RECENT, false); + } + } + + } + + if (logger.isDebugEnabled()) + { + logger.debug(folderInfo.getName() + " - Recent count: " + count + " reset: " + reset); + } + return count; + } + + /** + * Returns UIDNEXT value of the folder. + * + * @return UIDNEXT value. + */ + @Override + protected long getUidNextInternal() + { + return getUidValidity(); + } + + /** + * Returns UIDVALIDITY value of the folder. + * + * @return UIDVALIDITY value. + */ + @Override + protected long getUidValidityInternal() + { + return ((Date) serviceRegistry.getNodeService().getProperty(folderInfo.getNodeRef(), ContentModel.PROP_MODIFIED)).getTime(); + } + + /** + * 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. + */ + @Override + protected int getUnseenCountInternal() + { + if (messages.size() == 0) + { + List fileInfos = imapService.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); + getMessages(fileInfos); + } + + int count = 0; + Collection values = messages.values(); + for (SimpleStoredMessage message : values) + { + if (!getFlags(message).contains(Flags.Flag.SEEN)) + { + count++; + } + + } + if (logger.isDebugEnabled()) + { + logger.debug(folderInfo.getName() + " - Unseen count: " + count); + } + return count; + } + + /** + * 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. + * @throws FolderException + * @throws MessagingException + */ + @Override + protected void replaceFlagsInternal( + Flags flags, + long uid, + FolderListener silentListener, + boolean addUid) + throws FolderException, MessagingException + { + 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); + + Long uidNotification = addUid ? uid : null; + notifyFlagUpdate(msn, message.getFlags(), uidNotification, silentListener); + } + + /** + * 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. + * @throws MessagingException + * @throws FolderException + */ + @Override + protected void setFlagsInternal( + Flags flags, + boolean value, + long uid, + FolderListener silentListener, + boolean addUid) + throws MessagingException, 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); + + Long uidNotification = null; + if (addUid) + { + uidNotification = new Long(uid); + } + notifyFlagUpdate(msn, message.getFlags(), uidNotification, silentListener); + + } + + /** + * @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)) + { + return ((Date) serviceRegistry.getNodeService().getProperty(fileInfo.getNodeRef(), ContentModel.PROP_MODIFIED)).getTime(); + } + + return (Long) serviceRegistry.getNodeService().getProperty(fileInfo.getNodeRef(), ContentModel.PROP_NODE_DBID); + } + + private Flags getFlags(SimpleStoredMessage mess) + { + return ((AbstractMimeMessage) mess.getMimeMessage()).getFlags(); + } + + // ----------------------Getters and Setters---------------------------- + + public FileInfo getFolderInfo() + { + return folderInfo; + } + + public void setFolderName(String folderName) + { + this.folderName = folderName; + } + + public void setViewMode(String viewMode) + { + this.viewMode = viewMode; + } + + public void setMountPointName(String mountPointName) + { + this.mountPointName = mountPointName; + } + + public void setMountParent(NodeRef mountParent) + { + this.rootNodeRef = mountParent; + } + + /** + * Whether the folder is selectable. + * + * @return {@code boolean}. + */ + @Override + protected boolean isSelectableInternal() + { + + return this.selectable; + } + + /** + * Sets {@link #selectable} property. + * + * @param selectable - {@code boolean}. + */ + public void setSelectable(boolean selectable) + { + this.selectable = selectable; + // Map properties = folderInfo.getProperties(); + // properties.put(ImapModel.PROP_IMAP_FOLDER_SELECTABLE, this.selectable); + // imapHelper.setProperties(folderInfo, properties); + } + + /** + * Whether the folder is read-only for user. + * + * @return {@code boolean} + */ + @Override + protected 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/AlfrescoImapHostManager.java b/source/java/org/alfresco/repo/imap/AlfrescoImapHostManager.java index d158c1bf4a..57c2a4e417 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapHostManager.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapHostManager.java @@ -24,50 +24,26 @@ */ package org.alfresco.repo.imap; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; -import org.alfresco.model.ContentModel; -import org.alfresco.model.ImapModel; -import org.alfresco.repo.imap.config.ImapConfigBean; import org.alfresco.repo.imap.exception.AlfrescoImapFolderException; -import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileExistsException; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.model.FileNotFoundException; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.security.AccessStatus; -import org.alfresco.service.cmr.security.PermissionService; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.alfresco.service.transaction.TransactionService; import com.icegreen.greenmail.imap.AuthorizationException; import com.icegreen.greenmail.imap.ImapHostManager; import com.icegreen.greenmail.store.FolderException; import com.icegreen.greenmail.store.MailFolder; import com.icegreen.greenmail.user.GreenMailUser; -import com.icegreen.greenmail.util.GreenMailUtil; /** * @author Mike Shavnev */ public class AlfrescoImapHostManager implements ImapHostManager { - - private Log logger = LogFactory.getLog(AlfrescoImapHostManager.class); - - private ServiceRegistry serviceRegistry; - - private NodeService nodeService; - private FileFolderService fileFolderService; - private ImapHelper imapHelper; + private ImapService imapService; + private TransactionService transactionService; /** * Returns the hierarchy delimiter for mailboxes on this host. @@ -80,8 +56,9 @@ public class AlfrescoImapHostManager implements ImapHostManager } /** - * Returns an collection of mailboxes. Method searches mailboxes under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email - * Archive Views. This method serves LIST command of the IMAP protocol. + * Returns an collection of mailboxes. Method searches mailboxes under mount points defined for a specific user. + * Mount points include user's IMAP Virtualised Views and Email Archive Views. This method serves LIST command + * of the IMAP protocol. * * @param user User making the request * @param mailboxPattern String name of a mailbox possible including a wildcard. @@ -90,24 +67,27 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public Collection listMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException { - mailboxPattern = GreenMailUtil.convertFromUtf7(mailboxPattern); - - if (logger.isDebugEnabled()) + try { - logger.debug("Listing mailboxes: mailboxPattern=" + mailboxPattern); + return new ArrayList( + imapService.listMailboxes( + new AlfrescoImapUser( + user.getEmail(), + user.getLogin(), + user.getPassword()), + mailboxPattern)); } - mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern); - if (logger.isDebugEnabled()) + catch (Throwable e) { - logger.debug("Listing mailboxes: mailboxPattern in alfresco=" + mailboxPattern); + throw new FolderException(e.getMessage()); } - return listMailboxes(user, mailboxPattern, false); } /** - * Returns an collection of subscribed mailboxes. To appear in search result mailboxes should have {http://www.alfresco.org/model/imap/1.0}subscribed property specified for - * user. Method searches subscribed mailboxes under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email Archive Views. This - * method serves LSUB command of the IMAP protocol. + * Returns an collection of subscribed mailboxes. To appear in search result mailboxes should have + * {http://www.alfresco.org/model/imap/1.0}subscribed property specified for user. Method searches + * subscribed mailboxes under mount points defined for a specific user. Mount points include user's + * IMAP Virtualised Views and Email Archive Views. This method serves LSUB command of the IMAP protocol. * * @param user User making the request * @param mailboxPattern String name of a mailbox possible including a wildcard. @@ -116,258 +96,30 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public Collection listSubscribedMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException { - mailboxPattern = GreenMailUtil.convertFromUtf7(mailboxPattern); - - if (logger.isDebugEnabled()) + try + { + return new ArrayList( + imapService.listSubscribedMailboxes( + new AlfrescoImapUser( + user.getEmail(), + user.getLogin(), + user.getPassword()), + mailboxPattern)); + } + catch (Throwable e) { - logger.debug("Listing subscribed mailboxes: mailboxPattern=" + mailboxPattern); + throw new FolderException(e.getMessage()); } - mailboxPattern = imapHelper.getMailPathInRepo(mailboxPattern); - if (logger.isDebugEnabled()) - { - 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; - } - - /** - * Renames an existing mailbox. The specified mailbox must already exist, the requested name must not exist already but must be able to be created and the user must have rights - * to delete the existing mailbox and create a mailbox with the new name. Any inferior hierarchical names must also be renamed. If INBOX is renamed, the contents of INBOX are - * transferred to a new mailbox with the new name, but INBOX is not deleted. If INBOX has inferior mailbox these are not renamed. This method serves RENAME command of the IMAP - * protocol.

Method searches mailbox under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email Archive Views. + * Renames an existing mailbox. The specified mailbox must already exist, the requested name must not exist + * already but must be able to be created and the user must have rights to delete the existing mailbox and + * create a mailbox with the new name. Any inferior hierarchical names must also be renamed. If INBOX is renamed, + * the contents of INBOX are transferred to a new mailbox with the new name, but INBOX is not deleted. + * If INBOX has inferior mailbox these are not renamed. This method serves RENAME command of the IMAP + * protocol.

Method searches mailbox under mount points defined for a specific user. Mount points + * include user's IMAP Virtualised Views and Email Archive Views. * * @param user User making the request. * @param oldMailboxName String name of the existing folder @@ -375,102 +127,22 @@ public class AlfrescoImapHostManager implements ImapHostManager * @throws com.icegreen.greenmail.store.FolderException if an existing folder with the new name. * @throws AlfrescoImapFolderException if user does not have rights to create the new mailbox. */ - public void renameMailbox(GreenMailUser user, String oldMailboxName, String newMailboxName) throws FolderException, AuthorizationException { - oldMailboxName = GreenMailUtil.convertFromUtf7(oldMailboxName); - newMailboxName = GreenMailUtil.convertFromUtf7(newMailboxName); - if (logger.isDebugEnabled()) - { - logger.debug("Renaming folder: oldMailboxName=" + oldMailboxName + " newMailboxName=" + newMailboxName); - } - - AlfrescoImapMailFolder sourceNode = (AlfrescoImapMailFolder) getFolder(user, GreenMailUtil.convertInUtf7(oldMailboxName)); - - NodeRef root = imapHelper.getMailboxRootRef(oldMailboxName, user.getLogin()); - String mailboxRepoName = imapHelper.getMailPathInRepo(newMailboxName); - - StringTokenizer tokenizer = new StringTokenizer(mailboxRepoName, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER)); - - NodeRef parentNodeRef = root; - while (tokenizer.hasMoreTokens()) - { - String folderName = tokenizer.nextToken(); - - if (!tokenizer.hasMoreTokens()) - { try { - if (oldMailboxName.equalsIgnoreCase(AlfrescoImapConst.INBOX_NAME)) - { - // If you trying to rename INBOX - // - just copy it to another folder with new name - // and leave INBOX (with children) intact. - fileFolderService.copy(sourceNode.getFolderInfo().getNodeRef(), parentNodeRef, folderName); - List itemsForRemove = fileFolderService.list(sourceNode.getFolderInfo().getNodeRef()); - for (FileInfo fileInfo : itemsForRemove) - { - fileFolderService.delete(fileInfo.getNodeRef()); - } - + imapService.renameMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), oldMailboxName, newMailboxName); } - else + catch (Throwable e) { - 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"); - } - } - } + throw new FolderException(e.getMessage()); } - } /** - * Returns a reference to a newly created mailbox. The request should specify a mailbox that does not already exist on this server, that could exist on this server and that the - * user has rights to create. This method serves CREATE command of the IMAP protocol. + * Returns a reference to a newly created mailbox. The request should specify a mailbox that does not + * already exist on this server, that could exist on this server and that the user has rights to create. + * This method serves CREATE command of the IMAP protocol. * * @param user User making the request. * @param mailboxName String name of the target @@ -480,63 +152,19 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public MailFolder createMailbox(GreenMailUser user, String mailboxName) throws AuthorizationException, FolderException { - mailboxName = GreenMailUtil.convertFromUtf7(mailboxName); - if (logger.isDebugEnabled()) - { - logger.debug("Creating folder: " + mailboxName); + try + { + return imapService.createMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName); + } + catch (Throwable e) + { + throw new FolderException(e.getMessage()); } - - NodeRef root = imapHelper.getMailboxRootRef(mailboxName, user.getLogin()); - - String mountPointName = imapHelper.getMountPointName(mailboxName); - String mailboxRepoNam = imapHelper.getMailPathInRepo(mailboxName); - StringTokenizer tokenizer = new StringTokenizer(mailboxRepoNam, String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER)); - - NodeRef parentNodeRef = root; - - while (tokenizer.hasMoreTokens()) - { - String folderName = tokenizer.nextToken(); - - List folders = imapHelper.searchFolders(parentNodeRef, folderName, false, true); - - if (folders.size() == 0) - { - AccessStatus status = imapHelper.hasPermission(parentNodeRef, PermissionService.WRITE); - if (status == AccessStatus.DENIED) - { - if (logger.isDebugEnabled()) - { - logger.debug("Creating folder: Cant't create folder - Permission denied"); - } - throw new AlfrescoImapFolderException(AlfrescoImapFolderException.PERMISSION_DENIED); - } - - if (logger.isDebugEnabled()) - { - logger.debug("Create mailBox: " + mailboxName); - } - FileInfo mailFolder = FileFolderServiceImpl.makeFolders(fileFolderService, parentNodeRef, Arrays.asList(folderName), ContentModel.TYPE_FOLDER); - - return new AlfrescoImapMailFolder(user.getQualifiedMailboxName(), mailFolder, folderName, imapHelper.getViewMode(mailboxName), root, mountPointName, imapHelper); - - } - else - { - parentNodeRef = folders.get(0).getNodeRef(); - if (logger.isDebugEnabled()) - { - logger.debug("MailBox: " + folderName + " already exists"); - } - } - } - - throw new FolderException(FolderException.ALREADY_EXISTS_LOCALLY); } /** - * Deletes an existing MailBox. Specified mailbox must already exist on this server, and the user must have rights to delete it.

This method serves DELETE command of the - * IMAP protocol. + * Deletes an existing MailBox. Specified mailbox must already exist on this server, and the user + * must have rights to delete it.

This method serves DELETE command of the IMAP protocol. * * @param user User making the request. * @param mailboxName String name of the target @@ -544,41 +172,23 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public void deleteMailbox(GreenMailUser user, String mailboxName) throws FolderException, AuthorizationException { - AlfrescoImapMailFolder folder = (AlfrescoImapMailFolder) getFolder(user, mailboxName); - NodeRef nodeRef = folder.getFolderInfo().getNodeRef(); - - List childFolders = imapHelper.searchFolders(nodeRef, "*", false, false); - - if (childFolders.isEmpty()) - { - folder.signalDeletion(); - // Delete child folders and messages - fileFolderService.delete(nodeRef); - } - else - { - if (folder.isSelectable()) - { - // Delete all messages for this folder - // Don't delete subfolders and their messages - List messages = imapHelper.searchFiles(nodeRef, "*", ImapModel.TYPE_IMAP_CONTENT, false); - for (FileInfo message : messages) + try { - fileFolderService.delete(message.getNodeRef()); + imapService.deleteMailbox(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName); } - nodeService.addAspect(nodeRef, ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE, null); - } - else + catch (Throwable e) { - throw new FolderException(mailboxName + " - Can't delete a non-selectable store with children."); - } + throw new FolderException(e.getMessage()); } } /** - * Returns a reference to an existing Mailbox. The requested mailbox must already exists on this server and the requesting user must have at least lookup rights.

It is - * also can be used by to obtain hierarchy delimiter by the LIST command:

C: 2 list "" ""

S: * LIST () "." ""

S: 2 OK LIST completed.

Method searches - * mailbox under mount points defined for a specific user. Mount points include user's IMAP Virtualised Views and Email Archive Views. + * Returns a reference to an existing Mailbox. The requested mailbox must already exists on this server and the + * requesting user must have at least lookup rights.

It is also can be used by to obtain hierarchy delimiter + * by the LIST command:

C: 2 list "" ""

S: * LIST () "." ""

S: 2 OK LIST completed. + *

+ * Method searches mailbox under mount points defined for a specific user. Mount points include user's IMAP + * Virtualised Views and Email Archive Views. * * @param user User making the request. * @param mailboxName String name of the target. @@ -586,80 +196,22 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public MailFolder getFolder(GreenMailUser user, String mailboxName) { - mailboxName = GreenMailUtil.convertFromUtf7(mailboxName); - if (logger.isDebugEnabled()) - { - logger.debug("Getting folder: " + mailboxName); - } - - // If MailFolder object is used to obtain hierarchy delimiter by LIST command: - // Example: - // C: 2 list "" "" - // S: * LIST () "." "" - // S: 2 OK LIST completed. - if ("".equals(mailboxName)) - { - if (logger.isDebugEnabled()) - { - logger.debug("Request for the hierarchy delimiter"); + return imapService.getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailboxName); } - 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(GreenMailUser user, String mailboxName, boolean mustExist) throws FolderException - { - return getFolder(user, mailboxName); + public MailFolder getFolder(final GreenMailUser user, final 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()); + } } /** @@ -670,7 +222,14 @@ public class AlfrescoImapHostManager implements ImapHostManager */ public MailFolder getInbox(GreenMailUser user) throws FolderException { - return getFolder(user, AlfrescoImapConst.INBOX_NAME); + try + { + return getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), AlfrescoImapConst.INBOX_NAME); + } + catch (Throwable e) + { + throw new FolderException(e.getMessage()); + } } /** @@ -682,44 +241,23 @@ public class AlfrescoImapHostManager implements ImapHostManager } /** - * Subscribes a user to a mailbox. The mailbox must exist locally and the user must have rights to modify it.

This method serves SUBSCRIBE command of the IMAP protocol. + * Subscribes a user to a mailbox. The mailbox must exist locally and the user must have rights to modify it. + *

+ * This method serves SUBSCRIBE command of the IMAP protocol. * * @param user User making the request * @param mailbox String representation of a mailbox name. */ - public void subscribe(final GreenMailUser user, final String mailbox) throws FolderException + public void subscribe(GreenMailUser user, String mailbox) throws FolderException { - if (logger.isDebugEnabled()) + try { - logger.debug("Subscribing: " + mailbox); + imapService.subscribe(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailbox); + } + catch (Throwable e) + { + throw new FolderException(e.getMessage()); } - AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox); - nodeService.addAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED, null); -// This is a multiuser support. Commented due new requirements -// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() -// { -// public Void doWork() throws Exception -// { -// AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox); -// FileInfo fileInfo = mailFolder.getFolderInfo(); -// if (fileInfo != null) -// { -// String subscribedList = (String) nodeService.getProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED); -// if (subscribedList == null) -// { -// subscribedList = ""; -// } -// subscribedList = subscribedList.replaceAll(imapHelper.formatUserEntry(user.getLogin()), ""); -// subscribedList += imapHelper.formatUserEntry(user.getLogin()); -// nodeService.setProperty(fileInfo.getNodeRef(), ImapModel.PROP_IMAP_FOLDER_SUBSCRIBED, subscribedList); -// } -// else -// { -// logger.debug("MailBox: " + mailbox + "doesn't exsist. Maybe it was deleted earlier."); -// } -// return null; -// } -// }, AuthenticationUtil.getSystemUserName()); } /** @@ -728,137 +266,46 @@ public class AlfrescoImapHostManager implements ImapHostManager * @param user User making the request * @param mailbox String representation of a mailbox name. */ - public void unsubscribe(final GreenMailUser user, final String mailbox) throws FolderException + public void unsubscribe(GreenMailUser user, String mailbox) throws FolderException { - if (logger.isDebugEnabled()) + try { - logger.debug("Unsubscribing: " + mailbox); + imapService.unsubscribe(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), mailbox); + } + catch (Throwable e) + { + throw new FolderException(e.getMessage()); } - AlfrescoImapMailFolder mailFolder = (AlfrescoImapMailFolder) getFolder(user, mailbox); - nodeService.removeAspect(mailFolder.getFolderInfo().getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_SUBSCRIBED); - -// This is a multiuser support. Commented due new requirements -// AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() -// { -// 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 void setServiceRegistry(ServiceRegistry serviceRegistry) + public ImapService getImapService() { - this.serviceRegistry = serviceRegistry; + return imapService; } - public ServiceRegistry getServiceRegistry() + public void setImapService(ImapService imapService) { - return serviceRegistry; + this.imapService = imapService; } - public void setNodeService(NodeService nodeService) + public TransactionService getTransactionService() { - this.nodeService = nodeService; + return transactionService; } - public void setFileFolderService(FileFolderService fileFolderService) + public void setTransactionService(TransactionService transactionService) { - this.fileFolderService = fileFolderService; - } - - public void setImapHelper(ImapHelper imapHelper) - { - this.imapHelper = imapHelper; + this.transactionService = transactionService; } } diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapMailFolder.java b/source/java/org/alfresco/repo/imap/AlfrescoImapMailFolder.java deleted file mode 100755 index 3537733ec6..0000000000 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapMailFolder.java +++ /dev/null @@ -1,1084 +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.ByteArrayOutputStream; -import java.io.IOException; -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.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.cmr.model.FileExistsException; -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.security.AccessStatus; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.GUID; -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -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 - * requests. - * - * @author Mike Shavnev - */ -public class AlfrescoImapMailFolder implements MailFolder -{ - - private static Log logger = LogFactory.getLog(AlfrescoImapMailFolder.class); - - /** - * Reference to the {@link FileInfo} object representing the folder. - */ - private FileInfo folderInfo; - - /** - * Reference to the root node of the store where folder is placed. - */ - private NodeRef rootNodeRef; - - /** - * Name of the mailbox (e.g. "admin" for admin user). - */ - private String qualifiedMailboxName; - - /** - * Name of the folder. - */ - private String folderName; - - /** - * Defines view mode. Can be one of the following: {@link AlfrescoImapConst#MODE_ARCHIVE} or {@link AlfrescoImapConst#MODE_VIRTUAL}. - */ - private String viewMode; - - /** - * Name of the mount point. - */ - private String mountPointName; - - /** - * Reference to the {@link ImapHelper} object. - */ - private ImapHelper imapHelper; - - /** - * Defines whether the folder is selectable or not. - */ - private Boolean selectable; - - /** - * Defines whether the folder is read-only for user or not. - */ - private Boolean readOnly; - - - private Map messages = new TreeMap(); - private boolean isBodyGenerated = false; - - private static final Flags PERMANENT_FLAGS = new Flags(); - - private List listeners = new LinkedList(); - - static - { - PERMANENT_FLAGS.add(Flags.Flag.ANSWERED); - PERMANENT_FLAGS.add(Flags.Flag.DELETED); - PERMANENT_FLAGS.add(Flags.Flag.DRAFT); - PERMANENT_FLAGS.add(Flags.Flag.FLAGGED); - PERMANENT_FLAGS.add(Flags.Flag.SEEN); - } - - /** - * 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. - * @param folderName - name of the folder. - * @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 imapHelper - reference to the {@link ImapHelper} object. - */ - public AlfrescoImapMailFolder(String qualifiedMailboxName, FileInfo folderInfo, String folderName, String viewMode, NodeRef rootNodeRef, String mountPointName, - ImapHelper imapHelper) - { - this(qualifiedMailboxName, folderInfo, folderName, viewMode, rootNodeRef, mountPointName, imapHelper, null); - } - - /** - * 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. - * @param folderName - name of the folder. - * @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 imapHelper - reference to the {@link ImapHelper} object. - * @param selectable - defines whether the folder is selectable or not. - */ - public AlfrescoImapMailFolder(String qualifiedMailboxName, FileInfo folderInfo, String folderName, String viewMode, NodeRef rootNodeRef, String mountPointName, - ImapHelper imapHelper, Boolean selectable) - { - 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; - - // MailFolder object can be null if it is used to obtain hierarchy delimiter by LIST command: - // Example: - // C: 2 list "" "" - // S: * LIST () "." "" - // S: 2 OK LIST completed. - if (folderInfo != null) - { - if (selectable == null) - { - // isSelectable(); - Boolean storedSelectable = !imapHelper.getNodeService().hasAspect(folderInfo.getNodeRef(), ImapModel.ASPECT_IMAP_FOLDER_NONSELECTABLE); - if (storedSelectable == null) - { - setSelectable(true); - } - else - { - setSelectable(storedSelectable); - } - } - else - { - setSelectable(selectable); - } - - AccessStatus status = imapHelper.hasPermission(folderInfo.getNodeRef(), PermissionService.WRITE); - if (status == AccessStatus.DENIED) - { - readOnly = true; - } - else - { - readOnly = false; - } - - } - else - { - 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. - * - * @param message - message. - * @param flags - message flags. - * @param internalDate - not used. Current date used instead. - * @return - */ - public long appendMessage(MimeMessage message, Flags flags, Date internalDate) throws FolderException - { - 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); - } - - } - - } - - /** - * 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(long uid, MailFolder toFolder) throws FolderException - { - AlfrescoImapMailFolder toImapMailFolder = (AlfrescoImapMailFolder) toFolder; - - if (toImapMailFolder.isReadOnly()) - { - throw new FolderException("Can't create folder - Permission denied"); - } - - NodeRef toNodeRef = toImapMailFolder.getFolderInfo().getNodeRef(); - - SimpleStoredMessage message = messages.get(uid); - FileInfo copyMess = ((AlfrescoImapMessage) message.getMimeMessage()).getMessageInfo(); - - List fis = new LinkedList(); - - if (imapHelper.getNodeService().getType(copyMess.getNodeRef()).equals(ImapModel.TYPE_IMAP_CONTENT)) - { - - //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 - { - 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. - */ - public void deleteAllMessages() throws FolderException - { - if (this.readOnly) - { - throw new FolderException("Can't delete all - Permission denied"); - } - - for (SimpleStoredMessage mess : messages.values()) - { - AlfrescoImapMessage message = (AlfrescoImapMessage) mess.getMimeMessage(); - FileInfo fileInfo = message.getMessageInfo(); - imapHelper.setFlag(fileInfo, Flags.Flag.DELETED, true); - // comment out to physically remove content. - // fileFolderService.delete(fileInfo.getNodeRef()); - messages.remove(mess.getUid()); - } - } - - /** - * Deletes messages marked with {@link Flags.Flag#DELETED}. Note that this message deletes all messages with this flag. - */ - public void expunge() throws FolderException - { - if (this.readOnly) - { - throw new FolderException("Can't expunge - Permission denied"); - } - - Collection listMess = messages.values(); - for (SimpleStoredMessage mess : listMess) - { - - Flags flags = getFlags(mess); - if (flags.contains(Flags.Flag.DELETED)) - { - NodeRef nodeRef = ((AlfrescoImapMessage) mess.getMimeMessage()).getMessageInfo().getNodeRef(); - imapHelper.getFileFolderService().delete(nodeRef); - } - } - } - - /** - * Returns the number of the first unseen message. - * - * @return Number of the first unseen message. - */ - public int getFirstUnseen() - { - return 0; - } - - /** - * 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() - { - - // If MailFolder object is used to obtain hierarchy delimiter by LIST command: - // Example: - // C: 2 list "" "" - // S: * LIST () "." "" - // S: 2 OK LIST completed. - - if (rootNodeRef == null) - { - return ""; - } - - StringBuilder fullName = new StringBuilder(); - List pathList; - try - { - 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(AlfrescoImapConst.HIERARCHY_DELIMITER); - if (isFirst) - { - fullName.append("\""); - isFirst = false; - if (mountPointName != null) - { - fullName.append(mountPointName); - } - else - { - fullName.append(path.getName()); - } - } - else - { - fullName.append(path.getName()); - } - } - fullName.append("\""); - } - catch (FileNotFoundException e) - { - logger.error(e); - } - if (logger.isDebugEnabled()) - { - logger.debug("fullName: " + fullName); - } - return GreenMailUtil.convertInUtf7(fullName.toString()); - } - - /** - * Returns message by its UID. - * - * @param uid - UID of the message. - * @return message. - */ - public SimpleStoredMessage getMessage(long uid) - { - if (!isBodyGenerated) - { - // regenerate messages list and include message body into result - getMessages(); - } - return messages.get(uid); - } - - /** - * Returns count of the messages in the folder. - * - * @return Count of the messages. - */ - public int getMessageCount() - { - if (messages.size() == 0) - { - List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos, false); - } - if (logger.isDebugEnabled()) - { - logger.debug(folderInfo.getName() + " - Messages count:" + messages.size()); - } - return messages.size(); - } - - /** - * Returns UIDs of all messages in the folder. - * - * @return UIDS of the messages. - */ - public long[] getMessageUids() - { - if (messages == null || messages.size() == 0) - { - List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos, false); - } - int len = messages.size(); - long[] uids = new long[len]; - Set keys = messages.keySet(); - int i = 0; - for (Long key : keys) - { - uids[i++] = key; - } - return uids; - } - - /** - * Returns list of all messages in the folder. - * - * @return list of {@link SimpleStoredMessage} objects. - */ - public List getMessages() - { - List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - return getMessages(fileInfos, true); - } - - private List getMessages(List fileInfos, boolean generateBody) - { - isBodyGenerated = generateBody; - if (fileInfos == null || fileInfos.size() == 0) - { - messages = Collections.emptyMap(); - } - if (fileInfos.size() != messages.size() || generateBody) - { - for (FileInfo fileInfo : fileInfos) - { - try - { - Long key = getMessageUid(fileInfo); - SimpleStoredMessage message = new SimpleStoredMessage(new AlfrescoImapMessage(fileInfo, imapHelper, generateBody), new Date(), key); - messages.put(key, message); - if (logger.isDebugEnabled()) - { - logger.debug("Message added: " + fileInfo.getName()); - } - } - catch (MessagingException e) - { - logger.warn("Invalid message! File name:" + fileInfo.getName(), e); - } - } - } - return new LinkedList(messages.values()); - } - - /** - * Returns list of messages by filter. - * - * @param msgRangeFilter - {@link MsgRangeFilter} object representing filter. - * @return list of filtered messages. - */ - public List getMessages(MsgRangeFilter msgRangeFilter) - { - if (messages == null || messages.size() == 0 || !isBodyGenerated) - { - List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos, true); - } - List ret = new ArrayList(); - for (int i = 0; i < messages.size(); i++) - { - if (msgRangeFilter.includes(i + 1)) - { - ret.add(messages.get(i)); - } - } - - return ret; - } - - /** - * 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(long uid) throws FolderException - { - // 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) - { - // "==" is legal with primitives and autoboxing - if (key == uid) - { - return msn + 1; - } - msn++; - } - throw new FolderException("No such message."); - } - - /** - * Returns folder name. - * - * @return folder name. - */ - public String getName() - { - return folderName; - } - - /** - * 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() - { - List result = new ArrayList(); - - if (messages.size() == 0 || !isBodyGenerated) - { - List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos, true); - } - - Collection values = messages.values(); - for (SimpleStoredMessage message : values) - { - if (!getFlags(message).contains(Flags.Flag.DELETED)) - { - result.add(message); - } - - } - if (logger.isDebugEnabled()) - { - logger.debug(folderInfo.getName() + " - Non deleted messages count:" + result.size()); - } - return result; - } - - /** - * Returns permanent flags. - * - * @return {@link Flags} object containing flags. - */ - public Flags getPermanentFlags() - { - return PERMANENT_FLAGS; - } - - /** - * 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(boolean reset) - { - if (messages.size() == 0) - { - List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos, false); - } - - int count = 0; - Collection values = messages.values(); - for (SimpleStoredMessage message : values) - { - if (getFlags(message).contains(Flags.Flag.RECENT)) - { - count++; - if (reset) - { - imapHelper.setFlag(((AlfrescoImapMessage) message.getMimeMessage()).getMessageInfo(), Flags.Flag.RECENT, false); - } - } - - } - - if (logger.isDebugEnabled()) - { - logger.debug(folderInfo.getName() + " - Recent count: " + count + " reset: " + reset); - } - return count; - } - - /** - * Returns UIDNEXT value of the folder. - * - * @return UIDNEXT value. - */ - public long getUidNext() - { - return getUidValidity(); - } - - /** - * Returns UIDVALIDITY value of the folder. - * - * @return UIDVALIDITY value. - */ - public long getUidValidity() - { - return ((Date) imapHelper.getNodeService().getProperty(folderInfo.getNodeRef(), ContentModel.PROP_MODIFIED)).getTime(); - } - - /** - * 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() - { - if (messages.size() == 0) - { - List fileInfos = imapHelper.searchMails(folderInfo.getNodeRef(), "*", viewMode, false); - getMessages(fileInfos, false); - } - - int count = 0; - Collection values = messages.values(); - for (SimpleStoredMessage message : values) - { - if (!getFlags(message).contains(Flags.Flag.SEEN)) - { - count++; - } - - } - if (logger.isDebugEnabled()) - { - logger.debug(folderInfo.getName() + " - Unseen count: " + count); - } - 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. - * - * @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(Flags flags, long uid, FolderListener silentListener, boolean addUid) throws FolderException - { - int msn = getMsn(uid); - SimpleStoredMessage message = messages.get(uid); - 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. - * - * @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(Flags flags, boolean value, long uid, FolderListener silentListener, boolean addUid) throws FolderException - { - int msn = getMsn(uid); - SimpleStoredMessage message = (SimpleStoredMessage) messages.get(uid); - - 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) - { - uidNotification = new Long(uid); - } - notifyFlagUpdate(msn, message.getFlags(), uidNotification, silentListener); - - } - - /** - * 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 (imapHelper.getNodeService().getType(fileInfo.getNodeRef()).equals(ContentModel.TYPE_FOLDER)) - { - return ((Date) imapHelper.getNodeService().getProperty(fileInfo.getNodeRef(), ContentModel.PROP_MODIFIED)).getTime(); - } - - return (Long) imapHelper.getNodeService().getProperty(fileInfo.getNodeRef(), ContentModel.PROP_NODE_DBID); - } - - private Flags getFlags(SimpleStoredMessage mess) - { - return ((AlfrescoImapMessage) mess.getMimeMessage()).getFlags(); - } - - // ----------------------Getters and Setters---------------------------- - - public FileInfo getFolderInfo() - { - return folderInfo; - } - - public void setFolderName(String folderName) - { - this.folderName = folderName; - } - - public void setViewMode(String viewMode) - { - this.viewMode = viewMode; - } - - public void setMountPointName(String mountPointName) - { - this.mountPointName = mountPointName; - } - - public void setMountParent(NodeRef mountParent) - { - this.rootNodeRef = mountParent; - } - - /** - * Whether the folder is selectable. - * - * @return {@code boolean}. - */ - public boolean isSelectable() - { - - return this.selectable; - } - - /** - * Sets {@link #selectable} property. - * - * @param selectable - {@code boolean}. - */ - public void setSelectable(boolean selectable) - { - this.selectable = selectable; - // Map properties = folderInfo.getProperties(); - // properties.put(ImapModel.PROP_IMAP_FOLDER_SELECTABLE, this.selectable); - // imapHelper.setProperties(folderInfo, properties); - } - - - /** - * Whether the folder is read-only for user. - * @return {@code boolean} - */ - public boolean isReadOnly() - { - return readOnly; - } - -} diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapMessage.java b/source/java/org/alfresco/repo/imap/AlfrescoImapMessage.java deleted file mode 100755 index 1d2155586a..0000000000 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapMessage.java +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import static org.alfresco.repo.imap.AlfrescoImapConst.BASE_64_ENCODING; -import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_ID; -import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TRANSFER_ENCODING; -import static org.alfresco.repo.imap.AlfrescoImapConst.CONTENT_TYPE; -import static org.alfresco.repo.imap.AlfrescoImapConst.MIME_VERSION; -import static org.alfresco.repo.imap.AlfrescoImapConst.UTF_8; -import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_NODEREF_ID; -import static org.alfresco.repo.imap.AlfrescoImapConst.X_ALF_SERVER_UID; - -import java.io.IOException; -import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import javax.activation.DataHandler; -import javax.activation.DataSource; -import javax.mail.Address; -import javax.mail.Flags; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.Session; -import javax.mail.internet.AddressException; -import javax.mail.internet.ContentType; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import javax.mail.internet.MimeUtility; -import javax.mail.util.ByteArrayDataSource; - -import org.alfresco.i18n.I18NUtil; -import org.alfresco.model.ContentModel; -import org.alfresco.model.ImapModel; -import org.alfresco.repo.imap.ImapHelper.EmailBodyType; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Extended MimeMessage to represent a content stored in the Alfresco repository. - * - * @author Arseny Kovalchuk - */ -public class AlfrescoImapMessage extends MimeMessage -{ - /** Used if imapHelper.getDefaultFromAddress is not set */ - private static final String DEFAULT_EMAIL_FROM = "alfresco@alfresco.org"; - private static final String DEFAULT_EMAIL_TO = DEFAULT_EMAIL_FROM; - private static final String KOI8R_CHARSET = "koi8-r"; - - private static Log logger = LogFactory.getLog(AlfrescoImapMessage.class); - - private ImapHelper imapHelper; - private FileInfo messageInfo; - - /** - * Constructs {@link AlfrescoImapMessage} object. - * - * @param fileInfo - reference to the {@link FileInfo} object representing the message. - * @param imapHelper - reference to the {@link ImapHelper} object. - * @param generateBody - if {@code true} message body will be generated. - * - * @throws MessagingException if generation of the body fails. - */ - public AlfrescoImapMessage(FileInfo fileInfo, ImapHelper imapHelper, boolean generateBody) throws MessagingException - { - super(Session.getDefaultInstance(new Properties())); - this.messageInfo = fileInfo; - this.imapHelper = imapHelper; - if (generateBody) - { - setMessageHeaders(); - buildMessage(); - } - } - - /** - * Constructs {@link AlfrescoImapMessage} object. - * - * @param fileInfo - reference to the {@link FileInfo} object representing the message. - * @param imapHelper - reference to the {@link ImapHelper} object. - * @param message - {@link MimeMessage} - * @throws MessagingException - */ - public AlfrescoImapMessage(FileInfo fileInfo, ImapHelper imapHelper, MimeMessage message) throws MessagingException - { - super(message); - this.messageInfo = fileInfo; - this.imapHelper = imapHelper; - - setMessageHeaders(); - final NodeRef nodeRef = fileInfo.getNodeRef(); - Map 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 1efdeb32fa..03859294c4 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java @@ -46,15 +46,13 @@ public class AlfrescoImapServer extends AbstractLifecycleBean private ImapServer serverImpl; private int port = 143; - + private String host = "localhost"; private ImapHostManager imapHostManager; private UserManager imapUserManager; private boolean imapServerEnabled; - private ImapHelper imapHelper; - public void setImapServerEnabled(boolean imapServerEnabled) { this.imapServerEnabled = imapServerEnabled; @@ -65,6 +63,11 @@ public class AlfrescoImapServer extends AbstractLifecycleBean this.port = port; } + public void setHost(String host) + { + this.host = host; + } + public void setImapHostManager(ImapHostManager imapHostManager) { this.imapHostManager = imapHostManager; @@ -75,14 +78,9 @@ public class AlfrescoImapServer extends AbstractLifecycleBean this.imapUserManager = imapUserManager; } - public void setImapHelper(ImapHelper imapHelper) - { - this.imapHelper = imapHelper; - } - protected void onBootstrap(ApplicationEvent event) { - if (imapServerEnabled && imapHelper.isPatchApplied()) + if (imapServerEnabled) { Managers imapManagers = new Managers() { @@ -96,11 +94,11 @@ public class AlfrescoImapServer extends AbstractLifecycleBean return imapUserManager; } }; - serverImpl = new ImapServer(new ServerSetup(port, null, ServerSetup.PROTOCOL_IMAP), imapManagers); + serverImpl = new ImapServer(new ServerSetup(port, host, ServerSetup.PROTOCOL_IMAP), imapManagers); serverImpl.startService(null); if (logger.isInfoEnabled()) { - logger.info("IMAP service started on port " + this.port + "."); + logger.info("IMAP service started on host:port " + this.host + ":" + this.port + "."); } } else diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapUser.java b/source/java/org/alfresco/repo/imap/AlfrescoImapUser.java index ed5d31aa71..4e231e5087 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapUser.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapUser.java @@ -1,107 +1,103 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import javax.mail.internet.MimeMessage; - -import com.icegreen.greenmail.imap.ImapHostManager; -import com.icegreen.greenmail.mail.MovingMessage; -import com.icegreen.greenmail.user.GreenMailUser; -import com.icegreen.greenmail.user.UserException; - -/** - * Alfresco implementation of the GreenMailUser interface. - * - * @author Arseny Kovalchuk - */ -public class AlfrescoImapUser implements GreenMailUser -{ - private String userName; - private char[] password; - private String email; - - private ImapHostManager imapHostManager; - - public AlfrescoImapUser(String email, String login, String password, ImapHostManager imapHostManager) - { - this.email = email; - this.userName = login; - this.password = password.toCharArray(); - this.imapHostManager = imapHostManager; - } - - public void authenticate(String password) throws UserException - { - throw new UnsupportedOperationException(); - // This method is used in the POP3 greenmail implementation, so it is disabled for IMAP - // See AlfrescoImapUserManager.test() method. - } - - public void create() throws UserException - { - throw new UnsupportedOperationException(); - } - - public void delete() throws UserException - { - throw new UnsupportedOperationException(); - } - - public void deliver(MovingMessage msg) throws UserException - { - - } - - public void deliver(MimeMessage msg) throws UserException - { - - } - - public String getEmail() - { - return this.email; - } - - public String getLogin() - { - return this.userName; - } - - public String getPassword() - { - return new String(this.password); - } - - public String getQualifiedMailboxName() - { - return userName; - } - - public void setPassword(String password) - { - this.password = password.toCharArray(); - } - -} +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.imap; + +import javax.mail.internet.MimeMessage; + +import com.icegreen.greenmail.mail.MovingMessage; +import com.icegreen.greenmail.user.GreenMailUser; +import com.icegreen.greenmail.user.UserException; + +/** + * Alfresco implementation of the GreenMailUser interface. + * + * @author Arseny Kovalchuk + */ +public class AlfrescoImapUser implements GreenMailUser +{ + private String userName; + private char[] password; + private String email; + + public AlfrescoImapUser(String email, String login, String password) + { + this.email = email; + this.userName = login; + this.password = password.toCharArray(); + } + + public void authenticate(String password) throws UserException + { + throw new UnsupportedOperationException(); + // This method is used in the POP3 greenmail implementation, so it is disabled for IMAP + // See AlfrescoImapUserManager.test() method. + } + + public void create() throws UserException + { + throw new UnsupportedOperationException(); + } + + public void delete() throws UserException + { + throw new UnsupportedOperationException(); + } + + public void deliver(MovingMessage msg) throws UserException + { + throw new UnsupportedOperationException(); + } + + public void deliver(MimeMessage msg) throws UserException + { + throw new UnsupportedOperationException(); + } + + public String getEmail() + { + return this.email; + } + + public String getLogin() + { + return this.userName; + } + + public String getPassword() + { + return new String(this.password); + } + + public String getQualifiedMailboxName() + { + return userName; + } + + public void setPassword(String password) + { + this.password = password.toCharArray(); + } + +} diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java b/source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java index eb938cb728..a2b79ba2c1 100755 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapUserManager.java @@ -1,168 +1,150 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.security.authentication.AuthenticationException; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.security.AuthenticationService; -import org.alfresco.service.cmr.security.PersonService; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.icegreen.greenmail.imap.ImapHostManager; -import com.icegreen.greenmail.user.GreenMailUser; -import com.icegreen.greenmail.user.UserException; -import com.icegreen.greenmail.user.UserManager; - -/** - * @author Arseny Kovalchuk - */ -public class AlfrescoImapUserManager extends UserManager -{ - private Log logger = LogFactory.getLog(AlfrescoImapUserManager.class); - - protected Map userMap = Collections.synchronizedMap(new HashMap()); - protected ImapHostManager imapHostManager; - - protected AuthenticationService authenticationService; - protected PersonService personService; - protected NodeService nodeService; - - public AlfrescoImapUserManager() - { - super(null); - } - - public AlfrescoImapUserManager(ImapHostManager imapHostManager) - { - this(); - this.imapHostManager = imapHostManager; - } - - public GreenMailUser createUser(String email, String login, String password) throws UserException - { - // TODO: User creation/addition code should be implemented here (in the AlfrescoImapUserManager). - // Following code is not need and not used in the current implementation. - GreenMailUser user = new AlfrescoImapUser(email, login, password, imapHostManager); - user.create(); - addUser(user); - return user; - } - - protected void addUser(GreenMailUser user) - { - userMap.put(user.getLogin(), user); - } - - public GreenMailUser getUser(String login) - { - return (GreenMailUser) userMap.get(login); - } - - public GreenMailUser getUserByEmail(String email) - { - GreenMailUser ret = getUser(email); - if (null == ret) - { - for (GreenMailUser user : userMap.values()) - { - // TODO: NPE! - if (user.getEmail().trim().equalsIgnoreCase(email.trim())) - { - return user; - } - } - } - return ret; - } - - public void deleteUser(GreenMailUser user) throws UserException - { - user = (GreenMailUser) userMap.remove(user.getLogin()); - if (user != null) - { - user.delete(); - } - } - - /** - * The login method. - * - * @see com.icegreen.greenmail.imap.commands.LoginCommand#doProcess() - */ - public boolean test(String userid, String password) - { - try - { - authenticationService.authenticate(userid, password.toCharArray()); - String email = null; - if (personService.personExists(userid)) - { - NodeRef personNodeRef = personService.getPerson(userid); - email = (String) nodeService.getProperty(personNodeRef, ContentModel.PROP_EMAIL); - } - GreenMailUser user = new AlfrescoImapUser(email, userid, password, imapHostManager); - addUser(user); - } - catch (AuthenticationException ex) - { - logger.error("IMAP authentication failed for userid: " + userid); - return false; - } - return true; - } - - public ImapHostManager getImapHostManager() - { - return this.imapHostManager; - } - - public void setImapHostManager(ImapHostManager imapHostManager) - { - this.imapHostManager = imapHostManager; - } - - public void setAuthenticationService(AuthenticationService authenticationService) - { - this.authenticationService = authenticationService; - } - - public void setPersonService(PersonService personService) - { - this.personService = personService; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - -} +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.imap; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.icegreen.greenmail.user.GreenMailUser; +import com.icegreen.greenmail.user.UserException; +import com.icegreen.greenmail.user.UserManager; + +/** + * @author Arseny Kovalchuk + */ +public class AlfrescoImapUserManager extends UserManager +{ + private Log logger = LogFactory.getLog(AlfrescoImapUserManager.class); + + protected Map userMap = Collections.synchronizedMap(new HashMap()); + + protected AuthenticationService authenticationService; + protected PersonService personService; + protected NodeService nodeService; + + public AlfrescoImapUserManager() + { + super(null); + } + + 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); + user.create(); + addUser(user); + return user; + } + + protected void addUser(GreenMailUser user) + { + userMap.put(user.getLogin(), user); + } + + public GreenMailUser getUser(String login) + { + return (GreenMailUser) userMap.get(login); + } + + public GreenMailUser getUserByEmail(String email) + { + GreenMailUser ret = getUser(email); + if (null == ret) + { + for (GreenMailUser user : userMap.values()) + { + // TODO: NPE! + if (user.getEmail().trim().equalsIgnoreCase(email.trim())) + { + return user; + } + } + } + return ret; + } + + public void deleteUser(GreenMailUser user) throws UserException + { + user = (GreenMailUser) userMap.remove(user.getLogin()); + if (user != null) + { + user.delete(); + } + } + + /** + * The login method. + * + * @see com.icegreen.greenmail.imap.commands.LoginCommand#doProcess() + */ + public boolean test(String userid, String password) + { + try + { + authenticationService.authenticate(userid, password.toCharArray()); + String email = null; + if (personService.personExists(userid)) + { + NodeRef personNodeRef = personService.getPerson(userid); + email = (String) nodeService.getProperty(personNodeRef, ContentModel.PROP_EMAIL); + } + GreenMailUser user = new AlfrescoImapUser(email, userid, password); + addUser(user); + } + catch (AuthenticationException ex) + { + logger.error("IMAP authentication failed for userid: " + userid); + return false; + } + return true; + } + + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + +} diff --git a/source/java/org/alfresco/repo/imap/ContentModelMessage.java b/source/java/org/alfresco/repo/imap/ContentModelMessage.java new file mode 100755 index 0000000000..cb1025c6f4 --- /dev/null +++ b/source/java/org/alfresco/repo/imap/ContentModelMessage.java @@ -0,0 +1,100 @@ +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) ? messageFileInfo.getName() : prop; + prop = 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 deleted file mode 100755 index 7e493e825a..0000000000 --- a/source/java/org/alfresco/repo/imap/ImapHelper.java +++ /dev/null @@ -1,994 +0,0 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.imap; - -import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_HTML_TEMPLATE; -import static org.alfresco.repo.imap.AlfrescoImapConst.CLASSPATH_TEXT_PLAIN_TEMPLATE; -import static org.alfresco.repo.imap.AlfrescoImapConst.DICTIONARY_TEMPLATE_PREFIX; - -import java.io.Serializable; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import javax.mail.Flags; -import javax.mail.Flags.Flag; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.model.ImapModel; -import org.alfresco.repo.admin.patch.PatchInfo; -import org.alfresco.repo.admin.patch.PatchService; -import org.alfresco.repo.imap.config.ImapConfigBean; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.template.TemplateNode; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.preference.PreferenceService; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.repository.TemplateService; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AccessStatus; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.site.SiteInfo; -import org.alfresco.service.cmr.site.SiteService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.AbstractLifecycleBean; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.context.ApplicationEvent; - -/** - * Helper class to access repository services by IMAP components. Also contains a common helper methods to search and - * manage IMAP content and other usefull methods. Configured as {@code } 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 new file mode 100755 index 0000000000..8f534f09be --- /dev/null +++ b/source/java/org/alfresco/repo/imap/ImapModelMessage.java @@ -0,0 +1,140 @@ +/* + * 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; + +/** + * Extended MimeMessage to represent a content stored in the Alfresco repository. + * + * @author Arseny Kovalchuk + */ +public class ImapModelMessage extends AbstractMimeMessage +{ + /** + * 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 new file mode 100755 index 0000000000..8639d6beda --- /dev/null +++ b/source/java/org/alfresco/repo/imap/ImapService.java @@ -0,0 +1,199 @@ +/* + * 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 + * @since 3.2 + */ +public interface ImapService +{ + /** + * Returns an collection of mailboxes. This method serves LIST command of the IMAP protocol. + * + * @param user User making the request + * @param mailboxPattern String name of a mailbox possible including a wildcard. + * @return Collection of mailboxes matching the pattern. + */ + public List listMailboxes(AlfrescoImapUser user, String mailboxPattern); + + /** + * Returns an collection of subscribed mailboxes. This method serves LSUB command of the IMAP protocol. + * + * @param user User making the request + * @param mailboxPattern String name of a mailbox possible including a wildcard. + * @return Collection of mailboxes matching the pattern. + */ + public List listSubscribedMailboxes(AlfrescoImapUser user, String mailboxPattern); + + /** + * Returns a reference to a newly created mailbox. The request should specify a mailbox that does not already exist on this server, that could exist on this server and that the + * user has rights to create. This method serves CREATE command of the IMAP protocol. + * + * @param user User making the request. + * @param mailboxName String name of the target + * @return an Mailbox reference. + */ + public AlfrescoImapFolder createMailbox(AlfrescoImapUser user, String mailboxName); + + /** + * Deletes an existing MailBox. Specified mailbox must already exist on this server, and the user must have rights to delete it. This method serves DELETE command of the IMAP + * protocol. + * + * @param user User making the request. + * @param mailboxName String name of the target + * @throws com.icegreen.greenmail.store.FolderException if mailbox has a non-selectable store with children + */ + public void deleteMailbox(AlfrescoImapUser user, String mailboxName); + + /** + * Renames an existing mailbox. The specified mailbox must already exist, the requested name must not exist already but must be able to be created and the user must have rights + * to delete the existing mailbox and create a mailbox with the new name. Any inferior hierarchical names must also be renamed. If INBOX is renamed, the contents of INBOX are + * transferred to a new mailbox with the new name, but INBOX is not deleted. If INBOX has inferior mailbox these are not renamed. This method serves RENAME command of the IMAP + * protocol. + * + * @param user User making the request. + * @param oldMailboxName String name of the existing folder + * @param newMailboxName String target new name + */ + public void renameMailbox(AlfrescoImapUser user, String oldMailboxName, String newMailboxName); + + /** + * Returns a reference to an existing Mailbox. The requested mailbox must already exists on this server and the requesting user must have at least lookup rights.

It is + * also can be used by to obtain hierarchy delimiter by the LIST command:

C: 2 list "" ""

S: * LIST () "." ""

S: 2 OK LIST completed. + * + * @param user User making the request. + * @param mailboxName String name of the target. + * @return an Mailbox reference. + */ + public AlfrescoImapFolder getFolder(AlfrescoImapUser user, String mailboxName); + + /** + * Get root reference for the specified mailbox + * + * @param mailboxName mailbox name in IMAP client. + * @param userName + * @return NodeRef of root reference for the specified mailbox + */ + public NodeRef getMailboxRootRef(String mailboxName, String userName); + + /** + * Subscribes a user to a mailbox. The mailbox must exist locally and the user must have rights to modify it.

This method serves SUBSCRIBE command of the IMAP protocol. + * + * @param user User making the request + * @param mailbox String representation of a mailbox name. + */ + public void subscribe(AlfrescoImapUser user, String mailbox); + + /** + * Unsubscribes from a given mailbox.

This method serves UNSUBSCRIBE command of the IMAP protocol. + * + * @param user User making the request + * @param mailbox String representation of a mailbox name. + */ + public void unsubscribe(AlfrescoImapUser user, String mailbox); + + /** + * Search for files in specified context + * + * @param contextNodeRef context folder for search + * @param namePattern name pattern for search + * @param includeSubFolders include SubFolders + * @return list of files + */ + public List searchFiles(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders); + + /** + * Search for mailboxes in specified context + * + * @param contextNodeRef context folder for search + * @param namePattern name pattern for search + * @param includeSubFolders include SubFolders + * @param viewMode (ARCHIVE, MIXED or VIRTUAL) + * @return list of mailboxes that are visible from specified view + */ + public List searchFolders(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders, String viewMode); + + /** + * Search for emails in specified folder depend on view mode. + * + * @param contextNodeRef context folder for search + * @param namePattern name pattern for search + * @param viewMode (ARCHIVE, MIXED or VIRTUAL) + * @param includeSubFolders includeSubFolders + * @return list of emails that context folder contains. + */ + public List searchMails(NodeRef contextNodeRef, String namePattern, String viewMode, boolean includeSubFolders); + + /** + * Return flags that belong to the specified imap folder. + * + * @param messageInfo imap folder info. + * @return flags. + */ + public Flags getFlags(FileInfo messageFileInfo); + + /** + * 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 messageFileInfo, Flags flags, boolean value); + + /** + * Set flag 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 messageFileInfo, Flag flag, boolean value); + + /** + * @return Default From addreses + */ + public String getDefaultFromAddress(); + + /** + * @return Path to the folder containing templates, that will be used for generating body of message in VIRTUAL and MIXED views. + */ + public String getRepositoryTemplatePath(); + + /** + * @return Web application context url (e.g. http://localhost:8080/alfresco) + */ + public String getWebApplicationContextUrl(); + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java new file mode 100755 index 0000000000..96ecfb630d --- /dev/null +++ b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java @@ -0,0 +1,1403 @@ +/* + * 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.AbstractLifecycleBean; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.Utf7; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; + +/** + * @author Dmitry Vaserin + * @author Arseny Kovalchuk + * @since 3.2 + */ +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(); + ImapConfigBean[] ignoreExtractionFoldersBeans = new ImapConfigBean[0]; + 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); + } + + /** + * Bootstrap initialization bean for the service implementation. + * + * @author Derek Hulley + * @since 3.2 + */ + public static class ImapServiceBootstrap extends AbstractLifecycleBean + { + private ImapServiceImpl service; + private boolean imapServerEnabled; + + public void setService(ImapServiceImpl service) + { + this.service = service; + } + + public void setImapServerEnabled(boolean imapServerEnabled) + { + this.imapServerEnabled = imapServerEnabled; + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + if (imapServerEnabled) + { + service.startup(); + } + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + if (imapServerEnabled) + { + service.shutdown(); + } + } + } + + 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.ignoreExtractionFoldersBeans = ignoreExtractionFolders; + } + + public boolean getExtractAttachmentsEnabled() + { + return extractAttachmentsEnabled; + } + + public void setExtractAttachmentsEnabled(boolean extractAttachmentsEnabled) + { + this.extractAttachmentsEnabled = extractAttachmentsEnabled; + } + + // ---------------------- Lifecycle Methods ------------------------------ + + public void startup() + { + // Get NodeRefs for folders to ignore + this.ignoreExtractionFolders = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + { + public Set doWork() throws Exception + { + Set result = new HashSet(ignoreExtractionFoldersBeans.length * 2); + + for (ImapConfigBean bean : ignoreExtractionFoldersBeans) + { + 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()); + // Locate IMAP root folder + 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 void shutdown() + { + } + + public void init() + { + PropertyCheck.mandatory(this, "fileFolderService", fileFolderService); + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry); + PropertyCheck.mandatory(this, "imapRoot", imapRoot); + PropertyCheck.mandatory(this, "defaultFromAddress", defaultFromAddress); + PropertyCheck.mandatory(this, "repositoryTemplatePath", repositoryTemplatePath); + } + + // ---------------------- Service Methods -------------------------------- + + 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 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 root reference for the specified mailbox + */ + 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 = 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(ServiceRegistry.PREFERENCE_SERVICE); + 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"; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java b/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java new file mode 100755 index 0000000000..f33351f087 --- /dev/null +++ b/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java @@ -0,0 +1,452 @@ +package org.alfresco.repo.imap; + +import java.io.IOException; +import java.io.Serializable; +import java.util.LinkedList; +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.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; +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.FileFolderService; +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 TEST_IMAP_FOLDER_NAME = "aaa"; + + 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 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 FileFolderService fileFolderService; + + private AlfrescoImapUser user; + private ImapService imapService; + private UserTransaction txn; + + private NodeRef testImapFolderNodeRef; + 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(); + fileFolderService = serviceRegistry.getFileFolderService(); + + + 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); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + + + + ChildApplicationContextFactory imap = (ChildApplicationContextFactory) ctx.getBean("imap"); + ApplicationContext imapCtx = imap.getApplicationContext(); + ImapServiceImpl imapServiceImpl = (ImapServiceImpl)imapCtx.getBean("imapService"); + + // Creating IMAP test folder for IMAP root + LinkedList folders = new LinkedList(); + folders.add(TEST_IMAP_FOLDER_NAME); + FileFolderServiceImpl.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER); + + // Setting IMAP root + String imapRoot = storePath + companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_FOLDER_NAME; + imapServiceImpl.setImapRoot(imapRoot); + + // Starting IMAP + imapServiceImpl.startup(); + + nodeRefs = searchService.selectNodes(storeRootNodeRef, + companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_FOLDER_NAME, + null, + namespaceService, + false); + testImapFolderNodeRef = nodeRefs.get(0); + + + /* + * Importing test folders: + * + * Test folder contains: "___-___folder_a" + * + * "___-___folder_a" contains: "___-___folder_a_a", + * "___-___file_a", + * "Message_485.eml" (this is IMAP Message) + * + * "___-___folder_a_a" contains: "____-____file_a_a" + * + */ + importInternal("imapservice_test_folder_a.acp", testImapFolderNodeRef); + + 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(testImapFolderNodeRef, 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(testImapFolderNodeRef, 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(testImapFolderNodeRef, 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(testImapFolderNodeRef, 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(testImapFolderNodeRef, 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(testImapFolderNodeRef, 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(testImapFolderNodeRef, FILE_PATTERN, true); + assertNotNull(fi); + assertTrue(fi.size() > 0); + } + + public void testSearchMails() throws Exception + { + List fi = imapService.searchMails(testImapFolderNodeRef, "*", 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(testImapFolderNodeRef, "*", 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(testImapFolderNodeRef, anotherUserName, PermissionService.WRITE, true); + + reauthenticate(anotherUserName, anotherUserName); + + setFlags(messageFileInfo); + } + } + + public void testSetFlag() throws Exception + { + List fis = imapService.searchMails(testImapFolderNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true); + if (fis != null && fis.size() > 0) + { + FileInfo messageFileInfo = fis.get(0); + + reauthenticate(USER_NAME, USER_PASSWORD); + + permissionService.setPermission(testImapFolderNodeRef, 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(testImapFolderNodeRef, "*", AlfrescoImapConst.MODE_ARCHIVE, true); + if (fis != null && fis.size() > 0) + { + FileInfo messageFileInfo = fis.get(0); + + reauthenticate(USER_NAME, USER_PASSWORD); + + permissionService.setPermission(testImapFolderNodeRef, 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 new file mode 100755 index 0000000000..2207f16626 --- /dev/null +++ b/source/java/org/alfresco/repo/imap/IncomingImapMessage.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.imap; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeUtility; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.ImapModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.FileCopyUtils; + +/** + * This class is used to serve incoming IMAP message. E.g. when message is copied /moved into some IMAP older. + * + * @author Arseny Kovalchuk + */ +public class IncomingImapMessage extends AbstractMimeMessage +{ + private Log logger = LogFactory.getLog(IncomingImapMessage.class); + private ContentReader contentReader; + /** + * Constructs {@link IncomingImapMessage} object based on {@link MimeMessage} + * + * @param fileInfo - reference to the {@link FileInfo} object representing the message. + * @param imapHelper - reference to the {@link ImapHelper} object. + * @param message - {@link MimeMessage} + * @throws MessagingException + */ + public IncomingImapMessage(FileInfo fileInfo, ServiceRegistry serviceRegistry, MimeMessage message) throws MessagingException + { + super(Session.getDefaultInstance(new Properties())); + this.wrappedMessage = message; // temporary save it and then destroyed in writeContent() (to avoid memory leak with byte[] MimeMessage.content field) + this.buildMessage(fileInfo, serviceRegistry); + } + + @Override + public void buildMessageInternal() throws MessagingException + { + setMessageHeaders(); + // Add Imap Content Aspect with properties + NodeService nodeService = serviceRegistry.getNodeService(); + nodeService.addAspect(this.messageFileInfo.getNodeRef(), ImapModel.ASPECT_IMAP_CONTENT, null); + // Write content + writeContent(); + imapService.setFlags(messageFileInfo, flags, true); + + final NodeRef nodeRef = messageFileInfo.getNodeRef(); + Map 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/LoadTester.java b/source/java/org/alfresco/repo/imap/LoadTester.java new file mode 100755 index 0000000000..77538dae86 --- /dev/null +++ b/source/java/org/alfresco/repo/imap/LoadTester.java @@ -0,0 +1,145 @@ +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 LoadTester extends TestCase +{ + private Log logger = LogFactory.getLog(LoadTester.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_imap1000"; + private static final long MESSAGE_QUANTITY = 1000; + + + + @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); + + 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.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/RemoteLoadTester.java b/source/java/org/alfresco/repo/imap/RemoteLoadTester.java new file mode 100755 index 0000000000..cf963c10df --- /dev/null +++ b/source/java/org/alfresco/repo/imap/RemoteLoadTester.java @@ -0,0 +1,163 @@ +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 RemoteLoadTester extends TestCase +{ + + private Log logger = LogFactory.getLog(RemoteLoadTester.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(); + props.setProperty("mail.imap.partialfetch", "false"); + + // 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(); + fis.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/config/ImapConfigBean.java b/source/java/org/alfresco/repo/imap/config/ImapConfigBean.java index 4301e247da..8fd3b43938 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; /** - * Provides the parameters for an IMAP mount point (a mapping from an Alfresco node path to an IMAP folder name). + * Standard ImapConfig bean. */ public class ImapConfigBean implements BeanNameAware { @@ -35,9 +35,6 @@ public class ImapConfigBean implements BeanNameAware /** The IMAP folder name. */ private String name; - /** The mode (virtual or archive). */ - private String mode; - /** The Alfresco store name. */ private String store; @@ -63,27 +60,6 @@ public class ImapConfigBean implements BeanNameAware this.name = name; } - /** - * Gets the mode. - * - * @return the mode (virtual or archive) - */ - public String getMode() - { - return this.mode; - } - - /** - * Sets the mode. - * - * @param mode - * the new mode (virtual or archive) - */ - public void setMode(String mode) - { - this.mode = mode; - } - /** * Gets the Alfresco store name. * diff --git a/source/java/org/alfresco/repo/imap/config/ImapConfigMountPointsBean.java b/source/java/org/alfresco/repo/imap/config/ImapConfigMountPointsBean.java new file mode 100755 index 0000000000..25bc5eec4f --- /dev/null +++ b/source/java/org/alfresco/repo/imap/config/ImapConfigMountPointsBean.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.imap.config; + + +/** + * Provides the parameters for an IMAP mount point (a mapping from an Alfresco node path to an IMAP folder name). + */ +public class ImapConfigMountPointsBean extends ImapConfigBean +{ + + + /** The mode (virtual, archive or mixed). */ + private String mode; + + /** + * Gets the mode. + * + * @return the mode (virtual or archive) + */ + public String getMode() + { + return this.mode; + } + + /** + * Sets the mode. + * + * @param mode + * the new mode (virtual or archive) + */ + public void setMode(String mode) + { + this.mode = mode; + } + +} diff --git a/source/java/org/alfresco/repo/jscript/Imap.java b/source/java/org/alfresco/repo/jscript/Imap.java new file mode 100755 index 0000000000..5fbf2bd98b --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/Imap.java @@ -0,0 +1,73 @@ +package org.alfresco.repo.jscript; + +import org.alfresco.repo.model.Repository; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.util.ParameterCheck; + +public final class Imap extends BaseScopableProcessorExtension +{ + /** Service registry */ + private ServiceRegistry services; + + /** Default store reference */ + private StoreRef storeRef; + + /** Repository helper */ + private Repository repository; + + + /** + * Set the default store reference + * + * @param storeRef the default store reference + */ + public void setStoreUrl(String storeRef) + { + // ensure this is not set again by a script instance! + if (this.storeRef != null) + { + throw new IllegalStateException("Default store URL can only be set once."); + } + this.storeRef = new StoreRef(storeRef); + } + + /** + * Set the service registry + * + * @param services the service registry + */ + public void setServiceRegistry(ServiceRegistry services) + { + this.services = services; + } + + /** + * Set the repository helper + * + * @param repository the repository helper + */ + public void setRepositoryHelper(Repository repository) + { + this.repository = repository; + } + + /** + * Searches NodeRef to the IMAP home for specified user + * + * @param mailboxName the name of the mailbox + * @param userName the name of the user + */ + public ScriptNode getImapHomeRef(String mailboxName, String userName) + { + ScriptNode result = null; + NodeRef nodeRef = services.getImapService().getMailboxRootRef(mailboxName, userName); + if (nodeRef != null) + { + result = new ScriptNode(nodeRef, this.services, getScope()); + } + return result; + } + +} diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index 27332a2404..ead1f4cd3a 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -1,553 +1,563 @@ -/* - * Copyright (C) 2005-2008 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.service; - -import java.util.Collection; - -import org.alfresco.cmis.CMISDictionaryService; -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.lock.JobLockService; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.action.ActionService; -import org.alfresco.service.cmr.attributes.AttributeService; -import org.alfresco.service.cmr.audit.AuditService; -import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.avm.deploy.DeploymentService; -import org.alfresco.service.cmr.avm.locking.AVMLockingService; -import org.alfresco.service.cmr.avmsync.AVMSyncService; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.invitation.InvitationService; -import org.alfresco.service.cmr.lock.LockService; -import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; -import org.alfresco.service.cmr.ml.EditionService; -import org.alfresco.service.cmr.ml.MultilingualContentService; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.CopyService; -import org.alfresco.service.cmr.repository.CrossRepositoryCopyService; -import org.alfresco.service.cmr.repository.MimetypeService; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.ScriptService; -import org.alfresco.service.cmr.repository.TemplateService; -import org.alfresco.service.cmr.rule.RuleService; -import org.alfresco.service.cmr.search.CategoryService; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AuthenticationService; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.cmr.security.OwnableService; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.cmr.site.SiteService; -import org.alfresco.service.cmr.tagging.TaggingService; -import org.alfresco.service.cmr.thumbnail.ThumbnailService; -import org.alfresco.service.cmr.version.VersionService; -import org.alfresco.service.cmr.view.ExporterService; -import org.alfresco.service.cmr.view.ImporterService; -import org.alfresco.service.cmr.workflow.WorkflowService; -import org.alfresco.service.descriptor.DescriptorService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.wcm.asset.AssetService; -import org.alfresco.wcm.preview.PreviewURIService; -import org.alfresco.wcm.sandbox.SandboxService; -import org.alfresco.wcm.webproject.WebProjectService; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; - - -/** - * Implementation of a Service Registry based on the definition of - * Services contained within a Spring Bean Factory. - * - * @author David Caruana - */ -public class ServiceDescriptorRegistry - implements BeanFactoryAware, ServiceRegistry -{ - // Bean Factory within which the registry lives - private BeanFactory beanFactory = null; - - - /* (non-Javadoc) - * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) - */ - public void setBeanFactory(BeanFactory beanFactory) throws BeansException - { - this.beanFactory = beanFactory; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getServices() - */ - public Collection getServices() - { - // TODO: Implement - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#isServiceProvided(org.alfresco.repo.ref.QName) - */ - public boolean isServiceProvided(QName service) - { - // TODO: Implement - throw new UnsupportedOperationException(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getService(org.alfresco.repo.ref.QName) - */ - public Object getService(QName service) - { - return beanFactory.getBean(service.getLocalName()); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getDescriptorService() - */ - public DescriptorService getDescriptorService() - { - return (DescriptorService)getService(DESCRIPTOR_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getNodeService() - */ - public NodeService getNodeService() - { - return (NodeService)getService(NODE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getNodeService() - */ - public AuthenticationService getAuthenticationService() - { - return (AuthenticationService)getService(AUTHENTICATION_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getContentService() - */ - public ContentService getContentService() - { - return (ContentService)getService(CONTENT_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getMimetypeService() - */ - public MimetypeService getMimetypeService() - { - return (MimetypeService)getService(MIMETYPE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getVersionService() - */ - public VersionService getVersionService() - { - return (VersionService)getService(VERSION_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getLockService() - */ - public LockService getLockService() - { - return (LockService)getService(LOCK_SERVICE); - } - - public JobLockService getJobLockService() - { - return (JobLockService)getService(JOB_LOCK_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getDictionaryService() - */ - public DictionaryService getDictionaryService() - { - return (DictionaryService)getService(DICTIONARY_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getSearchService() - */ - public SearchService getSearchService() - { - return (SearchService)getService(SEARCH_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getTransactionService() - */ - public TransactionService getTransactionService() - { - return (TransactionService)getService(TRANSACTION_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getRetryingTransactionHelper() - */ - public RetryingTransactionHelper getRetryingTransactionHelper() - { - return (RetryingTransactionHelper)getService(RETRYING_TRANSACTION_HELPER); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCopyService() - */ - public CopyService getCopyService() - { - return (CopyService)getService(COPY_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCheckOutCheckInService() - */ - public CheckOutCheckInService getCheckOutCheckInService() - { - return (CheckOutCheckInService)getService(COCI_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCategoryService() - */ - public CategoryService getCategoryService() - { - return (CategoryService)getService(CATEGORY_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getNamespaceService() - */ - public NamespaceService getNamespaceService() - { - return (NamespaceService)getService(NAMESPACE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getImporterService() - */ - public ImporterService getImporterService() - { - return (ImporterService)getService(IMPORTER_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getExporterService() - */ - public ExporterService getExporterService() - { - return (ExporterService)getService(EXPORTER_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getRuleService() - */ - public RuleService getRuleService() - { - return (RuleService)getService(RULE_SERVICE); - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getActionService() - */ - public ActionService getActionService() - { - return (ActionService)getService(ACTION_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getPermissionService() - */ - public PermissionService getPermissionService() - { - return (PermissionService)getService(PERMISSIONS_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getAuthorityService() - */ - public AuthorityService getAuthorityService() - { - return (AuthorityService)getService(AUTHORITY_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getTemplateService() - */ - public TemplateService getTemplateService() - { - return (TemplateService)getService(TEMPLATE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getTemplateService() - */ - public FileFolderService getFileFolderService() - { - return (FileFolderService)getService(FILE_FOLDER_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getScriptService() - */ - public ScriptService getScriptService() - { - return (ScriptService)getService(SCRIPT_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getWorkflowService() - */ - public WorkflowService getWorkflowService() - { - return (WorkflowService)getService(WORKFLOW_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getWorkflowService() - */ - public AuditService getAuditService() - { - return (AuditService)getService(AUDIT_SERVICE); - } - - /** - * Get the AVMService. - * @return The AVMService or null if there is none. - */ - public AVMService getAVMService() - { - return (AVMService)getService(AVM_SERVICE); - } - - /** - * Get the AVMService. - * @return The AVMService or null if there is none. - */ - public AVMService getAVMLockingAwareService() - { - return (AVMService)getService(AVM_LOCKING_AWARE_SERVICE); - } - - /** - * Get the AVM Sync Service. - * @return The AVM Sync Service. - */ - public AVMSyncService getAVMSyncService() - { - return (AVMSyncService)getService(AVM_SYNC_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getOwnableService() - */ - public OwnableService getOwnableService() - { - return (OwnableService)getService(OWNABLE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getPersonService() - */ - public PersonService getPersonService() - { - return (PersonService)getService(PERSON_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getSiteService() - */ - public SiteService getSiteService() - { - return (SiteService) getService(SITE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCrossRepositoryCopyService() - */ - public CrossRepositoryCopyService getCrossRepositoryCopyService() - { - return (CrossRepositoryCopyService)getService(CROSS_REPO_COPY_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getAttributeService() - */ - public AttributeService getAttributeService() - { - return (AttributeService)getService(ATTRIBUTE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getContentFilterLanguagesService() - */ - public ContentFilterLanguagesService getContentFilterLanguagesService() - { - return (ContentFilterLanguagesService) getService(CONTENT_FILTER_LANGUAGES_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getAVMLockingService() - */ - public AVMLockingService getAVMLockingService() - { - return (AVMLockingService)getService(AVM_LOCKING_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getVirtServerRegistry() - */ - public VirtServerRegistry getVirtServerRegistry() - { - return (VirtServerRegistry)getService(VIRT_SERVER_REGISTRY); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getEditionService() - */ - public EditionService getEditionService() - { - return (EditionService) getService(EDITION_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getMultilingualContentService() - */ - public MultilingualContentService getMultilingualContentService() - { - return (MultilingualContentService) getService(MULTILINGUAL_CONTENT_SERVICE); - } - - /** - * @see org.alfresco.service.ServiceRegistry#getThumbnailService() - */ - public ThumbnailService getThumbnailService() - { - return (ThumbnailService)getService(THUMBNAIL_SERVICE); - } - - /** - * @see org.alfresco.service.ServiceRegistry#getTaggingService() - */ - public TaggingService getTaggingService() - { - return (TaggingService)getService(TAGGING_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getDeploymentService() - */ - public DeploymentService getDeploymentService() - { - return (DeploymentService) getService(DEPLOYMENT_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getWebProjectService() - */ - public WebProjectService getWebProjectService() - { - return (WebProjectService)getService(WEBPROJECT_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getSandboxService() - */ - public SandboxService getSandboxService() - { - return (SandboxService)getService(SANDBOX_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getAssetService() - */ - public AssetService getAssetService() - { - return (AssetService)getService(ASSET_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getPreviewURIService() - */ - public PreviewURIService getPreviewURIService() - { - return (PreviewURIService)getService(PREVIEW_URI_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getFormService() - */ - public FormService getFormService() - { - return (FormService)getService(FORM_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getInvitationService() - */ - public InvitationService getInvitationService() - { - return (InvitationService)getService(INVITATION_SERVICE); - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCMISService() - */ - public CMISServices getCMISService() - { - return (CMISServices)getService(CMIS_SERVICE); - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCMISDictionaryService() - */ - public CMISDictionaryService getCMISDictionaryService() - { - return (CMISDictionaryService)getService(CMIS_DICTIONARY_SERVICE); - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCMISQueryService() - */ - public CMISQueryService getCMISQueryService() - { - return (CMISQueryService)getService(CMIS_QUERY_SERVICE); - } -} +/* + * Copyright (C) 2005-2008 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.service; + +import java.util.Collection; + +import org.alfresco.cmis.CMISDictionaryService; +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.lock.JobLockService; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.attributes.AttributeService; +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.deploy.DeploymentService; +import org.alfresco.service.cmr.avm.locking.AVMLockingService; +import org.alfresco.service.cmr.avmsync.AVMSyncService; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.invitation.InvitationService; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; +import org.alfresco.service.cmr.ml.EditionService; +import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.CopyService; +import org.alfresco.service.cmr.repository.CrossRepositoryCopyService; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.repository.TemplateService; +import org.alfresco.service.cmr.rule.RuleService; +import org.alfresco.service.cmr.search.CategoryService; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.service.cmr.thumbnail.ThumbnailService; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.cmr.view.ExporterService; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.descriptor.DescriptorService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.wcm.asset.AssetService; +import org.alfresco.wcm.preview.PreviewURIService; +import org.alfresco.wcm.sandbox.SandboxService; +import org.alfresco.wcm.webproject.WebProjectService; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; + + +/** + * Implementation of a Service Registry based on the definition of + * Services contained within a Spring Bean Factory. + * + * @author David Caruana + */ +public class ServiceDescriptorRegistry + implements BeanFactoryAware, ServiceRegistry +{ + // Bean Factory within which the registry lives + private BeanFactory beanFactory = null; + + + /* (non-Javadoc) + * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) + */ + public void setBeanFactory(BeanFactory beanFactory) throws BeansException + { + this.beanFactory = beanFactory; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getServices() + */ + public Collection getServices() + { + // TODO: Implement + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#isServiceProvided(org.alfresco.repo.ref.QName) + */ + public boolean isServiceProvided(QName service) + { + // TODO: Implement + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getService(org.alfresco.repo.ref.QName) + */ + public Object getService(QName service) + { + return beanFactory.getBean(service.getLocalName()); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getDescriptorService() + */ + public DescriptorService getDescriptorService() + { + return (DescriptorService)getService(DESCRIPTOR_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getNodeService() + */ + public NodeService getNodeService() + { + return (NodeService)getService(NODE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getNodeService() + */ + public AuthenticationService getAuthenticationService() + { + return (AuthenticationService)getService(AUTHENTICATION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getContentService() + */ + public ContentService getContentService() + { + return (ContentService)getService(CONTENT_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getMimetypeService() + */ + public MimetypeService getMimetypeService() + { + return (MimetypeService)getService(MIMETYPE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getVersionService() + */ + public VersionService getVersionService() + { + return (VersionService)getService(VERSION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getLockService() + */ + public LockService getLockService() + { + return (LockService)getService(LOCK_SERVICE); + } + + public JobLockService getJobLockService() + { + return (JobLockService)getService(JOB_LOCK_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getDictionaryService() + */ + public DictionaryService getDictionaryService() + { + return (DictionaryService)getService(DICTIONARY_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getSearchService() + */ + public SearchService getSearchService() + { + return (SearchService)getService(SEARCH_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getTransactionService() + */ + public TransactionService getTransactionService() + { + return (TransactionService)getService(TRANSACTION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getRetryingTransactionHelper() + */ + public RetryingTransactionHelper getRetryingTransactionHelper() + { + return (RetryingTransactionHelper)getService(RETRYING_TRANSACTION_HELPER); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCopyService() + */ + public CopyService getCopyService() + { + return (CopyService)getService(COPY_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCheckOutCheckInService() + */ + public CheckOutCheckInService getCheckOutCheckInService() + { + return (CheckOutCheckInService)getService(COCI_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCategoryService() + */ + public CategoryService getCategoryService() + { + return (CategoryService)getService(CATEGORY_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getNamespaceService() + */ + public NamespaceService getNamespaceService() + { + return (NamespaceService)getService(NAMESPACE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getImporterService() + */ + public ImporterService getImporterService() + { + return (ImporterService)getService(IMPORTER_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getExporterService() + */ + public ExporterService getExporterService() + { + return (ExporterService)getService(EXPORTER_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getRuleService() + */ + public RuleService getRuleService() + { + return (RuleService)getService(RULE_SERVICE); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getActionService() + */ + public ActionService getActionService() + { + return (ActionService)getService(ACTION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getPermissionService() + */ + public PermissionService getPermissionService() + { + return (PermissionService)getService(PERMISSIONS_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getAuthorityService() + */ + public AuthorityService getAuthorityService() + { + return (AuthorityService)getService(AUTHORITY_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getTemplateService() + */ + public TemplateService getTemplateService() + { + return (TemplateService)getService(TEMPLATE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getTemplateService() + */ + public FileFolderService getFileFolderService() + { + return (FileFolderService)getService(FILE_FOLDER_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getScriptService() + */ + public ScriptService getScriptService() + { + return (ScriptService)getService(SCRIPT_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getWorkflowService() + */ + public WorkflowService getWorkflowService() + { + return (WorkflowService)getService(WORKFLOW_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getWorkflowService() + */ + public AuditService getAuditService() + { + return (AuditService)getService(AUDIT_SERVICE); + } + + /** + * Get the AVMService. + * @return The AVMService or null if there is none. + */ + public AVMService getAVMService() + { + return (AVMService)getService(AVM_SERVICE); + } + + /** + * Get the AVMService. + * @return The AVMService or null if there is none. + */ + public AVMService getAVMLockingAwareService() + { + return (AVMService)getService(AVM_LOCKING_AWARE_SERVICE); + } + + /** + * Get the AVM Sync Service. + * @return The AVM Sync Service. + */ + public AVMSyncService getAVMSyncService() + { + return (AVMSyncService)getService(AVM_SYNC_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getOwnableService() + */ + public OwnableService getOwnableService() + { + return (OwnableService)getService(OWNABLE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getPersonService() + */ + public PersonService getPersonService() + { + return (PersonService)getService(PERSON_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getSiteService() + */ + public SiteService getSiteService() + { + return (SiteService) getService(SITE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCrossRepositoryCopyService() + */ + public CrossRepositoryCopyService getCrossRepositoryCopyService() + { + return (CrossRepositoryCopyService)getService(CROSS_REPO_COPY_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getAttributeService() + */ + public AttributeService getAttributeService() + { + return (AttributeService)getService(ATTRIBUTE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getContentFilterLanguagesService() + */ + public ContentFilterLanguagesService getContentFilterLanguagesService() + { + return (ContentFilterLanguagesService) getService(CONTENT_FILTER_LANGUAGES_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getAVMLockingService() + */ + public AVMLockingService getAVMLockingService() + { + return (AVMLockingService)getService(AVM_LOCKING_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getVirtServerRegistry() + */ + public VirtServerRegistry getVirtServerRegistry() + { + return (VirtServerRegistry)getService(VIRT_SERVER_REGISTRY); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getEditionService() + */ + public EditionService getEditionService() + { + return (EditionService) getService(EDITION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getMultilingualContentService() + */ + public MultilingualContentService getMultilingualContentService() + { + return (MultilingualContentService) getService(MULTILINGUAL_CONTENT_SERVICE); + } + + /** + * @see org.alfresco.service.ServiceRegistry#getThumbnailService() + */ + public ThumbnailService getThumbnailService() + { + return (ThumbnailService)getService(THUMBNAIL_SERVICE); + } + + /** + * @see org.alfresco.service.ServiceRegistry#getTaggingService() + */ + public TaggingService getTaggingService() + { + return (TaggingService)getService(TAGGING_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getDeploymentService() + */ + public DeploymentService getDeploymentService() + { + return (DeploymentService) getService(DEPLOYMENT_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getWebProjectService() + */ + public WebProjectService getWebProjectService() + { + return (WebProjectService)getService(WEBPROJECT_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getSandboxService() + */ + public SandboxService getSandboxService() + { + return (SandboxService)getService(SANDBOX_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getAssetService() + */ + public AssetService getAssetService() + { + return (AssetService)getService(ASSET_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getPreviewURIService() + */ + public PreviewURIService getPreviewURIService() + { + return (PreviewURIService)getService(PREVIEW_URI_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getFormService() + */ + public FormService getFormService() + { + return (FormService)getService(FORM_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getInvitationService() + */ + public InvitationService getInvitationService() + { + return (InvitationService)getService(INVITATION_SERVICE); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCMISService() + */ + public CMISServices getCMISService() + { + return (CMISServices)getService(CMIS_SERVICE); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCMISDictionaryService() + */ + public CMISDictionaryService getCMISDictionaryService() + { + return (CMISDictionaryService)getService(CMIS_DICTIONARY_SERVICE); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCMISQueryService() + */ + public CMISQueryService getCMISQueryService() + { + 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 d88e085fcb..d8b2e2a4a6 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -1,517 +1,527 @@ -/* - * Copyright (C) 2005-2008 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.service; - -import java.util.Collection; - -import org.alfresco.cmis.CMISDictionaryService; -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.lock.JobLockService; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.cmr.action.ActionService; -import org.alfresco.service.cmr.attributes.AttributeService; -import org.alfresco.service.cmr.audit.AuditService; -import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.avm.deploy.DeploymentService; -import org.alfresco.service.cmr.avm.locking.AVMLockingService; -import org.alfresco.service.cmr.avmsync.AVMSyncService; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.invitation.InvitationService; -import org.alfresco.service.cmr.lock.LockService; -import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; -import org.alfresco.service.cmr.ml.EditionService; -import org.alfresco.service.cmr.ml.MultilingualContentService; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.CopyService; -import org.alfresco.service.cmr.repository.CrossRepositoryCopyService; -import org.alfresco.service.cmr.repository.MimetypeService; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.ScriptService; -import org.alfresco.service.cmr.repository.TemplateService; -import org.alfresco.service.cmr.rule.RuleService; -import org.alfresco.service.cmr.search.CategoryService; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AuthenticationService; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.cmr.security.OwnableService; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.cmr.site.SiteService; -import org.alfresco.service.cmr.tagging.TaggingService; -import org.alfresco.service.cmr.thumbnail.ThumbnailService; -import org.alfresco.service.cmr.version.VersionService; -import org.alfresco.service.cmr.view.ExporterService; -import org.alfresco.service.cmr.view.ImporterService; -import org.alfresco.service.cmr.workflow.WorkflowService; -import org.alfresco.service.descriptor.DescriptorService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.wcm.asset.AssetService; -import org.alfresco.wcm.preview.PreviewURIService; -import org.alfresco.wcm.sandbox.SandboxService; -import org.alfresco.wcm.webproject.WebProjectService; - - -/** - * This interface represents the registry of public Repository Services. - * The registry provides meta-data about each service and provides - * access to the service interface. - * - * @author David Caruana - */ -@PublicService -public interface ServiceRegistry -{ - // Service Bean Names - - static final String SERVICE_REGISTRY = "ServiceRegistry"; - - static final QName REGISTRY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ServiceRegistry"); - static final QName DESCRIPTOR_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DescriptorService"); - static final QName TRANSACTION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TransactionService"); - static final QName RETRYING_TRANSACTION_HELPER = QName.createQName(NamespaceService.ALFRESCO_URI, "retryingTransactionHelper"); - static final QName AUTHENTICATION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuthenticationService"); - static final QName NAMESPACE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NamespaceService"); - static final QName DICTIONARY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DictionaryService"); - static final QName NODE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NodeService"); - static final QName CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentService"); - static final QName MIMETYPE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MimetypeService"); - static final QName CONTENT_FILTER_LANGUAGES_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentFilterLanguagesService"); - static final QName MULTILINGUAL_CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MultilingualContentService"); - static final QName EDITION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "EditionService"); - static final QName SEARCH_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SearchService"); - static final QName CATEGORY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CategoryService"); - static final QName COPY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CopyService"); - static final QName LOCK_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "LockService"); - static final QName JOB_LOCK_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "JobLockService"); - static final QName VERSION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "VersionService"); - static final QName COCI_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CheckoutCheckinService"); - static final QName RULE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RuleService"); - static final QName IMPORTER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ImporterService"); - static final QName EXPORTER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ExporterService"); - static final QName ACTION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ActionService"); - static final QName PERMISSIONS_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "PermissionService"); - static final QName AUTHORITY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuthorityService"); - static final QName TEMPLATE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TemplateService"); - static final QName FILE_FOLDER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "FileFolderService"); - static final QName SCRIPT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ScriptService"); - static final QName WORKFLOW_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "WorkflowService"); - static final QName AUDIT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuditService"); - static final QName OWNABLE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "OwnableService"); - static final QName PERSON_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "PersonService"); - static final QName SITE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SiteService"); - static final QName ATTRIBUTE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AttributeService"); - static final QName THUMBNAIL_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ThumbnailService"); - static final QName TAGGING_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TaggingService"); - static final QName FORM_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "FormService"); - static final QName INVITATION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "InvitationService"); - - // WCM / AVM - static final QName AVM_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMService"); - static final QName AVM_LOCKING_AWARE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMLockingAwareService"); - static final QName AVM_SYNC_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMSyncService"); - static final QName CROSS_REPO_COPY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CrossRepositoryCopyService"); - static final QName AVM_LOCKING_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMLockingService"); - static final QName VIRT_SERVER_REGISTRY = QName.createQName(NamespaceService.ALFRESCO_URI, "VirtServerRegistry"); - static final QName DEPLOYMENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DeploymentService"); - static final QName WEBPROJECT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "WebProjectService"); - static final QName SANDBOX_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SandboxService"); - static final QName ASSET_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AssetService"); - static final QName PREVIEW_URI_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "WCMPreviewURIService"); - - // CMIS - 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"); - - - /** - * Get the list of services provided by the Repository - * - * @return list of provided Services - */ - @NotAuditable - Collection getServices(); - - /** - * Is the specified service provided by the Repository? - * - * @param service name of service to test provision of - * @return true => provided, false => not provided - */ - @NotAuditable - boolean isServiceProvided(QName service); - - /** - * Get the specified service. - * - * @param service name of service to retrieve - * @return the service interface (must cast to interface as described in service meta-data) - */ - @NotAuditable - Object getService(QName service); - - /** - * @return the descriptor service - */ - @NotAuditable - DescriptorService getDescriptorService(); - - /** - * @return the transaction service - */ - @NotAuditable - TransactionService getTransactionService(); - - /** - * @return the transaction service - */ - @NotAuditable - RetryingTransactionHelper getRetryingTransactionHelper(); - - /** - * @return the namespace service (or null, if one is not provided) - */ - @NotAuditable - NamespaceService getNamespaceService(); - - /** - * @return the authentication service (or null, if one is not provided) - */ - @NotAuditable - AuthenticationService getAuthenticationService(); - - /** - * @return the node service (or null, if one is not provided) - */ - @NotAuditable - NodeService getNodeService(); - - /** - * @return the content service (or null, if one is not provided) - */ - @NotAuditable - ContentService getContentService(); - - /** - * @return the mimetype service (or null, if one is not provided) - */ - @NotAuditable - MimetypeService getMimetypeService(); - - /** - * @return the content filter languages service (or null, if one is not provided) - */ - @NotAuditable - ContentFilterLanguagesService getContentFilterLanguagesService(); - - /** - * @return the search service (or null, if one is not provided) - */ - @NotAuditable - SearchService getSearchService(); - - /** - * @return the version service (or null, if one is not provided) - */ - @NotAuditable - VersionService getVersionService(); - - /** - * @return the lock service (or null, if one is not provided) - */ - @NotAuditable - LockService getLockService(); - - /** - * @return the job lock service (or null, if one is not provided) - */ - @NotAuditable - JobLockService getJobLockService(); - - /** - * @return the dictionary service (or null, if one is not provided) - */ - @NotAuditable - DictionaryService getDictionaryService(); - - /** - * @return the copy service (or null, if one is not provided) - */ - @NotAuditable - CopyService getCopyService(); - - /** - * @return the checkout / checkin service (or null, if one is not provided) - */ - @NotAuditable - CheckOutCheckInService getCheckOutCheckInService(); - - /** - * @return the category service (or null, if one is not provided) - */ - @NotAuditable - CategoryService getCategoryService(); - - /** - * @return the importer service or null if not present - */ - @NotAuditable - ImporterService getImporterService(); - - /** - * @return the exporter service or null if not present - */ - @NotAuditable - ExporterService getExporterService(); - - /** - * @return the rule service (or null, if one is not provided) - */ - @NotAuditable - RuleService getRuleService(); - - /** - * @return the action service (or null if one is not provided) - */ - @NotAuditable - ActionService getActionService(); - - /** - * @return the permission service (or null if one is not provided) - */ - @NotAuditable - PermissionService getPermissionService(); - - /** - * @return the authority service (or null if one is not provided) - */ - @NotAuditable - AuthorityService getAuthorityService(); - - /** - * @return the template service (or null if one is not provided) - */ - @NotAuditable - TemplateService getTemplateService(); - - /** - * @return the file-folder manipulation service (or null if one is not provided) - */ - @NotAuditable - FileFolderService getFileFolderService(); - - /** - * @return the script execution service (or null if one is not provided) - */ - @NotAuditable - ScriptService getScriptService(); - - /** - * @return the workflow service (or null if one is not provided) - */ - @NotAuditable - WorkflowService getWorkflowService(); - - /** - * @return the audit service (or null if one is not provided) - */ - @NotAuditable - AuditService getAuditService(); - - /** - * Get the AVMService. - * @return The AVM service (or null if one is not provided); - */ - @NotAuditable - AVMService getAVMService(); - - /** - * Get the AVMLockingAwareService. - * @return The AVM locking aware service (or null if one is not provided); - */ - @NotAuditable - AVMService getAVMLockingAwareService(); - - /** - * Get the AVM Sync Service. - * @return The AVM Sync Service. - */ - @NotAuditable - AVMSyncService getAVMSyncService(); - - /** - * Get the ownable service (or null if one is not provided) - * @return - */ - @NotAuditable - OwnableService getOwnableService(); - - /** - * Get the person service (or null if one is not provided) - * @return - */ - @NotAuditable - PersonService getPersonService(); - - /** - * Get the site service (or null if one is not provided) - * @return - */ - @NotAuditable - SiteService getSiteService(); - - /** - * Get the cross repository copy service (or null if one is not provided) - * @return - */ - @NotAuditable - CrossRepositoryCopyService getCrossRepositoryCopyService(); - - /** - * Get the attribute service (or null if one is not provided) - * @return - */ - @NotAuditable - AttributeService getAttributeService(); - - /** - * Get the AVM locking service (or null if one is not provided) - * @return - */ - @NotAuditable - AVMLockingService getAVMLockingService(); - - /** - * Get the Virtualisation Server registry service bean - * @return - */ - @NotAuditable - VirtServerRegistry getVirtServerRegistry(); - - /** - * Get the Multilingual Content Service - * @return - */ - @NotAuditable - MultilingualContentService getMultilingualContentService(); - - /** - * Get the Edition Service - * @return - */ - @NotAuditable - EditionService getEditionService(); - - /** - * Get the Thumbnail Service - * @return - */ - @NotAuditable - ThumbnailService getThumbnailService(); - - /** - * Get the Tagging Service - * @return - */ - @NotAuditable - TaggingService getTaggingService(); - - /** - * Get the WCM Deployment Service - * @return the deployment service (or null, if one is not provided) - */ - @NotAuditable - DeploymentService getDeploymentService(); - - /** - * Get the WCM WebProject Service - * @return - */ - @NotAuditable - WebProjectService getWebProjectService(); - - /** - * Get the WCM Sandbox Service - * @return - */ - @NotAuditable - SandboxService getSandboxService(); - - /** - * Get the WCM Asset Service - * @return - */ - @NotAuditable - AssetService getAssetService(); - - /** - * Get the WCM Preview URI Service - * @return - */ - @NotAuditable - PreviewURIService getPreviewURIService(); - - /** - * Get the form service (or null if one is not provided) - * @return - */ - @NotAuditable - FormService getFormService(); - - /** - * Get the invitation service (or null if one is not provided) - * @return the invitation service - */ - @NotAuditable - InvitationService getInvitationService(); - - /** - * Get the CMIS service (or null if one is not provided) - * @return the CMIS service - */ - @NotAuditable - CMISServices getCMISService(); - - /** - * Get the CMIS Dictionary service (or null if one is not provided) - * @return the CMIS Dictionary service - */ - @NotAuditable - CMISDictionaryService getCMISDictionaryService(); - - /** - * Get the CMIS Query service (or null if one is not provided) - * @return the CMIS Query service - */ - @NotAuditable - CMISQueryService getCMISQueryService(); -} +/* + * Copyright (C) 2005-2008 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.service; + +import java.util.Collection; + +import org.alfresco.cmis.CMISDictionaryService; +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.lock.JobLockService; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.attributes.AttributeService; +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.deploy.DeploymentService; +import org.alfresco.service.cmr.avm.locking.AVMLockingService; +import org.alfresco.service.cmr.avmsync.AVMSyncService; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.invitation.InvitationService; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; +import org.alfresco.service.cmr.ml.EditionService; +import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.CopyService; +import org.alfresco.service.cmr.repository.CrossRepositoryCopyService; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.repository.TemplateService; +import org.alfresco.service.cmr.rule.RuleService; +import org.alfresco.service.cmr.search.CategoryService; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.service.cmr.thumbnail.ThumbnailService; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.cmr.view.ExporterService; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.descriptor.DescriptorService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.wcm.asset.AssetService; +import org.alfresco.wcm.preview.PreviewURIService; +import org.alfresco.wcm.sandbox.SandboxService; +import org.alfresco.wcm.webproject.WebProjectService; + + +/** + * This interface represents the registry of public Repository Services. + * The registry provides meta-data about each service and provides + * access to the service interface. + * + * @author David Caruana + */ +@PublicService +public interface ServiceRegistry +{ + // Service Bean Names + + static final String SERVICE_REGISTRY = "ServiceRegistry"; + + static final QName REGISTRY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ServiceRegistry"); + static final QName DESCRIPTOR_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DescriptorService"); + static final QName TRANSACTION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TransactionService"); + static final QName RETRYING_TRANSACTION_HELPER = QName.createQName(NamespaceService.ALFRESCO_URI, "retryingTransactionHelper"); + static final QName AUTHENTICATION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuthenticationService"); + static final QName NAMESPACE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NamespaceService"); + static final QName DICTIONARY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DictionaryService"); + static final QName NODE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NodeService"); + static final QName CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentService"); + static final QName MIMETYPE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MimetypeService"); + static final QName CONTENT_FILTER_LANGUAGES_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentFilterLanguagesService"); + static final QName MULTILINGUAL_CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MultilingualContentService"); + static final QName EDITION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "EditionService"); + static final QName SEARCH_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SearchService"); + static final QName CATEGORY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CategoryService"); + static final QName COPY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CopyService"); + static final QName LOCK_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "LockService"); + static final QName JOB_LOCK_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "JobLockService"); + static final QName VERSION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "VersionService"); + static final QName COCI_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CheckoutCheckinService"); + static final QName RULE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RuleService"); + static final QName IMPORTER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ImporterService"); + static final QName EXPORTER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ExporterService"); + static final QName ACTION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ActionService"); + static final QName PERMISSIONS_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "PermissionService"); + static final QName AUTHORITY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuthorityService"); + static final QName TEMPLATE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TemplateService"); + static final QName FILE_FOLDER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "FileFolderService"); + static final QName SCRIPT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ScriptService"); + static final QName WORKFLOW_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "WorkflowService"); + static final QName AUDIT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuditService"); + static final QName OWNABLE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "OwnableService"); + static final QName PERSON_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "PersonService"); + static final QName SITE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SiteService"); + static final QName ATTRIBUTE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AttributeService"); + static final QName THUMBNAIL_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ThumbnailService"); + static final QName TAGGING_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TaggingService"); + static final QName FORM_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "FormService"); + static final QName INVITATION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "InvitationService"); + static final QName PREFERENCE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "PreferenceService"); + + // WCM / AVM + static final QName AVM_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMService"); + static final QName AVM_LOCKING_AWARE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMLockingAwareService"); + static final QName AVM_SYNC_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMSyncService"); + static final QName CROSS_REPO_COPY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CrossRepositoryCopyService"); + static final QName AVM_LOCKING_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMLockingService"); + static final QName VIRT_SERVER_REGISTRY = QName.createQName(NamespaceService.ALFRESCO_URI, "VirtServerRegistry"); + static final QName DEPLOYMENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DeploymentService"); + static final QName WEBPROJECT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "WebProjectService"); + static final QName SANDBOX_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SandboxService"); + static final QName ASSET_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AssetService"); + static final QName PREVIEW_URI_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "WCMPreviewURIService"); + + // CMIS + 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"); + + + /** + * Get the list of services provided by the Repository + * + * @return list of provided Services + */ + @NotAuditable + Collection getServices(); + + /** + * Is the specified service provided by the Repository? + * + * @param service name of service to test provision of + * @return true => provided, false => not provided + */ + @NotAuditable + boolean isServiceProvided(QName service); + + /** + * Get the specified service. + * + * @param service name of service to retrieve + * @return the service interface (must cast to interface as described in service meta-data) + */ + @NotAuditable + Object getService(QName service); + + /** + * @return the descriptor service + */ + @NotAuditable + DescriptorService getDescriptorService(); + + /** + * @return the transaction service + */ + @NotAuditable + TransactionService getTransactionService(); + + /** + * @return the transaction service + */ + @NotAuditable + RetryingTransactionHelper getRetryingTransactionHelper(); + + /** + * @return the namespace service (or null, if one is not provided) + */ + @NotAuditable + NamespaceService getNamespaceService(); + + /** + * @return the authentication service (or null, if one is not provided) + */ + @NotAuditable + AuthenticationService getAuthenticationService(); + + /** + * @return the node service (or null, if one is not provided) + */ + @NotAuditable + NodeService getNodeService(); + + /** + * @return the content service (or null, if one is not provided) + */ + @NotAuditable + ContentService getContentService(); + + /** + * @return the mimetype service (or null, if one is not provided) + */ + @NotAuditable + MimetypeService getMimetypeService(); + + /** + * @return the content filter languages service (or null, if one is not provided) + */ + @NotAuditable + ContentFilterLanguagesService getContentFilterLanguagesService(); + + /** + * @return the search service (or null, if one is not provided) + */ + @NotAuditable + SearchService getSearchService(); + + /** + * @return the version service (or null, if one is not provided) + */ + @NotAuditable + VersionService getVersionService(); + + /** + * @return the lock service (or null, if one is not provided) + */ + @NotAuditable + LockService getLockService(); + + /** + * @return the job lock service (or null, if one is not provided) + */ + @NotAuditable + JobLockService getJobLockService(); + + /** + * @return the dictionary service (or null, if one is not provided) + */ + @NotAuditable + DictionaryService getDictionaryService(); + + /** + * @return the copy service (or null, if one is not provided) + */ + @NotAuditable + CopyService getCopyService(); + + /** + * @return the checkout / checkin service (or null, if one is not provided) + */ + @NotAuditable + CheckOutCheckInService getCheckOutCheckInService(); + + /** + * @return the category service (or null, if one is not provided) + */ + @NotAuditable + CategoryService getCategoryService(); + + /** + * @return the importer service or null if not present + */ + @NotAuditable + ImporterService getImporterService(); + + /** + * @return the exporter service or null if not present + */ + @NotAuditable + ExporterService getExporterService(); + + /** + * @return the rule service (or null, if one is not provided) + */ + @NotAuditable + RuleService getRuleService(); + + /** + * @return the action service (or null if one is not provided) + */ + @NotAuditable + ActionService getActionService(); + + /** + * @return the permission service (or null if one is not provided) + */ + @NotAuditable + PermissionService getPermissionService(); + + /** + * @return the authority service (or null if one is not provided) + */ + @NotAuditable + AuthorityService getAuthorityService(); + + /** + * @return the template service (or null if one is not provided) + */ + @NotAuditable + TemplateService getTemplateService(); + + /** + * @return the file-folder manipulation service (or null if one is not provided) + */ + @NotAuditable + FileFolderService getFileFolderService(); + + /** + * @return the script execution service (or null if one is not provided) + */ + @NotAuditable + ScriptService getScriptService(); + + /** + * @return the workflow service (or null if one is not provided) + */ + @NotAuditable + WorkflowService getWorkflowService(); + + /** + * @return the audit service (or null if one is not provided) + */ + @NotAuditable + AuditService getAuditService(); + + /** + * Get the AVMService. + * @return The AVM service (or null if one is not provided); + */ + @NotAuditable + AVMService getAVMService(); + + /** + * Get the AVMLockingAwareService. + * @return The AVM locking aware service (or null if one is not provided); + */ + @NotAuditable + AVMService getAVMLockingAwareService(); + + /** + * Get the AVM Sync Service. + * @return The AVM Sync Service. + */ + @NotAuditable + AVMSyncService getAVMSyncService(); + + /** + * Get the ownable service (or null if one is not provided) + * @return + */ + @NotAuditable + OwnableService getOwnableService(); + + /** + * Get the person service (or null if one is not provided) + * @return + */ + @NotAuditable + PersonService getPersonService(); + + /** + * Get the site service (or null if one is not provided) + * @return + */ + @NotAuditable + SiteService getSiteService(); + + /** + * Get the cross repository copy service (or null if one is not provided) + * @return + */ + @NotAuditable + CrossRepositoryCopyService getCrossRepositoryCopyService(); + + /** + * Get the attribute service (or null if one is not provided) + * @return + */ + @NotAuditable + AttributeService getAttributeService(); + + /** + * Get the AVM locking service (or null if one is not provided) + * @return + */ + @NotAuditable + AVMLockingService getAVMLockingService(); + + /** + * Get the Virtualisation Server registry service bean + * @return + */ + @NotAuditable + VirtServerRegistry getVirtServerRegistry(); + + /** + * Get the Multilingual Content Service + * @return + */ + @NotAuditable + MultilingualContentService getMultilingualContentService(); + + /** + * Get the Edition Service + * @return + */ + @NotAuditable + EditionService getEditionService(); + + /** + * Get the Thumbnail Service + * @return + */ + @NotAuditable + ThumbnailService getThumbnailService(); + + /** + * Get the Tagging Service + * @return + */ + @NotAuditable + TaggingService getTaggingService(); + + /** + * Get the WCM Deployment Service + * @return the deployment service (or null, if one is not provided) + */ + @NotAuditable + DeploymentService getDeploymentService(); + + /** + * Get the WCM WebProject Service + * @return + */ + @NotAuditable + WebProjectService getWebProjectService(); + + /** + * Get the WCM Sandbox Service + * @return + */ + @NotAuditable + SandboxService getSandboxService(); + + /** + * Get the WCM Asset Service + * @return + */ + @NotAuditable + AssetService getAssetService(); + + /** + * Get the WCM Preview URI Service + * @return + */ + @NotAuditable + PreviewURIService getPreviewURIService(); + + /** + * Get the form service (or null if one is not provided) + * @return + */ + @NotAuditable + FormService getFormService(); + + /** + * Get the invitation service (or null if one is not provided) + * @return the invitation service + */ + @NotAuditable + InvitationService getInvitationService(); + + /** + * Get the CMIS service (or null if one is not provided) + * @return the CMIS service + */ + @NotAuditable + CMISServices getCMISService(); + + /** + * Get the CMIS Dictionary service (or null if one is not provided) + * @return the CMIS Dictionary service + */ + @NotAuditable + CMISDictionaryService getCMISDictionaryService(); + + /** + * Get the CMIS Query service (or null if one is not provided) + * @return the CMIS Query service + */ + @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 new file mode 100755 index 0000000000..a3a03ff7b0 --- /dev/null +++ b/source/java/org/alfresco/util/Utf7.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.util; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; + +import com.beetstra.jutf7.CharsetProvider; + +/** + * + * @author Mike Shavnev + * + */ +public class Utf7 +{ + public static final String UTF7 = "UTF-7"; + public static final String UTF7_OPTIONAL = "X-UTF-7-OPTIONAL"; + public static final String UTF7_MODIFIED = "X-MODIFIED-UTF-7"; + + + + /** + * Convert string from UTF-7 characters + * + * @param string Input string for decoding + * @return Decoded string + */ + public static String decode(String string, String charsetName) + { + if (string.length() <= 1) + { + return string; + } + CharsetProvider provider = new CharsetProvider(); + Charset charset = provider.charsetForName(charsetName); + CharBuffer charBuffer = charset.decode(ByteBuffer.wrap(string.getBytes())); + return charBuffer.toString(); + } + + /** + * Convert string to UTF-7 characters + * + * @param string Input string for decoding + * @return Encoded string + */ + public static String encode(String string, String charsetName) + { + if (string.length() <= 1) + { + return string; + } + CharsetProvider provider = new CharsetProvider(); + Charset charset = provider.charsetForName(charsetName); + ByteBuffer byteBuffer = charset.encode(string); + return new String(byteBuffer.array()).substring(0, byteBuffer.limit()); + } +}