Package org.apache.james.imapserver

Source Code of org.apache.james.imapserver.JamesHost$OpenMailbox

/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.imapserver;

import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.phoenix.Block;
import org.apache.james.AccessControlException;
import org.apache.james.AuthorizationException;
import org.apache.james.services.MailServer;
import org.apache.james.services.UsersRepository;
import org.apache.james.services.UsersStore;
import org.apache.james.util.Assert;

import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.*;


/**
* A single host that has an IMAP4rev1 messaging server.
* There should be one instance of this class per instance of James.
* An IMAP messaging system may span more than one host.
* <p><code>String</code> parameters representing mailbox names must be the
* full hierarchical name of the target, with namespace, as used by the
* specified user. Examples:
* '#mail.Inbox' or '#shared.finance.Q2Earnings'.
* <p>An imap Host must keep track of existing and deleted mailboxes.
*
* References: rfc 2060, rfc 2193, rfc 2221
* @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
* @version 0.1 on 14 Dec 2000
* @see FolderRecord
* @see RecordRepository
*/
public class JamesHost
        extends AbstractLogEnabled
        implements Host, Block, Configurable, Composable, Contextualizable, Initializable
{

    private Context context;
    private Configuration conf;
    private ComponentManager compMgr;
    private String rootPath; // ends with File.seperator
    private IMAPSystem imapSystem;
    //private UserManager usersManager;
    private UsersRepository localUsers;
    private RecordRepository recordRep;
    private OpenMailboxes openMailboxes; //maps absoluteName to ACLMailbox
//    private String namespaceToken;
//    private String privateNamespace;
//    private String privateNamespaceSeparator;
//    private String otherUsersNamespace;
//    private String otherUsersNamespaceSeparator;
//    private String sharedNamespace;
//    private String sharedNamespaceSeparator;

    public static final String HIERARCHY_SEPARATOR = ".";
    public static final char HIERARCHY_SEPARATOR_CHAR = '.';
    public static final String NAMESPACE_TOKEN = "#";
    public static final String PRIVATE_NAMESPACE_PREFIX = "";
    private static final String USER_NAMESPACE = "user";
    private static final String SHARE_NAMESPACE = "share";
    public static final String USER_NAMESPACE_PREFIX = NAMESPACE_TOKEN + USER_NAMESPACE;
    public static final String SHARE_NAMESPACE_PREFIX = NAMESPACE_TOKEN + SHARE_NAMESPACE;


    /*
     * Note on implemented namespaces.
     * 3 namespaces are (partially) implemented.
     * 1) Private namespace ie access to a user's own mailboxes.
     * Full mailbox names (ie what user sees) of the form:
     *   #mail.Inbox or #mail.apache.James
     * Absolute names of the form:
     *   #mail.fred.flintstone.Inbox or #mail.fred.flintstone.apache.James
     * 2) Other users namespace ie access to other users mailboxes
     * subject to access control, of course
     * Full mailbox names (ie what user sees) of the form:
     *   #users.captain.scarlet.Inbox or #users.RobinHood.apache.James
     * Absolute names of the form:
     *   #mail.captain.scarlet.Inbox or #mail.RobinHood.apache.James
     * 3) Shared mailboxes
     * not fully implemented.
     * Full mailbox names (ie what user sees) of the form:
     *   #shared.projectAlpha.masterplan or #shared.customerservice.Inbox
     * Absolute names of the form:
     *   #shared.projectAlpha.masterplan or #shared.customerservice.Inbox
     */

    /*
     * Note on filename extensions
     * Records in AvalonRecordRepository - no extension
     * Mailboxes - mbr
     * MimeMessage - msg
     * MessageAttributes - att
     */

    /* No constructor */

    public void configure( Configuration conf ) throws ConfigurationException
    {
        this.conf = conf;
    }

    public void contextualize( Context context )
    {
        this.context = context;
    }

    public void compose( ComponentManager comp )
    {
        compMgr = comp;
    }

    public void initialize() throws Exception
    {

        getLogger().info( "JamesHost init..." );

        imapSystem = (IMAPSystem) compMgr.lookup( IMAPSystem.ROLE );

        UsersStore usersStore = (UsersStore) compMgr.lookup( "org.apache.james.services.UsersStore" );
        localUsers = usersStore.getRepository( "LocalUsers" );

        String recordRepDest
                = conf.getChild( "recordRepository" ).getValue();
        recordRep = new DefaultRecordRepository();
        setupLogger( recordRep, "recordRep" );
        recordRep.setPath( recordRepDest );
        getLogger().info( "AvalonRecordRepository opened at " + recordRepDest );
        rootPath = conf.getChild( "mailboxRepository" ).getValue();
        if ( !rootPath.endsWith( File.separator ) ) {
            rootPath = rootPath + File.separator;
        }
        prepareDir( rootPath );

        // Create directories for user and shared mailboxes.
        String usersPath = getPath( USER_NAMESPACE_PREFIX );
        prepareDir( usersPath );
        String sharePath = getPath( SHARE_NAMESPACE_PREFIX );
        prepareDir( sharePath );
        getLogger().info( "IMAP Mailbox Repository opened at " + rootPath );
//        Configuration namespaces = conf.getChild("namespaces");
//        namespaceToken = namespaces.getAttribute("token");
//        privateNamespace
//            = namespaces.getChild("privateNamespace").getValue();
//        privateNamespaceSeparator
//            = namespaces.getChild("privateNamespace").getAttribute("separator");
//        otherUsersNamespace
//            = namespaces.getChild("otherusersNamespace").getValue();
//        otherUsersNamespaceSeparator
//            = namespaces.getChild("otherusersNamespace").getAttribute("separator");
//        sharedNamespace
//            = namespaces.getChild("sharedNamespace").getValue();
//        sharedNamespaceSeparator
//            = namespaces.getChild("sharedNamespace").getAttribute("separator");
//        getLogger().info("Handling mail for namespaces: "+ privateNamespace + ", " + otherUsersNamespace + ", " + sharedNamespace);
        openMailboxes = new OpenMailboxes(); // how big should this start?
        getLogger().info( "JamesHost ...init end" );
    }

    /**
     *  Checks that the Directory provided exists and is writable, creating it if it is not.
     */
    private void prepareDir( String dir )
    {
        File newFolder = new File( dir );
        if ( !newFolder.isDirectory() ) {
            if ( !newFolder.mkdir() ) {
                throw new RuntimeException( "Error: Cannot create directory: " + dir );
            }
        }
        else if ( !newFolder.canWrite() ) {
            throw new RuntimeException( "Error: Cannot write to directory: " + dir );
        }
    }

    /**
     * Establishes whether this host is the Home Server for the specified user.
     * Used during login to decide whether a LOGIN_REFERRAL has to be sent to
     * the client.
     *
     * @param username an email address
     * @returns true if inbox (and private mailfolders) are accessible through
     * this host.
     */
    public boolean isHomeServer( String username )
    {
        return localUsers.contains( username );
    }

    /**
     * Establishes if the specified user can access any mailboxes on this host.
     * Used during login process to decide what sort of LOGIN-REFERRAL must be
     * sent to client.
     *
     * @param username an email address
     * @returns true if the specified user has at least read access to any
     * mailboxes on this host.
     */
    public boolean hasLocalAccess( String username )
    {
        return localUsers.contains( username );
    }

    /**
     * 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.
     *
     * @param user email address on whose behalf the request is made.
     * @param mailboxName String name of the target.
     * @returns an Mailbox reference.
     * @throws AccessControlException if the user does not have at least
     * lookup rights.
     * @throws MailboxException if mailbox does not exist locally.
     */
    public synchronized ACLMailbox getMailbox( String user, String mailboxName )
            throws AccessControlException, MailboxException
    {
        Assert.isTrue( Assert.ON &&
                       user != null &&
                       user.length() > 0 &&
                       mailboxName != null );

        getLogger().debug( "Getting mailbox " + mailboxName + " for " + user );

        String absoluteName = getAbsoluteMailboxName( user, mailboxName );

        return getAbsoluteMailbox( user, absoluteName );
    }

    private synchronized ACLMailbox getAbsoluteMailbox( String user, String absoluteName )
            throws AccessControlException, MailboxException
    {
        Assert.isTrue( Assert.ON &&
                       user != null &&
                       absoluteName.startsWith( NAMESPACE_TOKEN ) );

        ACLMailbox mailbox = null;
        FolderRecord record = null;

        // Has a folder with this name ever been created?
        if ( !recordRep.containsRecord( absoluteName ) ) {
            throw new MailboxException( "Mailbox: " + absoluteName + " has never been created.", MailboxException.NOT_LOCAL );
        }
        else {
            record = recordRep.retrieve( absoluteName );
            if ( record.isDeleted() ) {
                throw new MailboxException( "Mailbox has been deleted", MailboxException.LOCAL_BUT_DELETED );
            }
            else if ( openMailboxes.contains( absoluteName ) ) {
                mailbox = openMailboxes.getMailbox( absoluteName );
                if ( ! mailbox.hasLookupRights( user ) ) {
                    throw new AccessControlException( "No lookup rights." );
                }
                openMailboxes.addReference( absoluteName );
                return mailbox;
            }
            else {
                String owner = record.getUser();
                String key = getPath( absoluteName );
                ObjectInputStream in = null;
                try {
                    in = new ObjectInputStream( new FileInputStream( key + File.separator + FileMailbox.MAILBOX_FILE_NAME ) );
                    mailbox = (FileMailbox) in.readObject();
                    setupLogger( mailbox );
                    mailbox.configure( conf );
                    mailbox.contextualize( context );
                    mailbox.compose( compMgr );
                    mailbox.reinitialize();
                }
                catch ( Exception e ) {
                    e.printStackTrace();
                    throw new RuntimeException( "Exception caught while reading FileMailbox: " + e );
                }
                finally {
                    if ( in != null ) {
                        try {
                            in.close();
                        }
                        catch ( Exception ignored ) {
                        }
                    }
                    notifyAll();
                }
                if ( !mailbox.hasLookupRights( user ) ) {
                    throw new AccessControlException( "No lookup rights." );
                }
                openMailboxes.addMailbox( absoluteName, mailbox );
                return mailbox;
            }
        }
    }

    /**
     * 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.
     * If a system allocates different namespaces to different hosts then a
     * request to create a mailbox in a namespace not served by this host would
     * be an error.
     * It is an error to create a mailbox with the name of a mailbox that has
     * been deleted, if that name is still in use.
     *
     * @param user email address on whose behalf the request is made.
     * @param mailboxName String name of the target
     * @returns an Mailbox reference.
     * @throws AccessControlException if the user does not have lookup rights
     * for parent or any needed ancestor folder
     * lookup rights.
     * @throws AuthorizationException if mailbox could be created locally but
     * user does not have create rights.
     * @throws MailboxException if mailbox already exists, locally or remotely,
     * or if mailbox cannot be created locally.
     * @see FolderRecord
     */
    public synchronized ACLMailbox createMailbox( String user, String mailboxName )
            throws AccessControlException, AuthorizationException,
            MailboxException

    {
        Assert.isTrue( Assert.ON &&
                       user != null &&
                       user.length() > 0 &&
                       mailboxName != null );
//        if (user == null || mailboxName == null) {
//            getLogger().error("Null parameters received in createMailbox(). " );
//            throw new RuntimeException("Null parameters received.");
//        } else if (user.equals("")
//                   ||(!mailboxName.startsWith(namespaceToken))) {
//            getLogger().error("Empty/ incorrect parameters received in createMailbox().");
//            throw new RuntimeException("Empty/incorrect parameters received.");
//        }
        String absoluteName = getAbsoluteMailboxName( user, mailboxName );
        Assert.isTrue( Assert.ON &&
                       absoluteName != null );
//        if (absoluteName == null) {
//            getLogger().error("Parameters in createMailbox() cannot be interpreted. ");
//            throw new RuntimeException("Parameters in createMailbox() cannot be interpreted.");
//        }
        getLogger().debug( "JamesHost createMailbox() for:  " + absoluteName );

        return createAbsoluteMailbox( user, absoluteName );
    }

    private synchronized ACLMailbox createAbsoluteMailbox( String user, String absoluteName )
            throws AccessControlException, AuthorizationException,
            MailboxException
    {
        Assert.isTrue( Assert.ON &&
                       absoluteName.startsWith( NAMESPACE_TOKEN ) &&
                       absoluteName.indexOf( HIERARCHY_SEPARATOR ) != -1 );

        ACLMailbox mailbox = null;
        FolderRecord record = null;
        ACLMailbox parentMailbox = null;

        // Has a folder with this name ever been created?
        if ( recordRep.containsRecord( absoluteName ) ) {
//            record = recordRep.retrieve( absoluteName );
//            if ( !record.isDeleted() ) {
                getLogger().error( "Attempt to create an existing Mailbox." );
                throw new MailboxException( "Mailbox already exists", MailboxException.ALREADY_EXISTS_LOCALLY );
//            }
        }
        else {
            // Get the directory holding the new mailbox.
            String parent
                    = absoluteName.substring( 0, absoluteName.lastIndexOf( HIERARCHY_SEPARATOR ) );
//            if (!(parent.startsWith(privateNamespace + privateNamespaceSeparator) || parent.startsWith(sharedNamespace + sharedNamespaceSeparator))) {
//                getLogger().warn("No such parent: " + parent);
//                throw new MailboxException("No such parent: " + parent);
//            }
            //Recurse to a created and not deleted mailbox
            try {
                parentMailbox = getAbsoluteMailbox( user, parent );
            }
            catch ( MailboxException mbe ) {
                if ( mbe.getStatus().equals( MailboxException.NOT_LOCAL )
                        || mbe.getStatus().equals( MailboxException.LOCAL_BUT_DELETED ) ) {
                    parentMailbox = createAbsoluteMailbox( user, parent );
                }
                else {
                    throw new MailboxException( mbe.getMessage(), mbe.getStatus() );
                }
            }
            // Does user have create rights in parent mailbox?
            boolean hasCreateRights = parentMailbox.hasCreateRights( user );
            releaseMailbox( user, parentMailbox );
            if ( ! hasCreateRights ) {
                releaseMailbox( user, parentMailbox );
                throw new AuthorizationException( "User does not have create rights." );
            }
            try {
                mailbox = new FileMailbox();
                setupLogger( mailbox );
                mailbox.configure( conf );
                mailbox.contextualize( context );
                mailbox.compose( compMgr );
                mailbox.prepareMailbox( user, absoluteName, user, recordRep.nextUIDValidity() );
                mailbox.initialize();
            }
            catch ( Exception e ) {
                getLogger().error( "Exception creating mailbox: " + e );
                throw new MailboxException( "Exception creating mailbox: " + e );
            }
            SimpleFolderRecord fr
                    = new SimpleFolderRecord( user, absoluteName );
            fr.initialize();
            recordRep.store( fr );
            openMailboxes.addMailbox( absoluteName, mailbox );
        }

        return mailbox;
    }

    /**
     * Releases a reference to a mailbox, allowing Host to do any housekeeping.
     *
     * @param mbox a non-null reference to an ACL Mailbox.
     */
    public void releaseMailbox( String user, ACLMailbox mailbox )
    {
        if ( mailbox == null ) {
            getLogger().debug( "Attempt to release mailbox with null reference" );
            return;
        }
        if ( user != MailServer.MDA ) {
            mailbox.unsetRecent();
        }
        String absoluteName = mailbox.getAbsoluteName();
        int count = openMailboxes.removeReference( absoluteName );
        if ( count == 0 ) {
            openMailboxes.removeMailbox( absoluteName );
            try {
                FolderRecord fr = recordRep.retrieve( absoluteName );
                fr.setUidValidity( mailbox.getUIDValidity() );
                fr.setHighestUid( mailbox.getNextUID() - 1 );
                fr.setLookupRights( mailbox.getUsersWithLookupRights() );
                fr.setReadRights( mailbox.getUsersWithReadRights() );
                fr.setMarked( mailbox.isMarked() );
                fr.setNotSelectableByAnyone( mailbox.isNotSelectableByAnyone() );
                fr.setExists( mailbox.getExists() );
                fr.setRecent( mailbox.getRecent() );
                fr.setUnseenbyUser( mailbox.getUnseenByUser() );
                recordRep.store( fr );
                mailbox.dispose();
                mailbox = null;
                getLogger().info( "Mailbox object destroyed: " + absoluteName );
            }
            catch ( Exception e ) {
                getLogger().error( "Exception destroying mailbox object: " + e );
                e.printStackTrace();
            }
        }
        else {
            getLogger().info( "Mailbox " + absoluteName + " now has " + count + "live references" );
        }
    }

    /**
     * Deletes an existing MailBox. Specified mailbox must already exist on
     * this server, and the user must have rights to delete it. (Mailbox delete
     * rights are implementation defined, one way is if the user would have the
     * right to create it).
     * Implementations must track deleted mailboxes
     *
     * @param user email address on whose behalf the request is made.
     * @param mailboxName String name of the target
     * @returns true if mailbox deleted successfully
     * @throws MailboxException if mailbox does not exist locally or is any
     * identities INBOX.
     * @throws AuthorizationException if mailbox exists locally but user does
     * not have rights to delete it.
     * @see FolderRecord
     */
    public boolean deleteMailbox( String user, String mailboxName )
            throws MailboxException, AuthorizationException, AccessControlException
    {
        Assert.isTrue( Assert.ON &&
                       user != null &&
                       mailboxName != null &&
                       user.length() > 0 &&
                       mailboxName.length() > 0 );

        String absoluteName = getAbsoluteMailboxName( user, mailboxName );
        getLogger().debug( "JamesHost deleteMailbox() called for:  " + absoluteName );
        return deleteAbsoluteMailbox( user, absoluteName );
    }

    private boolean deleteAbsoluteMailbox( String user, String absoluteName )
            throws MailboxException, AuthorizationException, AccessControlException
    {
        if ( ! recordRep.containsRecord( absoluteName ) ) {
            throw new MailboxException( "Mailbox doesn't exist" );
        }

        int count = openMailboxes.getReferenceCount( absoluteName );
        if ( count > 0 ) {
            throw new MailboxException( "Mailbox is currently selected by another user" );
        }

        ACLMailbox mailbox = getAbsoluteMailbox( user, absoluteName );
        if ( ! mailbox.hasDeleteRights( user ) ) {
            throw new AuthorizationException( "No delete rights" );
        }

        // Get child folders of this mailbox
        Collection childList = listMailboxes( MailServer.MDA, absoluteName, "%", false );
        if ( ! childList.isEmpty() ) {
            if ( mailbox.isNotSelectableByAnyone() ) {
                throw new MailboxException( "Mailbox with \\Noselect AND subfolders cannot be deleted" );
            }
            else {
                // Delete and expunge all messages, and set NotSelectableByAnyone.
                deleteAllMessages( mailbox, user );
                mailbox.setNotSelectableByAnyone( true );
                releaseMailbox( user, mailbox );
            }
        }
        else {
            deleteAllMessages( mailbox, user );
            Assert.isTrue( Assert.ON &&
                           mailbox.getExists() == 0 );

            openMailboxes.removeMailbox( absoluteName );
            recordRep.deleteRecord( recordRep.retrieve( absoluteName ) );
            mailbox.removeMailbox();
        }
        return true;
    }

    private void deleteAllMessages( ACLMailbox mailbox, String user ) throws AccessControlException, AuthorizationException
    {
        // Delete all messages in this box
        int messageCount = mailbox.getExists();
        for ( int i = 0; i < messageCount; i++ ) {
            mailbox.markDeleted( i + 1, user );
        }
        mailbox.expunge( user );
    }

    /**
     * Renames an existing MailBox. The specified mailbox must already
     * exist locally, the requested name must not exist locally already but
     * must be able to be created locally 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
     * folder with the new name, but INBOX is not deleted. If INBOX has
     * inferior mailboxes these are not renamed.
     * It is an error to create a mailbox with the name of a mailbox that has
     * been deleted, if that name is still in use.
     * Implementations must track deleted mailboxes
     *
     * @param user email address on whose behalf the request is made.
     * @param oldMailboxName String name of the existing mailbox
     * @param newMailboxName String target new name
     * @returns true if rename completed successfully
     * @throws MailboxException if mailbox does not exist locally, or there
     * is an existing mailbox with the new name.
     * @throws AuthorizationException if user does not have rights to delete
     * the existing mailbox or create the new mailbox.
     * @see FolderRecord
     */
    public boolean renameMailbox( String user, String oldMailboxName,
                                  String newMailboxName )
            throws MailboxException, AuthorizationException
    {
        Assert.notImplemented();
        return false;
    }

    /**
     * Returns the namespace which should be used for this user unless they
     * expicitly request another.
     *
     * @param username String an email address
     * @returns a String of a namespace
     */
    public String getDefaultNamespace( String username )
    {
        return PRIVATE_NAMESPACE_PREFIX;
    }

    /**
     * Return UIDValidity for named mailbox. Implementations should track
     * existing and deleted folders.
     *
     * @param mailbox String name of the existing mailbox
     * @returns  an integer containing the current UID Validity value.
     */
    //  public int getUIDValidity(String mailbox);

    /**
     * Returns an iterator over an unmodifiable collection of Strings
     * representing mailboxes on this host and their attributes. The specified
     * user must have at least lookup rights for each mailbox returned.
     * If the subscribedOnly flag is set, only mailboxes to which the
     * specified user is currently subscribed should be returned.
     * Implementations that may export circular hierarchies SHOULD restrict the
     * levels of hierarchy returned. The depth suggested by rfc 2683 is 20
     * hierarchy levels.
     * <p>The reference name must be non-empty. If the mailbox name is empty,
     * implementations must not throw either exception but must return a single
     * String (described below) if the reference name specifies a local mailbox
     * accessible to the user and a one-character String containing the
     * hierarchy delimiter of the referenced namespace, otherwise.
     * <p>Each String returned should be a space seperated triple of name
     * attributes, hierarchy delimiter and full mailbox name.   The mailbox
     * name should include the namespace and be relative to the specified user.
     * <p> RFC comments: Implementations SHOULD return quickly. They SHOULD
     * NOT go to excess trouble to calculate\Marked or \Unmarked status.
     * <p>JAMES comment: By elimination, implementations should usually include
     * \Noinferiors or \Noselect, if appropriate. Also, if the reference name
     * and mailbox name resolve to a single local mailbox, implementations
     * should establish all attributes.
     * <p> Note that servers cannot unilaterally remove mailboxes from the
     * subscribed list. A request with the subscribedOnly flag set that
     * attempts to list a deleted mailbox must return that mailbox with the
     * \Noselect attribute.
     *
     * @param username String non-empty email address of requester
     * @param referenceName String non-empty name, including namespace, of a
     * mailbox or level of mailbox hierarchy, relative to user.
     * @param mailboxName String name of a mailbox possible including a
     * wildcard.
     * @param subscribedOnly only return mailboxes currently subscribed.
     * @returns Collection of strings representing a set of mailboxes.
     * @throws AccessControlException if the user does not have at least
     * lookup rights to at least one mailbox in the set requested.
     * @throws MailboxException if the referenceName is not local or if
     * referenceName and mailbox name resolve to a single mailbox which does
     * not exist locally.
     */
    public synchronized Collection listMailboxes( String username,
                                                  String referenceName,
                                                  String mailboxName,
                                                  boolean subscribedOnly )
            throws MailboxException, AccessControlException
    {
        getLogger().debug( "Listing for user: " + username + " ref " + referenceName + " mailbox " + mailboxName );
        List responseList = new ArrayList();

        // For mailboxName == ""; return <"."> <namespace-of-reference>
        if ( mailboxName.equals( "" ) ) {
            String referenceNamespace = getNamespacePrefix( referenceName );
            if ( referenceNamespace == null ) {
                getLogger().error( "Weird arguments for LIST? referenceName was: " + referenceName + " and mailbox names was " + mailboxName );
                return null;
            }
            if ( referenceNamespace.length() == 0 ) {
                referenceNamespace = "\"\"";
            }

            String response = "(\\Noselect) \"" + HIERARCHY_SEPARATOR
                    + "\" " + referenceNamespace;
            responseList.add( response );
            return responseList;
        }

//        // If the mailbox name is absolute, ignore the reference
//        String searchName = mailboxName;
//        if ( mailboxName.startsWith( NAMESPACE_TOKEN ) ) {
//            searchName = referenceName + HIERARCHY_SEPARATOR + mailboxName;
//        }
//
//        getLogger().debug( "Refined mailboxName to: " + searchName );
//
//        //short-circuit evaluation for namespaces
//        String mailboxNamespace = getNamespacePrefix( mailboxName );
//        if ( mailboxNamespace != null &&
//                mailboxName.equals( mailboxNamespace + "%" ) ) {
//            String response = "(\\Noselect) \"" + HIERARCHY_SEPARATOR + "\" \"" + mailboxNamespace + "\"";
//            responseList.add( response );
//            return responseList;
//        }

        try { // for debugging purposes

            //Short-circuit for Netscape client calls - remove first % in, e.g. #mail%.%
            // Eventually we need to handle % anywhere in mailboxname
//            if ( mailboxNamespace != null &&
//                    mailboxName.startsWith( mailboxNamespace + "%" ) ) {
//                mailboxName = mailboxNamespace + mailboxName.substring( mailboxNamespace.length() + 1 );
//            }

            //mailboxName = mailboxName.substring(0,mailboxName.length() -1);
            getLogger().debug( "Refined mailboxName to: " + mailboxName );
            String userTarget;
            if ( mailboxName.startsWith( NAMESPACE_TOKEN ) ) {
                userTarget = mailboxName;
            }
            else {
                if ( referenceName.length() == 0 ||
                        referenceName.endsWith( HIERARCHY_SEPARATOR ) ) {
                    userTarget = referenceName + mailboxName;
                }
                else {
                    userTarget = referenceName + HIERARCHY_SEPARATOR + mailboxName;
                }
            }
            String target = getAbsoluteMailboxName( username, userTarget );
            getLogger().info( "Target is: " + target );
            if ( target == null ) {
                return new HashSet();
            }
            int firstPercent = target.indexOf( "%" );
            int firstStar = target.indexOf( "*" );
            getLogger().info( "First percent at index: " + firstPercent );
            getLogger().info( "First star at index: " + firstStar );

            // For now, only handle wildcards as last character of target.
            String targetMatch = target;
            boolean starWildcard = false;
            if ( firstStar > -1 ) {
                if ( firstStar != (target.length() - 1) ) {
                    getLogger().debug( "Non-terminal * in LIST search." );
                    return null;
                }
                starWildcard = true;
                targetMatch = target.substring( 0, target.length() - 1 );
            }
            boolean percentWildcard = false;
            if ( firstPercent > -1 ) {
                if ( firstPercent != (target.length() - 1) ) {
                    getLogger().debug( "Non-terminal % in LIST search." );
                    return null;
                }
                percentWildcard = true;
                targetMatch = target.substring( 0, target.length() - 1 );
            }

            Iterator all = recordRep.getAbsoluteNames();
            Set matches = new HashSet();

            while ( all.hasNext() ) {
                boolean match = false;
                String testMailboxName = (String) all.next();
                getLogger().info( "Test is: " + testMailboxName );

                if ( starWildcard ) {
                    match = testMailboxName.startsWith( targetMatch );
                }
                else if ( percentWildcard ) {
                    match = (testMailboxName.startsWith( targetMatch )
                            && testMailboxName.lastIndexOf( HIERARCHY_SEPARATOR ) < targetMatch.length());
                }
                else {
                    // no wildcards so exact or nothing
                    match = testMailboxName.equals( target );
                    getLogger().debug( "match/ no match at testMailboxName 1" );
                }
//                else if (firstStar == -1) {
//                    // only % wildcards
//                    if (!testMailboxName.startsWith(target.substring(0, firstPercent))) {
//                        match = false;
//                        getLogger().debug("fail match at testMailboxName 2");
//                    } else if (firstPercent == target.length() -1) {
//                        // only one % and it is terminating char
//                        target = target.substring(0, firstPercent);
//                        getLogger().debug("Refined target to: " + target);
//                        if (testMailboxName.equals(target)) {
//                            match = true;
//                            getLogger().debug("pass match at testMailboxName 3");
//                        } else if ( (testMailboxName.length() > target.length())
//                                    &&  (testMailboxName.indexOf('.', target.length())
//                                         == -1)
//                                    ) {
//                            match = true;
//                            getLogger().debug("pass match at testMailboxName 4");
//                        } else {
//                            match = false;
//                            getLogger().debug("fail match at testMailboxName 5");
//                        }
//                    } else {
//                        int secondPercent = target.indexOf("%", firstPercent + 1);
//                        match = false; // unfinished
//                        getLogger().debug("fail match at testMailboxName 6");
//                    }
//                } else {
//                    //at least one star
//                    int firstWildcard = -1;
//                    if (firstPercent != -1 && firstStar == -1) {
//                        firstWildcard = firstPercent;
//                    } else if (firstStar != -1 && firstPercent == -1) {
//                        firstWildcard = firstStar;
//                    } else if (firstPercent < firstStar) {
//                        firstWildcard = firstPercent;
//                    } else {
//                        firstWildcard = firstStar;
//                    }
//
//                    if (!testMailboxName.startsWith(target.substring(0, firstWildcard))) {
//                        match = false;
//                    } else {
//                        match = false;
//                    }
//                }

                if ( match && subscribedOnly ) {
                    ACLMailbox mailbox = getAbsoluteMailbox( username, testMailboxName );
                    if (! mailbox.isSubscribed( username ) ) {
                        match = false;
                    }
                    releaseMailbox( username, mailbox );
                }

                if ( match ) {
                    getLogger().info( "Processing match for : " + testMailboxName );
                    FolderRecord record = recordRep.retrieve( testMailboxName );
                    ACLMailbox mailbox = null;
                    StringBuffer buf = new StringBuffer();
                    buf.append( "(" );
                    if ( !record.isDeleted() && openMailboxes.contains( target ) ) {
                        mailbox = openMailboxes.getMailbox( target );
                    }
                    if ( record.isDeleted() ) {
                        buf.append( "\\Noselect" );
                    }
                    else if ( openMailboxes.contains( target ) ) {
                        mailbox = openMailboxes.getMailbox( target );
                        if ( !mailbox.isSelectable( username ) ) {
                            buf.append( "\\Noselect" );
                        }
                        if ( mailbox.isMarked() ) {
                            buf.append( "\\Marked" );
                        }
                        else {
                            buf.append( "\\Unmarked" );
                        }
                    }
                    else {
                        if ( !record.isSelectable( username ) ) {
                            buf.append( "\\Noselect" );
                        }
                        if ( record.isMarked() ) {
                            buf.append( "\\Marked" );
                        }
                        else {
                            buf.append( "\\Unmarked" );
                        }
                    }
                    buf.append( ") \"" );
                    buf.appendHIERARCHY_SEPARATOR );
                    buf.append( "\" " );
                    buf.append( getUserAwareMailboxName( username, testMailboxName ) );
                    matches.add( buf.toString() );
                }
            }
            return matches;
        }
        catch ( Exception e ) {
            getLogger().error( "Exception with list request for mailbox " + mailboxName );
            e.printStackTrace();
            return null;
        }
    }

    private String getNamespacePrefix( String mailbox )
    {
        if ( mailbox.startsWith( USER_NAMESPACE_PREFIX ) ) {
            return USER_NAMESPACE_PREFIX;
        }
        else if ( mailbox.startsWith( SHARE_NAMESPACE_PREFIX ) ) {
            return SHARE_NAMESPACE_PREFIX;
        }
        else {
            return PRIVATE_NAMESPACE_PREFIX;
        }
    }

    /**
     * Subscribes a userName to a mailbox. The mailbox must exist locally and the
     * userName must have at least lookup rights to it.
     *
     * @param username String representation of an email address
     * @param mailbox String representation of a mailbox name.
     * @returns true if subscribe completes successfully
     * @throws AccessControlException if the mailbox exists but the userName does
     * not have lookup rights.
     * @throws MailboxException if the mailbox does not exist locally.
     */
    public boolean subscribe( String userName, String mailboxName )
            throws MailboxException, AccessControlException
    {
        Assert.isTrue( Assert.ON &&
                       userName != null &&
                       mailboxName != null &&
                       userName.length() > 0 &&
                       mailboxName.length() > 0 );

        String absoluteName = getAbsoluteMailboxName( userName, mailboxName );
        ACLMailbox mailbox = getAbsoluteMailbox( userName, absoluteName );

        mailbox.subscribe( userName );
        releaseMailbox( userName, mailbox );

        return true;
    }

    /**
     * Unsubscribes from a given mailbox.
     *
     * @param username String representation of an email address
     * @param mailbox String representation of a mailbox name.
     * @returns true if unsubscribe completes successfully
     */
    public boolean unsubscribe( String userName, String mailboxName )
            throws MailboxException, AccessControlException
    {
        Assert.isTrue( Assert.ON &&
                       userName != null &&
                       mailboxName != null &&
                       userName.length() > 0 &&
                       mailboxName.length() > 0 );

        String absoluteName = getAbsoluteMailboxName( userName, mailboxName );
        ACLMailbox mailbox = getAbsoluteMailbox( userName, absoluteName );

        mailbox.unsubscribe( userName );
        releaseMailbox( userName, mailbox );

        return true;
    }

    /**
     * Returns a string giving the status of a mailbox on requested criteria.
     * Currently defined staus items are:
     * MESSAGES - Nummber of messages in mailbox
     * RECENT - Number of messages with \Recent flag set
     * UIDNEXT - The UID that will be assigned to the next message entering
     * the mailbox
     * UIDVALIDITY - The current UIDValidity value for the mailbox
     * UNSEEN - The number of messages which do not have the \Seen flag set.
     *
     * @param username String non-empty email address of requester
     * @param mailboxName String name of a mailbox (no wildcards allowed).
     * @param dataItems Vector of one or more Strings each of a single
     * status item.
     * @returns String consisting of space seperated pairs:
     * dataItem-space-number.
     * @throws AccessControlException if the user does not have at least
     * lookup rights to the mailbox requested.
     * @throws MailboxException if the mailboxName does not exist locally.
     */
    public String getMailboxStatus( String username, String mailboxName,
                                    List dataItems )
            throws MailboxException, AccessControlException
    {
        String absoluteName = getAbsoluteMailboxName( username, mailboxName );
        ACLMailbox mailbox = null;
        FolderRecord record = null;
        Iterator it = dataItems.iterator();
        String response = null;

        // Has a folder with this name ever been created?
        if ( !recordRep.containsRecord( absoluteName ) ) {
            throw new MailboxException( "Mailbox: " + absoluteName + " has never been created.", MailboxException.NOT_LOCAL );
        }
        else {
            record = recordRep.retrieve( absoluteName );
            if ( record.isDeleted() ) {
                throw new MailboxException( "Mailbox has been deleted", MailboxException.LOCAL_BUT_DELETED );
            }
            else if ( openMailboxes.contains( absoluteName ) ) {
                response = new String();
                mailbox = openMailboxes.getMailbox( absoluteName );
                if ( !mailbox.hasLookupRights( username ) ) {
                    throw new AccessControlException( "No lookup rights." );
                }
                while ( it.hasNext() ) {
                    String dataItem = (String) it.next();
                    if ( dataItem.equalsIgnoreCase( "MESSAGES" ) ) {
                        response += "MESSAGES " + mailbox.getExists();
                    }
                    else if ( dataItem.equalsIgnoreCase( "RECENT" ) ) {
                        response += "RECENT " + mailbox.getRecent();
                    }
                    else if ( dataItem.equalsIgnoreCase( "UIDNEXT" ) ) {
                        response += "UIDNEXT " + mailbox.getNextUID();
                    }
                    else if ( dataItem.equalsIgnoreCase( "UIDVALIDITY" ) ) {
                        response += "UIDVALIDITY " + mailbox.getUIDValidity();
                    }
                    else if ( dataItem.equalsIgnoreCase( "UNSEEN" ) ) {
                        response += "UNSEEN " + mailbox.getUnseen( username );
                    }
                    if ( it.hasNext() ) {
                        response += " ";
                    }
                }
                return response;
            }
            else {
                if ( !record.hasLookupRights( username ) ) {
                    throw new AccessControlException( "No lookup rights." );
                }
                response = new String();
                while ( it.hasNext() ) {
                    String dataItem = (String) it.next();
                    if ( dataItem.equalsIgnoreCase( "MESSAGES" ) ) {
                        response += "MESSAGES " + record.getExists();
                    }
                    else if ( dataItem.equalsIgnoreCase( "RECENT" ) ) {
                        response += "RECENT " + record.getRecent();
                    }
                    else if ( dataItem.equalsIgnoreCase( "UIDNEXT" ) ) {
                        response += "UIDNEXT " + (record.getHighestUid() + 1);
                    }
                    else if ( dataItem.equalsIgnoreCase( "UIDVALIDITY" ) ) {
                        response += "UIDVALIDITY " + record.getUidValidity();
                    }
                    else if ( dataItem.equalsIgnoreCase( "UNSEEN" ) ) {
                        response += "UNSEEN " + record.getUnseen( username );
                    }
                    if ( it.hasNext() ) {
                        response += " ";
                    }
                }
                return response;
            }
        }
    }

    /**
     * Convert a user-based full mailbox name into a server absolute name.
     * If the fullMailboxName begins with the namespace token,
     * return as-is.
     * If not, need to resolve the Mailbox name for this user.
     * Example:
     * <br> Convert "INBOX" for user "Fred.Flinstone" into
     * absolute name: "#user.Fred.Flintstone.INBOX"
     *
     * @returns String of absoluteName, null if not valid selection
     */
    private String getAbsoluteMailboxName( String user, String fullMailboxName )
    {
        // First decode the mailbox name as an Atom / String.

        if ( fullMailboxName.startsWith( NAMESPACE_TOKEN ) ) {
            return fullMailboxName.toLowerCase();
        }
        else {
            if ( fullMailboxName.length() == 0 ) {
                return USER_NAMESPACE_PREFIX + HIERARCHY_SEPARATOR + user;
            }
            else {
                return USER_NAMESPACE_PREFIX + HIERARCHY_SEPARATOR + user + HIERARCHY_SEPARATOR + fullMailboxName.toLowerCase();
            }
        }


//        if (fullMailboxName.equals(privateNamespace)) {
//            return fullMailboxName + user + privateNamespaceSeparator;
//        } else if (fullMailboxName.equals(privateNamespace
//                                          + privateNamespaceSeparator)) {
//            return fullMailboxName + user + privateNamespaceSeparator;
//        } else if (fullMailboxName.startsWith(privateNamespace)) {
//            return new String(privateNamespace + privateNamespaceSeparator
//                              + user + privateNamespaceSeparator
//                              + fullMailboxName.substring(privateNamespace.length()  + privateNamespaceSeparator.length()));
//        } else if (fullMailboxName.equals(otherUsersNamespace)) {
//            return null;
//        }else if (fullMailboxName.equals(otherUsersNamespace
//                                         + otherUsersNamespaceSeparator)) {
//            return null;
//        } else if (fullMailboxName.startsWith(otherUsersNamespace)) {
//
//            return new String(privateNamespace + privateNamespaceSeparator
//                              + fullMailboxName.substring(otherUsersNamespace.length() + otherUsersNamespaceSeparator.length()));
//
//        } else if (fullMailboxName.startsWith(sharedNamespace)) {
//            return fullMailboxName;
//        } else {
//            return null;
//        }
    }

    /**
     * Convert a server absolute name into a user-aware mailbox name.
     * If the absolute name starts with #user.<username>.
     * remove this section.
     * Otherwise, return as-is.
     *
     * @returns user-aware mailbox name
     */
    private String getUserAwareMailboxName( String user, String absoluteName )
    {
        String userPrefix = USER_NAMESPACE_PREFIX + HIERARCHY_SEPARATOR + user + HIERARCHY_SEPARATOR;
        if ( absoluteName.startsWith( userPrefix ) ) {
            return absoluteName.substring( userPrefix.length() );
        }
        else {
            return absoluteName;
        }

//        if(absoluteName.startsWith(privateNamespace)) {
//            if (absoluteName.equals(privateNamespace + privateNamespaceSeparator  + user)) {
//                return new String(privateNamespace );
//            } else if (absoluteName.startsWith(privateNamespace + privateNamespaceSeparator  + user)){
//                return new String(privateNamespace
//                                  + absoluteName.substring(privateNamespace.length()  + privateNamespaceSeparator.length()  + user.length()));
//            } else {
//                // It's another users mailbox
//                // Where is separator between name and mailboxes?
//                int pos = absoluteName.substring(privateNamespace.length() + privateNamespaceSeparator.length()).indexOf(privateNamespaceSeparator);
//                return new String(otherUsersNamespace
//                                  + otherUsersNamespaceSeparator
//                                  + absoluteName.substring(pos ));
//            }
//        } else if (absoluteName.startsWith(sharedNamespace)) {
//            return absoluteName;
//        } else {
//            return null;
//        }
    }

    /**
     * Return the file-system path to a given absoluteName mailbox.
     *
     * @param absoluteName the user-independent name of the mailbox
     * @param owner string name of owner of mailbox
     */
    String getPath( String absoluteName )
    {
        Assert.isTrue( Assert.ON &&
                       absoluteName.startsWith( NAMESPACE_TOKEN ) );

        // Remove the leading '#' and replace Hierarchy separators with file separators.
        String filePath = absoluteName.substring( NAMESPACE_TOKEN.length() );
        filePath = filePath.replace( HIERARCHY_SEPARATOR_CHAR, File.separatorChar );
        return rootPath + filePath;

//        String path;
//        if (absoluteName.startsWith(privateNamespace)) {
//            String path1 = rootPath + owner;
//            String path2
//                = absoluteName.substring(privateNamespace.length()
//                                         + privateNamespaceSeparator.length()
//                                         + owner.length());
//            path = path1 + path2.replace(privateNamespaceSeparator.charAt(0), File.separatorChar);
//        } else if (absoluteName.startsWith(sharedNamespace)) {
//            String path3 = absoluteName.substring(namespaceToken.length());
//            path = rootPath + File.separator + path3.replace(privateNamespaceSeparator.charAt(0), File.separatorChar);
//        } else {
//            path = null;
//        }
//        return path;
    }

    public boolean createPrivateMailAccount( String user )
    {
        Assert.isTrue( Assert.ON &&
                       user != null &&
                       user.length() > 0 );

        String userRootName
                = getAbsoluteMailboxName( user, "" );
        String userInboxName
                = getAbsoluteMailboxName( user, "INBOX" );
        SimpleFolderRecord userRootRecord
                = new SimpleFolderRecord( user,
                                          userRootName );
        SimpleFolderRecord userInboxRecord
                = new SimpleFolderRecord( user, userInboxName );

        ACLMailbox userRootFolder = new FileMailbox();
        ACLMailbox userInbox = new FileMailbox();
        try {
            setupLogger( userRootFolder );
            userRootFolder.configure( conf );
            userRootFolder.contextualize( context );
            userRootFolder.compose( compMgr );
            userRootFolder.prepareMailbox( user, userRootName, user, recordRep.nextUIDValidity() );
            setupLogger( userInbox );
            userInbox.configure( conf );
            userInbox.contextualize( context );
            userInbox.compose( compMgr );
            userInbox.prepareMailbox( user, userInboxName, user, recordRep.nextUIDValidity() );
            userRootFolder.initialize();
            userRootFolder.setNotSelectableByAnyone( true );
            userInbox.initialize();
            userInbox.setRights( user, MailServer.MDA, "lrswi" );
        }
        catch ( Exception e ) {
            getLogger().error( "Exception creating new account ", e );
            return false;
        }
        userInboxRecord.initialize();
        userInboxRecord.setUidValidity( userInbox.getUIDValidity() );
        userInboxRecord.setHighestUid( userInbox.getNextUID() - 1 );
        userInboxRecord.setLookupRights( userInbox.getUsersWithLookupRights() );
        userInboxRecord.setReadRights( userInbox.getUsersWithReadRights() );
        userInboxRecord.setNotSelectableByAnyone( userInbox.isNotSelectableByAnyone() );
        userRootRecord.initialize();
        userRootRecord.setLookupRights( userRootFolder.getUsersWithLookupRights() );
        userRootRecord.setReadRights( userRootFolder.getUsersWithReadRights() );
        userRootRecord.setNotSelectableByAnyone( userRootFolder.isNotSelectableByAnyone() );
        recordRep.store( userRootRecord );
        recordRep.store( userInboxRecord );

        //No one is using these mailboxes
        //try {
        //      userRootFolder.destroy();
        //      userInbox.destroy();
        //} catch (Exception e) {
        //    getLogger().error("Exception closing new account mailbox: " + e);
        //    return false;
        //}
        userRootFolder = null;
        userInbox = null;

        return true;
    }

    private static final class OpenMailboxes
    {
        private Map _mailboxes = new HashMap();

        boolean contains( String absoluteName )
        {
            return _mailboxes.containsKey( absoluteName );
        }

        private OpenMailbox getOpen( String absoluteName )
        {
            return (OpenMailbox)_mailboxes.get( absoluteName );
        }

        void addMailbox( String absoluteName, ACLMailbox mailbox )
        {
            OpenMailbox openMailbox = new OpenMailbox( mailbox );
            _mailboxes.put( absoluteName, openMailbox );
        }

        void removeMailbox( String absoluteName )
        {
            _mailboxes.remove( absoluteName );
        }

        ACLMailbox getMailbox( String absoluteName )
        {
            return getOpen( absoluteName ).getMailbox();
        }

        int addReference( String absoluteName )
        {
            return getOpen( absoluteName ).addReference();
        }

        int removeReference( String absoluteName )
        {
            return getOpen( absoluteName ).removeReference();
        }

        int getReferenceCount( String absoluteName )
        {
            OpenMailbox openMailbox = getOpen( absoluteName );
            if ( openMailbox == null ) {
                return 0;
            }
            else {
                return openMailbox.getReferenceCount();
            }
        }
    }

    private static final class OpenMailbox
    {
        private ACLMailbox _mailbox;
        private int _referenceCount;

        OpenMailbox( ACLMailbox mailbox )
        {
            _mailbox = mailbox;
            _referenceCount = 1;
        }

        ACLMailbox getMailbox()
        {
            return _mailbox;
        }
        int getReferenceCount()
        {
            return _referenceCount;
        }

        int addReference()
        {
            return ++_referenceCount;
        }
        int removeReference()
        {
            return --_referenceCount;
        }
    }
}
TOP

Related Classes of org.apache.james.imapserver.JamesHost$OpenMailbox

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
div>