Index: mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java (revision 1242500) +++ mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java (working copy) @@ -61,11 +61,12 @@ * * @param session * @return writable + * @throws MailboxException * @deprecated use * {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)} */ @Deprecated - boolean isWriteable(MailboxSession session); + boolean isWriteable(MailboxSession session) throws MailboxException; /** * Return true if {@link MessageResult#getModSeq()} is stored in a permanent Index: mailbox/api/src/main/java/org/apache/james/mailbox/acl/MailboxACLResolver.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/acl/MailboxACLResolver.java (revision 1242500) +++ mailbox/api/src/main/java/org/apache/james/mailbox/acl/MailboxACLResolver.java (working copy) @@ -20,6 +20,8 @@ package org.apache.james.mailbox.acl; +import javax.mail.Flags; + import org.apache.james.mailbox.exception.UnsupportedRightException; import org.apache.james.mailbox.model.MailboxACL; import org.apache.james.mailbox.model.MailboxACL.MailboxACLRight; @@ -110,4 +112,43 @@ */ public abstract MailboxACLRights listRights(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException; + /** + * Maps the given {@code mailboxACLRights} to READ-WRITE and READ-ONLY + * response codes. + * + * From RFC 4314 section 5.2: + * + * The server SHOULD include a READ-WRITE response code in the tagged OK + * response if at least one of the "i", "e", or "shared flag rights"(***) is + * granted to the current user. + * + * The server MUST include a READ-ONLY response code in the tagged OK + * response to a SELECT command if none of the following rights is granted + * to the current user: "i", "e", and "shared flag rights"(***). + * + * @param mailboxACLRights + * the rights applicable to the user and resource in question. + * This method supposes that any global ACLs were already applied + * to the {@code mailboxACLRights} parameter before this method + * is called. + * @param sharedFlags + * From RFC 4314 section 5.2: If the ACL server implements some + * flags as shared for a mailbox (i.e., the ACL for the mailbox + * MAY be set up so that changes to those flags are visible to + * another user), let’s call the set of rights associated with + * these flags (as described in Section 4) for that mailbox + * collectively as "shared flag rights". Note that the + * "shared flag rights" set MAY be different for different + * mailboxes. + * + * If the server doesn’t support "shared multiuser write access" + * to a mailbox or doesn’t implement shared flags on the mailbox, + * "shared flag rights" for the mailbox is defined to be the + * empty set. + * + * @return + * @throws UnsupportedRightException + */ + public abstract boolean isReadWrite(MailboxACLRights mailboxACLRights, Flags sharedFlags) throws UnsupportedRightException; + } Index: mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java (revision 1242500) +++ mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java (working copy) @@ -24,6 +24,9 @@ import java.util.Map; import java.util.Map.Entry; +import javax.mail.Flags; +import javax.mail.Flags.Flag; + import org.apache.james.mailbox.exception.UnsupportedRightException; import org.apache.james.mailbox.model.MailboxACL; import org.apache.james.mailbox.model.SimpleMailboxACL; @@ -31,6 +34,7 @@ import org.apache.james.mailbox.model.MailboxACL.MailboxACLRight; import org.apache.james.mailbox.model.MailboxACL.MailboxACLRights; import org.apache.james.mailbox.model.MailboxACL.NameType; +import org.apache.james.mailbox.model.SimpleMailboxACL.Rfc4314Rights; import com.sun.mail.mbox.Mailbox; @@ -44,7 +48,7 @@ * the result is computed afterwards with * nonNegativeUnion.except(negativeUnion). * - * Allows for setting dictinct global ACL for users' mailboxes on one hand and + * Allows for setting distinct global ACL for users' mailboxes on one hand and * group (a.k.a shared) mailboxes on the other hand. E.g. the zero parameter * constructor uses full rights for user mailboxes and * full-except-administration rights for group mailboxes. @@ -140,9 +144,7 @@ return false; } - /* - * (non-Javadoc) - * + /** * @see * org.apache.james.mailbox.MailboxACLResolver#applyGlobalACL(org.apache * .james.mailbox.MailboxACL, boolean) @@ -152,9 +154,7 @@ return resourceOwnerIsGroup ? resourceACL.union(groupGlobalACL) : resourceACL.union(userGlobalACL); } - /* - * (non-Javadoc) - * + /** * @see * org.apache.james.mailbox.store.mail.MailboxACLResolver#hasRight(java. * lang.String, org.apache.james.mailbox.store.mail.MailboxACLResolver. @@ -200,9 +200,41 @@ return result; } - /* - * (non-Javadoc) - * + /** + * @see org.apache.james.mailbox.acl.MailboxACLResolver#isReadWrite(org.apache.james.mailbox.model.MailboxACL.MailboxACLRights, javax.mail.Flags) + */ + @Override + public boolean isReadWrite(MailboxACLRights mailboxACLRights, Flags sharedFlags) throws UnsupportedRightException { + /* the two fast cases first */ + if (mailboxACLRights.contains(Rfc4314Rights.i_Insert_RIGHT) || mailboxACLRights.contains(Rfc4314Rights.e_PerformExpunge_RIGHT)) { + return true; + } + /* + * then go through shared flags. RFC 4314 section 4: + * + * Changing flags: STORE + * + * - the server MUST check if the user has "t" right + * + * - when the user modifies \Deleted flag "s" right + * + * - when the user modifies \Seen flag "w" right - for all other message flags. + * + */ + else if (sharedFlags != null) { + if (sharedFlags.contains(Flag.DELETED) && mailboxACLRights.contains(Rfc4314Rights.t_DeleteMessages_RIGHT)) { + return true; + } else if (sharedFlags.contains(Flag.SEEN) && mailboxACLRights.contains(Rfc4314Rights.s_WriteSeenFlag_RIGHT)) { + return true; + } else { + boolean hasWriteRight = mailboxACLRights.contains(Rfc4314Rights.w_Write_RIGHT); + return hasWriteRight && (sharedFlags.contains(Flag.ANSWERED) || sharedFlags.contains(Flag.DRAFT) || sharedFlags.contains(Flag.FLAGGED) || sharedFlags.contains(Flag.RECENT) || sharedFlags.contains(Flag.USER)); + } + } + return false; + } + + /** * @see * org.apache.james.mailbox.store.mail.MailboxACLResolver#rightsOf(java. * lang.String, org.apache.james.mailbox.store.mail.MailboxACLResolver. Index: mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java (revision 1242500) +++ mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java (working copy) @@ -133,7 +133,7 @@ boolean isSupported(MailboxACLRight right); /** - * Tells whether this conatins the given right. + * Tells whether this contains the given right. * * @param right * @return @@ -141,7 +141,7 @@ * iff the given right is not supported. */ boolean contains(MailboxACLRight right) throws UnsupportedRightException; - + /** * Performs the set theoretic operation of relative complement of * toRemove MailboxACLRights in this MailboxACLRights. Index: mailbox/api/src/main/java/org/apache/james/mailbox/model/SimpleMailboxACL.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/model/SimpleMailboxACL.java (revision 1242500) +++ mailbox/api/src/main/java/org/apache/james/mailbox/model/SimpleMailboxACL.java (working copy) @@ -354,9 +354,7 @@ } } - /* - * (non-Javadoc) - * + /** * @see * org.apache.james.mailbox.MailboxACL.MailboxACLRights#contains(org * .apache.james.mailbox.MailboxACL.MailboxACLRight) @@ -381,9 +379,7 @@ } } - /* - * (non-Javadoc) - * + /** * @see * org.apache.james.mailbox.MailboxACL.MailboxACLRights#except(org.apache * .james.mailbox.MailboxACL.MailboxACLRights) Index: mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java =================================================================== --- mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java (revision 1242500) +++ mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java (working copy) @@ -45,17 +45,20 @@ import org.apache.james.mailbox.MessageManager; import org.apache.james.mailbox.acl.GroupMembershipResolver; import org.apache.james.mailbox.acl.MailboxACLResolver; +import org.apache.james.mailbox.acl.UnionMailboxACLResolver; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.ReadOnlyException; import org.apache.james.mailbox.exception.UnsupportedRightException; import org.apache.james.mailbox.model.MailboxACL; +import org.apache.james.mailbox.model.MailboxACL.MailboxACLRight; +import org.apache.james.mailbox.model.MailboxACL.MailboxACLRights; import org.apache.james.mailbox.model.MessageMetaData; import org.apache.james.mailbox.model.MessageRange; +import org.apache.james.mailbox.model.MessageResult.FetchGroup; import org.apache.james.mailbox.model.MessageResultIterator; import org.apache.james.mailbox.model.SearchQuery; +import org.apache.james.mailbox.model.SimpleMailboxACL; import org.apache.james.mailbox.model.UpdatedFlags; -import org.apache.james.mailbox.model.MailboxACL.MailboxACLRight; -import org.apache.james.mailbox.model.MessageResult.FetchGroup; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.MessageMapper.FetchType; import org.apache.james.mailbox.store.mail.MessageMapperFactory; @@ -77,20 +80,23 @@ import org.apache.james.mime4j.stream.RecursionMode; /** - * Base class for {@link org.apache.james.mailbox.MessageManager} implementations. + * Base class for {@link org.apache.james.mailbox.MessageManager} + * implementations. * - * This base class take care of dispatching events to the registered {@link MailboxListener} and so help - * with handling concurrent {@link MailboxSession}'s. + * This base class take care of dispatching events to the registered + * {@link MailboxListener} and so help with handling concurrent + * {@link MailboxSession}'s. * * - * + * */ -public class StoreMessageManager implements org.apache.james.mailbox.MessageManager{ +public class StoreMessageManager implements org.apache.james.mailbox.MessageManager { /** * The minimal Permanent flags the {@link MessageManager} must support.
* - * Be sure this static instance will never get modifed later! + * Be sure this static instance will never get modifed + * later! */ protected final static Flags MINIMAL_PERMANET_FLAGS; static { @@ -103,22 +109,23 @@ } private final Mailbox mailbox; - - private final MailboxEventDispatcher dispatcher; - + + private final MailboxEventDispatcher dispatcher; + private final MessageMapperFactory mapperFactory; private final MessageSearchIndex index; - + private final MailboxACLResolver aclResolver; - + private final GroupMembershipResolver groupMembershipResolver; - private MailboxPathLocker locker; + private MailboxPathLocker locker; private int fetchBatchSize; - - public StoreMessageManager(final MessageMapperFactory mapperFactory, final MessageSearchIndex index, final MailboxEventDispatcher dispatcher, final MailboxPathLocker locker, final Mailbox mailbox, final MailboxACLResolver aclResolver, final GroupMembershipResolver groupMembershipResolver) throws MailboxException { + + public StoreMessageManager(final MessageMapperFactory mapperFactory, final MessageSearchIndex index, final MailboxEventDispatcher dispatcher, final MailboxPathLocker locker, final Mailbox mailbox, final MailboxACLResolver aclResolver, + final GroupMembershipResolver groupMembershipResolver) throws MailboxException { this.mailbox = mailbox; this.dispatcher = dispatcher; this.mapperFactory = mapperFactory; @@ -127,12 +134,11 @@ this.aclResolver = aclResolver; this.groupMembershipResolver = groupMembershipResolver; } - + public void setFetchBatchSize(int fetchBatchSize) { this.fetchBatchSize = fetchBatchSize; } - - + /** * Return the {@link MailboxPathLocker} * @@ -141,7 +147,7 @@ protected MailboxPathLocker getLocker() { return locker; } - + /** * Return the {@link MailboxEventDispatcher} for this Mailbox * @@ -150,18 +156,17 @@ protected MailboxEventDispatcher getDispatcher() { return dispatcher; } - + /** * Return the underlying {@link Mailbox} * * @return mailbox * @throws MailboxException */ - + public Mailbox getMailboxEntity() throws MailboxException { return mailbox; } - /** * Return {@link Flags} which are permanent stored by the mailbox. By @@ -188,12 +193,26 @@ return new Flags(MINIMAL_PERMANET_FLAGS); } - - + /** + * Returns the flags which are shared for the current mailbox, i.e. the + * flags set up so that changes to those flags are visible to another user. + * See RFC 4314 section 5.2. + * + * In this implementation, all permanent flags are shared, ergo we simply + * return {@link #getPermanentFlags(MailboxSession)} + * + * @see UnionMailboxACLResolver#isReadWrite(MailboxACLRights, Flags) + * + * @param session + * @return + */ + protected Flags getSharedPermanentFlags(MailboxSession session) { + return getPermanentFlags(session); + } /** - * Return true. If an subclass don't want to store mod-sequences in a permanent way just override this - * and return false + * Return true. If an subclass don't want to store mod-sequences in a + * permanent way just override this and return false * * @return true */ @@ -202,60 +221,61 @@ } /** - * @see org.apache.james.mailbox.MessageManager#expunge(org.apache.james.mailbox.model.MessageRange, org.apache.james.mailbox.MailboxSession) + * @see org.apache.james.mailbox.MessageManager#expunge(org.apache.james.mailbox.model.MessageRange, + * org.apache.james.mailbox.MailboxSession) */ public Iterator expunge(final MessageRange set, MailboxSession mailboxSession) throws MailboxException { if (!isWriteable(mailboxSession)) { - throw new ReadOnlyException(new StoreMailboxPath(getMailboxEntity()),mailboxSession.getPathDelimiter()); + throw new ReadOnlyException(new StoreMailboxPath(getMailboxEntity()), mailboxSession.getPathDelimiter()); } Map uids = deleteMarkedInMailbox(set, mailboxSession); - + dispatcher.expunged(mailboxSession, uids, getMailboxEntity()); - return uids.keySet().iterator(); + return uids.keySet().iterator(); } /** - * @see org.apache.james.mailbox.MessageManager#appendMessage(java.io.InputStream, java.util.Date, org.apache.james.mailbox.MailboxSession, boolean, javax.mail.Flags) + * @see org.apache.james.mailbox.MessageManager#appendMessage(java.io.InputStream, + * java.util.Date, org.apache.james.mailbox.MailboxSession, boolean, + * javax.mail.Flags) */ - public long appendMessage(final InputStream msgIn, Date internalDate, - final MailboxSession mailboxSession, final boolean isRecent, final Flags flagsToBeSet) - throws MailboxException { - + public long appendMessage(final InputStream msgIn, Date internalDate, final MailboxSession mailboxSession, final boolean isRecent, final Flags flagsToBeSet) throws MailboxException { + File file = null; TeeInputStream tmpMsgIn = null; BodyOffsetInputStream bIn = null; FileOutputStream out = null; SharedFileInputStream contentIn = null; - + if (!isWriteable(mailboxSession)) { - throw new ReadOnlyException(new StoreMailboxPath(getMailboxEntity()),mailboxSession.getPathDelimiter()); + throw new ReadOnlyException(new StoreMailboxPath(getMailboxEntity()), mailboxSession.getPathDelimiter()); } - + try { - // Create a temporary file and copy the message to it. We will work with the file as + // Create a temporary file and copy the message to it. We will work + // with the file as // source for the InputStream file = File.createTempFile("imap", ".msg"); out = new FileOutputStream(file); - + tmpMsgIn = new TeeInputStream(msgIn, out); - + bIn = new BodyOffsetInputStream(tmpMsgIn); - // Disable line length... This should be handled by the smtp server component and not the parser itself + // Disable line length... This should be handled by the smtp server + // component and not the parser itself // https://issues.apache.org/jira/browse/IMAP-122 MimeConfig config = new MimeConfig(); config.setMaxLineLen(-1); config.setMaxHeaderLen(-1); final MimeTokenStream parser = new MimeTokenStream(config, new DefaultBodyDescriptorBuilder()); - + parser.setRecursionMode(RecursionMode.M_NO_RECURSE); parser.parse(bIn); final HeaderImpl header = new HeaderImpl(); - + EntityState next = parser.next(); - while (next != EntityState.T_BODY - && next != EntityState.T_END_OF_STREAM - && next != EntityState.T_START_MULTIPART) { + while (next != EntityState.T_BODY && next != EntityState.T_END_OF_STREAM && next != EntityState.T_START_MULTIPART) { if (next == EntityState.T_FIELD) { header.addField(parser.getField()); } @@ -293,27 +313,27 @@ } else { propertyBuilder.setCharset(codeset); } - + final String boundary = descriptor.getBoundary(); if (boundary != null) { propertyBuilder.setBoundary(boundary); - } + } if ("text".equalsIgnoreCase(mediaType)) { final CountingInputStream bodyStream = new CountingInputStream(parser.getInputStream()); bodyStream.readAll(); long lines = bodyStream.getLineCount(); bodyStream.close(); next = parser.next(); - if (next == EntityState.T_EPILOGUE) { + if (next == EntityState.T_EPILOGUE) { final CountingInputStream epilogueStream = new CountingInputStream(parser.getInputStream()); epilogueStream.readAll(); - lines+=epilogueStream.getLineCount(); + lines += epilogueStream.getLineCount(); epilogueStream.close(); } propertyBuilder.setTextualLineCount(lines); } - + final Flags flags; if (flagsToBeSet == null) { flags = new Flags(); @@ -331,8 +351,9 @@ internalDate = new Date(); } byte[] discard = new byte[4096]; - while(tmpMsgIn.read(discard) != -1) { - // consume the rest of the stream so everything get copied to the file now + while (tmpMsgIn.read(discard) != -1) { + // consume the rest of the stream so everything get copied to + // the file now // via the TeeInputStream } int bodyStartOctet = (int) bIn.getBodyStartOffset(); @@ -348,14 +369,14 @@ @Override public Long execute() throws MailboxException { MessageMetaData data = appendMessageToStore(message, mailboxSession); - + SortedMap uids = new TreeMap(); uids.put(data.getUid(), data); dispatcher.added(mailboxSession, uids, getMailboxEntity()); return data.getUid(); } }, true); - + } catch (IOException e) { throw new MailboxException("Unable to parse message", e); } catch (MimeException e) { @@ -369,7 +390,8 @@ // delete the temporary file if one was specified if (file != null) { if (!file.delete()) { - // Don't throw an IOException. The message could be appended and the temporary file + // Don't throw an IOException. The message could be appended + // and the temporary file // will be deleted hopefully some day } } @@ -377,7 +399,6 @@ } - /** * Create a new {@link Message} for the given data * @@ -387,76 +408,78 @@ * @param content * @param flags * @return membership - * @throws MailboxException + * @throws MailboxException */ - protected Message createMessage(Date internalDate, final int size, int bodyStartOctet, - final SharedInputStream content, final Flags flags, final PropertyBuilder propertyBuilder) throws MailboxException { + protected Message createMessage(Date internalDate, final int size, int bodyStartOctet, final SharedInputStream content, final Flags flags, final PropertyBuilder propertyBuilder) throws MailboxException { return new SimpleMessage(internalDate, size, bodyStartOctet, content, flags, propertyBuilder, getMailboxEntity().getMailboxId()); } - + /** * This mailbox is writable + * + * @throws MailboxException */ - public boolean isWriteable(MailboxSession session) { - return true; + public boolean isWriteable(MailboxSession session) throws MailboxException { + return aclResolver.isReadWrite(myRights(session), getSharedPermanentFlags(session)); } - - + /** - * @see MessageManager#getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup) + * @see MessageManager#getMetaData(boolean, MailboxSession, + * org.apache.james.mailbox.MessageManager.MetaData.FetchGroup) */ - public MetaData getMetaData(boolean resetRecent, MailboxSession mailboxSession, - org.apache.james.mailbox.MessageManager.MetaData.FetchGroup fetchGroup) throws MailboxException { - + public MetaData getMetaData(boolean resetRecent, MailboxSession mailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup fetchGroup) throws MailboxException { + final List recent; final Flags permanentFlags = getPermanentFlags(mailboxSession); final long uidValidity = getMailboxEntity().getUidValidity(); - final long uidNext = mapperFactory.getMessageMapper(mailboxSession).getLastUid(mailbox) +1; - final long highestModSeq = mapperFactory.getMessageMapper(mailboxSession).getHighestModSeq(mailbox); - final long messageCount; + final long uidNext = mapperFactory.getMessageMapper(mailboxSession).getLastUid(mailbox) + 1; + final long highestModSeq = mapperFactory.getMessageMapper(mailboxSession).getHighestModSeq(mailbox); + final long messageCount; final long unseenCount; final Long firstUnseen; switch (fetchGroup) { - case UNSEEN_COUNT: - unseenCount = countUnseenMessagesInMailbox(mailboxSession); - messageCount = getMessageCount(mailboxSession); - firstUnseen = null; - recent = recent(resetRecent, mailboxSession); + case UNSEEN_COUNT: + unseenCount = countUnseenMessagesInMailbox(mailboxSession); + messageCount = getMessageCount(mailboxSession); + firstUnseen = null; + recent = recent(resetRecent, mailboxSession); - break; - case FIRST_UNSEEN: - firstUnseen = findFirstUnseenMessageUid(mailboxSession); - messageCount = getMessageCount(mailboxSession); - unseenCount = 0; - recent = recent(resetRecent, mailboxSession); + break; + case FIRST_UNSEEN: + firstUnseen = findFirstUnseenMessageUid(mailboxSession); + messageCount = getMessageCount(mailboxSession); + unseenCount = 0; + recent = recent(resetRecent, mailboxSession); - break; - case NO_UNSEEN: - firstUnseen = null; - unseenCount = 0; - messageCount = getMessageCount(mailboxSession); - recent = recent(resetRecent, mailboxSession); + break; + case NO_UNSEEN: + firstUnseen = null; + unseenCount = 0; + messageCount = getMessageCount(mailboxSession); + recent = recent(resetRecent, mailboxSession); - break; - default: - firstUnseen = null; - unseenCount = 0; - messageCount = -1; - // just reset the recent but not include them in the metadata - if (resetRecent) { - recent(resetRecent, mailboxSession); - } - recent = new ArrayList(); - break; + break; + default: + firstUnseen = null; + unseenCount = 0; + messageCount = -1; + // just reset the recent but not include them in the metadata + if (resetRecent) { + recent(resetRecent, mailboxSession); + } + recent = new ArrayList(); + break; } - MailboxACL resolvedAcl = aclResolver.applyGlobalACL(mailbox.getACL(), isGroupFolder(mailboxSession) ); - return new MailboxMetaData(recent, permanentFlags, uidValidity, uidNext,highestModSeq, messageCount, unseenCount, firstUnseen, isWriteable(mailboxSession), isModSeqPermanent(mailboxSession), resolvedAcl ); + MailboxACL resolvedAcl = getResolvedMailboxACL(mailboxSession); + return new MailboxMetaData(recent, permanentFlags, uidValidity, uidNext, highestModSeq, messageCount, unseenCount, firstUnseen, isWriteable(mailboxSession), isModSeqPermanent(mailboxSession), resolvedAcl); } - /** - * Check if the given {@link Flags} contains {@link Flags} which are not included in the returned {@link Flags} of {@link #getPermanentFlags(MailboxSession)}. - * If any are found, these are removed from the given {@link Flags} instance. The only exception is the {@link Flag#RECENT} flag. + * Check if the given {@link Flags} contains {@link Flags} which are not + * included in the returned {@link Flags} of + * {@link #getPermanentFlags(MailboxSession)}. If any are found, these are + * removed from the given {@link Flags} instance. The only exception is the + * {@link Flag#RECENT} flag. * * This flag is never removed! * @@ -464,52 +487,54 @@ * @param session */ private void trimFlags(Flags flags, MailboxSession session) { - + Flags permFlags = getPermanentFlags(session); - + Flag[] systemFlags = flags.getSystemFlags(); - for (int i = 0; i < systemFlags.length; i++) { + for (int i = 0; i < systemFlags.length; i++) { Flag f = systemFlags[i]; - + if (f != Flag.RECENT && permFlags.contains(f) == false) { flags.remove(f); } } - // if the permFlags contains the special USER flag we can skip this as all user flags are allowed + // if the permFlags contains the special USER flag we can skip this as + // all user flags are allowed if (permFlags.contains(Flags.Flag.USER) == false) { String[] uFlags = flags.getUserFlags(); - for (int i = 0; i setFlags(final Flags flags, final boolean value, final boolean replace, - final MessageRange set, MailboxSession mailboxSession) throws MailboxException { - + public Map setFlags(final Flags flags, final boolean value, final boolean replace, final MessageRange set, MailboxSession mailboxSession) throws MailboxException { + if (!isWriteable(mailboxSession)) { - throw new ReadOnlyException(new StoreMailboxPath(getMailboxEntity()),mailboxSession.getPathDelimiter()); + throw new ReadOnlyException(new StoreMailboxPath(getMailboxEntity()), mailboxSession.getPathDelimiter()); } final SortedMap newFlagsByUid = new TreeMap(); trimFlags(flags, mailboxSession); - + final MessageMapper messageMapper = mapperFactory.getMessageMapper(mailboxSession); - + Iterator it = messageMapper.execute(new Mapper.Transaction>() { public Iterator run() throws MailboxException { - return messageMapper.updateFlags(getMailboxEntity(),flags, value, replace, set); + return messageMapper.updateFlags(getMailboxEntity(), flags, value, replace, set); } }); - + final SortedMap uFlags = new TreeMap(); while (it.hasNext()) { @@ -517,14 +542,12 @@ newFlagsByUid.put(flag.getUid(), flag.getNewFlags()); uFlags.put(flag.getUid(), flag); } - + dispatcher.flagsUpdated(mailboxSession, new ArrayList(uFlags.keySet()), getMailboxEntity(), new ArrayList(uFlags.values())); return newFlagsByUid; } - - /** * Copy the {@link MessageRange} to the {@link StoreMessageManager} * @@ -535,9 +558,9 @@ */ public List copyTo(final MessageRange set, final StoreMessageManager toMailbox, final MailboxSession session) throws MailboxException { if (!toMailbox.isWriteable(session)) { - throw new ReadOnlyException(new StoreMailboxPath(toMailbox.getMailboxEntity()),session.getPathDelimiter()); + throw new ReadOnlyException(new StoreMailboxPath(toMailbox.getMailboxEntity()), session.getPathDelimiter()); } - + return locker.executeWithLock(session, new StoreMailboxPath(toMailbox.getMailboxEntity()), new MailboxPathLocker.LockAwareExecution>() { @Override @@ -548,7 +571,7 @@ } }, true); } - + protected MessageMetaData appendMessageToStore(final Message message, MailboxSession session) throws MailboxException { final MessageMapper mapper = mapperFactory.getMessageMapper(session); return mapperFactory.getMessageMapper(session).execute(new Mapper.Transaction() { @@ -556,7 +579,7 @@ public MessageMetaData run() throws MailboxException { return mapper.add(getMailboxEntity(), message); } - + }); } @@ -568,7 +591,9 @@ } /** - * @see org.apache.james.mailbox.MessageManager#getMessages(org.apache.james.mailbox.model.MessageRange, org.apache.james.mailbox.model.MessageResult.FetchGroup, org.apache.james.mailbox.MailboxSession) + * @see org.apache.james.mailbox.MessageManager#getMessages(org.apache.james.mailbox.model.MessageRange, + * org.apache.james.mailbox.model.MessageResult.FetchGroup, + * org.apache.james.mailbox.MailboxSession) */ public MessageResultIterator getMessages(final MessageRange set, FetchGroup fetchGroup, MailboxSession mailboxSession) throws MailboxException { final MessageMapper messageMapper = mapperFactory.getMessageMapper(mailboxSession); @@ -587,19 +612,20 @@ protected List recent(final boolean reset, MailboxSession mailboxSession) throws MailboxException { if (reset) { if (!isWriteable(mailboxSession)) { - throw new ReadOnlyException(new StoreMailboxPath(getMailboxEntity()),mailboxSession.getPathDelimiter()); + throw new ReadOnlyException(new StoreMailboxPath(getMailboxEntity()), mailboxSession.getPathDelimiter()); } } final MessageMapper messageMapper = mapperFactory.getMessageMapper(mailboxSession); - + return messageMapper.execute(new Mapper.Transaction>() { public List run() throws MailboxException { final List members = messageMapper.findRecentMessageUidsInMailbox(getMailboxEntity()); - // Convert to MessageRanges so we may be able to optimize the flag update + // Convert to MessageRanges so we may be able to optimize the + // flag update List ranges = MessageRange.toRanges(members); - for (MessageRange range:ranges) { + for (MessageRange range : ranges) { if (reset) { // only call save if we need to messageMapper.updateFlags(getMailboxEntity(), new Flags(Flag.RECENT), false, false, range); @@ -607,11 +633,11 @@ } return members; } - + }); - + } - + protected Map deleteMarkedInMailbox(final MessageRange range, final MailboxSession session) throws MailboxException { final MessageMapper messageMapper = mapperFactory.getMessageMapper(session); @@ -621,12 +647,13 @@ public Map run() throws MailboxException { return messageMapper.expungeMarkedForDeletionInMailbox(getMailboxEntity(), range); } - - }); + + }); } /** - * @see org.apache.james.mailbox.MessageManager#search(org.apache.james.mailbox.model.SearchQuery, org.apache.james.mailbox.MailboxSession) + * @see org.apache.james.mailbox.MessageManager#search(org.apache.james.mailbox.model.SearchQuery, + * org.apache.james.mailbox.MailboxSession) */ public Iterator search(SearchQuery query, MailboxSession mailboxSession) throws MailboxException { return index.search(mailboxSession, getMailboxEntity(), query); @@ -651,7 +678,9 @@ } /** - * @see org.apache.james.mailbox.store.AbstractStoreMessageManager#copy(org.apache.james.mailbox.model.MessageRange, org.apache.james.mailbox.store.AbstractStoreMessageManager, org.apache.james.mailbox.MailboxSession) + * @see org.apache.james.mailbox.store.AbstractStoreMessageManager#copy(org.apache.james.mailbox.model.MessageRange, + * org.apache.james.mailbox.store.AbstractStoreMessageManager, + * org.apache.james.mailbox.MailboxSession) */ private SortedMap copy(MessageRange set, final StoreMessageManager to, final MailboxSession session) throws MailboxException { MessageMapper messageMapper = mapperFactory.getMessageMapper(session); @@ -686,30 +715,75 @@ * @return uid * @throws MailboxException */ - protected Long findFirstUnseenMessageUid(MailboxSession session) throws MailboxException{ + protected Long findFirstUnseenMessageUid(MailboxSession session) throws MailboxException { MessageMapper messageMapper = mapperFactory.getMessageMapper(session); return messageMapper.findFirstUnseenMessageUid(getMailboxEntity()); } - + + /** + * @see org.apache.james.mailbox.MessageManager#hasRight(org.apache.james.mailbox.MailboxACL.MailboxACLRight, + * org.apache.james.mailbox.MailboxSession) + */ public boolean hasRight(MailboxACLRight right, MailboxSession session) throws UnsupportedRightException { User user = session.getUser(); String userName = user != null ? user.getUserName() : null; - + return aclResolver.hasRight(userName, groupMembershipResolver, right, mailbox.getACL(), mailbox.getUser(), isGroupFolder(session)); } - + /** - * Returns true if the current mailbox does not reside neither in private nor other users' namespace. - * + * Returns the rights applicable to the user who has sent the current + * request. + * * @param session + * @return the rights applicable to the user who has sent the request, + * returns {@link SimpleMailboxACL#NO_RIGHTS} if + * {@code session.getUser()} is null. + * @throws UnsupportedRightException + */ + public MailboxACLRights myRights(MailboxSession session) throws MailboxException { + User user = session.getUser(); + if (user != null) { + return listRigths(user.getUserName(), session); + } else { + return SimpleMailboxACL.NO_RIGHTS; + } + } + + /** + * Returns the rights applicable to the given user. + * + * @param user + * @param session * @return + * @throws UnsupportedRightException */ + public MailboxACLRights listRigths(String user, MailboxSession session) throws UnsupportedRightException { + return aclResolver.listRights(user, groupMembershipResolver, mailbox.getACL(), mailbox.getUser(), isGroupFolder(session)); + } + + /** + * Applies the global ACL (if there are any) to the mailbox ACL. + * + * @param mailboxSession + * @return the ACL of the present mailbox merged with the global ACL (if + * there are any). + * @throws UnsupportedRightException + */ + protected MailboxACL getResolvedMailboxACL(MailboxSession mailboxSession) throws UnsupportedRightException { + return aclResolver.applyGlobalACL(mailbox.getACL(), isGroupFolder(mailboxSession)); + } + + /** + * Returns true if the current mailbox does not reside neither in private + * nor other users' namespace. + * + * @param session + * @return + */ protected boolean isGroupFolder(MailboxSession session) { final String ns = mailbox.getNamespace(); - return ns == null || ( - !ns.equals(session.getPersonalSpace()) - && !ns.equals(session.getOtherUsersSpace()) - ); + return ns == null || (!ns.equals(session.getPersonalSpace()) && !ns.equals(session.getOtherUsersSpace())); } - + }