📄 imapfolder.java
字号:
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* @(#)IMAPFolder.java 1.85 07/09/05
*/
package com.sun.mail.imap;
import java.util.Date;
import java.util.Vector;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.io.*;
import javax.mail.*;
import javax.mail.event.*;
import javax.mail.internet.*;
import javax.mail.search.*;
import com.sun.mail.util.*;
import com.sun.mail.iap.*;
import com.sun.mail.imap.protocol.*;
/**
* This class implements an IMAP folder. <p>
*
* A closed IMAPFolder object shares a protocol connection with its IMAPStore
* object. When the folder is opened, it gets its own protocol connection. <p>
*
* Applications that need to make use of IMAP-specific features may cast
* a <code>Folder</code> object to an <code>IMAPFolder</code> object and
* use the methods on this class. The {@link #getQuota getQuota} and
* {@link #setQuota setQuota} methods support the IMAP QUOTA extension.
* Refer to <A HREF="http://www.ietf.org/rfc/rfc2087.txt">RFC 2087</A>
* for more information. <p>
*
* The {@link #getACL getACL}, {@link #addACL addACL},
* {@link #removeACL removeACL}, {@link #addRights addRights},
* {@link #removeRights removeRights}, {@link #listRights listRights}, and
* {@link #myRights myRights} methods support the IMAP ACL extension.
* Refer to <A HREF="http://www.ietf.org/rfc/rfc2086.txt">RFC 2086</A>
* for more information. <p>
*
* The {@link #doCommand doCommand} method and
* {@link IMAPFolder.ProtocolCommand IMAPFolder.ProtocolCommand}
* interface support use of arbitrary IMAP protocol commands. <p>
*
* See the <a href="package-summary.html">com.sun.mail.imap</a> package
* documentation for further information on the IMAP protocol provider. <p>
*
* <strong>WARNING:</strong> The APIs unique to this class should be
* considered <strong>EXPERIMENTAL</strong>. They may be changed in the
* future in ways that are incompatible with applications using the
* current APIs.
*
* @version 1.85, 07/09/05
* @author John Mani
* @author Bill Shannon
* @author Jim Glennon
*/
/*
* The folder object itself serves as a lock for the folder's state
* EXCEPT for the message cache (see below), typically by using
* synchronized methods. When checking that a folder is open or
* closed, the folder's lock must be held. It's important that the
* folder's lock is acquired before the messageCacheLock (see below).
* Thus, the locking hierarchy is that the folder lock, while optional,
* must be acquired before the messageCacheLock, if it's acquired at
* all. Be especially careful of callbacks that occur while holding
* the messageCacheLock into (e.g.) superclass Folder methods that are
* synchronized. Note that methods in IMAPMessage will acquire the
* messageCacheLock without acquiring the folder lock. <p>
*
* When a folder is opened, it creates a messageCache (a Vector) of
* empty IMAPMessage objects. Each Message has a messageNumber - which
* is its index into the messageCache, and a sequenceNumber - which is
* its IMAP sequence-number. All operations on a Message which involve
* communication with the server, use the message's sequenceNumber. <p>
*
* The most important thing to note here is that the server can send
* unsolicited EXPUNGE notifications as part of the responses for "most"
* commands. Refer RFC2060, sections 5.3 & 5.5 for gory details. Also,
* the server sends these notifications AFTER the message has been
* expunged. And once a message is expunged, the sequence-numbers of
* those messages after the expunged one are renumbered. This essentially
* means that the mapping between *any* Message and its sequence-number
* can change in the period when a IMAP command is issued and its responses
* are processed. Hence we impose a strict locking model as follows: <p>
*
* We define one mutex per folder - this is just a Java Object (named
* messageCacheLock). Any time a command is to be issued to the IMAP
* server (i.e., anytime the corresponding IMAPProtocol method is
* invoked), follow the below style:
*
* synchronized (messageCacheLock) { // ACQUIRE LOCK
* issue command ()
*
* // The response processing is typically done within
* // the handleResponse() callback. A few commands (Fetch,
* // Expunge) return *all* responses and hence their
* // processing is done here itself. Now, as part of the
* // processing unsolicited EXPUNGE responses, we renumber
* // the necessary sequence-numbers. Thus the renumbering
* // happens within this critical-region, surrounded by
* // locks.
* process responses ()
* } // RELEASE LOCK
*
* This technique is used both by methods in IMAPFolder and by methods
* in IMAPMessage and other classes that operate on data in the folder.
* Note that holding the messageCacheLock has the side effect of
* preventing the folder from being closed, and thus ensuring that the
* folder's protocol object is still valid. The protocol object should
* only be accessed while holding the messageCacheLock (except for calls
* to IMAPProtocol.isREV1(), which don't need to be protected because it
* doesn't access the server).
*
* Note that interactions with the Store's protocol connection do
* not have to be protected as above, since the Store's protocol is
* never in a "meaningful" SELECT-ed state.
*/
public class IMAPFolder extends Folder implements UIDFolder, ResponseHandler {
protected String fullName; // full name
protected String name; // name
protected int type; // folder type.
protected char separator; // separator
protected Flags availableFlags; // available flags
protected Flags permanentFlags; // permanent flags
protected boolean exists = false; // whether this folder really exists ?
protected boolean isNamespace = false; // folder is a namespace name
protected String[] attributes; // name attributes from LIST response
protected IMAPProtocol protocol; // this folder's own protocol object
protected Vector messageCache; // message cache
protected Object messageCacheLock; // accessor lock for message cache
protected Hashtable uidTable; // UID->Message hashtable
/* An IMAP delimiter is a 7bit US-ASCII character. (except NUL).
* We use '\uffff' (a non 7bit character) to indicate that we havent
* yet determined what the separator character is.
* We use '\u0000' (NUL) to indicate that no separator character
* exists, i.e., a flat hierarchy
*/
static final protected char UNKNOWN_SEPARATOR = '\uffff';
private boolean opened = false; // is this folder opened ?
/* This field tracks the state of this folder. If the folder is closed
* due to external causes (i.e, not thru the close() method), then
* this field will remain false. If the folder is closed thru the
* close() method, then this field is set to true.
*
* If reallyClosed is false, then a FolderClosedException is
* generated when a method is invoked on any Messaging object
* owned by this folder. If reallyClosed is true, then the
* IllegalStateException runtime exception is thrown.
*/
private boolean reallyClosed = true;
/*
* The idleState field supports the IDLE command.
* Normally when executing an IMAP command we hold the
* messageCacheLock and often the folder lock (see above).
* While executing the IDLE command we can't hold either
* of these locks or it would prevent other threads from
* entering Folder methods even far enough to check whether
* an IDLE command is in progress. We need to check before
* issuing another command so that we can abort the IDLE
* command.
*
* The idleState field is protected by the messageCacheLock.
* The RUNNING state is the normal state and means no IDLE
* command is in progress. The IDLE state means we've issued
* an IDLE command and are reading responses. The ABORTING
* state means we've sent the DONE continuation command and
* are waiting for the thread running the IDLE command to
* break out of its read loop.
*
* When an IDLE command is in progress, the thread calling
* the idle method will be reading from the IMAP connection
* while holding neither the folder lock nor the messageCacheLock.
* It's obviously critical that no other thread try to send a
* command or read from the connection while in this state.
* However, other threads can send the DONE continuation
* command that will cause the server to break out of the IDLE
* loop and send the ending tag response to the IDLE command.
* The thread in the idle method that's reading the responses
* from the IDLE command will see this ending response and
* complete the idle method, setting the idleState field back
* to RUNNING, and notifying any threads waiting to use the
* connection.
*
* All uses of the IMAP connection (IMAPProtocol object) must
* be done while holding the messageCacheLock and must be
* preceeded by a check to make sure an IDLE command is not
* running, and abort the IDLE command if necessary. While
* waiting for the IDLE command to complete, these other threads
* will give up the messageCacheLock, but might still be holding
* the folder lock. This check is done by the getProtocol()
* method, resulting in a typical usage pattern of:
*
* synchronized (messageCacheLock) {
* IMAPProtocol p = getProtocol(); // may block waiting for IDLE
* // ... use protocol
* }
*/
private static final int RUNNING = 0; // not doing IDLE command
private static final int IDLE = 1; // IDLE command in effect
private static final int ABORTING = 2; // IDLE command aborting
private int idleState = RUNNING;
private int total = -1; // total number of messages in the
// message cache
private int recent = -1; // number of recent messages
private int realTotal = -1; // total number of messages on
// the server
private long uidvalidity = -1; // UIDValidity
private long uidnext = -1; // UIDNext
private boolean doExpungeNotification = true; // used in expunge handler
private Status cachedStatus = null;
private long cachedStatusTime = 0;
private boolean debug = false;
private PrintStream out; // debug output stream
private boolean connectionPoolDebug;
/**
* A fetch profile item for fetching headers.
* This inner class extends the <code>FetchProfile.Item</code>
* class to add new FetchProfile item types, specific to IMAPFolders.
*
* @see FetchProfile
*/
public static class FetchProfileItem extends FetchProfile.Item {
protected FetchProfileItem(String name) {
super(name);
}
/**
* HEADERS is a fetch profile item that can be included in a
* <code>FetchProfile</code> during a fetch request to a Folder.
* This item indicates that the headers for messages in the specified
* range are desired to be prefetched. <p>
*
* An example of how a client uses this is below: <p>
* <blockquote><pre>
*
* FetchProfile fp = new FetchProfile();
* fp.add(IMAPFolder.FetchProfileItem.HEADERS);
* folder.fetch(msgs, fp);
*
* </pre></blockquote><p>
*/
public static final FetchProfileItem HEADERS =
new FetchProfileItem("HEADERS");
/**
* SIZE is a fetch profile item that can be included in a
* <code>FetchProfile</code> during a fetch request to a Folder.
* This item indicates that the sizes of the messages in the specified
* range are desired to be prefetched. <p>
*
* SIZE should move to FetchProfile.Item in JavaMail 1.3.
*/
public static final FetchProfileItem SIZE =
new FetchProfileItem("SIZE");
}
/**
* Constructor used to create a possibly non-existent folder.
*
* @param fullName fullname of this folder
* @param separator the default separator character for this
* folder's namespace
* @param store the Store
*/
protected IMAPFolder(String fullName, char separator, IMAPStore store) {
super(store);
if (fullName == null)
throw new NullPointerException("Folder name is null");
this.fullName = fullName;
this.separator = separator;
messageCacheLock = new Object();
debug = store.getSession().getDebug();
connectionPoolDebug = ((IMAPStore)store).getConnectionPoolDebug();
out = store.getSession().getDebugOut();
if (out == null) // should never happen
out = System.out;
/*
* Work around apparent bug in Exchange. Exchange
* will return a name of "Public Folders/" from
* LIST "%".
*
* If name has one separator, and it's at the end,
* assume this is a namespace name and treat it
* accordingly. Usually this will happen as a result
* of the list method, but this also allows getFolder
* to work with namespace names.
*/
this.isNamespace = false;
if (separator != UNKNOWN_SEPARATOR && separator != '\0') {
int i = this.fullName.indexOf(separator);
if (i > 0 && i == this.fullName.length() - 1) {
this.fullName = this.fullName.substring(0, i);
this.isNamespace = true;
}
}
}
/**
* Constructor used to create a possibly non-existent folder.
*
* @param fullName fullname of this folder
* @param separator the default separator character for this
* folder's namespace
* @param store the Store
*/
protected IMAPFolder(String fullName, char separator, IMAPStore store,
boolean isNamespace) {
this(fullName, separator, store);
this.isNamespace = isNamespace;
}
/**
* Constructor used to create an existing folder.
*/
protected IMAPFolder(ListInfo li, IMAPStore store) {
this(li.name, li.separator, store);
if (li.hasInferiors)
type |= HOLDS_FOLDERS;
if (li.canOpen)
type |= HOLDS_MESSAGES;
exists = true;
attributes = li.attrs;
}
/*
* Ensure that this folder exists. If 'exists' has been set to true,
* we don't attempt to validate it with the server again. Note that
* this can result in a possible loss of sync with the server.
*/
private void checkExists() throws MessagingException {
// If the boolean field 'exists' is false, check with the
// server by invoking exists() ..
if (!exists && !exists())
throw new FolderNotFoundException(
this, fullName + " not found");
}
/*
* Ensure the folder is closed.
* ASSERT: Must be called with this folder's synchronization lock held.
*/
private void checkClosed() {
if (opened)
throw new IllegalStateException(
"This operation is not allowed on an open folder"
);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -