The ability of the current James code to handle multiple namespaces is much worse than I hoped initially. I carry on the discussion here as MailboxPath is the medium used to transport the namespace information throughout James from IMAP to store backends and back.
What I say here for MailboxPath also holds mutatis mutandis for org.apache.james.mailbox.store.mail.model.Mailbox which mirrors MailboxPath attributes. I will abbreviate org.apache.james.mailbox.store.mail.model.Mailbox as model.Mailbox to avoid confusion with "mailbox" in its common sense.
The undelying problem seems to be the following:
The exact meaning of the given IMAP mailbox name may strongly depend on its contextual interpretation. This has to do with namespace prefixes and (ii) the special name "INBOX".
Example: When an IMAP client sends e.g. LIST "" "#private.folder.%", the mailbox name "#private.folder" does not have any definitive meaning untill it is combined with the name of the current user. Similarly "INBOX" is "the primary mailbox for this user on this server".
Not only that there is no central place where this interpretation happens, much worse, one could say that this interpretation does not really happen: namespace prefixes and INBOX names are forwarded to storage backends which blindly (with minor exceptions) take them as parts of the given mailbox'es primary key.
Example: when user "u1" asks for folder "#private.folder.subfolder", all storage backends basically do what the JPA storage does: SELECT mailbox FROM Mailbox mailbox WHERE mailbox.name = :nameParam and mailbox.user= :userParam and mailbox.namespace= :namespaceParam
Please note that this makes no harm at present as only the single personal namespace is in play.
Now, if we turn multiple namespaces on, look what will happen:
Say that other users' mailboxes will be available to the current user "u1" under the namespace "#otherUsers". Say further that the currently set ACLs grant the user "u1" full rights to all folders of the user "u2". Now, say "u1" wants to pass his folder "#private.sales.orders" to the user "u2" (e.g. because "u1" gets retired). To make it happen, "u1" moves (i.e. uses IMAP RENAME command) "#private.sales.orders" to "#otherUsers.u2.sales.orders". What will the JPA storage backend do? - rougly the following:
UPDATE Mailbox mailbox SET
mailbox.name = 'u2.sales.orders'
mailbox.user = null
mailbox.namespace = '#otherUsers'
WHERE mailbox.name = 'sales.orders'
and mailbox.user = 'u1'
and mailbox.namespace = '#private'
Clearly, after this update, "u2" will not see "sales.orders" between his folders.
Similar could be demonstrated also for COPY, LIST and probably also other IMAP commands.
Here I am trying to propose a strategy how this problem should be addressed. Please comment.
First of all there should be a central place where this mailbox name interpretation takes place. As "INBOX" and namespaces are IMAP notions, they should be interpreted in the IMAP module. Further, MailboxPath should be made absolute in its nature. And finally, the storage backends should play only with such absolute MailboxPaths.
What I propose for MailboxPath and model.Mailbox:
I still stick with (1) and (2) from my above post.
I have abandoned (3), I rather prefer (8).
(4) MailboxPath should be absolute, i.e. independent from any contextual interpretation:
(4.1) independent from any "current folder" notion
(4.2) independent from any "current user" notion
Any contextual interpretation should take place outside of MailboxPath. Currently I see only IMAP as a place where any interpretation should take place; see (5).
(5) A consequence of (4) is that namespace attribute should be removed from MailboxPath.
Why: Namespace prefixes may be context dependent. The notion of namespace comes from IMAP. Any context dependency of namespace prefixes should be resolved in the IMAP module.
Example: the exact meaning of a MailboxPath located in "personal" (a.k.a "private") namespace depends on the current user. So if an IMAP client authenticated as user "u1" is asking for folder "#personal.folder.subfolder", the folder name should be resolved by the IMAP module as e.g. "users.u1.folder.subfolder".
The opposite direction: if a store backend returns "users.u1.folder.subfolder" to the IMAP module, the IMAP module should transform it to "#personal.folder.subfolder" if the current user is "u1".
(6) INBOX: The same holds for the special folder name "INBOX": If an IMAP client authenticated as user "u1" is asking for folder "INBOX", the folder name should be resolved by the IMAP module as e.g. "users.u1".
(7) Another consequence of (4) is that the user attribute can be removed from MailboxPath. It is not necessary for the interpretation of the meaning of the given MailboxPath.
(7.1) However, the information who is the owner of the given folder should be stored in model.Mailbox.
The owner attribute of model.Mailbox should allow for storing both users and groups as owners.
(7.2) A new boolean attribute ownerGroup should be added to model.Mailbox storing the information whether the owner is a group or an individual user. This is needed for the interpretation of the "owner" ACL key.
(8) MailboxPath should be delimiter agnostic: its attributes must not contain path delimiters. Currently the name attribute may contain path delimiters. Name should be replaced with an immutable collection of the given path's segments; i.e. "folder.subfolder" would be stored e.g. as new String
. Every module which handles with MailboxPaths should be responsible for the selection of the appropriate delimiter and serialization of the path. The IMAP module may choose to use '.' and a filesystem-based storage may choose '/' as a hierarchy delimiter.
(9) All data stored by MailboxPath should be unescaped. Every module which handles with MailboxPaths (IMAP, Storage, Sieve, ...) should be responsible for its own escaping/unescaping, e.g. to avoid conflicts with the hierarchy delimiter it has chosen.