KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > imapserver > FileMailbox


1 /***********************************************************************
2  * Copyright (c) 2000-2004 The Apache Software Foundation. *
3  * All rights reserved. *
4  * ------------------------------------------------------------------- *
5  * Licensed under the Apache License, Version 2.0 (the "License"); you *
6  * may not use this file except in compliance with the License. You *
7  * may obtain a copy of the License at: *
8  * *
9  * http://www.apache.org/licenses/LICENSE-2.0 *
10  * *
11  * Unless required by applicable law or agreed to in writing, software *
12  * distributed under the License is distributed on an "AS IS" BASIS, *
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14  * implied. See the License for the specific language governing *
15  * permissions and limitations under the License. *
16  ***********************************************************************/

17
18 package org.apache.james.imapserver;
19
20 import org.apache.avalon.cornerstone.services.store.ObjectRepository;
21 import org.apache.avalon.cornerstone.services.store.Store;
22 import org.apache.avalon.framework.component.ComponentManager;
23 import org.apache.avalon.framework.configuration.Configuration;
24 import org.apache.avalon.framework.configuration.ConfigurationException;
25 import org.apache.avalon.framework.context.Context;
26 import org.apache.avalon.framework.logger.AbstractLogEnabled;
27 import org.apache.avalon.phoenix.BlockContext;
28 import org.apache.james.imapserver.AccessControlException;
29 import org.apache.james.imapserver.AuthorizationException;
30 import org.apache.james.core.MimeMessageWrapper;
31 import org.apache.james.services.UsersRepository;
32 import org.apache.james.services.UsersStore;
33 import org.apache.james.util.Assert;
34
35 import javax.mail.internet.InternetHeaders JavaDoc;
36 import javax.mail.internet.MimeMessage JavaDoc;
37 import javax.mail.MessagingException JavaDoc;
38 import java.io.*;
39 import java.util.*;
40
41 /**
42  * Object representing an IMAP4rev1 mailbox (folder) on a local file system.
43  * The mailbox contains messages, message attributes and an access control
44  * list.
45  *
46  * <p> from Interface Mailbox
47  *
48  * <p>Mailbox Related Flags (rfc2060 name attributes)
49  * <br> \Noinferiors It is not possible for any child levels of hierarchy
50  * to exist under this name; no child levels exist now and none can be created
51  * in the future.
52  * <br> \Noselect It is not possible to use this name as a selectable
53  * mailbox.
54  * <br> \Marked The mailbox has been marked "interesting" by the
55  * server; the mailbox probably contains messages that have been added since
56  * the last time the mailbox was selected.
57  * <br> \Unmarked The mailbox does not contain any additional
58  * messages since the last time the mailbox was selected.
59  *
60  * <p>Message related flags.
61  * <br>The flags allowed per message are specific to each mailbox.
62  * <br>The minimum list (rfc2060 system flags) is:
63  * <br> \Seen Message has been read
64  * <br> \Answered Message has been answered
65  * <br> \Flagged Message is "flagged" for urgent/special attention
66  * <br> \Deleted Message is "deleted" for removal by later EXPUNGE
67  * <br> \Draft Message has not completed composition (marked as a draft).
68  * <br> \Recent Message is "recently" arrived in this mailbox. This
69  * session is the first session to have been notified about this message;
70  * subsequent sessions will not see \Recent set for this message. This flag
71  * can not be altered by the client.
72  * <br> If it is not possible to determine whether or not this
73  * session is the first session to be notified about a message, then that
74  * message SHOULD be considered recent.
75  *
76  * <p> From interface ACL </p>
77  *
78  * The standard rights in RFC2086 are:
79  * <br>l - lookup (mailbox is visible to LIST/LSUB commands)
80  * <br>r - read (SELECT the mailbox, perform CHECK, FETCH, PARTIAL, SEARCH,
81  * COPY from mailbox)
82  * <br>s - keep seen/unseen information across sessions (STORE SEEN flag)
83  * <br>w - write (STORE flags other than SEEN and DELETED)
84  * <br>i - insert (perform APPEND, COPY into mailbox)
85  * <br>p - post (send mail to submission address for mailbox, not enforced by
86  * IMAP4 itself)
87  * <br>c - create (CREATE new sub-mailboxes in any implementation-defined
88  * hierarchy)
89  * <br>d - delete (STORE DELETED flag, perform EXPUNGE)
90  * <br>a - administer (perform SETACL)
91  *
92  * <p> Serialization. Not all fields are serialized. Dispose() must be called to
93  * write serialized fiels to disc before finishing with this object.
94  *
95  * <p> Deserialization. On recover from disc, configure, compose,
96  * contextualize and reInit must be called before object is ready for use.
97  *
98  * Reference: RFC 2060
99  * @version 0.2 on 04 Aug 2002
100  */

101 public class FileMailbox
102     extends AbstractLogEnabled
103     implements ACLMailbox, Serializable {
104
105     public static final String JavaDoc MAILBOX_FILE_NAME = "mailbox.mbr";
106
107     private static final String JavaDoc MESSAGE_EXTENSION = ".msg";
108     private static final String JavaDoc ATTRIBUTES_EXTENSION = ".att";
109     private static final String JavaDoc FLAGS_EXTENSION = ".flags";
110
111     private static final int NUMBER_OF_RIGHTS = 9;
112     // Map string identities to boolean[NUMBER_OF_RIGHTS] arrays of rights
113
//The rights are, in order: l,r,s,w,i,p,c,d,a
114
private static final int LOOKUP = 0;
115     private static final int READ = 1;
116     private static final int KEEP_SEEN = 2;
117     private static final int WRITE = 3;
118     private static final int INSERT = 4;
119     private static final int POST = 5;
120     private static final int CREATE = 6;
121     private static final int DELETE = 7;
122     private static final int ADMIN = 8;
123     private static final boolean[] NO_RIGHTS
124         = {false, false, false, false, false, false, false, false, false};
125     private static final boolean[] ALL_RIGHTS
126         = {true, true, true, true, true, true, true, true, true};
127     private static final String JavaDoc DENY_ACCESS = "Access denied by ACL";
128     private static final String JavaDoc DENY_AUTH = "Action not authorized for: ";
129     private static final String JavaDoc OPTIONAL_RIGHTS = "l r s w i p c d a";
130     private static final char[] DELETE_MODS
131         = {'-', 'l', 'r', 's', 'w', 'i', 'p', 'c', 'd', 'a'};
132
133     /* Transient fields - reInit must be called on recover from disc. */
134     private transient BlockContext context;
135     private transient Configuration conf;
136     private transient ComponentManager compMgr;
137     private transient UsersRepository localUsers;
138     private transient HashSet listeners;
139
140     /* Serialized fileds */
141     private String JavaDoc path; // does not end with File.separator
142
private String JavaDoc rootPath; // does not end with File.separator
143
private File directory ;
144     private String JavaDoc owner;
145     private String JavaDoc absoluteName;
146     private Map acl;
147     private String JavaDoc name;
148     private int uidValidity;
149     private int mailboxSize; // octets
150
private boolean inferiorsAllowed;
151     private boolean marked;
152     private boolean notSelectableByAnyone;
153
154     // Sets the UID for all Mailboxes
155
private static HighestUID highestUID;
156
157     // The message sequence number of a msg is its index in List sequence + 1
158
private List sequence; //List of UIDs of messages in mailbox
159

160     // Set of UIDs of messages with recent flag set
161
private Set recentMessages;
162
163     // Set of UIDs of messages with delete flag set
164
private Set messagesForDeletion;
165
166     //map of user String to Integer uid, 0 for no unseen messages
167
private Map oldestUnseenMessage;
168
169     // Set of subscribed users.
170
private Set subscribedUsers = new HashSet(1000);
171
172     public void configure(Configuration conf) throws ConfigurationException {
173         this.conf = conf;
174     }
175
176     public void contextualize(Context context) {
177         this.context = (BlockContext)context;
178     }
179
180     public void compose(ComponentManager comp) {
181         compMgr = comp;
182     }
183
184     public void prepareMailbox(String JavaDoc user, String JavaDoc absName, String JavaDoc initialAdminUser, int uidValidity ) {
185         Assert.isTrue( Assert.ON && user != null && user.length() > 0 );
186         owner = user;
187
188         Assert.isTrue( Assert.ON && absName != null && (absName.length() > 0));
189         absoluteName = absName;
190
191         Assert.isTrue( Assert.ON && initialAdminUser != null && initialAdminUser.length() > 0 );
192         acl = new HashMap(7);
193         acl.put(initialAdminUser, ALL_RIGHTS);
194
195         Assert.isTrue( Assert.ON && uidValidity > 0 );
196         this.uidValidity = uidValidity;
197     }
198
199     public void initialize() throws Exception JavaDoc {
200         
201         mailboxSize = 0;
202         inferiorsAllowed = true;
203         marked = false;
204         notSelectableByAnyone = false;
205         oldestUnseenMessage = new HashMap();
206         listeners = new HashSet();
207         sequence = new ArrayList();
208         recentMessages = new HashSet();
209         messagesForDeletion = new HashSet();
210         getLogger().info("FileMailbox init for " + absoluteName);
211         UsersStore usersStore = (UsersStore)compMgr.lookup( "org.apache.james.services.UsersStore" );
212         localUsers = usersStore.getRepository("LocalUsers");
213         rootPath = conf.getChild( "mailboxRepository" ).getValue();
214         if (!rootPath.endsWith(File.separator)) {
215             rootPath = rootPath + File.separator;
216         }
217         
218         path = getPath( absoluteName, owner, rootPath );
219         name = absoluteName.substring(absoluteName.lastIndexOf( JamesHost.HIERARCHY_SEPARATOR ) + 1);
220         if (name.equals(owner)) {
221             name = "";
222         }
223         //Check for writable directory
224
getLogger().info("MailboxDir " + path);
225         System.out.println("MailboxDir TO WRITE TO " + path);
226         File mailboxDir = new File(path);
227         if (mailboxDir.exists()) {
228             throw new RuntimeException JavaDoc("Error: Attempt to overwrite mailbox directory at " + path);
229         } else if (! mailboxDir.mkdir()){
230             throw new RuntimeException JavaDoc("Error: Cannot create mailbox directory at " + path);
231         } else if (!mailboxDir.canWrite()) {
232             throw new RuntimeException JavaDoc("Error: Cannot write to directory at " + path);
233         }
234         writeMailbox();
235         getLogger().info("FileMailbox init complete: " + absoluteName);
236     }
237
238
239
240     /**
241      * Return the file-system path to a given absoluteName mailbox.
242      *
243      * @param absoluteName the user-independent name of the mailbox
244      * @param owner string name of owner of mailbox
245      */

246     private String JavaDoc getPath( String JavaDoc absoluteName, String JavaDoc owner, String JavaDoc rootPath )
247     {
248         Assert.isTrue( Assert.ON &&
249                        absoluteName.startsWith( JamesHost.NAMESPACE_TOKEN ) );
250
251         // Remove the leading '#' and replace Hierarchy separators with file separators.
252
String JavaDoc filePath = absoluteName.substring( JamesHost.NAMESPACE_TOKEN.length() );
253         filePath = filePath.replace( JamesHost.HIERARCHY_SEPARATOR_CHAR, File.separatorChar );
254         return rootPath + filePath;
255     }
256     
257     /**
258      * Re-initialises mailbox after reading from file-system. Cannot assume that this is the same instance of James that wrote it.
259      *
260      * <p> Contract is that re-init must be called after configure, contextualize, compose.
261      */

262     public void reinitialize() throws Exception JavaDoc {
263         listeners = new HashSet();
264         getLogger().info("FileMailbox reInit for " + absoluteName);
265         UsersStore usersStore = (UsersStore)compMgr.lookup( "org.apache.james.services.UsersStore" );
266         localUsers = usersStore.getRepository("LocalUsers");
267         rootPath
268             = conf.getChild("mailboxRepository").getValue();
269         if (!rootPath.endsWith(File.separator)) {
270             rootPath = rootPath + File.separator;
271         }
272         path = getPath( absoluteName, owner, rootPath );
273     }
274
275     /**
276      * Call when host has finished with a mailbox.
277      * This is really a stop rather than destroy.
278      * Writes current mailbox object to disc.
279      */

280     public void dispose() {
281         writeMailbox();
282         getLogger().info("FileMailbox object destroyed: " + absoluteName);
283     }
284
285     /**
286      * Permanently deletes the mailbox.
287      */

288     public void removeMailbox()
289     {
290         // First delete the mailbox file
291
path = getPath( absoluteName, owner, rootPath );
292         String JavaDoc mailboxRecordFile = path + File.separator + MAILBOX_FILE_NAME;
293         File mailboxRecord = new File( mailboxRecordFile );
294         Assert.isTrue( Assert.ON &&
295                        mailboxRecord.exists() &&
296                        mailboxRecord.isFile() );
297         mailboxRecord.delete();
298
299         // Check for empty directory before deleting.
300
File mailboxDir = new File(path);
301         Assert.isTrue( Assert.ON &&
302                        mailboxDir.exists() );
303         Assert.isTrue( Assert.ON &&
304                        mailboxDir.isDirectory() );
305         Assert.isTrue( Assert.ON &&
306                        mailboxDir.list().length == 0 );
307         mailboxDir.delete();
308     }
309     
310     /**
311      * Renames this Mailbox.
312      * @param usernmae The Username who calles this Command.
313      * @param newmailboxname The new name for this Mailbox.
314      * @return true if everythink was sucessfully, else false.
315      * @throws MailboxException if mailbox does not exist locally.
316      * @throws AuthorizationException if the user has no rights for changing the name of the Mailbox.
317      */

318     public boolean renameMailbox(String JavaDoc username, String JavaDoc newmailboxname) throws MailboxException, AuthorizationException {
319         try {
320             path = getPath( absoluteName, owner, rootPath );
321             
322             StringTokenizer strt = new StringTokenizer(newmailboxname,".");
323             String JavaDoc lastnameofmailbox="";
324             while(strt.hasMoreTokens())
325                 lastnameofmailbox=strt.nextToken();
326             
327             File fle = new File(this.path);
328             fle.renameTo(new File(fle.getParent(), lastnameofmailbox));
329             
330             this.path=fle.getParent()+File.separator+lastnameofmailbox;
331             this.absoluteName = this.absoluteName.substring(0,this.absoluteName.length()-this.name.length())+lastnameofmailbox;
332             this.name=lastnameofmailbox;
333             
334             
335             this.writeMailbox();
336             return true;
337         }catch(Exception JavaDoc e) {
338             e.printStackTrace();
339             return false;
340         }
341     }
342     
343     /**
344      * THIS IS AN INTERIM SOLUTION !
345      *
346      * @param usernmae The Username who calles this Command.
347      * @param oldabsolutename The old name of the parent.
348      * @param newabsolutename The new name for the parent.
349      * @return true if everythink was sucessfully, else false.
350      * @throws MailboxException if mailbox does not exist locally.
351      * @throws AuthorizationException if the user has no rights for changing the name of the Mailbox.
352      */

353     public boolean renameSubMailbox(String JavaDoc username, String JavaDoc oldabsolutename, String JavaDoc newname) {
354         try {
355             System.out.println("renameSubMailbox ABSOLUTE NAME "+this.absoluteName);
356             StringTokenizer strt = new StringTokenizer(oldabsolutename,".");
357             StringBuffer JavaDoc strbuff = new StringBuffer JavaDoc();
358             
359             for(int i=0;i<strt.countTokens();i++){
360                 String JavaDoc token = strt.nextToken();
361                 if(strbuff.length()>0) strbuff.append(".");
362                 strbuff.append(token);
363             }
364             strbuff.append(".");
365             strbuff.append(newname);
366
367             this.absoluteName = strbuff.toString()+this.absoluteName.substring(oldabsolutename.length());
368             System.out.println("renameSubMailbox TOKEN CONVERTED: "+this.absoluteName);
369             this.writeMailbox();
370             return true;
371         }catch(Exception JavaDoc e) {
372             e.printStackTrace();
373             return false;
374         }
375     }
376     
377     /**
378      * Returns true once this Mailbox has been checkpointed.
379      * This implementation just writes its mailbox record to disc. Unless something is
380      * broken all messages added, amended or removed from this mailbox will have been
381      * handled by this object.
382      * <br> This implementation always returns true.
383      *
384      * @return true
385      */

386     public synchronized boolean checkpoint() {
387         writeMailbox();
388         getLogger().info("FileMailbox: " + absoluteName + " checkpointed.");
389         return true;
390     }
391
392     /**
393      * Remove \Recent flag from all messages in mailbox. Should be called
394      * whenever a user session finishes.
395      */

396     public synchronized void unsetRecent() {
397         Iterator it = recentMessages.iterator();
398         while(it.hasNext()) {
399             Integer JavaDoc uidObj =(Integer JavaDoc)it.next();
400             int uid = uidObj.intValue();
401             Flags flags = readFlags(uid);
402             if (flags != null) {
403                 flags.setRecent(false);
404                 writeFlags(uid, flags);
405             }
406         }
407         recentMessages.clear();
408     }
409
410
411     // Methods that don't involve the ACL ------------------
412

413     /**
414      * Returns name of this mailbox relative to its parent in the mailbox
415      * hierarchy.
416      * Example: 'NewIdeas'
417      *
418      * @return String name of mailbox relative to its immeadiate parent in
419      * the mailbox hierarchy.
420      */

421     public String JavaDoc getName() {
422         return name;
423     }
424
425     /**
426      * Returns absolute, that is user-independent, hierarchical name of
427      * mailbox (including namespace)
428      * Example: '#mail.fred.flintstone.apache.James.NewIdeas'
429      *
430      * @return String name of mailbox in absolute form
431      */

432     public String JavaDoc getAbsoluteName() {
433         return absoluteName;
434     }
435
436     /** Returns namespace starting with namespace token.
437      * Example: '#mail'
438      *
439      * @return String containing user-independent namespace of this mailbox.
440      */

441     // public String getNamespace();
442

443     /** Returns true if the argument is the relative or absolute name of
444      * this mailbox
445      *
446      * @param name possible name for this Mailbox
447      * @return true if name matches either getName() or getAbsoluteName()
448      */

449     public boolean matchesName(String JavaDoc testName) {
450         return (name.equals(testName) || name.equals(absoluteName));
451     }
452
453     /**
454      * Returns the current unique id validity value of this mailbox.
455      *
456      * @return int current 32 bit unique id validity value of this mailbox
457      */

458     public int getUIDValidity() {
459         return uidValidity;
460     }
461
462     /**
463      * Returns the 32 bit uid available for the next message.
464      *
465      * @return int the next UID that would be used.
466      */

467     public int getNextUID() {
468         return highestUID.get() + 1;
469     }
470
471     /**
472      * Returns mailbox size in octets. Should only include actual messages
473      * and not any implementation-specific data, such as message attributes.
474      *
475      * @return int mailbox size in octets
476      */

477     public synchronized int getMailboxSize() {
478         return mailboxSize;
479     }
480
481     /**
482      * Indicates if child folders may be created. It does not indicate which
483      * users can create child folders.
484      *
485      * @return boolean TRUE if inferiors aree allowed
486      */

487     public boolean getInferiorsAllowed() {
488         return inferiorsAllowed;
489     }
490
491     /**
492      * Indicates that messages have been added since this mailbox was last
493      * selected by any user.
494      *
495      * @return boolean TRUE if new messages since any user last selected
496      * mailbox
497      */

498     public synchronized boolean isMarked() {
499         return marked;
500     }
501
502     /**
503      * Returns all flags supported by this mailbox.
504      * e.g. \Answered \Deleted
505      *
506      * @return String a space seperated list of message flags which are
507      * supported by this mailbox.
508      */

509     public String JavaDoc getSupportedFlags() {
510         return SYSTEM_FLAGS;
511     }
512     /**
513      * Indicates no of messages with \Recent flag set
514      *
515      * @return int no of messages with \Recent flag set
516      */

517     public synchronized int getRecent() {
518         return recentMessages.size();
519     }
520
521     /**
522      * Indicates the oldest unseen message for the specified user.
523      *
524      * @return int Message Sequence Number of first message without \Seen
525      * flag set for this User. 0 means no unseen messages in this mailbox.
526      */

527     public synchronized int getOldestUnseen(String JavaDoc user) {
528         int response = 0;
529         if (oldestUnseenMessage.containsKey(user)) {
530             Integer JavaDoc uidObj = ((Integer JavaDoc)oldestUnseenMessage.get(user));
531             if (! (uidObj.intValue() == 0)) {
532                 response = sequence.indexOf(uidObj) + 1;
533             }
534         } else {
535             if (sequence.size() > 0) {
536                 response = 1;
537                 oldestUnseenMessage.put(user, (Integer JavaDoc)sequence.get(0));
538             } else {
539                 oldestUnseenMessage.put(user, (new Integer JavaDoc(0)));
540             }
541         }
542         return response;
543     }
544
545     /**
546      * Indicates number of messages in folder
547      *
548      * @return int number of messages
549      */

550     public synchronized int getExists() {
551         return sequence.size();
552     }
553
554     /**
555      * Indicates the number of unseen messages for the specified user.
556      *
557      * @return int number of messages without \Seen flag set for this User.
558      */

559     public int getUnseen(String JavaDoc user) {
560         if (oldestUnseenMessage.containsKey(user)) {
561             int response = 0; //indicates no unseen messages
562
Integer JavaDoc uidObj = ((Integer JavaDoc)oldestUnseenMessage.get(user));
563             int oldUID = uidObj.intValue();
564             if (oldUID != 0) {
565                 ListIterator lit
566                     = sequence.listIterator(sequence.indexOf(uidObj));
567                 while (lit.hasNext() ) {
568                     int uid = ((Integer JavaDoc)lit.next()).intValue();
569                     Flags flags = readFlags(uid);
570                     if (!flags.isSeen(user)) {
571                         response ++;
572                     }
573                 }
574             }
575             return response;
576         } else { // user has never selected mailbox
577
return sequence.size();
578         }
579     }
580
581     /** Mailbox Events are used to inform registered listeners of events in the Mailbox.
582      * E.g. if mail is delivered to an Inbox or if another user appends/ deletes a message.
583      */

584     public synchronized void addMailboxEventListener(MailboxEventListener mel) {
585         listeners.add(mel);
586     }
587
588
589     public synchronized void removeMailboxEventListener(MailboxEventListener mel) {
590         listeners.remove(mel);
591     }
592
593     /**
594      * Mark this mailbox as not selectable by anyone.
595      * Example folders at the roots of hierarchies, e. #mail for each user.
596      *
597      * @param state true if folder is not selectable by anyone
598      */

599     public void setNotSelectableByAnyone(boolean state) {
600         notSelectableByAnyone = state;
601     }
602
603     public boolean isNotSelectableByAnyone() {
604         return notSelectableByAnyone;
605     }
606
607     // Methods for the embedded ACL ------------------------
608

609     /**
610      * Store access rights for a given identity.
611      * The setter is the user setting the rights, the identifier is the user
612      * whose rights are affected.
613      * The setter and identifier arguments must be non-null and non-empty.
614      * The modification argument must be non-null and follow the syntax of the
615      * third argument to a SETACL command.
616      * If the modification argument is an empty string, that identifier is
617      * removed from the ACL, if currently present.
618      *
619      * @param setter String representing user attempting to set rights, must
620      * be non-null and non-empty
621      * @param identity String representing user whose rights are being set,
622      * must be non-null and non-empty
623      * @param modification String representing the change in rights, following
624      * the syntax specified in rfc 2086. Note a blank string means delete all
625      * rights for given identity.
626      * @return true if requested modification succeeded. A return value of
627      * false means an error other than an AccessControlException or
628      * AuthorizationException.
629      * @throws AccessControlException if setter does not have lookup rights for
630      * this mailbox (ie they should not know this mailbox exists).
631      * @throws AuthorizationException if specified setter does not have the
632      * administer right (ie the right to write ACL rights), or if the result
633      * of this method would leave no identities with admin rights.
634      */

635     public boolean setRights(String JavaDoc setter, String JavaDoc identifier,
636                              String JavaDoc modification)
637         throws AccessControlException, AuthorizationException {
638
639         boolean[] settersRights = (boolean[]) acl.get(setter);
640         if (settersRights == null
641             || (settersRights[LOOKUP] == false)) {
642             throw new AccessControlException(DENY_ACCESS);
643         } else if (settersRights[ADMIN] == false) {
644             throw new AuthorizationException(DENY_AUTH + setter);
645         }
646         boolean[] existingRights = (boolean[]) acl.get(identifier);
647         char[] mods = modification.toCharArray();
648         if (mods.length == 0) { // means delete all
649
mods = DELETE_MODS;
650         }
651         if(existingRights == null) {
652             if ( mods[0] == REMOVE_RIGHTS ) {
653                 return false;
654             } else {
655                 existingRights = new boolean[NUMBER_OF_RIGHTS];
656                 System.arraycopy(NO_RIGHTS, 0, existingRights, 0,
657                                  NUMBER_OF_RIGHTS);
658             }
659         }
660
661         boolean change;
662         boolean[] rights = new boolean[NUMBER_OF_RIGHTS];
663
664         if (mods[0] == ADD_RIGHTS) {
665             change = true;
666             System.arraycopy(existingRights, 0, rights, 0,
667                              NUMBER_OF_RIGHTS);
668         } else if (mods[0] == REMOVE_RIGHTS) {
669             change = false;
670             System.arraycopy(existingRights, 0, rights, 0,
671                              NUMBER_OF_RIGHTS);
672         } else { // means replace
673
System.arraycopy(NO_RIGHTS, 0, rights, 0,
674                              NUMBER_OF_RIGHTS);
675             char[] new_mods = new char[mods.length + 1];
676             System.arraycopy(mods, 0, new_mods, 1, mods.length);
677             mods = new_mods;
678             change = true;
679         }
680
681         for (int i=1; i <mods.length; i++) {
682             switch(mods[i]) {
683             case LOOKUP_RIGHTS: rights[LOOKUP] = change;
684                 break;
685             case READ_RIGHTS: rights[READ] = change;
686                 break;
687             case KEEP_SEEN_RIGHTS: rights[KEEP_SEEN] = change;
688                 break;
689             case WRITE_RIGHTS: rights[WRITE] = change;
690                 break;
691             case INSERT_RIGHTS: rights[INSERT] = change;
692                 break;
693             case POST_RIGHTS: rights[POST] = change;
694                 break;
695             case CREATE_RIGHTS: rights[CREATE] = change;
696                 break;
697             case DELETE_RIGHTS: rights[DELETE] = change;
698                 break;
699             case ADMIN_RIGHTS: rights[ADMIN] = change;
700                 break;
701             default: return false;
702             }
703         }
704
705         // All rights above lookup require lookup
706
if(rights[LOOKUP] == false && !Arrays.equals(rights, NO_RIGHTS)) {
707             return false;
708         }
709         // Each right requires all the rights before it.
710
int count = 0;
711         for (int i=1; i< NUMBER_OF_RIGHTS; i++) {
712             if(rights[i-1] ^ rights[i]) {
713                 count++;
714             }
715         }
716         switch (count) {
717         case 0: // now Admin or deleted
718
if (rights[ADMIN]) {
719                 acl.put(identifier, rights);
720                 break;
721             } else {
722                 if (otherAdmin(identifier)) {
723                     acl.remove(identifier);
724                     break;
725                 } else {
726                     return false;
727                 }
728             }
729         case 2: // not allowed
730
return false;
731         case 1: // not Admin, check there remains an Admin
732
// Iterator namesIt = acl.keySet().iterator();
733
//boolean otherAdmin = false;
734
//while(namesIt.hasNext() && !otherAdmin) {
735
//String name = (String)namesIt.next();
736
//if (name != identifier) {
737
// boolean[] otherRights = (boolean[]) acl.get(name);
738
// otherAdmin = otherRights[ADMIN];
739
//}
740
//}
741
if (otherAdmin(identifier)) {
742                 acl.put(identifier, rights);
743                 break;
744             } else {
745                 return false;
746             }
747         default: // not allowed
748
return false;
749         }
750         writeMailbox();
751         return true;
752     }
753
754     /**
755      * Check there is a person other than identifier who has Admin rights.
756      */

757     private boolean otherAdmin(String JavaDoc identifier) {
758         Iterator namesIt = acl.keySet().iterator();
759         boolean result = false;
760         while(namesIt.hasNext() && !result) {
761             String JavaDoc name = (String JavaDoc)namesIt.next();
762             if (!name.equals(identifier)) {
763                 boolean[] otherRights = (boolean[]) acl.get(name);
764                 result = otherRights[ADMIN];
765             }
766         }
767         return result;
768     }
769
770     /**
771      * Retrieve access rights for a specific identity.
772      *
773      * @param getter String representing user attempting to get the rights,
774      * must be non-null and non-empty
775      * @param identity String representing user whose rights are being got,
776      * must be non-null and non-empty
777      * @return String of rights usingrfc2086 syntax, empty if identity has no
778      * rights in this mailbox.
779      * @throws AccessControlException if getter does not have lookup rights for
780      * this mailbox (ie they should not know this mailbox exists).
781      * @throws AuthorizationException if implementation does not wish to expose
782      * ACL for this identity to this getter.
783      */

784     public String JavaDoc getRights(String JavaDoc getter, String JavaDoc identity)
785         throws AccessControlException, AuthorizationException {
786         boolean[] gettersRights = (boolean[]) acl.get(getter);
787         if (gettersRights == null
788             || (gettersRights[LOOKUP] == false)) {
789             throw new AccessControlException(DENY_ACCESS);
790         } else if (!getter.equals(identity) && gettersRights[ADMIN] == false) {
791             throw new AuthorizationException(DENY_AUTH + getter);
792         }
793         boolean[] rights = (boolean[]) acl.get(identity);
794         if (rights == null) {
795             return null;
796         } else {
797             StringBuffer JavaDoc buf = new StringBuffer JavaDoc(NUMBER_OF_RIGHTS);
798             for (int i = 0; i<NUMBER_OF_RIGHTS; i++) {
799                 if (rights[i]) {
800                     buf.append(RIGHTS[i]);
801                 }
802             }
803             return buf.toString();
804         }
805     }
806
807     /**
808      * Retrieves a String of one or more <identity space rights> who have
809      * rights in this ACL
810      *
811      * @param getter String representing user attempting to get the rights,
812      * must be non-null and non-empty
813      * @return String of rights sets usingrfc2086 syntax
814      * @throws AccessControlException if getter does not have lookup rights for
815      * this mailbox (ie they should not know this mailbox exists).
816      * @throws AuthorizationException if implementation does not wish to expose
817      * ACL to this getter.
818      */

819     public String JavaDoc getAllRights(String JavaDoc getter)
820         throws AccessControlException, AuthorizationException {
821         boolean[] gettersRights = (boolean[]) acl.get(getter);
822         if (gettersRights == null
823             || (gettersRights[LOOKUP] == false)) {
824             throw new AccessControlException(DENY_ACCESS);
825         } else if ( gettersRights[ADMIN] == false) {
826             throw new AuthorizationException(DENY_AUTH + getter);
827         }
828         Iterator namesIt = acl.keySet().iterator();
829         StringBuffer JavaDoc response = new StringBuffer JavaDoc(20*acl.size());
830         while(namesIt.hasNext()) {
831             String JavaDoc name = (String JavaDoc)namesIt.next();
832             response.append("<" + name + " ");
833             boolean[] rights = (boolean[]) acl.get(name);
834             for (int i = 0; i<NUMBER_OF_RIGHTS; i++) {
835                 if (rights[i]) {
836                     response.append(RIGHTS[i]);
837                 }
838             }
839             response.append("> ");
840         }
841
842         return response.toString();
843     }
844
845     /**
846      * Retrieve rights which will always be granted to the specified identity.
847      *
848      * @param getter String representing user attempting to get the rights,
849      * must be non-null and non-empty
850      * @param identity String representing user whose rights are being got,
851      * must be non-null and non-empty
852      * @return String of rights usingrfc2086 syntax, empty if identity has no
853      * guaranteed rights in this mailbox.
854      * @throws AccessControlException if getter does not have lookup rights for
855      * this mailbox (ie they should not know this mailbox exists).
856      * @throws AuthorizationException if implementation does not wish to expose
857      * ACL for this identity to this getter.
858      */

859     public String JavaDoc getRequiredRights(String JavaDoc getter, String JavaDoc identity)
860         throws AccessControlException, AuthorizationException {
861         boolean[] gettersRights = (boolean[]) acl.get(getter);
862         if (gettersRights == null
863             || (gettersRights[LOOKUP] == false)) {
864             throw new AccessControlException(DENY_ACCESS);
865         } else if (!getter.equals(identity) && gettersRights[ADMIN] == false) {
866             throw new AuthorizationException(DENY_AUTH + getter);
867         }
868
869         return "\"\"";
870     }
871
872     /**
873      * Retrieve rights which may be granted to the specified identity.
874      * @param getter String representing user attempting to get the rights,
875      * must be non-null and non-empty
876      * @param identity String representing user whose rights are being got,
877      * must be non-null and non-empty
878      * @return String of rights usingrfc2086 syntax, empty if identity has no
879      * guaranteed rights in this mailbox.
880      * @throws AccessControlException if getter does not have lookup rights for
881      * this mailbox (ie they should not know this mailbox exists).
882      * @throws AuthorizationException if implementation does not wish to expose
883      * ACL for this identity to this getter.
884      */

885     public String JavaDoc getOptionalRights(String JavaDoc getter, String JavaDoc identity)
886         throws AccessControlException, AuthorizationException {
887         boolean[] gettersRights = (boolean[]) acl.get(getter);
888         if (gettersRights == null
889             || (gettersRights[LOOKUP] == false)) {
890             throw new AccessControlException(DENY_ACCESS);
891         } else if (!getter.equals(identity) && gettersRights[ADMIN] == false) {
892             throw new AuthorizationException(DENY_AUTH + getter);
893         }
894
895         return OPTIONAL_RIGHTS;
896     }
897
898     /**
899      * Helper boolean methods.
900      * Provided for cases where you need to check the ACL before selecting the
901      * mailbox.
902      *
903      * @param username String representing user
904      * @return true if user has the requested right.
905      * &throws AccessControlException if username does not have lookup rights.
906      * (Except for hasLookupRights which just returns false.
907      */

908     public boolean hasLookupRights(String JavaDoc username) {
909         boolean[] usersRights = (boolean[]) acl.get(username);
910         return (( usersRights == null || (usersRights[LOOKUP] == false))
911                 ? false : true);
912     }
913
914     public boolean hasReadRights(String JavaDoc username)
915         throws AccessControlException {
916         boolean[] usersRights = (boolean[]) acl.get(username);
917         if (usersRights == null || (usersRights[LOOKUP] == false)) {
918             throw new AccessControlException(DENY_ACCESS);
919         }
920         return usersRights[READ];
921     }
922
923     public boolean hasKeepSeenRights(String JavaDoc username)
924         throws AccessControlException {
925         boolean[] usersRights = (boolean[]) acl.get(username);
926         if (usersRights == null || (usersRights[LOOKUP] == false)) {
927             throw new AccessControlException(DENY_ACCESS);
928         }
929         return usersRights[KEEP_SEEN];
930     }
931
932     public boolean hasWriteRights(String JavaDoc username)
933         throws AccessControlException {
934         boolean[] usersRights = (boolean[]) acl.get(username);
935         if (usersRights == null || (usersRights[LOOKUP] == false)) {
936             throw new AccessControlException(DENY_ACCESS);
937         }
938         return usersRights[WRITE];
939     }
940
941     public boolean hasInsertRights(String JavaDoc username)
942         throws AccessControlException {
943         boolean[] usersRights = (boolean[]) acl.get(username);
944         if (usersRights == null || (usersRights[LOOKUP] == false)) {
945             throw new AccessControlException(DENY_ACCESS);
946         }
947         return usersRights[INSERT];
948     }
949
950     public boolean hasCreateRights(String JavaDoc username)
951         throws AccessControlException {
952         boolean[] usersRights = (boolean[]) acl.get(username);
953         if (usersRights == null || (usersRights[LOOKUP] == false)) {
954             throw new AccessControlException(DENY_ACCESS);
955         }
956         return usersRights[CREATE];
957     }
958
959     public boolean hasDeleteRights(String JavaDoc username)
960         throws AccessControlException {
961         boolean[] usersRights = (boolean[]) acl.get(username);
962         if (usersRights == null || (usersRights[LOOKUP] == false)) {
963             throw new AccessControlException(DENY_ACCESS);
964         }
965         return usersRights[DELETE];
966     }
967
968     public boolean hasAdminRights(String JavaDoc username)
969         throws AccessControlException {
970         boolean[] usersRights = (boolean[]) acl.get(username);
971         if (usersRights == null || (usersRights[LOOKUP] == false)) {
972             throw new AccessControlException(DENY_ACCESS);
973         }
974         return usersRights[ADMIN];
975     }
976
977     // Mailbox methods using the ACL ---------------------------
978

979     /**
980      * Indicates if this folder may be selected by the specified user. Requires
981      * user to have at least read rights. It does not indicate whether user
982      * can write to mailbox
983      *
984      * @param username String represnting user
985      * @return boolean TRUE if specified user can Select mailbox.
986      * @throws AccessControlException if username does not have lookup rights
987      */

988     public synchronized boolean isSelectable(String JavaDoc username)
989         throws AccessControlException {
990         return ( ! notSelectableByAnyone && hasReadRights(username) );
991     }
992
993     /**
994      * Indicates if specified user can change any flag on a permanent basis,
995      * except for \Recent which can never be changed by a user.
996      *
997      * @param username String represnting user
998      * @return true if specified user can change all flags permanently.
999      */

1000    public synchronized boolean allFlags(String JavaDoc username)
1001        throws AccessControlException {
1002        // relies on implementation that each right implies those
1003
// before it in list: l,r,s,w,i,p,c,d,a
1004
return hasDeleteRights(username);
1005    }
1006
1007    /**
1008     * Indicates which flags this user can change permanently. If allFlags()
1009     * returns true for this user, then this method must have the same return
1010     * value as getSupportedFlags.
1011     *
1012     * @param username String represnting user
1013     * @return String a space seperated list of message flags which this user
1014     * can set permanently
1015     */

1016    public synchronized String JavaDoc getPermanentFlags(String JavaDoc username)
1017        throws AccessControlException {
1018        if (hasDeleteRights(username)) {
1019            return SYSTEM_FLAGS;
1020        } else if (hasWriteRights(username)) {
1021            return "\\Seen \\Answered \\Flagged \\Draft";
1022        } else if (hasKeepSeenRights(username)) {
1023            return "\\Seen";
1024        } else {
1025            return "";
1026        }
1027    }
1028
1029    /**
1030     * Provides a reference to the access control list for this mailbox.
1031     *
1032     * @return the AccessControlList for this Mailbox
1033     */

1034    // public ACL getACL();
1035

1036    /**
1037     * Indicates state in which the mailbox will be opened by specified user.
1038     * A return value of true indicates Read Only, false indicates Read-Write
1039     * and an AccessControlException is thrown if user does not have read
1040     * rights.
1041     * <p>Implementations decide if Read Only means only lookup and read
1042     * rights (lr) or lookup, read and keep seen rights (lrs). This may even
1043     * vary between mailboxes.
1044     *
1045     * @param username String represnting user
1046     * @return true if specified user can only open the mailbox Read-Only.
1047     * @throws AccessControlException if the user can not open this mailbox
1048     * at least Read-Only.
1049     */

1050    public synchronized boolean isReadOnly(String JavaDoc username)
1051        throws AccessControlException {
1052        return (! hasWriteRights(username));
1053    }
1054
1055    // Message handling methods ---------------------------
1056

1057    /**
1058     * Stores a message in this mailbox. User must have insert rights.
1059     *
1060     * @param message the MimeMessage to be stored
1061     * @param username String represnting user
1062     * @return boolean true if successful
1063     * @throws AccessControlException if username does not have lookup rights
1064     * for this mailbox.
1065     * @throws AuthorizationException if username has lookup rights but does
1066     * not have insert rights.
1067     */

1068    public synchronized boolean store(MimeMessage JavaDoc message, String JavaDoc username)
1069        throws AccessControlException, AuthorizationException,
1070               IllegalArgumentException JavaDoc {
1071
1072        if (message == null || username == null) {
1073            getLogger().error("Null argument received in store.");
1074            throw new IllegalArgumentException JavaDoc("Null argument received in store.");
1075        }
1076        if (!hasInsertRights(username)) { //throws AccessControlException
1077
throw new AuthorizationException("Not authorized to insert.");
1078        }
1079
1080        SimpleMessageAttributes attrs = new SimpleMessageAttributes();
1081        try {
1082            setupLogger(attrs);
1083            attrs.setAttributesFor(message);
1084        } catch (javax.mail.MessagingException JavaDoc me) {
1085            throw new RuntimeException JavaDoc("Exception creating SimpleMessageAttributes: " + me);
1086        }
1087        Flags flags = new Flags();
1088        flags.initialize();
1089        return store(message, username, attrs, flags);
1090    }
1091
1092    /**
1093     * Stores a message in this mailbox, using passed MessageAttributes and
1094     * Flags. User must have insert rights.
1095     * <br>Current implementation requires MessageAttributs to be of
1096     * class SimpleMessageAttributes
1097     *
1098     * @param mail the message to be stored
1099     * @param username String represnting user
1100     * @param msgAttrs non-null MessageAttributes for use with this Message
1101     * @return boolean true if successful
1102     * @throws AccessControlException if username does not have lookup
1103     * rights for this mailbox.
1104     * @throws AuthorizationException if username has lookup rights but does
1105     * not have insert rights.
1106     */

1107    public boolean store(MimeMessage JavaDoc message, String JavaDoc username,
1108                         MessageAttributes msgAttrs, Flags flags)
1109        throws AccessControlException, AuthorizationException,
1110               IllegalArgumentException JavaDoc {
1111
1112        if (msgAttrs == null || message == null || username == null) {
1113            getLogger().error("Null argument received in store.");
1114            throw new IllegalArgumentException JavaDoc("Null argument received in store.");
1115        }
1116        if (! (msgAttrs instanceof SimpleMessageAttributes)) {
1117            getLogger().error("Wrong class for Attributes");
1118            throw new IllegalArgumentException JavaDoc("Wrong class for Attributes");
1119        }
1120        SimpleMessageAttributes attrs = (SimpleMessageAttributes)msgAttrs;
1121
1122        highestUID.increase();
1123        int newUID = highestUID.get();
1124        attrs.setUID(newUID);
1125        sequence.add(new Integer JavaDoc(newUID));
1126        attrs.setMessageSequenceNumber(sequence.size());
1127
1128        BufferedOutputStream outMsg = null;
1129        ObjectOutputStream outAttrs = null;
1130
1131        try {
1132            // SK:UPDATE
1133
path = getPath( absoluteName, owner, rootPath );
1134            outMsg = new BufferedOutputStream( new FileOutputStream(path + File.separator + newUID + MESSAGE_EXTENSION));
1135            message.writeTo(outMsg);
1136            outMsg.close();
1137            outAttrs = new ObjectOutputStream( new FileOutputStream(path + File.separator + newUID + ATTRIBUTES_EXTENSION));
1138            outAttrs.writeObject(attrs);
1139            outAttrs.close();
1140        } catch(Exception JavaDoc e) {
1141            getLogger().error("Error writing message to disc: " + e);
1142            e.printStackTrace();
1143            throw new
1144                RuntimeException JavaDoc("Exception caught while storing Mail: "
1145                                 + e);
1146        } finally {
1147            try {
1148                outMsg.close();
1149                outAttrs.close();
1150            } catch (IOException ie) {
1151                getLogger().error("Error closing streams: " + ie);
1152            }
1153        }
1154        marked = true;
1155        if (flags.isRecent()) {
1156            recentMessages.add(new Integer JavaDoc(newUID));
1157        }
1158        if (flags.isDeleted()) {
1159            messagesForDeletion.add(new Integer JavaDoc(newUID));
1160        }
1161        //if (!flags.isSeen(username)) {
1162
//If a user had no unseen messages, they do, now.
1163
Iterator it = oldestUnseenMessage.keySet().iterator();
1164        while (it.hasNext()) {
1165            String JavaDoc user = (String JavaDoc)it.next();
1166            if ( ((Integer JavaDoc)oldestUnseenMessage.get(user)).intValue() == -1) {
1167                oldestUnseenMessage.put(user, new Integer JavaDoc(newUID));
1168            }
1169        }
1170        //}
1171
writeFlags(newUID, flags);
1172        getLogger().info("Mail " + newUID + " written in " + absoluteName);
1173
1174        return true;
1175    }
1176
1177    /**
1178     * Retrieves a message given a message sequence number.
1179     *
1180     * @param msn the message sequence number
1181     * @param username String represnting user
1182     * @return an MimeMessageWrapper object containing the message, null if no message with
1183     * the given msn.
1184     * @throws AccessControlException if user does not have read rights for
1185     * this mailbox.
1186     * @throws AuthorizationException if user has lookup rights but does not
1187     * have read rights.
1188     */

1189    public synchronized MimeMessageWrapper retrieve(int msn, String JavaDoc user)
1190        throws AccessControlException, AuthorizationException {
1191        if (!hasReadRights(user)) { //throws AccessControlException
1192
throw new AuthorizationException("Not authorized to read.");
1193        }
1194
1195        if (msn > sequence.size()) {
1196            return null;
1197        } else {
1198            int uid = ((Integer JavaDoc)sequence.get(msn - 1)).intValue();
1199            return retrieveUID(uid, user);
1200        }
1201    }
1202
1203
1204    /**
1205     * Retrieves a message given a unique identifier.
1206     *
1207     * @param uid the unique identifier of a message
1208     * @param username String represnting user
1209     * @return an MimeMessageWrapper object containing the message, null if no message with
1210     * the given msn.
1211     * @throws AccessControlException if user does not have read rights for
1212     * this mailbox.
1213     * @throws AuthorizationException if user has lookup rights but does not
1214     * have read rights.
1215     */

1216    public synchronized MimeMessageWrapper retrieveUID(int uid, String JavaDoc user)
1217        throws AccessControlException, AuthorizationException {
1218        if (!hasReadRights(user)) { //throws AccessControlException
1219
throw new AuthorizationException("Not authorized to read.");
1220        }
1221        MimeMessageWrapper response = null;
1222        if (sequence.contains(new Integer JavaDoc(uid))) {
1223            //BufferedInputStream inMsg = null;
1224
try {
1225                path = getPath( absoluteName, owner, rootPath );
1226        MimeMessageFileSource source = new MimeMessageFileSource(path + File.separator + uid + MESSAGE_EXTENSION);
1227                response = new MimeMessageWrapper(source);
1228             // inMsg.close();
1229
} catch(Exception JavaDoc e) {
1230                getLogger().error("Error reading message from disc: " + e);
1231                e.printStackTrace();
1232                throw new
1233                    RuntimeException JavaDoc("Exception caught while retrieving Mail: "
1234                                     + e);
1235            } //finally {
1236
// try {
1237
// inMsg.close();
1238
// } catch (IOException ie) {
1239
// getLogger().error("Error closing streams: " + ie);
1240
// }
1241
// }
1242
getLogger().info("MimeMessageWrapper " + uid + " read from " + absoluteName);
1243            return response;
1244        } else {
1245            return null;
1246        }
1247    }
1248
1249    /**
1250     * Marks a message for deletion given a message sequence number.
1251     *
1252     * @param msn the message sequence number
1253     * @param username String represnting user
1254     * @return boolean true if successful.
1255     * @throws AccessControlException if user does not have read rights for
1256     * this mailbox.
1257     * @throws AuthorizationException if user has lookup rights but does not
1258     * have delete rights.
1259     */

1260    public synchronized boolean markDeleted(int msn, String JavaDoc user)
1261        throws AccessControlException, AuthorizationException {
1262        if (!hasDeleteRights(user)) { //throws AccessControlException
1263
throw new AuthorizationException("Not authorized to delete.");
1264        }
1265
1266        Assert.notImplemented();
1267        //TBD
1268
return false;
1269    }
1270
1271    /**
1272     * Marks a message for deletion given a unique identifier.
1273     *
1274     * @param uidunique identifier
1275     * @param username String represnting user
1276     * @return boolean true if successful, false if failed including no
1277     * message with the given uid.
1278     * @throws AccessControlException if user does not have read rights for
1279     * this mailbox.
1280     * @throws AuthorizationException if user has lookup rights but does not
1281     * have delete rights.
1282     */

1283    public synchronized boolean markDeletedUID(int uid, String JavaDoc user)
1284        throws AccessControlException, AuthorizationException {
1285        if (!hasDeleteRights(user)) { //throws AccessControlException
1286
throw new AuthorizationException("Not authorized to delete.");
1287        }
1288
1289        Assert.notImplemented();
1290        //TBD
1291
return false;
1292    }
1293
1294    /**
1295     * Returns the message attributes for a message.
1296     *
1297     * @param msn message sequence number
1298     * @param username String represnting user
1299     * @return MessageAttributes for message, null if no such message.
1300     * Changing the MessageAttributes object must not affect the actual
1301     * MessageAttributes.
1302     * @throws AccessControlException if user does not have read rights for
1303     * this mailbox.
1304     * @throws AuthorizationException if user has lookup rights but does not
1305     * have delete rights.
1306     */

1307    public synchronized MessageAttributes getMessageAttributes(int msn, String JavaDoc user)
1308        throws AccessControlException, AuthorizationException {
1309        if (!hasReadRights(user)) { //throws AccessControlException
1310
throw new AuthorizationException("Not authorized to read.");
1311        }
1312        System.out.println("msn: "+msn);
1313        System.out.println("sequence.size: "+sequence.size());
1314        if (msn > sequence.size()) {
1315            return null;
1316        } else {
1317            int uid = ((Integer JavaDoc)sequence.get(msn - 1)).intValue();
1318            return getMessageAttributesUID(uid, user);
1319        }
1320    }
1321
1322    /**
1323     * Returns the message attributes for a message.
1324     *
1325     * @param uid unique identifier
1326     * @param username String represnting user
1327     * @return MessageAttributes for message, null if no such message.
1328     * Changing the MessageAttributes object must not affect the actual
1329     * MessageAttributes.
1330     * @throws AccessControlException if user does not have read rights for
1331     * this mailbox.
1332     * @throws AuthorizationException if user has lookup rights but does not
1333     * have delete rights.
1334     */

1335    public synchronized MessageAttributes getMessageAttributesUID(int uid, String JavaDoc user)
1336        throws AccessControlException, AuthorizationException {
1337        if (!hasReadRights(user)) { //throws AccessControlException
1338
throw new AuthorizationException("Not authorized to read.");
1339        }
1340        System.out.println("getMessageAttributesUID()");
1341        System.out.println("uid: "+uid);
1342        System.out.println("user: "+user);
1343        System.out.println("sequence.size: "+sequence.size());
1344        SimpleMessageAttributes response = null;
1345        if (sequence.contains(new Integer JavaDoc(uid))) {
1346                System.out.println("reading from disk");
1347
1348            ObjectInputStream inAttrs = null;
1349            try {
1350                path = getPath( absoluteName, owner, rootPath );
1351                System.out.println( "FileInputStream("+(path + File.separator + uid + ATTRIBUTES_EXTENSION));
1352                inAttrs = new ObjectInputStream( new FileInputStream(path + File.separator + uid + ATTRIBUTES_EXTENSION));
1353                System.out.println("inAttrs="+inAttrs);
1354                response = (SimpleMessageAttributes)inAttrs.readObject();
1355                System.out.println("response="+response);
1356                if (response != null) {
1357                System.out.println("response.parts="+response.parts);
1358                if (response.parts != null) {
1359                System.out.println("response.parts.len="+response.parts.length);
1360                System.out.println("response.parts[0]="+response.parts[0]);
1361                }
1362                }
1363                setupLogger(response);
1364            } catch(Exception JavaDoc e) {
1365                getLogger().error("Error reading attributes from disc: " + e);
1366                e.printStackTrace();
1367                throw new
1368                    RuntimeException JavaDoc("Exception caught while retrieving Message attributes: "
1369                                     + e);
1370            } finally {
1371                try {
1372                    inAttrs.close();
1373                } catch (IOException ie) {
1374                    getLogger().error("Error closing streams: " + ie);
1375                }
1376            }
1377            getLogger().info("MessageAttributes for " + uid + " read from " + absoluteName);
1378            return response;
1379        } else {
1380            return null;
1381        }
1382    }
1383
1384    /**
1385     * Updates the attributes of a message.This may be incorporated into setFlags().
1386     *
1387     * @param MessageAttributes of a message already in this Mailbox
1388     * @throws AccessControlException if user does not have read rights for
1389     * this mailbox.
1390     * @throws AuthorizationException if user has lookup rights but does not
1391     * have delete rights.
1392     */

1393    public boolean updateMessageAttributes(MessageAttributes attrs, String JavaDoc user)
1394        throws AccessControlException, AuthorizationException {
1395        if (!hasKeepSeenRights(user)) { //throws AccessControlException
1396
throw new AuthorizationException("Not authorized to store flags.");
1397        }
1398        int uid = attrs.getUID();
1399        if (sequence.contains(new Integer JavaDoc(uid))) {
1400
1401            // Really, we should check whether the exact change is authorized.
1402
ObjectOutputStream outAttrs = null;
1403            try {
1404                path = getPath( absoluteName, owner, rootPath );
1405                outAttrs = new ObjectOutputStream( new FileOutputStream(path + File.separator + uid + ATTRIBUTES_EXTENSION));
1406                outAttrs.writeObject(attrs);
1407                outAttrs.close();
1408            } catch(Exception JavaDoc e) {
1409                getLogger().error("Error writing message to disc: " + e);
1410                e.printStackTrace();
1411                throw new
1412                    RuntimeException JavaDoc("Exception caught while storing Attributes: "
1413                                     + e);
1414            } finally {
1415                try {
1416                    outAttrs.close();
1417                } catch (IOException ie) {
1418                    getLogger().error("Error closing streams: " + ie);
1419                }
1420            }
1421            getLogger().info("MessageAttributes for " + uid + " written in " + absoluteName);
1422
1423            return true;
1424        } else {
1425            return false;
1426        }
1427    }
1428
1429    /**
1430     * Get the IMAP-formatted String of flags for specified message.
1431     *
1432     * @param msn message sequence number for a message in this mailbox
1433     * @param username String represnting user
1434     * @return flags for this message and user, null if no such message.
1435     * @throws AccessControlException if user does not have lookup rights for
1436     * this mailbox.
1437     * @throws AuthorizationException if user has lookup rights but does not
1438     * have read rights.
1439     */

1440    public synchronized String JavaDoc getFlags(int msn, String JavaDoc user)
1441        throws AccessControlException, AuthorizationException {
1442        if (!hasReadRights(user)) { //throws AccessControlException
1443
throw new AuthorizationException("Not authorized to read.");
1444        }
1445        if (msn > sequence.size()) {
1446            return null;
1447        } else {
1448            int uid = ((Integer JavaDoc)sequence.get(msn - 1)).intValue();
1449            return getFlagsUID(uid, user);
1450        }
1451    }
1452
1453    /**
1454     * Get the IMAP-formatted String of flags for specified message.
1455     *
1456     * @param uid UniqueIdentifier for a message in this mailbox
1457     * @param username String represnting user
1458     * @return flags for this message and user, null if no such message.
1459     * @throws AccessControlException if user does not have lookup rights for
1460     * this mailbox.
1461     * @throws AuthorizationException if user has lookup rights but does not
1462     * have read rights.
1463     */

1464    public synchronized String JavaDoc getFlagsUID(int uid, String JavaDoc user)
1465        throws AccessControlException, AuthorizationException {
1466        if (!hasReadRights(user)) { //throws AccessControlException
1467
throw new AuthorizationException("Not authorized to read.");
1468        }
1469        java.util.Iterator JavaDoc it = sequence.iterator();
1470        while(it.hasNext())
1471            System.out.println("FILEMESSAGES...."+it.next().toString());
1472            
1473        
1474        if (!sequence.contains(new Integer JavaDoc(uid))) {
1475            System.out.println("SEQUENCENOOO");
1476            return null;
1477        } else {
1478            System.out.println("FLAGSRETURNED");
1479            Flags flags = readFlags(uid);
1480            return flags.getFlags(user);
1481        }
1482    }
1483    /**
1484     * Updates the flags for a message.
1485     *
1486     * @param msn MessageSequenceNumber of a message already in this Mailbox
1487     * @param username String represnting user
1488     * @param request IMAP-formatted String representing requested change to
1489     * flags.
1490     * @return true if succeeded, false otherwise, including no such message
1491     * @throws AccessControlException if user does not have read rights for
1492     * this mailbox.
1493     * @throws AuthorizationException if user has lookup rights but does not
1494     * have delete rights.
1495     */

1496    public synchronized boolean setFlags(int msn, String JavaDoc user, String JavaDoc request)
1497        throws AccessControlException, AuthorizationException,
1498               IllegalArgumentException JavaDoc {
1499        if (!hasKeepSeenRights(user)) { //throws AccessControlException
1500
throw new AuthorizationException("Not authorized to store any flags.");
1501        }
1502        if (msn > sequence.size()) {
1503            return false;
1504        } else {
1505            int uid = ((Integer JavaDoc)sequence.get(msn - 1)).intValue();
1506            return setFlagsUID(uid, user, request);
1507        }
1508    }
1509
1510    /**
1511     * Updates the flags for a message.
1512     *
1513     * @param uid Unique Identifier of a message already in this Mailbox
1514     * @param username String represnting user
1515     * @param request IMAP-formatted String representing requested change to
1516     * flags.
1517     * @throws AccessControlException if user does not have read rights for
1518     * this mailbox.
1519     * @throws AuthorizationException if user has lookup rights but does not
1520     * have delete rights.
1521     */

1522    public synchronized boolean setFlagsUID(int uid, String JavaDoc user, String JavaDoc request)
1523        throws AccessControlException, AuthorizationException,
1524               IllegalArgumentException JavaDoc {
1525        if (!hasKeepSeenRights(user)) { //throws AccessControlException
1526
throw new AuthorizationException("Not authorized to store any flags.");
1527        }
1528        if ((request.toUpperCase().indexOf("DELETED") != -1) && (!hasDeleteRights(user))) { //throws AccessControlException
1529
throw new AuthorizationException("Not authorized to delete.");
1530        }
1531        if (sequence.contains(new Integer JavaDoc(uid))) {
1532
1533            Flags flags = readFlags(uid);
1534            boolean wasRecent = flags.isRecent();
1535            boolean wasDeleted = flags.isDeleted();
1536            boolean wasSeen = flags.isSeen(user);
1537
1538            if (flags.setFlags(request, user)) {
1539
1540                if (flags.isDeleted()) {
1541                    if (! wasDeleted) { messagesForDeletion.add(new Integer JavaDoc(uid)); }
1542                }
1543                if (flags.isSeen(user) != wasSeen) {
1544                    if (flags.isSeen(user)) {
1545                        int previousOld = ((Integer JavaDoc)oldestUnseenMessage.get(user)).intValue();
1546                        if (uid == previousOld) {
1547                            int newOld = findOldestUnseen(user, previousOld);
1548                            oldestUnseenMessage.put(user, (new Integer JavaDoc(newOld)));
1549                        }
1550                    } else { // seen flag unset
1551
if (uid < ((Integer JavaDoc)oldestUnseenMessage.get(user)).intValue()) {
1552                            oldestUnseenMessage.put(user, (new Integer JavaDoc(uid)));
1553                        }
1554                    }
1555                }
1556
1557                writeFlags(uid, flags);
1558                getLogger().debug("Flags for message uid " + uid + " in " + absoluteName + " updated.");
1559                return true;
1560            } else {
1561                return false;
1562            }
1563        } else {
1564            return false;
1565        }
1566
1567    }
1568
1569    private int findOldestUnseen(String JavaDoc user, int previousOld)
1570        throws AccessControlException, AuthorizationException {
1571        int response = 0; //indicates no unseen messages
1572
try {
1573            ListIterator lit = sequence.listIterator(previousOld);
1574            boolean found = false;
1575            while (!found && lit.hasNext() ) {
1576                int uid = ((Integer JavaDoc)lit.next()).intValue();
1577                Flags flags = readFlags(uid);
1578                if (!flags.isSeen(user)) {
1579                    response = uid;
1580                    found = true;
1581                }
1582            }
1583        }catch(Exception JavaDoc e) {
1584            // (because) BUG: Do nothing. Have investigated an error on fetching sequence.listIterator(previousOld);
1585
// with an error - but currently I don't know why :)
1586
}finally{
1587            return response;
1588        }
1589    }
1590
1591    private Flags readFlags(int uid) {
1592        Flags response = null;
1593        if (sequence.contains(new Integer JavaDoc(uid))) {
1594            ObjectInputStream inFlags = null;
1595            try {
1596                path = getPath( absoluteName, owner, rootPath );
1597                inFlags = new ObjectInputStream( new FileInputStream(path + File.separator + uid + FLAGS_EXTENSION));
1598                response = (Flags)inFlags.readObject();
1599            } catch(Exception JavaDoc e) {
1600                getLogger().error("Error reading flags from disc: " + e);
1601                e.printStackTrace();
1602                throw new
1603                    RuntimeException JavaDoc("Exception caught while retrieving Message flags: "
1604                                     + e);
1605            } finally {
1606                try {
1607                    inFlags.close();
1608                } catch (IOException ie) {
1609                    getLogger().error("Error closing streams: " + ie);
1610                }
1611            }
1612            getLogger().info("Flags for " + uid + " read from " + absoluteName);
1613        }
1614        return response;
1615    }
1616
1617    private boolean writeFlags(int uid, Flags flags) {
1618        if (sequence.contains(new Integer JavaDoc(uid))) {
1619            ObjectOutputStream outFlags = null;
1620            try {
1621                path = getPath( absoluteName, owner, rootPath );
1622                outFlags = new ObjectOutputStream( new FileOutputStream(path + File.separator + uid + FLAGS_EXTENSION));
1623                outFlags.writeObject(flags);
1624                outFlags.close();
1625            } catch(Exception JavaDoc e) {
1626                getLogger().error("Error writing message to disc: " + e);
1627                e.printStackTrace();
1628                throw new
1629                    RuntimeException JavaDoc("Exception caught while storing Flags: "
1630                                     + e);
1631            } finally {
1632                try {
1633                    outFlags.close();
1634                } catch (IOException ie) {
1635                    getLogger().error("Error closing streams: " + ie);
1636                }
1637            }
1638            getLogger().info("Flags for " + uid + " written in " + absoluteName);
1639            writeMailbox();
1640            return true;
1641        } else {
1642            writeMailbox();
1643            return false;
1644        }
1645    }
1646
1647    /**
1648     * Removes all messages marked Deleted. User must have delete rights.
1649     *
1650     * @param username String represnting user
1651     * @return true if successful
1652     * @throws AccessControlException if user does not have read rights for
1653     * this mailbox.
1654     * @throws AuthorizationException if user has delete rights but does not
1655     * have delete rights.
1656     */

1657    public synchronized boolean expunge(String JavaDoc user)
1658        throws AccessControlException, AuthorizationException {
1659        if (!hasDeleteRights(user)) { //throws AccessControlException
1660
throw new AuthorizationException("Not authorized to delete for user '" + user + "'.");
1661        }
1662        Iterator it = messagesForDeletion.iterator();
1663
1664        while (it.hasNext()) {
1665            Integer JavaDoc uidObj = (Integer JavaDoc)it.next();
1666            int uid = uidObj.intValue();
1667            if (sequence.contains(uidObj)) {
1668                try {
1669                    //SAM
1670
try
1671                    {
1672                        MimeMessageWrapper message = retrieveUID(uid, user);
1673                        System.out.println("(before)decrementing mailboxSize ("+getName()+") = " + mailboxSize);
1674                        System.out.println("decre message.getMessageSize() = " + (int)message.getMessageSize());
1675                        mailboxSize -= (int)message.getMessageSize();
1676                        System.out.println("(after)decrementing mailboxSize ("+getName()+") = " + mailboxSize);
1677                    }
1678                    catch (MessagingException JavaDoc me)
1679                    {
1680                            //ignore
1681
}
1682                    //END
1683
path = getPath( absoluteName, owner, rootPath );
1684                    final File msgFile = new File(path + File.separator + uid + MESSAGE_EXTENSION );
1685                    msgFile.delete();
1686                    final File attrFile = new File(path + File.separator + uid + ATTRIBUTES_EXTENSION );
1687                    attrFile.delete();
1688                    //SAM
1689
final File flagFile = new File(path + File.separator + uid + FLAGS_EXTENSION );
1690                    flagFile.delete();
1691                    //END
1692
sequence.remove(uidObj);
1693                } catch ( final Exception JavaDoc e ) {
1694                    throw new RuntimeException JavaDoc( "Exception caught while removing" +
1695                                                " a message: " + e );
1696                }
1697            }
1698        }
1699
1700        for (int i = 0; i < sequence.size(); i++) {
1701            System.err.println("Message with msn " + i + " has uid " + sequence.get(i));
1702        }
1703        writeMailbox();
1704        return true;
1705    }
1706
1707    private void writeMailbox() {
1708        String JavaDoc mailboxRecordFile = path + File.separator + MAILBOX_FILE_NAME;
1709        ObjectOutputStream out = null;
1710        FileOutputStream fout = null;
1711        try {
1712            fout = new FileOutputStream( mailboxRecordFile );
1713            out = new ObjectOutputStream( fout );
1714            out.writeObject(this);
1715            out.flush();
1716            out.close();
1717            fout.flush();
1718            fout.close();
1719        } catch(Exception JavaDoc e) {
1720            if (out != null) {
1721                try {
1722                    out.close();
1723                } catch (Exception JavaDoc ignored) {
1724                }
1725            }
1726            if (fout != null) {
1727                try {
1728                    fout.close();
1729                } catch (Exception JavaDoc ignored) {
1730                }
1731            }
1732            e.printStackTrace();
1733            throw new
1734                RuntimeException JavaDoc("Exception caught while storing Mailbox: " + e);
1735        }
1736        getLogger().info("FileMailbox written: " + absoluteName);
1737    }
1738
1739
1740    /**
1741     * Lists uids of messages in mailbox indexed by MSN.
1742     *
1743     * @param username String represnting user
1744     * @return List of Integers wrapping uids of message
1745     */

1746    public List listUIDs(String JavaDoc user) {
1747        return new ArrayList(Collections.unmodifiableList(sequence));
1748    }
1749
1750    public Set getUsersWithLookupRights() {
1751        Set response = new HashSet();
1752        Iterator it = acl.keySet().iterator();
1753        while (it.hasNext()) {
1754            String JavaDoc user = (String JavaDoc) it.next();
1755            boolean[] rights = (boolean[]) acl.get(user);
1756            if (rights[LOOKUP] == true) {
1757                response.add(user);
1758            }
1759        }
1760        return response;
1761    }
1762
1763    public Set getUsersWithReadRights() {
1764        Set response = new HashSet();
1765        Iterator it = acl.keySet().iterator();
1766        while (it.hasNext()) {
1767            String JavaDoc user = (String JavaDoc) it.next();
1768            boolean[] rights = (boolean[]) acl.get(user);
1769            if (rights[READ] == true) {
1770                response.add(user);
1771            }
1772        }
1773        return response;
1774    }
1775
1776    public Map getUnseenByUser() {
1777        Map response = new HashMap();
1778        Iterator it = oldestUnseenMessage.keySet().iterator();
1779        while (it.hasNext()) {
1780            String JavaDoc user = (String JavaDoc) it.next();
1781            Integer JavaDoc uidObj = ((Integer JavaDoc)oldestUnseenMessage.get(user));
1782            int oldUID = uidObj.intValue();
1783            if (oldUID == 0) {
1784                response.put(user, uidObj);
1785            } else {
1786                int count = 0;
1787                ListIterator lit
1788                    = sequence.listIterator(sequence.indexOf(uidObj));
1789                while (lit.hasNext() ) {
1790                    int uid = ((Integer JavaDoc)lit.next()).intValue();
1791                    Flags flags = readFlags(uid);
1792                    if (!flags.isSeen(user)) {
1793                        count ++;
1794                    }
1795                }
1796                response.put(user, new Integer JavaDoc(count));
1797            }
1798        }
1799        return response;
1800    }
1801
1802
1803    public InternetHeaders JavaDoc getInternetHeaders(int msn, String JavaDoc user)
1804        throws AccessControlException, AuthorizationException {
1805        if (!hasReadRights(user)) { //throws AccessControlException
1806
throw new AuthorizationException("Not authorized to read.");
1807        }
1808        if (msn > sequence.size()) {
1809            return null;
1810        } else {
1811            int uid = ((Integer JavaDoc)sequence.get(msn - 1)).intValue();
1812            return getInternetHeadersUID(uid, user);
1813        }
1814    }
1815
1816    public InternetHeaders JavaDoc getInternetHeadersUID(int uid, String JavaDoc user)
1817        throws AccessControlException, AuthorizationException {
1818        InternetHeaders JavaDoc response = null;
1819        if (sequence.contains(new Integer JavaDoc(uid))) {
1820            BufferedInputStream inMsg = null;
1821            try {
1822                inMsg = new BufferedInputStream( new FileInputStream(path + File.separator + uid + MESSAGE_EXTENSION));
1823                response = new InternetHeaders JavaDoc(inMsg);
1824                inMsg.close();
1825            } catch(Exception JavaDoc e) {
1826                getLogger().error("Error reading headers of message from disc: " + e);
1827                e.printStackTrace();
1828                throw new
1829                    RuntimeException JavaDoc("Exception caughtt while retrieving InternetHeaders: " + e);
1830            } finally {
1831                try {
1832                    inMsg.close();
1833                } catch (IOException ie) {
1834                    getLogger().error("Error closing streams: " + ie);
1835                }
1836            }
1837            getLogger().info("InternetHeaders for message " + uid + " read from "
1838                        + absoluteName);
1839            return response;
1840        } else {
1841            return null;
1842        }
1843    }
1844
1845    /**
1846     * Returns true if the named user is subscribed to this Mailbox.
1847     */

1848    public boolean isSubscribed( String JavaDoc userName )
1849    {
1850        return subscribedUsers.contains( userName.toLowerCase() );
1851    }
1852
1853    /**
1854     * Subscribes a user to this Mailbox.
1855     */

1856    public void subscribe( String JavaDoc userName )
1857    {
1858        subscribedUsers.add( userName.toLowerCase() );
1859    }
1860
1861    /**
1862     * Unsubscrive a user from this Mailbox.
1863     */

1864    public void unsubscribe( String JavaDoc userName )
1865    {
1866        subscribedUsers.remove( userName.toLowerCase() );
1867    }
1868}
1869
1870
1871
1872
Popular Tags