/* * Copyright (C) 2005-2011 Alfresco Software Limited. * * This file is part of Alfresco * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ package org.alfresco.repo.imap; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.alfresco.repo.imap.exception.AlfrescoImapFolderException; import org.alfresco.repo.imap.exception.AlfrescoImapRuntimeException; import org.alfresco.util.Utf7; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.icegreen.greenmail.imap.AuthorizationException; import com.icegreen.greenmail.imap.ImapHostManager; import com.icegreen.greenmail.store.FolderException; import com.icegreen.greenmail.store.MailFolder; import com.icegreen.greenmail.user.GreenMailUser; /** * This Host Manager is assumed to be session local, i.e. there is one instance per IMAP TCP connection. This means that * it can locally cache items being interacted with during the session and its knowledge of which folders / messages do * or do not exist will match that of the client. * * @author Mike Shavnev * @author David Ward */ public class AlfrescoImapHostManager implements ImapHostManager { private ImapService imapService; private Map folderCache; private static Log logger = LogFactory.getLog(AlfrescoImapHostManager.class); /** * @param imapService */ public AlfrescoImapHostManager(ImapService imapService) { this.imapService = imapService; } /** * Returns the hierarchy delimiter for mailboxes on this host. * * @return The hierarchy delimiter character. */ public char getHierarchyDelimiter() { return AlfrescoImapConst.HIERARCHY_DELIMITER; } /** * Returns an collection of mailboxes. Method searches mailboxes under mount points defined for a specific user. * Mount points include user's IMAP Virtualised Views and Email Archive Views. This method serves LIST command * of the IMAP protocol. * * @param user User making the request * @param mailboxPattern String name of a mailbox possible including a wildcard. * @return Collection of mailboxes matching the pattern. * @throws com.icegreen.greenmail.store.FolderException */ public Collection listMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException { try { AlfrescoImapUser alfrescoUser = new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()); return registerMailboxes(imapService.listMailboxes(alfrescoUser, getUnqualifiedMailboxPattern( alfrescoUser, mailboxPattern), false)); } catch (Throwable e) { logger.debug(e.getMessage(), e); throw new FolderException(e.getMessage()); } } /** * Returns an collection of subscribed mailboxes. To appear in search result mailboxes should have * {http://www.alfresco.org/model/imap/1.0}subscribed property specified for user. Method searches * subscribed mailboxes under mount points defined for a specific user. Mount points include user's * IMAP Virtualised Views and Email Archive Views. This method serves LSUB command of the IMAP protocol. * * @param user User making the request * @param mailboxPattern String name of a mailbox possible including a wildcard. * @return Collection of mailboxes matching the pattern. * @throws com.icegreen.greenmail.store.FolderException */ public Collection listSubscribedMailboxes(GreenMailUser user, String mailboxPattern) throws FolderException { try { AlfrescoImapUser alfrescoUser = new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()); return registerMailboxes(imapService.listMailboxes(alfrescoUser, getUnqualifiedMailboxPattern( alfrescoUser, mailboxPattern), true)); } catch (Throwable e) { logger.debug(e.getMessage(), e); throw new FolderException(e.getMessage()); } } /** * 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 * @param newMailboxName String target new name * @throws com.icegreen.greenmail.store.FolderException if an existing folder with the new name. * @throws AlfrescoImapFolderException if user does not have rights to create the new mailbox. */ public void renameMailbox(GreenMailUser user, String oldMailboxName, String newMailboxName) throws FolderException, AuthorizationException { try { AlfrescoImapUser alfrescoUser = new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()); String oldFolderPath = getUnqualifiedMailboxPattern(alfrescoUser, oldMailboxName); String newFolderpath = getUnqualifiedMailboxPattern(alfrescoUser, newMailboxName); imapService.renameMailbox(alfrescoUser, oldFolderPath, newFolderpath); if (folderCache != null) { folderCache.remove(oldFolderPath); folderCache.remove(newFolderpath); } } catch (Throwable e) { logger.debug(e.getMessage(), e); 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. * * @param user User making the request. * @param mailboxName String name of the target * @return an Mailbox reference. * @throws com.icegreen.greenmail.store.FolderException if mailbox already exists * @throws AlfrescoImapFolderException if user does not have rights to create the new mailbox. */ public MailFolder createMailbox(GreenMailUser user, String mailboxName) throws AuthorizationException, FolderException { try { AlfrescoImapUser alfrescoUser = new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()); return registerMailBox(imapService.getOrCreateMailbox(alfrescoUser, getUnqualifiedMailboxPattern( alfrescoUser, mailboxName), false, true)); } catch (Throwable e) { logger.debug(e.getMessage(), e); throw new FolderException(e.getMessage()); } } /** * 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(GreenMailUser user, String mailboxName) throws FolderException, AuthorizationException { try { AlfrescoImapUser alfrescoUser = new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()); String folderPath = getUnqualifiedMailboxPattern(alfrescoUser, mailboxName); imapService.deleteMailbox(alfrescoUser, folderPath); if (folderCache != null) { folderCache.remove(folderPath); } } catch (Throwable e) { logger.debug(e.getMessage(), e); 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. * * @param user User making the request. * @param mailboxName String name of the target. * @return an Mailbox reference. */ public MailFolder getFolder(GreenMailUser user, String mailboxName) { AlfrescoImapUser alfrescoUser = new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()); String folderPath = getUnqualifiedMailboxPattern( alfrescoUser, mailboxName); if (folderCache == null) { registerMailBox(imapService.getOrCreateMailbox(alfrescoUser, mailboxName, true, false)); } AlfrescoImapFolder result = folderCache.get(folderPath); // if folder isn't in cache then add it via registerMailBox method return result != null ? result : registerMailBox(imapService.getOrCreateMailbox(alfrescoUser, mailboxName, true, false)); } /** * Simply calls {@link #getFolder(GreenMailUser, String)}.

Added to implement {@link ImapHostManager}. */ public MailFolder getFolder(final GreenMailUser user, final String mailboxName, boolean mustExist) throws FolderException { try { return getFolder(user, mailboxName); } catch (AlfrescoImapRuntimeException e) { if (!mustExist) { return null; } else if (e.getCause() instanceof FolderException) { throw (FolderException) e.getCause(); } throw e; } } /** * Returns a reference to the user's INBOX. * * @param user The user making the request. * @return The user's Inbox. */ public MailFolder getInbox(GreenMailUser user) throws FolderException { return getFolder(new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()), AlfrescoImapConst.INBOX_NAME); } /** * Not supported. May be used by GreenMailUser.create() method.

Added to implement {@link ImapHostManager}. */ public void createPrivateMailAccount(GreenMailUser user) throws FolderException { throw new UnsupportedOperationException(); } /** * Subscribes a user to a mailbox. The mailbox must exist locally and the user must have rights to modify it. *

* 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(GreenMailUser user, String mailbox) throws FolderException { try { AlfrescoImapUser alfrescoUser = new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()); imapService.subscribe(alfrescoUser, getUnqualifiedMailboxPattern(alfrescoUser, mailbox)); } catch (Throwable e) { logger.debug(e.getMessage(), e); throw new FolderException(e.getMessage()); } } /** * 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(GreenMailUser user, String mailbox) throws FolderException { try { AlfrescoImapUser alfrescoUser = new AlfrescoImapUser(user.getEmail(), user.getLogin(), user.getPassword()); imapService.unsubscribe(alfrescoUser, getUnqualifiedMailboxPattern(alfrescoUser, mailbox)); } catch (Throwable e) { logger.debug(e.getMessage(), e); throw new FolderException(e.getMessage()); } } /** * Not supported. Used by GreenMail class. */ public List getAllMessages() { throw new UnsupportedOperationException(); } private String getUnqualifiedMailboxPattern(AlfrescoImapUser user, String mailboxPattern) { mailboxPattern = Utf7.decode(mailboxPattern, Utf7.UTF7_MODIFIED); if (mailboxPattern.startsWith(AlfrescoImapConst.NAMESPACE_PREFIX)) { int sepIndex = mailboxPattern.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); return sepIndex == -1 ? mailboxPattern.endsWith("*") ? "*" : "" : mailboxPattern.substring(sepIndex + 1); } return mailboxPattern; } /** * Caches returned mail folder collection and converts to a generic MailFolder collection. * * @param mailboxes * a Collection of Alfresco mailboxes * @return a generic MailFolder collection associated with the session */ private Collection registerMailboxes(Collection mailboxes) { int size = mailboxes.size(); if (size == 0) { return Collections.emptyList(); } Collection result = new ArrayList(size); if (folderCache == null) { folderCache = new HashMap(size * 2); } for (AlfrescoImapFolder mailbox : mailboxes) { result.add(registerMailBox(mailbox)); } return result; } /** * Caches a returned mail folder and converts to a generic MailFolder. * * @param mailbox * an Alfresco mailbox * @return a generic MailFolder associated with the session */ private MailFolder registerMailBox(AlfrescoImapFolder mailbox) { String folderPath = mailbox.getFolderPath(); if (folderCache == null) { folderCache = new HashMap(2); } if ((mailbox.isSelectable() || folderPath.isEmpty())) { AlfrescoImapFolder oldFolder = folderCache.get(folderPath); if (oldFolder != null && oldFolder.getFolderInfo().getNodeRef().equals(mailbox.getFolderInfo().getNodeRef()) && oldFolder.reset()) { mailbox = oldFolder; } else { folderCache.put(folderPath, mailbox); } } return mailbox; } }