FileDocCategorySizeDatePackage
IMAPProtocol.javaAPI DocJavaMail 1.4.362099Tue Nov 17 10:38:10 GMT 2009com.sun.mail.imap.protocol

IMAPProtocol

public class IMAPProtocol extends Protocol
This class extends the iap.Protocol object and implements IMAP semantics. In general, there is a method corresponding to each IMAP protocol command. The typical implementation issues the appropriate protocol command, collects all responses, processes those responses that are specific to this command and then dispatches the rest (the unsolicited ones) to the dispatcher using the notifyResponseHandlers(r).
author
John Mani
author
Bill Shannon

Fields Summary
private boolean
connected
private boolean
rev1
private boolean
authenticated
private Map
capabilities
private List
authmechs
private String[]
searchCharsets
private String
name
private SaslAuthenticator
saslAuthenticator
private ByteArray
ba
private static final byte[]
CRLF
private String
idleTag
private static final byte[]
DONE
Constructors Summary
public IMAPProtocol(String name, String host, int port, boolean debug, PrintStream out, Properties props, boolean isSSL)
Constructor. Opens a connection to the given host at given port.

param
host host to connect to
param
port portnumber to connect to
param
debug debug mode
param
props Properties object used by this protocol


                 	     	                            
           
			     
			     
	super(host, port, debug, out, props, "mail." + name, isSSL);

	try {
	    this.name = name;

	    if (capabilities == null)
		capability();

	    if (hasCapability("IMAP4rev1"))
		rev1 = true;

	    searchCharsets = new String[2]; // 2, for now.
	    searchCharsets[0] = "UTF-8";
	    searchCharsets[1] = MimeUtility.mimeCharset(
				    MimeUtility.getDefaultJavaCharset()
				);

	    connected = true;	// must be last statement in constructor
	} finally {
	    /*
	     * If we get here because an exception was thrown, we need
	     * to disconnect to avoid leaving a connected socket that
	     * no one will be able to use because this object was never
	     * completely constructed.
	     */
	    if (!connected)
		disconnect();
	}
    
Methods Summary
public voidappend(java.lang.String mbox, javax.mail.Flags f, java.util.Date d, com.sun.mail.iap.Literal data)
APPEND Command.

see
"RFC2060, section 6.3.11"

	appenduid(mbox, f, d, data, false);	// ignore return value
    
public com.sun.mail.imap.AppendUIDappenduid(java.lang.String mbox, javax.mail.Flags f, java.util.Date d, com.sun.mail.iap.Literal data)
APPEND Command, return uid from APPENDUID response code.

see
"RFC2060, section 6.3.11"

	return appenduid(mbox, f, d, data, true);
    
public com.sun.mail.imap.AppendUIDappenduid(java.lang.String mbox, javax.mail.Flags f, java.util.Date d, com.sun.mail.iap.Literal data, boolean uid)

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);

	if (f != null) { // set Flags in appended message
	    // can't set the \Recent flag in APPEND
	    if (f.contains(Flags.Flag.RECENT)) {
		f = new Flags(f);		// copy, don't modify orig
		f.remove(Flags.Flag.RECENT);	// remove RECENT from copy
	    }

	    /*
	     * HACK ALERT: We want the flag_list to be written out
	     * without any checking/processing of the bytes in it. If
	     * I use writeString(), the flag_list will end up being
	     * quoted since it contains "illegal" characters. So I
	     * am depending on implementation knowledge that writeAtom()
	     * does not do any checking/processing - it just writes out
	     * the bytes. What we really need is a writeFoo() that just
	     * dumps out its argument.
	     */
	    args.writeAtom(createFlagList(f));
	}
	if (d != null) // set INTERNALDATE in appended message
	    args.writeString(INTERNALDATE.format(d));

	args.writeBytes(data);

	Response[] r = command("APPEND", args);

	// dispatch untagged responses
	notifyResponseHandlers(r);

	// Handle result of this command
	handleResult(r[r.length-1]);

	if (uid)
	    return getAppendUID(r[r.length-1]);
	else
	    return null;
    
public synchronized voidauthlogin(java.lang.String u, java.lang.String p)
The AUTHENTICATE command with AUTH=LOGIN authenticate scheme

see
"RFC2060, section 6.2.1"

	Vector v = new Vector();
	String tag = null;
	Response r = null;
	boolean done = false;

	try {
	    tag = writeCommand("AUTHENTICATE LOGIN", null);
	} catch (Exception ex) {
	    // Convert this into a BYE response
	    r = Response.byeResponse(ex);
	    done = true;
	}

	OutputStream os = getOutputStream(); // stream to IMAP server

	/* Wrap a BASE64Encoder around a ByteArrayOutputstream
	 * to craft b64 encoded username and password strings
	 *
	 * Note that the encoded bytes should be sent "as-is" to the
	 * server, *not* as literals or quoted-strings.
	 *
	 * Also note that unlike the B64 definition in MIME, CRLFs 
	 * should *not* be inserted during the encoding process. So, I
	 * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine,
	 * which should be sufficiently large !
	 *
	 * Finally, format the line in a buffer so it can be sent as
	 * a single packet, to avoid triggering a bug in SUN's SIMS 2.0
	 * server caused by patch 105346.
	 */

	ByteArrayOutputStream bos = new ByteArrayOutputStream();
	OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE);
	boolean first = true;

	while (!done) { // loop till we are done
	    try {
		r = readResponse();
	    	if (r.isContinuation()) {
		    // Server challenge ..
		    String s;
		    if (first) { // Send encoded username
			s = u;
			first = false;
		    } else 	// Send encoded password
			s = p;
		    
		    // obtain b64 encoded bytes
		    b64os.write(ASCIIUtility.getBytes(s));
		    b64os.flush(); 	// complete the encoding

		    bos.write(CRLF); 	// CRLF termination
		    os.write(bos.toByteArray()); // write out line
		    os.flush(); 	// flush the stream
		    bos.reset(); 	// reset buffer
		} else if (r.isTagged() && r.getTag().equals(tag))
		    // Ah, our tagged response
		    done = true;
		else if (r.isBYE()) // outta here
		    done = true;
		else // hmm .. unsolicited response here ?!
		    v.addElement(r);
	    } catch (Exception ioex) {
		// convert this into a BYE response
		r = Response.byeResponse(ioex);
		done = true;
	    }
	}

	/* Dispatch untagged responses.
	 * NOTE: in our current upper level IMAP classes, we add the
	 * responseHandler to the Protocol object only *after* the 
	 * connection has been authenticated. So, for now, the below
	 * code really ends up being just a no-op.
	 */
	Response[] responses = new Response[v.size()];
	v.copyInto(responses);
	notifyResponseHandlers(responses);

	// Handle the final OK, NO, BAD or BYE response
	handleResult(r);
	// If the response includes a CAPABILITY response code, process it
	setCapabilities(r);
	// if we get this far without an exception, we're authenticated
	authenticated = true;
    
public synchronized voidauthntlm(java.lang.String authzid, java.lang.String u, java.lang.String p)
The AUTHENTICATE command with AUTH=NTLM authentication scheme. This is based heavly on the {@link #authlogin} method.

param
authzid the authorization id
param
u the username
param
p the password
throws
ProtocolException as thrown by {@link Protocol#handleResult}.
see
"RFC3501, section 6.2.2"
see
"RFC2595, section 6"
since
JavaMail 1.4.3

	Vector v = new Vector();
	String tag = null;
	Response r = null;
	boolean done = false;

	/*
	 * Generate the first response right away because Ntlm will
	 * return null if the NTLM authentication support isn't
	 * available and then we can fail cleanly without invoking
	 * the AUTHENTICATE command.
	 */
	Ntlm ntlm = new Ntlm(debug ? out : null);
	int flags = 0;
	String domain = null;
	String type1Msg = null;
	try {
	    flags = PropUtil.getIntProperty(props,
		"mail." + name + ".auth.ntlm.flags", 0);
	    boolean useUnicode = PropUtil.getBooleanProperty(props,
		"mail." + name + ".auth.ntlm.unicode", true);
	    domain = props.getProperty(
		"mail." + name + ".auth.ntlm.domain", "");
	    type1Msg = ntlm.generateType1Msg(useUnicode, flags,
		domain, getLocalHost());
	    if (type1Msg == null) {
		if (debug)
		    out.println("IMAP DEBUG: Can't load NTLM authenticator");
		throw new ProtocolException("Can't load NTLM authenticator");
	    }
	} catch (IOException ex) {
	    throw new ProtocolException("Error generating NTLM response", ex);
	}

	try {
	    tag = writeCommand("AUTHENTICATE NTLM", null);
	} catch (Exception ex) {
	    // Convert this into a BYE response
	    r = Response.byeResponse(ex);
	    done = true;
	}

	OutputStream os = getOutputStream(); // stream to IMAP server
	boolean first = true;

	while (!done) { // loop till we are done
	    try {
		r = readResponse();
	    	if (r.isContinuation()) {
		    // Server challenge ..
		    String s;
		    if (first) {
			s = type1Msg;
			first = false;
		    } else {
			int lmCompatibility = PropUtil.getIntProperty(props,
			    "mail." + name + ".auth.ntlm.lmcompat", 3);
			s = ntlm.generateType3Msg(u, p,
			    domain, getLocalHost(),
			    r.getRest(),
			    flags,
			    lmCompatibility);
		    }
 
		    os.write(ASCIIUtility.getBytes(s));
		    os.write(CRLF); 	// CRLF termination
		    os.flush(); 	// flush the stream
		} else if (r.isTagged() && r.getTag().equals(tag))
		    // Ah, our tagged response
		    done = true;
		else if (r.isBYE()) // outta here
		    done = true;
		else // hmm .. unsolicited response here ?!
		    v.addElement(r);
	    } catch (Exception ioex) {
		// convert this into a BYE response
		r = Response.byeResponse(ioex);
		done = true;
	    }
	}

	/*
	 * Dispatch untagged responses.
	 * NOTE: in our current upper level IMAP classes, we add the
	 * responseHandler to the Protocol object only *after* the
	 * connection has been authenticated. So, for now, the below
	 * code really ends up being just a no-op.
	 */
	Response[] responses = new Response[v.size()];
	v.copyInto(responses);
	notifyResponseHandlers(responses);

	// Handle the final OK, NO, BAD or BYE response
	handleResult(r);
	// If the response includes a CAPABILITY response code, process it
	setCapabilities(r);
	// if we get this far without an exception, we're authenticated
	authenticated = true;
    
public synchronized voidauthplain(java.lang.String authzid, java.lang.String u, java.lang.String p)
The AUTHENTICATE command with AUTH=PLAIN authentication scheme. This is based heavly on the {@link #authlogin} method.

param
authzid the authorization id
param
u the username
param
p the password
throws
ProtocolException as thrown by {@link Protocol#handleResult}.
see
"RFC3501, section 6.2.2"
see
"RFC2595, section 6"
since
JavaMail 1.3.2

	Vector v = new Vector();
	String tag = null;
	Response r = null;
	boolean done = false;

	try {
	    tag = writeCommand("AUTHENTICATE PLAIN", null);
	} catch (Exception ex) {
	    // Convert this into a BYE response
	    r = Response.byeResponse(ex);
	    done = true;
	}

	OutputStream os = getOutputStream(); // stream to IMAP server

	/* Wrap a BASE64Encoder around a ByteArrayOutputstream
	 * to craft b64 encoded username and password strings
	 *
	 * Note that the encoded bytes should be sent "as-is" to the
	 * server, *not* as literals or quoted-strings.
	 *
	 * Also note that unlike the B64 definition in MIME, CRLFs
	 * should *not* be inserted during the encoding process. So, I
	 * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine,
	 * which should be sufficiently large !
	 *
	 * Finally, format the line in a buffer so it can be sent as
	 * a single packet, to avoid triggering a bug in SUN's SIMS 2.0
	 * server caused by patch 105346.
	 */

	ByteArrayOutputStream bos = new ByteArrayOutputStream();
	OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE);

	while (!done) { // loop till we are done
	    try {
		r = readResponse();
		if (r.isContinuation()) {
		    // Server challenge ..
		    final String nullByte = "\0";
		    String s = authzid + nullByte + u + nullByte + p;

		    // obtain b64 encoded bytes
		    b64os.write(ASCIIUtility.getBytes(s));
		    b64os.flush(); 	// complete the encoding

		    bos.write(CRLF); 	// CRLF termination
		    os.write(bos.toByteArray()); // write out line
		    os.flush(); 	// flush the stream
		    bos.reset(); 	// reset buffer
		} else if (r.isTagged() && r.getTag().equals(tag))
		    // Ah, our tagged response
		    done = true;
		else if (r.isBYE()) // outta here
		    done = true;
		else // hmm .. unsolicited response here ?!
		    v.addElement(r);
	    } catch (Exception ioex) {
		// convert this into a BYE response
		r = Response.byeResponse(ioex);
		done = true;
	    }
	}

	/* Dispatch untagged responses.
	 * NOTE: in our current upper level IMAP classes, we add the
	 * responseHandler to the Protocol object only *after* the
	 * connection has been authenticated. So, for now, the below
	 * code really ends up being just a no-op.
	 */
	Response[] responses = new Response[v.size()];
	v.copyInto(responses);
	notifyResponseHandlers(responses);

	// Handle the final OK, NO, BAD or BYE response
	handleResult(r);
	// If the response includes a CAPABILITY response code, process it
	setCapabilities(r);
	// if we get this far without an exception, we're authenticated
	authenticated = true;
    
public voidcapability()
CAPABILITY command.

see
"RFC2060, section 6.1.1"

	// Check CAPABILITY
	Response[] r = command("CAPABILITY", null);

	if (!r[r.length-1].isOK())
	    throw new ProtocolException(r[r.length-1].toString());

	capabilities = new HashMap(10);
	authmechs = new ArrayList(5);
	for (int i = 0, len = r.length; i < len; i++) {
	    if (!(r[i] instanceof IMAPResponse))
		continue;

	    IMAPResponse ir = (IMAPResponse)r[i];

	    // Handle *all* untagged CAPABILITY responses.
	    //   Though the spec seemingly states that only
	    // one CAPABILITY response string is allowed (6.1.1),
	    // some server vendors claim otherwise.
	    if (ir.keyEquals("CAPABILITY"))
		parseCapabilities(ir);
	}
    
public voidcheck()
CHECK Command.

see
"RFC2060, section 6.4.1"

	simpleCommand("CHECK", null);
    
public voidclose()
CLOSE Command.

see
"RFC2060, section 6.4.2"

	simpleCommand("CLOSE", null);
    
public voidcopy(com.sun.mail.imap.protocol.MessageSet[] msgsets, java.lang.String mbox)
COPY command.

	copy(MessageSet.toString(msgsets), mbox);
    
public voidcopy(int start, int end, java.lang.String mbox)

	copy(String.valueOf(start) + ":" + String.valueOf(end),
		    mbox);
    
private voidcopy(java.lang.String msgSequence, java.lang.String mbox)

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeAtom(msgSequence);
	args.writeString(mbox);

	simpleCommand("COPY", args);
    
public voidcreate(java.lang.String mbox)
CREATE Command.

see
"RFC2060, section 6.3.3"

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);

	simpleCommand("CREATE", args);
    
private java.lang.StringcreateFlagList(javax.mail.Flags flags)
Creates an IMAP flag_list from the given Flags object.

	StringBuffer sb = new StringBuffer();
	sb.append("("); // start of flag_list

	Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags
	boolean first = true;
	for (int i = 0; i < sf.length; i++) {
	    String s;
	    Flags.Flag f = sf[i];
	    if (f == Flags.Flag.ANSWERED)
		s = "\\Answered";
	    else if (f == Flags.Flag.DELETED)
		s = "\\Deleted";
	    else if (f == Flags.Flag.DRAFT)
		s = "\\Draft";
	    else if (f == Flags.Flag.FLAGGED)
		s = "\\Flagged";
	    else if (f == Flags.Flag.RECENT)
		s = "\\Recent";
	    else if (f == Flags.Flag.SEEN)
		s = "\\Seen";
	    else
		continue;	// skip it
	    if (first)
		first = false;
	    else
		sb.append(' ");
	    sb.append(s);
	}

	String[] uf = flags.getUserFlags(); // get the user flag strings
	for (int i = 0; i < uf.length; i++) {
	    if (first)
		first = false;
	    else
		sb.append(' ");
	    sb.append(uf[i]);
	}

	sb.append(")"); // terminate flag_list
	return sb.toString();
    
public voiddelete(java.lang.String mbox)
DELETE Command.

see
"RFC2060, section 6.3.4"

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);

	simpleCommand("DELETE", args);
    
public voiddeleteACL(java.lang.String mbox, java.lang.String user)
DELETEACL Command.

see
"RFC2086"

	if (!hasCapability("ACL")) 
	    throw new BadCommandException("ACL not supported");

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);
	args.writeString(user);

	Response[] r = command("DELETEACL", args);
	Response response = r[r.length-1];

	// dispatch untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
    
public voiddisconnect()
Close socket connection. This method just makes the Protocol.disconnect() method public.

	super.disconnect();
	authenticated = false;	// just in case
    
private com.sun.mail.imap.protocol.ListInfo[]doList(java.lang.String cmd, java.lang.String ref, java.lang.String pat)

	// encode the mbox as per RFC2060
	ref = BASE64MailboxEncoder.encode(ref);
	pat = BASE64MailboxEncoder.encode(pat);

	Argument args = new Argument();	
	args.writeString(ref);
	args.writeString(pat);

	Response[] r = command(cmd, args);

	ListInfo[] linfo = null;
	Response response = r[r.length-1];

	if (response.isOK()) { // command succesful 
	    Vector v = new Vector(1);
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		if (ir.keyEquals(cmd)) {
		    v.addElement(new ListInfo(ir));
		    r[i] = null;
		}
	    }
	    if (v.size() > 0) {
		linfo = new ListInfo[v.size()];
		v.copyInto(linfo);
	    }
	}
	
	// Dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
	return linfo;
    
public com.sun.mail.imap.protocol.MailboxInfoexamine(java.lang.String mbox)
EXAMINE Command.

see
"RFC2060, section 6.3.2"

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);

	Response[] r = command("EXAMINE", args);

	// Note that MailboxInfo also removes those responses
	// it knows about
	MailboxInfo minfo = new MailboxInfo(r);
	minfo.mode = Folder.READ_ONLY; // Obviously

	// dispatch any remaining untagged responses
	notifyResponseHandlers(r);

	handleResult(r[r.length-1]);
	return minfo;
    
public voidexpunge()
EXPUNGE Command.

see
"RFC2060, section 6.4.3"

	simpleCommand("EXPUNGE", null);
    
public com.sun.mail.iap.Response[]fetch(com.sun.mail.imap.protocol.MessageSet[] msgsets, java.lang.String what)

	return fetch(MessageSet.toString(msgsets), what, false);
    
public com.sun.mail.iap.Response[]fetch(int start, int end, java.lang.String what)

	return fetch(String.valueOf(start) + ":" + String.valueOf(end), 
		     what, false);
    
public com.sun.mail.iap.Response[]fetch(int msg, java.lang.String what)

	return fetch(String.valueOf(msg), what, false);
    
private com.sun.mail.iap.Response[]fetch(java.lang.String msgSequence, java.lang.String what, boolean uid)

	if (uid)
	    return command("UID FETCH " + msgSequence +" (" + what + ")",null);
	else
	    return command("FETCH " + msgSequence + " (" + what + ")", null);
    
public com.sun.mail.imap.protocol.BODYfetchBody(int msgno, java.lang.String section)
Fetch given BODY section.

	return fetchBody(msgno, section, false);
    
protected com.sun.mail.imap.protocol.BODYfetchBody(int msgno, java.lang.String section, boolean peek)

	Response[] r;

	if (peek)
	    r = fetch(msgno,
		     "BODY.PEEK[" + (section == null ? "]" : section + "]"));
	else
	    r = fetch(msgno,
		     "BODY[" + (section == null ? "]" : section + "]"));

	notifyResponseHandlers(r);

	Response response = r[r.length-1];
	if (response.isOK())
	    return (BODY)FetchResponse.getItem(r, msgno, BODY.class);
	else if (response.isNO())
	    return null;
	else {
	    handleResult(response);
	    return null;
	}
    
public com.sun.mail.imap.protocol.BODYfetchBody(int msgno, java.lang.String section, int start, int size)
Partial FETCH of given BODY section.

	return fetchBody(msgno, section, start, size, false, null);
    
public com.sun.mail.imap.protocol.BODYfetchBody(int msgno, java.lang.String section, int start, int size, com.sun.mail.iap.ByteArray ba)
Partial FETCH of given BODY section.

	return fetchBody(msgno, section, start, size, false, ba);
    
protected com.sun.mail.imap.protocol.BODYfetchBody(int msgno, java.lang.String section, int start, int size, boolean peek, com.sun.mail.iap.ByteArray ba)

	this.ba = ba;	// save for later use by getResponseBuffer
	Response[] r = fetch(
			msgno, (peek ? "BODY.PEEK[" : "BODY[" ) +
			(section == null ? "]<" : (section +"]<")) +
			String.valueOf(start) + "." +
			String.valueOf(size) + ">"
		       );

	notifyResponseHandlers(r);

	Response response = r[r.length-1];
	if (response.isOK())
	    return (BODY)FetchResponse.getItem(r, msgno, BODY.class);
	else if (response.isNO())
	    return null;
	else {
	    handleResult(response);
	    return null;
	}
    
public com.sun.mail.imap.protocol.BODYSTRUCTUREfetchBodyStructure(int msgno)
Fetch the BODYSTRUCTURE of the specified message.

	Response[] r = fetch(msgno, "BODYSTRUCTURE");
	notifyResponseHandlers(r);

	Response response = r[r.length-1];
	if (response.isOK())
	    return (BODYSTRUCTURE)FetchResponse.getItem(r, msgno, 
					BODYSTRUCTURE.class);
	else if (response.isNO())
	    return null;
	else {
	    handleResult(response);
	    return null;
	}
    
public javax.mail.FlagsfetchFlags(int msgno)
Fetch the FLAGS for the given message.

	Flags flags = null;
	Response[] r = fetch(msgno, "FLAGS");

	// Search for our FLAGS response
	for (int i = 0, len = r.length; i < len; i++) {
	    if (r[i] == null ||
		!(r[i] instanceof FetchResponse) ||
		((FetchResponse)r[i]).getNumber() != msgno)
		continue;		
	    
	    FetchResponse fr = (FetchResponse)r[i];
	    if ((flags = (Flags)fr.getItem(Flags.class)) != null) {
		r[i] = null; // remove this response
		break;
	    }
	}

	// dispatch untagged responses
	notifyResponseHandlers(r);
	handleResult(r[r.length-1]);
	return flags;
    
public com.sun.mail.imap.protocol.RFC822DATAfetchRFC822(int msgno, java.lang.String what)
Fetch the specified RFC822 Data item. 'what' names the item to be fetched. 'what' can be null to fetch the whole message.

	Response[] r = fetch(msgno,
			     what == null ? "RFC822" : "RFC822." + what
			    );

	// dispatch untagged responses
	notifyResponseHandlers(r);

	Response response = r[r.length-1]; 
	if (response.isOK())
	    return (RFC822DATA)FetchResponse.getItem(r, msgno, 
					RFC822DATA.class);
	else if (response.isNO())
	    return null;
	else {
	    handleResult(response);
	    return null;
	}
    
public com.sun.mail.imap.protocol.UIDfetchSequenceNumber(long uid)
Get the sequence number for the given UID. A UID object containing the sequence number is returned. If the given UID is invalid, null is returned.

	UID u = null;
	Response[] r = fetch(String.valueOf(uid), "UID", true);	

	for (int i = 0, len = r.length; i < len; i++) {
	    if (r[i] == null || !(r[i] instanceof FetchResponse))
		continue;
	    
	    FetchResponse fr = (FetchResponse)r[i];
	    if ((u = (UID)fr.getItem(UID.class)) != null) {
		if (u.uid == uid) // this is the one we want
		    break;
		else
		    u = null;
	    }
	}
		
	notifyResponseHandlers(r);
	handleResult(r[r.length-1]);
	return u;
    
public com.sun.mail.imap.protocol.UID[]fetchSequenceNumbers(long start, long end)
Get the sequence numbers for UIDs ranging from start till end. UID objects that contain the sequence numbers are returned. If no UIDs in the given range are found, an empty array is returned.

	Response[] r = fetch(String.valueOf(start) + ":" + 
				(end == UIDFolder.LASTUID ? "*" : 
				String.valueOf(end)),
			     "UID", true);	

	UID u;
	Vector v = new Vector();
	for (int i = 0, len = r.length; i < len; i++) {
	    if (r[i] == null || !(r[i] instanceof FetchResponse))
		continue;
	    
	    FetchResponse fr = (FetchResponse)r[i];
	    if ((u = (UID)fr.getItem(UID.class)) != null)
		v.addElement(u);
	}
		
	notifyResponseHandlers(r);
	handleResult(r[r.length-1]);

	UID[] ua = new UID[v.size()];
	v.copyInto(ua);
	return ua;
    
public com.sun.mail.imap.protocol.UID[]fetchSequenceNumbers(long[] uids)
Get the sequence numbers for UIDs ranging from start till end. UID objects that contain the sequence numbers are returned. If no UIDs in the given range are found, an empty array is returned.

	StringBuffer sb = new StringBuffer();
	for (int i = 0; i < uids.length; i++) {
	    if (i > 0)
		sb.append(",");
	    sb.append(String.valueOf(uids[i]));
	}

	Response[] r = fetch(sb.toString(), "UID", true);	

	UID u;
	Vector v = new Vector();
	for (int i = 0, len = r.length; i < len; i++) {
	    if (r[i] == null || !(r[i] instanceof FetchResponse))
		continue;
	    
	    FetchResponse fr = (FetchResponse)r[i];
	    if ((u = (UID)fr.getItem(UID.class)) != null)
		v.addElement(u);
	}
		
	notifyResponseHandlers(r);
	handleResult(r[r.length-1]);

	UID[] ua = new UID[v.size()];
	v.copyInto(ua);
	return ua;
    
public com.sun.mail.imap.protocol.UIDfetchUID(int msgno)
Fetch the IMAP UID for the given message.

	Response[] r = fetch(msgno, "UID");

	// dispatch untagged responses
	notifyResponseHandlers(r);

	Response response = r[r.length-1]; 
	if (response.isOK())
	    return (UID)FetchResponse.getItem(r, msgno, UID.class);
	else if (response.isNO()) // XXX: Issue NOOP ?
	    return null;
	else {
	    handleResult(response);
	    return null; // NOTREACHED
	}
    
public com.sun.mail.imap.ACL[]getACL(java.lang.String mbox)
GETACL Command.

see
"RFC2086"

	if (!hasCapability("ACL")) 
	    throw new BadCommandException("ACL not supported");

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);

	Response[] r = command("GETACL", args);
	Response response = r[r.length-1];

	// Grab all ACL responses
	Vector v = new Vector();
	if (response.isOK()) { // command succesful 
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		if (ir.keyEquals("ACL")) {
		    // acl_data ::= "ACL" SPACE mailbox
		    //		*(SPACE identifier SPACE rights)
		    // read name of mailbox and throw away
		    ir.readAtomString();
		    String name = null;
		    while ((name = ir.readAtomString()) != null) {
			String rights = ir.readAtomString();
			if (rights == null)
			    break;
			ACL acl = new ACL(name, new Rights(rights));
			v.addElement(acl);
		    }
		    r[i] = null;
		}
	    }
	}

	// dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
	ACL[] aa = new ACL[v.size()];
	v.copyInto(aa);
	return aa;
    
private com.sun.mail.imap.AppendUIDgetAppendUID(com.sun.mail.iap.Response r)
If the response contains an APPENDUID response code, extract it and return an AppendUID object with the information.

	if (!r.isOK())
	    return null;
	byte b;
	while ((b = r.readByte()) > 0 && b != (byte)'[")
	    ;
	if (b == 0)
	    return null;
	String s;
	s = r.readAtom();
	if (!s.equalsIgnoreCase("APPENDUID"))
	    return null;

	long uidvalidity = r.readLong();
	long uid = r.readLong();
	return new AppendUID(uidvalidity, uid);
    
public java.util.MapgetCapabilities()
Return the map of capabilities returned by the server.

since
JavaMail 1.4.1

	return capabilities;
    
java.io.OutputStreamgetIMAPOutputStream()

	return getOutputStream();
    
public javax.mail.Quota[]getQuota(java.lang.String root)
GETQUOTA Command. Returns an array of Quota objects, representing the quotas for this quotaroot.

see
"RFC2087"

	if (!hasCapability("QUOTA")) 
	    throw new BadCommandException("QUOTA not supported");

	Argument args = new Argument();	
	args.writeString(root);

	Response[] r = command("GETQUOTA", args);

	Quota quota = null;
	Vector v = new Vector();
	Response response = r[r.length-1];

	// Grab all QUOTA responses
	if (response.isOK()) { // command succesful 
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		if (ir.keyEquals("QUOTA")) {
		    quota = parseQuota(ir);
		    v.addElement(quota);
		    r[i] = null;
		}
	    }
	}

	// dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
	Quota[] qa = new Quota[v.size()];
	v.copyInto(qa);
	return qa;
    
public javax.mail.Quota[]getQuotaRoot(java.lang.String mbox)
GETQUOTAROOT Command. Returns an array of Quota objects, representing the quotas for this mailbox and, indirectly, the quotaroots for this mailbox.

see
"RFC2087"

	if (!hasCapability("QUOTA")) 
	    throw new BadCommandException("GETQUOTAROOT not supported");

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);

	Response[] r = command("GETQUOTAROOT", args);

	Response response = r[r.length-1];

	Hashtable tab = new Hashtable();

	// Grab all QUOTAROOT and QUOTA responses
	if (response.isOK()) { // command succesful 
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		if (ir.keyEquals("QUOTAROOT")) {
		    // quotaroot_response
		    //		       ::= "QUOTAROOT" SP astring *(SP astring)

		    // read name of mailbox and throw away
		    ir.readAtomString();
		    // for each quotaroot add a placeholder quota
		    String root = null;
		    while ((root = ir.readAtomString()) != null)
			tab.put(root, new Quota(root));
		    r[i] = null;
		} else if (ir.keyEquals("QUOTA")) {
		    Quota quota = parseQuota(ir);
		    Quota q = (Quota)tab.get(quota.quotaRoot);
		    if (q != null && q.resources != null) {
			// XXX - should merge resources
		    }
		    tab.put(quota.quotaRoot, quota);
		    r[i] = null;
		}
	    }
	}

	// dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);

	Quota[] qa = new Quota[tab.size()];
	Enumeration e = tab.elements();
	for (int i = 0; e.hasMoreElements(); i++)
	    qa[i] = (Quota)e.nextElement();
	return qa;
    
protected com.sun.mail.iap.ByteArraygetResponseBuffer()
Return a buffer to read a response into. The buffer is provided by fetchBody and is used only once.

	ByteArray ret = ba;
	ba = null;
	return ret;
    
public booleanhasCapability(java.lang.String c)
Check whether the given capability is supported by this server. Returns true if so, otherwise returns false.

	return capabilities.containsKey(c.toUpperCase(Locale.ENGLISH));
    
public voididleAbort()
Abort an IDLE command. While one thread is blocked in readIdleResponse(), another thread will use this method to abort the IDLE command, which will cause the server to send the closing tag for the IDLE command, which readIdleResponse() and processIdleResponse() will see and terminate the IDLE state.

since
JavaMail 1.4.1


                                                     	      
         
	OutputStream os = getOutputStream();
	try {
	    os.write(DONE);
	    os.flush();
	} catch (IOException ex) {
	    // nothing to do, hope to detect it again later
	}
    
public synchronized voididleStart()
IDLE Command.

If the server supports the IDLE command extension, the IDLE command is issued and this method blocks until a response has been received. Once the first response has been received, the IDLE command is terminated and all responses are collected and handled and this method returns.

Note that while this method is blocked waiting for a response, no other threads may issue any commands to the server that would use this same connection.

see
"RFC2177"
since
JavaMail 1.4.1

	if (!hasCapability("IDLE")) 
	    throw new BadCommandException("IDLE not supported");

	Response r;
	// write the command
	try {
	    idleTag = writeCommand("IDLE", null);
	    r = readResponse();
	} catch (LiteralException lex) {
	    r = lex.getResponse();
	} catch (Exception ex) {
	    // Convert this into a BYE response
	    r = Response.byeResponse(ex);
	}

	if (!r.isContinuation())
	    handleResult(r);
    
public booleanisAuthenticated()
Returns true if the connection has been authenticated, either due to a successful login, or due to a PREAUTH greeting response.

	return authenticated;
    
public booleanisREV1()
Returns true if this is a IMAP4rev1 server

	return rev1;
    
private int[]issueSearch(java.lang.String msgSequence, javax.mail.search.SearchTerm term, java.lang.String charset)


	// Generate a search-sequence with the given charset
	Argument args = SearchSequence.generateSequence(term, 
			  charset == null ? null : 
					    MimeUtility.javaCharset(charset)
			);
	args.writeAtom(msgSequence);

	Response[] r;

	if (charset == null) // text is all US-ASCII
	    r = command("SEARCH", args);
	else
	    r = command("SEARCH CHARSET " + charset, args);

	Response response = r[r.length-1];
	int[] matches = null;

	// Grab all SEARCH responses
	if (response.isOK()) { // command succesful
	    Vector v = new Vector();
	    int num;
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		// There *will* be one SEARCH response.
		if (ir.keyEquals("SEARCH")) {
		    while ((num = ir.readNumber()) != -1)
			v.addElement(new Integer(num));
		    r[i] = null;
		}
	    }

	    // Copy the vector into 'matches'
	    int vsize = v.size();
	    matches = new int[vsize];
	    for (int i = 0; i < vsize; i++)
		matches[i] = ((Integer)v.elementAt(i)).intValue();
	}

	// dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
	return matches;
    
public com.sun.mail.imap.protocol.ListInfo[]list(java.lang.String ref, java.lang.String pattern)
LIST Command.

see
"RFC2060, section 6.3.8"

	return doList("LIST", ref, pattern);
    
public com.sun.mail.imap.Rights[]listRights(java.lang.String mbox, java.lang.String user)
LISTRIGHTS Command.

see
"RFC2086"

	if (!hasCapability("ACL")) 
	    throw new BadCommandException("ACL not supported");

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);
	args.writeString(user);

	Response[] r = command("LISTRIGHTS", args);
	Response response = r[r.length-1];

	// Grab LISTRIGHTS response
	Vector v = new Vector();
	if (response.isOK()) { // command succesful 
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		if (ir.keyEquals("LISTRIGHTS")) {
		    // listrights_data ::= "LISTRIGHTS" SPACE mailbox
		    //		SPACE identifier SPACE rights *(SPACE rights)
		    // read name of mailbox and throw away
		    ir.readAtomString();
		    // read identifier and throw away
		    ir.readAtomString();
		    String rights;
		    while ((rights = ir.readAtomString()) != null)
			v.addElement(new Rights(rights));
		    r[i] = null;
		}
	    }
	}

	// dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
	Rights[] ra = new Rights[v.size()];
	v.copyInto(ra);
	return ra;
    
public voidlogin(java.lang.String u, java.lang.String p)
LOGIN Command.

see
"RFC2060, section 6.2.2"

	Argument args = new Argument();
	args.writeString(u);
	args.writeString(p);

	Response[] r = command("LOGIN", args);

	// dispatch untagged responses
	notifyResponseHandlers(r);

	// Handle result of this command
	handleResult(r[r.length-1]);
	// If the response includes a CAPABILITY response code, process it
	setCapabilities(r[r.length-1]);
	// if we get this far without an exception, we're authenticated
	authenticated = true;
    
public voidlogout()
LOGOUT Command.

see
"RFC2060, section 6.1.3"

	try {
	    Response[] r = command("LOGOUT", null);

	    authenticated = false;
	    // dispatch any unsolicited responses.
	    //  NOTE that the BYE response is dispatched here as well
	    notifyResponseHandlers(r);
	} finally {
	    disconnect();
	}
    
public com.sun.mail.imap.protocol.ListInfo[]lsub(java.lang.String ref, java.lang.String pattern)
LSUB Command.

see
"RFC2060, section 6.3.9"

	return doList("LSUB", ref, pattern);
    
public com.sun.mail.imap.RightsmyRights(java.lang.String mbox)
MYRIGHTS Command.

see
"RFC2086"

	if (!hasCapability("ACL")) 
	    throw new BadCommandException("ACL not supported");

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);

	Response[] r = command("MYRIGHTS", args);
	Response response = r[r.length-1];

	// Grab MYRIGHTS response
	Rights rights = null;
	if (response.isOK()) { // command succesful 
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		if (ir.keyEquals("MYRIGHTS")) {
		    // myrights_data ::= "MYRIGHTS" SPACE mailbox SPACE rights
		    // read name of mailbox and throw away
		    ir.readAtomString();
		    String rs = ir.readAtomString();
		    if (rights == null)
			rights = new Rights(rs);
		    r[i] = null;
		}
	    }
	}

	// dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
	return rights;
    
public com.sun.mail.imap.protocol.Namespacesnamespace()
NAMESPACE Command.

see
"RFC2342"

	if (!hasCapability("NAMESPACE")) 
	    throw new BadCommandException("NAMESPACE not supported");

	Response[] r = command("NAMESPACE", null);

	Namespaces namespace = null;
	Response response = r[r.length-1];

	// Grab NAMESPACE response
	if (response.isOK()) { // command succesful 
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		if (ir.keyEquals("NAMESPACE")) {
		    if (namespace == null)
			namespace = new Namespaces(ir);
		    r[i] = null;
		}
	    }
	}

	// dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
	return namespace;
    
public voidnoop()
The NOOP command.

see
"RFC2060, section 6.1.2"

	if (debug)
	    out.println("IMAP DEBUG: IMAPProtocol noop");
	simpleCommand("NOOP", null);
    
protected voidparseCapabilities(com.sun.mail.iap.Response r)
Parse the capabilities from a CAPABILITY response or from a CAPABILITY response code attached to (e.g.) an OK response.

	String s;
	while ((s = r.readAtom(']")) != null) {
	    if (s.length() == 0) {
		if (r.peekByte() == (byte)']")
		    break;
		/*
		 * Probably found something here that's not an atom.
		 * Rather than loop forever or fail completely, we'll
		 * try to skip this bogus capability.  This is known
		 * to happen with:
		 *   Netscape Messaging Server 4.03 (built Apr 27 1999)
		 * that returns:
		 *   * CAPABILITY * CAPABILITY IMAP4 IMAP4rev1 ...
		 * The "*" in the middle of the capability list causes
		 * us to loop forever here.
		 */
		r.skipToken();
	    } else {
		capabilities.put(s.toUpperCase(Locale.ENGLISH), s);
		if (s.regionMatches(true, 0, "AUTH=", 0, 5)) {
		    authmechs.add(s.substring(5));
		    if (debug)
			out.println("IMAP DEBUG: AUTH: " + s.substring(5));
		}
	    }
	}
    
private javax.mail.QuotaparseQuota(com.sun.mail.iap.Response r)
Parse a QUOTA response.

	// quota_response ::= "QUOTA" SP astring SP quota_list
	String quotaRoot = r.readAtomString();	// quotaroot ::= astring
	Quota q = new Quota(quotaRoot);
	r.skipSpaces();
	// quota_list ::= "(" #quota_resource ")"
	if (r.readByte() != '(")
	    throw new ParsingException("parse error in QUOTA");

	Vector v = new Vector();
	while (r.peekByte() != ')") {
	    // quota_resource ::= atom SP number SP number
	    String name = r.readAtom();
	    if (name != null) {
		long usage = r.readLong();
		long limit = r.readLong();
		Quota.Resource res = new Quota.Resource(name, usage, limit);
		v.addElement(res);
	    }
	}
	r.readByte();
	q.resources = new Quota.Resource[v.size()];
	v.copyInto(q.resources);
	return q;
    
public com.sun.mail.imap.protocol.BODYpeekBody(int msgno, java.lang.String section)
Fetch given BODY section, without marking the message as SEEN.

	return fetchBody(msgno, section, true);
    
public com.sun.mail.imap.protocol.BODYpeekBody(int msgno, java.lang.String section, int start, int size)
Partial FETCH of given BODY section, without setting SEEN flag.

	return fetchBody(msgno, section, start, size, true, null);
    
public com.sun.mail.imap.protocol.BODYpeekBody(int msgno, java.lang.String section, int start, int size, com.sun.mail.iap.ByteArray ba)
Partial FETCH of given BODY section, without setting SEEN flag.

	return fetchBody(msgno, section, start, size, true, ba);
    
protected voidprocessGreeting(com.sun.mail.iap.Response r)
Check the greeting when first connecting; look for PREAUTH response.

	super.processGreeting(r);	// check if it's BAD
	if (r.isOK()) {			// check if it's OK
	    setCapabilities(r);
	    return;
	}
	// only other choice is PREAUTH
	IMAPResponse ir = (IMAPResponse)r;
	if (ir.keyEquals("PREAUTH")) {
	    authenticated = true;
	    setCapabilities(r);
	} else
	    throw new ConnectionException(this, r);
    
public booleanprocessIdleResponse(com.sun.mail.iap.Response r)
Process a response returned by readIdleResponse(). This method will be called with appropriate locks held so that the processing of the response is safe.

since
JavaMail 1.4.1

	Response[] responses = new Response[1];
	responses[0] = r;
	boolean done = false;		// done reading responses?
	notifyResponseHandlers(responses);

	if (r.isBYE()) // shouldn't wait for command completion response
	    done = true;

	// If this is a matching command completion response, we are done
	if (r.isTagged() && r.getTag().equals(idleTag))
	    done = true;

	if (done)
	    idleTag = null;	// no longer in IDLE

	handleResult(r);
	return !done;
    
public voidproxyauth(java.lang.String u)
PROXYAUTH Command.

see
"Netscape/iPlanet/SunONE Messaging Server extension"

	Argument args = new Argument();
	args.writeString(u);

	simpleCommand("PROXYAUTH", args);
    
public synchronized com.sun.mail.iap.ResponsereadIdleResponse()
While an IDLE command is in progress, read a response sent from the server. The response is read with no locks held so that when the read blocks waiting for the response from the server it's not holding locks that would prevent other threads from interrupting the IDLE command.

since
JavaMail 1.4.1

	if (idleTag == null)
	    return null;	// IDLE not in progress
	Response r = null;
	while (r == null) {
	    try {
		r = readResponse();
	    } catch (InterruptedIOException iioex) {
		/*
		 * If a socket timeout was set, the read will timeout
		 * before the IDLE times out.  In that case, just go
		 * back and read some more.  After all, the point of
		 * IDLE is to sit here and wait until something happens.
		 */
		if (iioex.bytesTransferred == 0)
		    r = null;	// keep trying
		else
		    // convert this into a BYE response
		    r = Response.byeResponse(iioex);
	    } catch (IOException ioex) {
		// convert this into a BYE response
		r = Response.byeResponse(ioex);
	    } catch (ProtocolException pex) {
		// convert this into a BYE response
		r = Response.byeResponse(pex);
	    }
	}
	return r;
    
public com.sun.mail.iap.ResponsereadResponse()
Read a response from the server.

	// assert Thread.holdsLock(this);
	// can't assert because it's called from constructor
	return IMAPResponse.readResponse(this);
    
public voidrename(java.lang.String o, java.lang.String n)
RENAME Command.

see
"RFC2060, section 6.3.5"

	// encode the mbox as per RFC2060
	o = BASE64MailboxEncoder.encode(o);
	n = BASE64MailboxEncoder.encode(n);

	Argument args = new Argument();	
	args.writeString(o);
	args.writeString(n);

	simpleCommand("RENAME", args);
    
public voidsasllogin(java.lang.String[] allowed, java.lang.String realm, java.lang.String authzid, java.lang.String u, java.lang.String p)
SASL-based login.

	if (saslAuthenticator == null) {
	    try {
		Class sac = Class.forName(
		    "com.sun.mail.imap.protocol.IMAPSaslAuthenticator");
		Constructor c = sac.getConstructor(new Class[] {
					IMAPProtocol.class,
					String.class,
					Properties.class,
					Boolean.TYPE,
					PrintStream.class,
					String.class
					});
		saslAuthenticator = (SaslAuthenticator)c.newInstance(
					new Object[] {
					this,
					name,
					props,
					debug ? Boolean.TRUE : Boolean.FALSE,
					out,
					host
					});
	    } catch (Exception ex) {
		if (debug)
		    out.println("IMAP DEBUG: Can't load SASL authenticator: " +
								ex);
		// probably because we're running on a system without SASL
		return;	// not authenticated, try without SASL
	    }
	}

	// were any allowed mechanisms specified?
	List v;
	if (allowed != null && allowed.length > 0) {
	    // remove anything not supported by the server
	    v = new ArrayList(allowed.length);
	    for (int i = 0; i < allowed.length; i++)
		if (authmechs.contains(allowed[i]))	// XXX - case must match
		    v.add(allowed[i]);
	} else {
	    // everything is allowed
	    v = authmechs;
	}
	String[] mechs = (String[])v.toArray(new String[v.size()]);
	if (saslAuthenticator.authenticate(mechs, realm, authzid, u, p))
	    authenticated = true;
    
public int[]search(com.sun.mail.imap.protocol.MessageSet[] msgsets, javax.mail.search.SearchTerm term)
Issue the given search criterion on the specified message sets. Returns array of matching sequence numbers. An empty array is returned if no matches are found.

param
msgsets array of MessageSets
param
term SearchTerm
return
array of matching sequence numbers.

	return search(MessageSet.toString(msgsets), term);
    
public int[]search(javax.mail.search.SearchTerm term)
Issue the given search criterion on all messages in this folder. Returns array of matching sequence numbers. An empty array is returned if no matches are found.

param
term SearchTerm
return
array of matching sequence numbers.

	return search("ALL", term);
    
private int[]search(java.lang.String msgSequence, javax.mail.search.SearchTerm term)

	// Check if the search "text" terms contain only ASCII chars
	if (SearchSequence.isAscii(term)) {
	    try {
		return issueSearch(msgSequence, term, null);
	    } catch (IOException ioex) { /* will not happen */ }
	}
	
	/* The search "text" terms do contain non-ASCII chars. We need to
	 * use SEARCH CHARSET <charset> ...
	 *	The charsets we try to use are UTF-8 and the locale's
	 * default charset. If the server supports UTF-8, great, 
	 * always use it. Else we try to use the default charset.
	 */

	// Cycle thru the list of charsets
	for (int i = 0; i < searchCharsets.length; i++) {
	    if (searchCharsets[i] == null)
		continue;

	    try {
		return issueSearch(msgSequence, term, searchCharsets[i]);
	    } catch (CommandFailedException cfx) {
		/* Server returned NO. For now, I'll just assume that 
		 * this indicates that this charset is unsupported.
		 * We can check the BADCHARSET response code once
		 * that's spec'd into the IMAP RFC ..
		 */
		searchCharsets[i] = null;
		continue;
	    } catch (IOException ioex) {
		/* Charset conversion failed. Try the next one */
		continue;
	    } catch (ProtocolException pex) {
		throw pex;
	    } catch (SearchException sex) {
		throw sex;
	    }
	}

	// No luck.
	throw new SearchException("Search failed");
    
public com.sun.mail.imap.protocol.MailboxInfoselect(java.lang.String mbox)
SELECT Command.

see
"RFC2060, section 6.3.1"

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);

	Response[] r = command("SELECT", args);

	// Note that MailboxInfo also removes those responses 
	// it knows about
	MailboxInfo minfo = new MailboxInfo(r);
	
	// dispatch any remaining untagged responses
	notifyResponseHandlers(r);

	Response response = r[r.length-1];

	if (response.isOK()) { // command succesful 
	    if (response.toString().indexOf("READ-ONLY") != -1)
		minfo.mode = Folder.READ_ONLY;
	    else
		minfo.mode = Folder.READ_WRITE;
	} 
	
	handleResult(response);
	return minfo;
    
public voidsetACL(java.lang.String mbox, char modifier, com.sun.mail.imap.ACL acl)
SETACL Command.

see
"RFC2086"

	if (!hasCapability("ACL")) 
	    throw new BadCommandException("ACL not supported");

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);
	args.writeString(acl.getName());
	String rights = acl.getRights().toString();
	if (modifier == '+" || modifier == '-")
	    rights = modifier + rights;
	args.writeString(rights);

	Response[] r = command("SETACL", args);
	Response response = r[r.length-1];

	// dispatch untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
    
protected voidsetCapabilities(com.sun.mail.iap.Response r)
If the response contains a CAPABILITY response code, extract it and save the capabilities.

	byte b;
	while ((b = r.readByte()) > 0 && b != (byte)'[")
	    ;
	if (b == 0)
	    return;
	String s;
	s = r.readAtom();
	if (!s.equalsIgnoreCase("CAPABILITY"))
	    return;
	capabilities = new HashMap(10);
	authmechs = new ArrayList(5);
	parseCapabilities(r);
    
public voidsetQuota(javax.mail.Quota quota)
SETQUOTA Command. Set the indicated quota on the corresponding quotaroot.

see
"RFC2087"

	if (!hasCapability("QUOTA")) 
	    throw new BadCommandException("QUOTA not supported");

	Argument args = new Argument();	
	args.writeString(quota.quotaRoot);
	Argument qargs = new Argument();	
	if (quota.resources != null) {
	    for (int i = 0; i < quota.resources.length; i++) {
		qargs.writeAtom(quota.resources[i].name);
		qargs.writeNumber(quota.resources[i].limit);
	    }
	}
	args.writeArgument(qargs);

	Response[] r = command("SETQUOTA", args);
	Response response = r[r.length-1];

	// XXX - It's not clear from the RFC whether the SETQUOTA command
	// will provoke untagged QUOTA responses.  If it does, perhaps
	// we should grab them here and return them?

	/*
	Quota quota = null;
	Vector v = new Vector();

	// Grab all QUOTA responses
	if (response.isOK()) { // command succesful 
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		if (ir.keyEquals("QUOTA")) {
		    quota = parseQuota(ir);
		    v.addElement(quota);
		    r[i] = null;
		}
	    }
	}
	*/

	// dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
	/*
	Quota[] qa = new Quota[v.size()];
	v.copyInto(qa);
	return qa;
	*/
    
public voidstartTLS()
STARTTLS Command.

see
"RFC3501, section 6.2.1"

	try {
	    super.startTLS("STARTTLS");
	} catch (ProtocolException pex) {
	    if (debug)
		out.println("IMAP DEBUG: STARTTLS ProtocolException: " + pex);
	    // ProtocolException just means the command wasn't recognized,
	    // or failed.  This should never happen if we check the
	    // CAPABILITY first.
	    throw pex;
	} catch (Exception ex) {
	    if (debug)
		out.println("IMAP DEBUG: STARTTLS Exception: " + ex);
	    // any other exception means we have to shut down the connection
	    // generate an artificial BYE response and disconnect
	    Response[] r = { Response.byeResponse(ex) };
	    notifyResponseHandlers(r);
	    disconnect();
	    throw new ProtocolException("STARTTLS failure", ex);
	}
    
public com.sun.mail.imap.protocol.Statusstatus(java.lang.String mbox, java.lang.String[] items)
STATUS Command.

see
"RFC2060, section 6.3.10"

	if (!isREV1() && !hasCapability("IMAP4SUNVERSION")) 
	    // STATUS is rev1 only, however the non-rev1 SIMS2.0 
	    // does support this.
	    throw new BadCommandException("STATUS not supported");

	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);

	Argument args = new Argument();	
	args.writeString(mbox);

	Argument itemArgs = new Argument();
	if (items == null)
	    items = Status.standardItems;

	for (int i = 0, len = items.length; i < len; i++)
	    itemArgs.writeAtom(items[i]);
	args.writeArgument(itemArgs);

	Response[] r = command("STATUS", args);

	Status status = null;
	Response response = r[r.length-1];

	// Grab all STATUS responses
	if (response.isOK()) { // command succesful 
	    for (int i = 0, len = r.length; i < len; i++) {
		if (!(r[i] instanceof IMAPResponse))
		    continue;

		IMAPResponse ir = (IMAPResponse)r[i];
		if (ir.keyEquals("STATUS")) {
		    if (status == null)
			status = new Status(ir);
		    else // collect 'em all
			Status.add(status, new Status(ir));
		    r[i] = null;
		}
	    }
	}

	// dispatch remaining untagged responses
	notifyResponseHandlers(r);
	handleResult(response);
	return status;
    
public voidstoreFlags(com.sun.mail.imap.protocol.MessageSet[] msgsets, javax.mail.Flags flags, boolean set)

	storeFlags(MessageSet.toString(msgsets), flags, set);
    
public voidstoreFlags(int start, int end, javax.mail.Flags flags, boolean set)

	storeFlags(String.valueOf(start) + ":" + String.valueOf(end),
		   flags, set);
    
public voidstoreFlags(int msg, javax.mail.Flags flags, boolean set)
Set the specified flags on this message.

 
	storeFlags(String.valueOf(msg), flags, set);
    
private voidstoreFlags(java.lang.String msgset, javax.mail.Flags flags, boolean set)

	Response[] r;
	if (set)
	    r = command("STORE " + msgset + " +FLAGS " + 
			 createFlagList(flags), null);
	else
	    r = command("STORE " + msgset + " -FLAGS " + 
			createFlagList(flags), null);
	
	// Dispatch untagged responses
	notifyResponseHandlers(r);
	handleResult(r[r.length-1]);
    
public voidsubscribe(java.lang.String mbox)
SUBSCRIBE Command.

see
"RFC2060, section 6.3.6"

	Argument args = new Argument();	
	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);
	args.writeString(mbox);

	simpleCommand("SUBSCRIBE", args);
    
protected booleansupportsNonSyncLiterals()
Returns whether this Protocol supports non-synchronizing literals.

	return hasCapability("LITERAL+");
    
public voiduidexpunge(com.sun.mail.imap.protocol.UIDSet[] set)
UID EXPUNGE Command.

see
"RFC2359, section 4.1"

	if (!hasCapability("UIDPLUS")) 
	    throw new BadCommandException("UID EXPUNGE not supported");
	simpleCommand("UID EXPUNGE " + UIDSet.toString(set), null);
    
public voidunsubscribe(java.lang.String mbox)
UNSUBSCRIBE Command.

see
"RFC2060, section 6.3.7"

	Argument args = new Argument();	
	// encode the mbox as per RFC2060
	mbox = BASE64MailboxEncoder.encode(mbox);
	args.writeString(mbox);

	simpleCommand("UNSUBSCRIBE", args);