📄 imapprotocol.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. *//* * @(#)IMAPProtocol.java 1.64 07/05/04 */package com.sun.mail.imap.protocol;import java.io.*;import java.util.*;import java.text.*;import java.lang.reflect.*;import javax.mail.*;import javax.mail.internet.*;import javax.mail.search.*;import com.sun.mail.util.*;import com.sun.mail.iap.*;import com.sun.mail.imap.ACL;import com.sun.mail.imap.Rights;import com.sun.mail.imap.AppendUID;/** * 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 <code>notifyResponseHandlers(r)</code>. * * @version 1.64, 07/05/04 * @author John Mani * @author Bill Shannon */public class IMAPProtocol extends Protocol { private boolean connected = false; // did constructor succeed? private boolean rev1 = false; // REV1 server ? private boolean authenticated; // authenticated? // WARNING: authenticated may be set to true in superclass // constructor, don't initialize it here. private Map capabilities = null; private List authmechs = null; private String[] searchCharsets; // array of search charsets private String name; private SaslAuthenticator saslAuthenticator; // if SASL is being used private ByteArray ba; // a buffer for fetchBody private static final byte[] CRLF = { (byte)'\r', (byte)'\n'}; /** * 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 */ public IMAPProtocol(String name, String host, int port, boolean debug, PrintStream out, Properties props, boolean isSSL) throws IOException, ProtocolException { 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(); } } /** * CAPABILITY command. * * @see "RFC2060, section 6.1.1" */ public void capability() throws ProtocolException { // 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); } } /** * If the response contains a CAPABILITY response code, extract * it and save the capabilities. */ protected void setCapabilities(Response r) { 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); } /** * Parse the capabilities from a CAPABILITY response or from * a CAPABILITY response code attached to (e.g.) an OK response. */ protected void parseCapabilities(Response r) { 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)); } } } } /** * Check the greeting when first connecting; look for PREAUTH response. */ protected void processGreeting(Response r) throws ProtocolException { 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); } /** * Returns <code>true</code> if the connection has been authenticated, * either due to a successful login, or due to a PREAUTH greeting response. */ public boolean isAuthenticated() { return authenticated; } /** * Returns <code>true</code> if this is a IMAP4rev1 server */ public boolean isREV1() { return rev1; } /** * Returns whether this Protocol supports non-synchronizing literals. */ protected boolean supportsNonSyncLiterals() { return hasCapability("LITERAL+"); } /** * Read a response from the server. */ public Response readResponse() throws IOException, ProtocolException { // assert Thread.holdsLock(this); // can't assert because it's called from constructor return IMAPResponse.readResponse(this); } /** * Check whether the given capability is supported by * this server. Returns <code>true</code> if so, otherwise * returns false. */ public boolean hasCapability(String c) { return capabilities.containsKey(c.toUpperCase(Locale.ENGLISH)); } /** * Return the map of capabilities returned by the server. * * @since JavaMail 1.4.1 */ public Map getCapabilities() { return capabilities; } /** * Close socket connection. * * This method just makes the Protocol.disconnect() method * public. */ public void disconnect() { super.disconnect(); authenticated = false; // just in case } /** * The NOOP command. * * @see "RFC2060, section 6.1.2" */ public void noop() throws ProtocolException { if (debug) out.println("IMAP DEBUG: IMAPProtocol noop"); simpleCommand("NOOP", null); } /** * LOGOUT Command. * * @see "RFC2060, section 6.1.3" */ public void logout() throws ProtocolException { // XXX - what happens if exception is thrown? Response[] r = command("LOGOUT", null); authenticated = false; // dispatch any unsolicited responses. // NOTE that the BYE response is dispatched here as well notifyResponseHandlers(r); disconnect(); } /** * LOGIN Command. * * @see "RFC2060, section 6.2.2" */ public void login(String u, String p) throws ProtocolException { 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; } /** * The AUTHENTICATE command with AUTH=LOGIN authenticate scheme * * @see "RFC2060, section 6.2.1" */ public synchronized void authlogin(String u, String p) throws ProtocolException { 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; } /** * 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 */ public synchronized void authplain(String authzid, String u, String p) throws ProtocolException { 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -