Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
JavaMail 1.6.2 Source Code Files
JavaMail Source Code Files are provided in the source package file, httpcomponents-client-5.2-src.zip.
You can browse JavaMail Source Code files below:
✍: FYIcenter.com
⏎ com/sun/mail/imap/protocol/IMAPProtocol.java
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2018 Oracle and/or its affiliates. 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://oss.oracle.com/licenses/CDDL+GPL-1.1 * or 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 LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [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. */ package com.sun.mail.imap.protocol; import java.io.*; import java.util.*; import java.text.*; import java.lang.reflect.*; import java.util.logging.Level; import java.nio.charset.StandardCharsets; import javax.mail.*; import javax.mail.internet.*; import javax.mail.search.*; import com.sun.mail.util.PropUtil; import com.sun.mail.util.MailLogger; import com.sun.mail.util.ASCIIUtility; import com.sun.mail.util.BASE64EncoderStream; import com.sun.mail.iap.*; import com.sun.mail.auth.Ntlm; import com.sun.mail.imap.ACL; import com.sun.mail.imap.Rights; import com.sun.mail.imap.AppendUID; import com.sun.mail.imap.CopyUID; import com.sun.mail.imap.SortTerm; import com.sun.mail.imap.ResyncData; import com.sun.mail.imap.Utility; /** * 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>. * * @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 referralException; // throw exception for IMAP REFERRAL? private boolean noauthdebug = true; // hide auth info in debug output private boolean authenticated; // authenticated? // WARNING: authenticated may be set to true in superclass // constructor, don't initialize it here. private Map<String, String> capabilities; // WARNING: capabilities may be initialized as a result of superclass // constructor, don't initialize it here. private List<String> authmechs; // WARNING: authmechs may be initialized as a result of superclass // constructor, don't initialize it here. private boolean utf8; // UTF-8 support enabled? protected SearchSequence searchSequence; protected String[] searchCharsets; // array of search charsets protected Set<String> enabled; // enabled capabilities - RFC 5161 private String name; private SaslAuthenticator saslAuthenticator; // if SASL is being used private String proxyAuthUser; // user name used with PROXYAUTH private ByteArray ba; // a buffer for fetchBody private static final byte[] CRLF = { (byte)'\r', (byte)'\n'}; private static final FetchItem[] fetchItems = { }; /** * Constructor. * Opens a connection to the given host at given port. * * @param name the protocol name * @param host host to connect to * @param port port number to connect to * @param props Properties object used by this protocol * @param isSSL true if SSL should be used * @param logger the MailLogger to use for debug output * @exception IOException for I/O errors * @exception ProtocolException for protocol failures */ public IMAPProtocol(String name, String host, int port, Properties props, boolean isSSL, MailLogger logger) throws IOException, ProtocolException { super(host, port, props, "mail." + name, isSSL, logger); try { this.name = name; noauthdebug = !PropUtil.getBooleanProperty(props, "mail.debug.auth", false); // in case it was not initialized in processGreeting referralException = PropUtil.getBooleanProperty(props, prefix + ".referralexception", false); 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(); } } /** * Constructor for debugging. * * @param in the InputStream from which to read * @param out the PrintStream to which to write * @param props Properties object used by this protocol * @param debug true to enable debugging output * @exception IOException for I/O errors */ public IMAPProtocol(InputStream in, PrintStream out, Properties props, boolean debug) throws IOException { super(in, out, props, debug); this.name = "imap"; noauthdebug = !PropUtil.getBooleanProperty(props, "mail.debug.auth", false); if (capabilities == null) capabilities = new HashMap<>(); 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 } /** * Return an array of FetchItem objects describing the * FETCH items supported by this protocol. Subclasses may * override this method to combine their FetchItems with * the FetchItems returned by the superclass. * * @return an array of FetchItem objects * @since JavaMail 1.4.6 */ public FetchItem[] getFetchItems() { return fetchItems; } /** * CAPABILITY command. * * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.1.1" */ public void capability() throws ProtocolException { // Check CAPABILITY Response[] r = command("CAPABILITY", null); Response response = r[r.length-1]; if (response.isOK()) handleCapabilityResponse(r); handleResult(response); } /** * Handle any untagged CAPABILITY response in the Response array. * * @param r the responses */ public void handleCapabilityResponse(Response[] r) { boolean first = true; 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")) { if (first) { // clear out current when first response seen capabilities = new HashMap<>(10); authmechs = new ArrayList<>(5); first = false; } parseCapabilities(ir); } } } /** * If the response contains a CAPABILITY response code, extract * it and save the capabilities. * * @param r the response */ 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. * * @param r the CAPABILITY 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 (logger.isLoggable(Level.FINE)) logger.fine("AUTH: " + s.substring(5)); } } } } /** * Check the greeting when first connecting; look for PREAUTH response. * * @param r the greeting response * @exception ProtocolException for protocol failures */ @Override protected void processGreeting(Response r) throws ProtocolException { if (r.isBYE()) { checkReferral(r); // may throw exception throw new ConnectionException(this, r); } if (r.isOK()) { // check if it's OK // XXX - is a REFERRAL response code really allowed here? // XXX - referralException hasn't been initialized in c'tor yet referralException = PropUtil.getBooleanProperty(props, prefix + ".referralexception", false); if (referralException) checkReferral(r); setCapabilities(r); return; } // only other choice is PREAUTH assert r instanceof IMAPResponse; IMAPResponse ir = (IMAPResponse)r; if (ir.keyEquals("PREAUTH")) { authenticated = true; setCapabilities(r); } else { disconnect(); throw new ConnectionException(this, r); } } /** * Check for an IMAP login REFERRAL response code. * * @exception IMAPReferralException if REFERRAL response code found * @see "RFC 2221" */ private void checkReferral(Response r) throws IMAPReferralException { String s = r.getRest(); // get the text after the response if (s.startsWith("[")) { // a response code int i = s.indexOf(' '); if (i > 0 && s.substring(1, i).equalsIgnoreCase("REFERRAL")) { String url, msg; int j = s.indexOf(']'); if (j > 0) { // should always be true; url = s.substring(i + 1, j); msg = s.substring(j + 1).trim(); } else { url = s.substring(i + 1); msg = ""; } if (r.isBYE()) disconnect(); throw new IMAPReferralException(msg, url); } } } /** * Returns <code>true</code> if the connection has been authenticated, * either due to a successful login, or due to a PREAUTH greeting response. * * @return true if the connection has been authenticated */ public boolean isAuthenticated() { return authenticated; } /** * Returns <code>true</code> if this is an IMAP4rev1 server * * @return true if this is an IMAP4rev1 server */ public boolean isREV1() { return rev1; } /** * Returns whether this Protocol supports non-synchronizing literals. * * @return true if non-synchronizing literals are supported */ @Override protected boolean supportsNonSyncLiterals() { return hasCapability("LITERAL+"); } /** * Read a response from the server. * * @return the response * @exception IOException for I/O errors * @exception ProtocolException for protocol failures */ @Override public Response readResponse() throws IOException, ProtocolException { // assert Thread.holdsLock(this); // can't assert because it's called from constructor IMAPResponse r = new IMAPResponse(this); if (r.keyEquals("FETCH")) r = new FetchResponse(r, getFetchItems()); return r; } /** * Check whether the given capability is supported by * this server. Returns <code>true</code> if so, otherwise * returns false. * * @param c the capability name * @return true if the server has the capability */ public boolean hasCapability(String c) { if (c.endsWith("*")) { c = c.substring(0, c.length() - 1).toUpperCase(Locale.ENGLISH); Iterator<String> it = capabilities.keySet().iterator(); while (it.hasNext()) { if (it.next().startsWith(c)) return true; } return false; } return capabilities.containsKey(c.toUpperCase(Locale.ENGLISH)); } /** * Return the map of capabilities returned by the server. * * @return the Map of capabilities * @since JavaMail 1.4.1 */ public Map<String, String> getCapabilities() { return capabilities; } /** * Does the server support UTF-8? * * @since JavaMail 1.6.0 */ public boolean supportsUtf8() { return utf8; } /** * Close socket connection. * * This method just makes the Protocol.disconnect() method * public. */ @Override public void disconnect() { super.disconnect(); authenticated = false; // just in case } /** * The NOOP command. * * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.1.2" */ public void noop() throws ProtocolException { logger.fine("IMAPProtocol noop"); simpleCommand("NOOP", null); } /** * LOGOUT Command. * * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.1.3" */ public void logout() throws ProtocolException { 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(); } } /** * LOGIN Command. * * @param u the username * @param p the password * @throws ProtocolException as thrown by {@link Protocol#handleResult}. * @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 = null; try { if (noauthdebug && isTracing()) { logger.fine("LOGIN command trace suppressed"); suspendTracing(); } r = command("LOGIN", args); } finally { resumeTracing(); } // handle an illegal but not uncommon untagged CAPABILTY response handleCapabilityResponse(r); // dispatch untagged responses notifyResponseHandlers(r); // Handle result of this command if (noauthdebug && isTracing()) logger.fine("LOGIN command result: " + r[r.length-1]); handleLoginResult(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 * * @param u the username * @param p the password * @throws ProtocolException as thrown by {@link Protocol#handleResult}. * @see "RFC2060, section 6.2.1" */ public synchronized void authlogin(String u, String p) throws ProtocolException { List<Response> v = new ArrayList<>(); String tag = null; Response r = null; boolean done = false; try { if (noauthdebug && isTracing()) { logger.fine("AUTHENTICATE LOGIN command trace suppressed"); suspendTracing(); } 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(s.getBytes(StandardCharsets.UTF_8)); 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; // hmm .. unsolicited response here ?! } catch (Exception ioex) { // convert this into a BYE response r = Response.byeResponse(ioex); done = true; } v.add(r); } } finally { resumeTracing(); } Response[] responses = v.toArray(new Response[v.size()]); // handle an illegal but not uncommon untagged CAPABILTY response handleCapabilityResponse(responses); /* * 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. */ notifyResponseHandlers(responses); // Handle the final OK, NO, BAD or BYE response if (noauthdebug && isTracing()) logger.fine("AUTHENTICATE LOGIN command result: " + r); handleLoginResult(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 { List<Response> v = new ArrayList<>(); String tag = null; Response r = null; boolean done = false; try { if (noauthdebug && isTracing()) { logger.fine("AUTHENTICATE PLAIN command trace suppressed"); suspendTracing(); } 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 == null ? "" : authzid) + nullByte + u + nullByte + p; // obtain b64 encoded bytes b64os.write(s.getBytes(StandardCharsets.UTF_8)); 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; // hmm .. unsolicited response here ?! } catch (Exception ioex) { // convert this into a BYE response r = Response.byeResponse(ioex); done = true; } v.add(r); } } finally { resumeTracing(); } Response[] responses = v.toArray(new Response[v.size()]); // handle an illegal but not uncommon untagged CAPABILTY response handleCapabilityResponse(responses); /* * 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. */ notifyResponseHandlers(responses); // Handle the final OK, NO, BAD or BYE response if (noauthdebug && isTracing()) logger.fine("AUTHENTICATE PLAIN command result: " + r); handleLoginResult(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=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 */ public synchronized void authntlm(String authzid, String u, String p) throws ProtocolException { List<Response> v = new ArrayList<>(); String tag = null; Response r = null; boolean done = false; String type1Msg = null; int flags = PropUtil.getIntProperty(props, "mail." + name + ".auth.ntlm.flags", 0); String domain = props.getProperty( "mail." + name + ".auth.ntlm.domain", ""); Ntlm ntlm = new Ntlm(domain, getLocalHost(), u, p, logger); try { if (noauthdebug && isTracing()) { logger.fine("AUTHENTICATE NTLM command trace suppressed"); suspendTracing(); } 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 = ntlm.generateType1Msg(flags); first = false; } else { s = ntlm.generateType3Msg(r.getRest()); } os.write(s.getBytes(StandardCharsets.UTF_8)); 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; // hmm .. unsolicited response here ?! } catch (Exception ioex) { // convert this into a BYE response r = Response.byeResponse(ioex); done = true; } v.add(r); } } finally { resumeTracing(); } Response[] responses = v.toArray(new Response[v.size()]); // handle an illegal but not uncommon untagged CAPABILTY response handleCapabilityResponse(responses); /* * 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. */ notifyResponseHandlers(responses); // Handle the final OK, NO, BAD or BYE response if (noauthdebug && isTracing()) logger.fine("AUTHENTICATE NTLM command result: " + r); handleLoginResult(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=XOAUTH2 authentication scheme. * This is based heavly on the {@link #authlogin} method. * * @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.5.5 */ public synchronized void authoauth2(String u, String p) throws ProtocolException { List<Response> v = new ArrayList<>(); String tag = null; Response r = null; boolean done = false; try { if (noauthdebug && isTracing()) { logger.fine("AUTHENTICATE XOAUTH2 command trace suppressed"); suspendTracing(); } try { Argument args = new Argument(); args.writeAtom("XOAUTH2"); if (hasCapability("SASL-IR")) { String resp = "user=" + u + "\001auth=Bearer " + p + "\001\001"; byte[] ba = BASE64EncoderStream.encode( resp.getBytes(StandardCharsets.UTF_8)); String irs = ASCIIUtility.toString(ba, 0, ba.length); args.writeAtom(irs); } tag = writeCommand("AUTHENTICATE", args); } catch (Exception ex) { // Convert this into a BYE response r = Response.byeResponse(ex); done = true; } OutputStream os = getOutputStream(); // stream to IMAP server while (!done) { // loop till we are done try { r = readResponse(); if (r.isContinuation()) { // Server challenge .. String resp = "user=" + u + "\001auth=Bearer " + p + "\001\001"; byte[] b = BASE64EncoderStream.encode( resp.getBytes(StandardCharsets.UTF_8)); os.write(b); // write out response 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; // hmm .. unsolicited response here ?! } catch (Exception ioex) { // convert this into a BYE response r = Response.byeResponse(ioex); done = true; } v.add(r); } } finally { resumeTracing(); } Response[] responses = v.toArray(new Response[v.size()]); // handle an illegal but not uncommon untagged CAPABILTY response handleCapabilityResponse(responses); /* * 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. */ notifyResponseHandlers(responses); // Handle the final OK, NO, BAD or BYE response if (noauthdebug && isTracing()) logger.fine("AUTHENTICATE XOAUTH2 command result: " + r); handleLoginResult(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; } /** * SASL-based login. * * @param allowed the SASL mechanisms we're allowed to use * @param realm the SASL realm * @param authzid the authorization id * @param u the username * @param p the password * @exception ProtocolException for protocol failures */ public void sasllogin(String[] allowed, String realm, String authzid, String u, String p) throws ProtocolException { boolean useCanonicalHostName = PropUtil.getBooleanProperty(props, "mail." + name + ".sasl.usecanonicalhostname", false); String serviceHost; if (useCanonicalHostName) serviceHost = getInetAddress().getCanonicalHostName(); else serviceHost = host; 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, MailLogger.class, String.class }); saslAuthenticator = (SaslAuthenticator)c.newInstance( new Object[] { this, name, props, logger, serviceHost }); } catch (Exception ex) { logger.log(Level.FINE, "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<String> 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 = v.toArray(new String[v.size()]); try { if (noauthdebug && isTracing()) { logger.fine("SASL authentication command trace suppressed"); suspendTracing(); } if (saslAuthenticator.authenticate(mechs, realm, authzid, u, p)) { if (noauthdebug && isTracing()) logger.fine("SASL authentication succeeded"); authenticated = true; } else { if (noauthdebug && isTracing()) logger.fine("SASL authentication failed"); } } finally { resumeTracing(); } } // XXX - for IMAPSaslAuthenticator access to protected method OutputStream getIMAPOutputStream() { return getOutputStream(); } /** * Handle the result response for a LOGIN or AUTHENTICATE command. * Look for IMAP login REFERRAL. * * @param r the response * @exception ProtocolException for protocol failures * @since JavaMail 1.5.5 */ protected void handleLoginResult(Response r) throws ProtocolException { if (hasCapability("LOGIN-REFERRALS") && (!r.isOK() || referralException)) checkReferral(r); handleResult(r); } /** * PROXYAUTH Command. * * @param u the PROXYAUTH user name * @exception ProtocolException for protocol failures * @see "Netscape/iPlanet/SunONE Messaging Server extension" */ public void proxyauth(String u) throws ProtocolException { Argument args = new Argument(); args.writeString(u); simpleCommand("PROXYAUTH", args); proxyAuthUser = u; } /** * Get the user name used with the PROXYAUTH command. * Returns null if PROXYAUTH was not used. * * @return the PROXYAUTH user name * @since JavaMail 1.5.1 */ public String getProxyAuthUser() { return proxyAuthUser; } /** * UNAUTHENTICATE Command. * * @exception ProtocolException for protocol failures * @see "Netscape/iPlanet/SunONE Messaging Server extension" * @since JavaMail 1.5.1 */ public void unauthenticate() throws ProtocolException { if (!hasCapability("X-UNAUTHENTICATE")) throw new BadCommandException("UNAUTHENTICATE not supported"); simpleCommand("UNAUTHENTICATE", null); authenticated = false; } /** * ID Command, for Yahoo! Mail IMAP server. * * @param guid the GUID * @exception ProtocolException for protocol failures * @deprecated As of JavaMail 1.5.1, replaced by * {@link #id(Map) id(Map<String,String>)} * @since JavaMail 1.4.4 */ @Deprecated public void id(String guid) throws ProtocolException { // support this for now, but remove it soon Map<String,String> gmap = new HashMap<>(); gmap.put("GUID", guid); id(gmap); } /** * STARTTLS Command. * * @exception ProtocolException for protocol failures * @see "RFC3501, section 6.2.1" */ public void startTLS() throws ProtocolException { try { super.startTLS("STARTTLS"); } catch (ProtocolException pex) { logger.log(Level.FINE, "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) { logger.log(Level.FINE, "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); } } /** * COMPRESS Command. Only supports DEFLATE. * * @exception ProtocolException for protocol failures * @see "RFC 4978" */ public void compress() throws ProtocolException { try { super.startCompression("COMPRESS DEFLATE"); } catch (ProtocolException pex) { logger.log(Level.FINE, "COMPRESS 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) { logger.log(Level.FINE, "COMPRESS 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("COMPRESS failure", ex); } } /** * Encode a mailbox name appropriately depending on whether or not * the server supports UTF-8, and add the encoded name to the * Argument. * * @param args the arguments * @param name the name to encode * @since JavaMail 1.6.0 */ protected void writeMailboxName(Argument args, String name) { if (utf8) args.writeString(name, StandardCharsets.UTF_8); else // encode the mbox as per RFC2060 args.writeString(BASE64MailboxEncoder.encode(name)); } /** * SELECT Command. * * @param mbox the mailbox name * @return MailboxInfo if successful * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.1" */ public MailboxInfo select(String mbox) throws ProtocolException { return select(mbox, null); } /** * SELECT Command with QRESYNC data. * * @param mbox the mailbox name * @param rd the ResyncData * @return MailboxInfo if successful * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.1" * @see "RFC5162, section 3.1" * @since JavaMail 1.5.1 */ public MailboxInfo select(String mbox, ResyncData rd) throws ProtocolException { Argument args = new Argument(); writeMailboxName(args, mbox); if (rd != null) { if (rd == ResyncData.CONDSTORE) { if (!hasCapability("CONDSTORE")) throw new BadCommandException("CONDSTORE not supported"); args.writeArgument(new Argument().writeAtom("CONDSTORE")); } else { if (!hasCapability("QRESYNC")) throw new BadCommandException("QRESYNC not supported"); args.writeArgument(resyncArgs(rd)); } } 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; } /** * EXAMINE Command. * * @param mbox the mailbox name * @return MailboxInfo if successful * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.2" */ public MailboxInfo examine(String mbox) throws ProtocolException { return examine(mbox, null); } /** * EXAMINE Command with QRESYNC data. * * @param mbox the mailbox name * @param rd the ResyncData * @return MailboxInfo if successful * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.2" * @see "RFC5162, section 3.1" * @since JavaMail 1.5.1 */ public MailboxInfo examine(String mbox, ResyncData rd) throws ProtocolException { Argument args = new Argument(); writeMailboxName(args, mbox); if (rd != null) { if (rd == ResyncData.CONDSTORE) { if (!hasCapability("CONDSTORE")) throw new BadCommandException("CONDSTORE not supported"); args.writeArgument(new Argument().writeAtom("CONDSTORE")); } else { if (!hasCapability("QRESYNC")) throw new BadCommandException("QRESYNC not supported"); args.writeArgument(resyncArgs(rd)); } } 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; } /** * Generate a QRESYNC argument list based on the ResyncData. */ private static Argument resyncArgs(ResyncData rd) { Argument cmd = new Argument(); cmd.writeAtom("QRESYNC"); Argument args = new Argument(); args.writeNumber(rd.getUIDValidity()); args.writeNumber(rd.getModSeq()); UIDSet[] uids = Utility.getResyncUIDSet(rd); if (uids != null) args.writeString(UIDSet.toString(uids)); cmd.writeArgument(args); return cmd; } /** * ENABLE Command. * * @param cap the name of the capability to enable * @exception ProtocolException for protocol failures * @see "RFC 5161" * @since JavaMail 1.5.1 */ public void enable(String cap) throws ProtocolException { if (!hasCapability("ENABLE")) throw new BadCommandException("ENABLE not supported"); Argument args = new Argument(); args.writeAtom(cap); simpleCommand("ENABLE", args); if (enabled == null) enabled = new HashSet<>(); enabled.add(cap.toUpperCase(Locale.ENGLISH)); // update the utf8 flag utf8 = isEnabled("UTF8=ACCEPT"); } /** * Is the capability/extension enabled? * * @param cap the capability name * @return true if enabled * @see "RFC 5161" * @since JavaMail 1.5.1 */ public boolean isEnabled(String cap) { if (enabled == null) return false; else return enabled.contains(cap.toUpperCase(Locale.ENGLISH)); } /** * UNSELECT Command. * * @exception ProtocolException for protocol failures * @see "RFC 3691" * @since JavaMail 1.4.4 */ public void unselect() throws ProtocolException { if (!hasCapability("UNSELECT")) throw new BadCommandException("UNSELECT not supported"); simpleCommand("UNSELECT", null); } /** * STATUS Command. * * @param mbox the mailbox * @param items the STATUS items to request * @return STATUS results * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.10" */ public Status status(String mbox, String[] items) throws ProtocolException { if (!isREV1() && !hasCapability("IMAP4SUNVERSION")) // STATUS is rev1 only, however the non-rev1 SIMS2.0 // does support this. throw new BadCommandException("STATUS not supported"); Argument args = new Argument(); writeMailboxName(args, 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; } /** * CREATE Command. * * @param mbox the mailbox to create * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.3" */ public void create(String mbox) throws ProtocolException { Argument args = new Argument(); writeMailboxName(args, mbox); simpleCommand("CREATE", args); } /** * DELETE Command. * * @param mbox the mailbox to delete * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.4" */ public void delete(String mbox) throws ProtocolException { Argument args = new Argument(); writeMailboxName(args, mbox); simpleCommand("DELETE", args); } /** * RENAME Command. * * @param o old mailbox name * @param n new mailbox name * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.5" */ public void rename(String o, String n) throws ProtocolException { Argument args = new Argument(); writeMailboxName(args, o); writeMailboxName(args, n); simpleCommand("RENAME", args); } /** * SUBSCRIBE Command. * * @param mbox the mailbox * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.6" */ public void subscribe(String mbox) throws ProtocolException { Argument args = new Argument(); writeMailboxName(args, mbox); simpleCommand("SUBSCRIBE", args); } /** * UNSUBSCRIBE Command. * * @param mbox the mailbox * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.7" */ public void unsubscribe(String mbox) throws ProtocolException { Argument args = new Argument(); writeMailboxName(args, mbox); simpleCommand("UNSUBSCRIBE", args); } /** * LIST Command. * * @param ref reference string * @param pattern pattern to list * @return LIST results * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.8" */ public ListInfo[] list(String ref, String pattern) throws ProtocolException { return doList("LIST", ref, pattern); } /** * LSUB Command. * * @param ref reference string * @param pattern pattern to list * @return LSUB results * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.9" */ public ListInfo[] lsub(String ref, String pattern) throws ProtocolException { return doList("LSUB", ref, pattern); } /** * Execute the specified LIST-like command (e.g., "LIST" or "LSUB"), * using the reference and pattern. * * @param cmd the list command * @param ref the reference string * @param pat the pattern * @return array of ListInfo results * @exception ProtocolException for protocol failures * @since JavaMail 1.4.6 */ protected ListInfo[] doList(String cmd, String ref, String pat) throws ProtocolException { Argument args = new Argument(); writeMailboxName(args, ref); writeMailboxName(args, pat); Response[] r = command(cmd, args); ListInfo[] linfo = null; Response response = r[r.length-1]; if (response.isOK()) { // command succesful List<ListInfo> v = new ArrayList<>(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.add(new ListInfo(ir)); r[i] = null; } } if (v.size() > 0) { linfo = v.toArray(new ListInfo[v.size()]); } } // Dispatch remaining untagged responses notifyResponseHandlers(r); handleResult(response); return linfo; } /** * APPEND Command. * * @param mbox the mailbox * @param f the message Flags * @param d the message date * @param data the message data * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.11" */ public void append(String mbox, Flags f, Date d, Literal data) throws ProtocolException { appenduid(mbox, f, d, data, false); // ignore return value } /** * APPEND Command, return uid from APPENDUID response code. * * @param mbox the mailbox * @param f the message Flags * @param d the message date * @param data the message data * @return APPENDUID data * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.3.11" */ public AppendUID appenduid(String mbox, Flags f, Date d, Literal data) throws ProtocolException { return appenduid(mbox, f, d, data, true); } public AppendUID appenduid(String mbox, Flags f, Date d, Literal data, boolean uid) throws ProtocolException { Argument args = new Argument(); writeMailboxName(args, 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; } /** * If the response contains an APPENDUID response code, extract * it and return an AppendUID object with the information. */ private AppendUID getAppendUID(Response r) { 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); } /** * CHECK Command. * * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.4.1" */ public void check() throws ProtocolException { simpleCommand("CHECK", null); } /** * CLOSE Command. * * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.4.2" */ public void close() throws ProtocolException { simpleCommand("CLOSE", null); } /** * EXPUNGE Command. * * @exception ProtocolException for protocol failures * @see "RFC2060, section 6.4.3" */ public void expunge() throws ProtocolException { simpleCommand("EXPUNGE", null); } /** * UID EXPUNGE Command. * * @param set UIDs to expunge * @exception ProtocolException for protocol failures * @see "RFC4315, section 2" */ public void uidexpunge(UIDSet[] set) throws ProtocolException { if (!hasCapability("UIDPLUS")) throw new BadCommandException("UID EXPUNGE not supported"); simpleCommand("UID EXPUNGE " + UIDSet.toString(set), null); } /** * Fetch the BODYSTRUCTURE of the specified message. * * @param msgno the message number * @return the BODYSTRUCTURE item * @exception ProtocolException for protocol failures */ public BODYSTRUCTURE fetchBodyStructure(int msgno) throws ProtocolException { Response[] r = fetch(msgno, "BODYSTRUCTURE"); notifyResponseHandlers(r); Response response = r[r.length-1]; if (response.isOK()) return FetchResponse.getItem(r, msgno, BODYSTRUCTURE.class); else if (response.isNO()) return null; else { handleResult(response); return null; } } /** * Fetch given BODY section, without marking the message * as SEEN. * * @param msgno the message number * @param section the body section * @return the BODY item * @exception ProtocolException for protocol failures */ public BODY peekBody(int msgno, String section) throws ProtocolException { return fetchBody(msgno, section, true); } /** * Fetch given BODY section. * * @param msgno the message number * @param section the body section * @return the BODY item * @exception ProtocolException for protocol failures */ public BODY fetchBody(int msgno, String section) throws ProtocolException { return fetchBody(msgno, section, false); } protected BODY fetchBody(int msgno, String section, boolean peek) throws ProtocolException { Response[] r; if (section == null) section = ""; String body = (peek ? "BODY.PEEK[" : "BODY[") + section + "]"; return fetchSectionBody(msgno, section, body); } /** * Partial FETCH of given BODY section, without setting SEEN flag. * * @param msgno the message number * @param section the body section * @param start starting byte count * @param size number of bytes to fetch * @return the BODY item * @exception ProtocolException for protocol failures */ public BODY peekBody(int msgno, String section, int start, int size) throws ProtocolException { return fetchBody(msgno, section, start, size, true, null); } /** * Partial FETCH of given BODY section. * * @param msgno the message number * @param section the body section * @param start starting byte count * @param size number of bytes to fetch * @return the BODY item * @exception ProtocolException for protocol failures */ public BODY fetchBody(int msgno, String section, int start, int size) throws ProtocolException { return fetchBody(msgno, section, start, size, false, null); } /** * Partial FETCH of given BODY section, without setting SEEN flag. * * @param msgno the message number * @param section the body section * @param start starting byte count * @param size number of bytes to fetch * @param ba the buffer into which to read the response * @return the BODY item * @exception ProtocolException for protocol failures */ public BODY peekBody(int msgno, String section, int start, int size, ByteArray ba) throws ProtocolException { return fetchBody(msgno, section, start, size, true, ba); } /** * Partial FETCH of given BODY section. * * @param msgno the message number * @param section the body section * @param start starting byte count * @param size number of bytes to fetch * @param ba the buffer into which to read the response * @return the BODY item * @exception ProtocolException for protocol failures */ public BODY fetchBody(int msgno, String section, int start, int size, ByteArray ba) throws ProtocolException { return fetchBody(msgno, section, start, size, false, ba); } protected BODY fetchBody(int msgno, String section, int start, int size, boolean peek, ByteArray ba) throws ProtocolException { this.ba = ba; // save for later use by getResponseBuffer if (section == null) section = ""; String body = (peek ? "BODY.PEEK[" : "BODY[") + section + "]<" + String.valueOf(start) + "." + String.valueOf(size) + ">"; return fetchSectionBody(msgno, section, body); } /** * Fetch the given body section of the given message, using the * body string "body". * * @param msgno the message number * @param section the body section * @param body the body string * @return the BODY item * @exception ProtocolException for protocol failures */ protected BODY fetchSectionBody(int msgno, String section, String body) throws ProtocolException { Response[] r; r = fetch(msgno, body); notifyResponseHandlers(r); Response response = r[r.length-1]; if (response.isOK()) { List<BODY> bl = FetchResponse.getItems(r, msgno, BODY.class); if (bl.size() == 1) return bl.get(0); // the common case if (logger.isLoggable(Level.FINEST)) logger.finest("got " + bl.size() + " BODY responses for section " + section); // more then one BODY response, have to find the right one for (BODY br : bl) { if (logger.isLoggable(Level.FINEST)) logger.finest("got BODY section " + br.getSection()); if (br.getSection().equalsIgnoreCase(section)) return br; // that's the one! } return null; // couldn't find it } else if (response.isNO()) return null; else { handleResult(response); return null; } } /** * Return a buffer to read a response into. * The buffer is provided by fetchBody and is * used only once. * * @return the buffer to use */ @Override protected ByteArray getResponseBuffer() { ByteArray ret = ba; ba = null; return ret; } /** * Fetch the specified RFC822 Data item. 'what' names * the item to be fetched. 'what' can be <code>null</code> * to fetch the whole message. * * @param msgno the message number * @param what the item to fetch * @return the RFC822DATA item * @exception ProtocolException for protocol failures */ public RFC822DATA fetchRFC822(int msgno, String what) throws ProtocolException { Response[] r = fetch(msgno, what == null ? "RFC822" : "RFC822." + what ); // dispatch untagged responses notifyResponseHandlers(r); Response response = r[r.length-1]; if (response.isOK()) return FetchResponse.getItem(r, msgno, RFC822DATA.class); else if (response.isNO()) return null; else { handleResult(response); return null; } } /** * Fetch the FLAGS for the given message. * * @param msgno the message number * @return the Flags * @exception ProtocolException for protocol failures */ public Flags fetchFlags(int msgno) throws ProtocolException { 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 = fr.getItem(FLAGS.class)) != null) { r[i] = null; // remove this response break; } } // dispatch untagged responses notifyResponseHandlers(r); handleResult(r[r.length-1]); return flags; } /** * Fetch the IMAP UID for the given message. * * @param msgno the message number * @return the UID * @exception ProtocolException for protocol failures */ public UID fetchUID(int msgno) throws ProtocolException { Response[] r = fetch(msgno, "UID"); // dispatch untagged responses notifyResponseHandlers(r); Response response = r[r.length-1]; if (response.isOK()) return FetchResponse.getItem(r, msgno, UID.class); else if (response.isNO()) // XXX: Issue NOOP ? return null; else { handleResult(response); return null; // NOTREACHED } } /** * Fetch the IMAP MODSEQ for the given message. * * @param msgno the message number * @return the MODSEQ * @exception ProtocolException for protocol failures * @since JavaMail 1.5.1 */ public MODSEQ fetchMODSEQ(int msgno) throws ProtocolException { Response[] r = fetch(msgno, "MODSEQ"); // dispatch untagged responses notifyResponseHandlers(r); Response response = r[r.length-1]; if (response.isOK()) return FetchResponse.getItem(r, msgno, MODSEQ.class); else if (response.isNO()) // XXX: Issue NOOP ? return null; else { handleResult(response); return null; // NOTREACHED } } /** * Get the sequence number for the given UID. Nothing is returned; * the FETCH UID response must be handled by the reponse handler, * along with any possible EXPUNGE responses, to ensure that the * UID is matched with the correct sequence number. * * @param uid the UID * @exception ProtocolException for protocol failures * @since JavaMail 1.5.3 */ public void fetchSequenceNumber(long uid) throws ProtocolException { Response[] r = fetch(String.valueOf(uid), "UID", true); notifyResponseHandlers(r); handleResult(r[r.length-1]); } /** * Get the sequence numbers for UIDs ranging from start till end. * Since the range may be large and sparse, an array of the UIDs actually * found is returned. The caller must map these to messages after * the FETCH UID responses have been handled by the reponse handler, * along with any possible EXPUNGE responses, to ensure that the * UIDs are matched with the correct sequence numbers. * * @param start first UID * @param end last UID * @return array of sequence numbers * @exception ProtocolException for protocol failures * @since JavaMail 1.5.3 */ public long[] fetchSequenceNumbers(long start, long end) throws ProtocolException { Response[] r = fetch(String.valueOf(start) + ":" + (end == UIDFolder.LASTUID ? "*" : String.valueOf(end)), "UID", true); UID u; List<UID> v = new ArrayList<>(); 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 = fr.getItem(UID.class)) != null) v.add(u); } notifyResponseHandlers(r); handleResult(r[r.length-1]); long[] lv = new long[v.size()]; for (int i = 0; i < v.size(); i++) lv[i] = v.get(i).uid; return lv; } /** * Get the sequence numbers for UIDs specified in the array. * Nothing is returned. The caller must map the UIDs to messages after * the FETCH UID responses have been handled by the reponse handler, * along with any possible EXPUNGE responses, to ensure that the * UIDs are matched with the correct sequence numbers. * * @param uids the UIDs * @exception ProtocolException for protocol failures * @since JavaMail 1.5.3 */ public void fetchSequenceNumbers(long[] uids) throws ProtocolException { StringBuilder sb = new StringBuilder(); 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); notifyResponseHandlers(r); handleResult(r[r.length-1]); } /** * Get the sequence numbers for messages changed since the given * modseq and with UIDs ranging from start till end. * Also, prefetch the flags for the returned messages. * * @param start first UID * @param end last UID * @param modseq the MODSEQ * @return array of sequence numbers * @exception ProtocolException for protocol failures * @see "RFC 4551" * @since JavaMail 1.5.1 */ public int[] uidfetchChangedSince(long start, long end, long modseq) throws ProtocolException { String msgSequence = String.valueOf(start) + ":" + (end == UIDFolder.LASTUID ? "*" : String.valueOf(end)); Response[] r = command("UID FETCH " + msgSequence + " (FLAGS) (CHANGEDSINCE " + String.valueOf(modseq) + ")", null); List<Integer> v = new ArrayList<>(); for (int i = 0, len = r.length; i < len; i++) { if (r[i] == null || !(r[i] instanceof FetchResponse)) continue; FetchResponse fr = (FetchResponse)r[i]; v.add(Integer.valueOf(fr.getNumber())); } notifyResponseHandlers(r); handleResult(r[r.length-1]); // Copy the list into 'matches' int vsize = v.size(); int[] matches = new int[vsize]; for (int i = 0; i < vsize; i++) matches[i] = v.get(i).intValue(); return matches; } public Response[] fetch(MessageSet[] msgsets, String what) throws ProtocolException { return fetch(MessageSet.toString(msgsets), what, false); } public Response[] fetch(int start, int end, String what) throws ProtocolException { return fetch(String.valueOf(start) + ":" + String.valueOf(end), what, false); } public Response[] fetch(int msg, String what) throws ProtocolException { return fetch(String.valueOf(msg), what, false); } private Response[] fetch(String msgSequence, String what, boolean uid) throws ProtocolException { if (uid) return command("UID FETCH " + msgSequence +" (" + what + ")",null); else return command("FETCH " + msgSequence + " (" + what + ")", null); } /** * COPY command. * * @param msgsets the messages to copy * @param mbox the mailbox to copy them to * @exception ProtocolException for protocol failures */ public void copy(MessageSet[] msgsets, String mbox) throws ProtocolException { copyuid(MessageSet.toString(msgsets), mbox, false); } /** * COPY command. * * @param start start message number * @param end end message number * @param mbox the mailbox to copy them to * @exception ProtocolException for protocol failures */ public void copy(int start, int end, String mbox) throws ProtocolException { copyuid(String.valueOf(start) + ":" + String.valueOf(end), mbox, false); } /** * COPY command, return uid from COPYUID response code. * * @param msgsets the messages to copy * @param mbox the mailbox to copy them to * @return COPYUID response data * @exception ProtocolException for protocol failures * @see "RFC 4315, section 3" */ public CopyUID copyuid(MessageSet[] msgsets, String mbox) throws ProtocolException { return copyuid(MessageSet.toString(msgsets), mbox, true); } /** * COPY command, return uid from COPYUID response code. * * @param start start message number * @param end end message number * @param mbox the mailbox to copy them to * @return COPYUID response data * @exception ProtocolException for protocol failures * @see "RFC 4315, section 3" */ public CopyUID copyuid(int start, int end, String mbox) throws ProtocolException { return copyuid(String.valueOf(start) + ":" + String.valueOf(end), mbox, true); } private CopyUID copyuid(String msgSequence, String mbox, boolean uid) throws ProtocolException { if (uid && !hasCapability("UIDPLUS")) throw new BadCommandException("UIDPLUS not supported"); Argument args = new Argument(); args.writeAtom(msgSequence); writeMailboxName(args, mbox); Response[] r = command("COPY", args); // dispatch untagged responses notifyResponseHandlers(r); // Handle result of this command handleResult(r[r.length-1]); if (uid) return getCopyUID(r); else return null; } /** * MOVE command. * * @param msgsets the messages to move * @param mbox the mailbox to move them to * @exception ProtocolException for protocol failures * @see "RFC 6851" * @since JavaMail 1.5.4 */ public void move(MessageSet[] msgsets, String mbox) throws ProtocolException { moveuid(MessageSet.toString(msgsets), mbox, false); } /** * MOVE command. * * @param start start message number * @param end end message number * @param mbox the mailbox to move them to * @exception ProtocolException for protocol failures * @see "RFC 6851" * @since JavaMail 1.5.4 */ public void move(int start, int end, String mbox) throws ProtocolException { moveuid(String.valueOf(start) + ":" + String.valueOf(end), mbox, false); } /** * MOVE Command, return uid from COPYUID response code. * * @param msgsets the messages to move * @param mbox the mailbox to move them to * @return COPYUID response data * @exception ProtocolException for protocol failures * @see "RFC 6851" * @see "RFC 4315, section 3" * @since JavaMail 1.5.4 */ public CopyUID moveuid(MessageSet[] msgsets, String mbox) throws ProtocolException { return moveuid(MessageSet.toString(msgsets), mbox, true); } /** * MOVE Command, return uid from COPYUID response code. * * @param start start message number * @param end end message number * @param mbox the mailbox to move them to * @return COPYUID response data * @exception ProtocolException for protocol failures * @see "RFC 6851" * @see "RFC 4315, section 3" * @since JavaMail 1.5.4 */ public CopyUID moveuid(int start, int end, String mbox) throws ProtocolException { return moveuid(String.valueOf(start) + ":" + String.valueOf(end), mbox, true); } /** * MOVE Command, return uid from COPYUID response code. * * @see "RFC 6851" * @see "RFC 4315, section 3" * @since JavaMail 1.5.4 */ private CopyUID moveuid(String msgSequence, String mbox, boolean uid) throws ProtocolException { if (!hasCapability("MOVE")) throw new BadCommandException("MOVE not supported"); if (uid && !hasCapability("UIDPLUS")) throw new BadCommandException("UIDPLUS not supported"); Argument args = new Argument(); args.writeAtom(msgSequence); writeMailboxName(args, mbox); Response[] r = command("MOVE", args); // dispatch untagged responses notifyResponseHandlers(r); // Handle result of this command handleResult(r[r.length-1]); if (uid) return getCopyUID(r); else return null; } /** * If the response contains a COPYUID response code, extract * it and return a CopyUID object with the information. * * @param rr the responses to examine * @return the COPYUID response code data, or null if not found * @since JavaMail 1.5.4 */ protected CopyUID getCopyUID(Response[] rr) { // most likely in the last response, so start there and work backward for (int i = rr.length - 1; i >= 0; i--) { Response r = rr[i]; if (r == null || !r.isOK()) continue; byte b; while ((b = r.readByte()) > 0 && b != (byte)'[') ; if (b == 0) continue; String s; s = r.readAtom(); if (!s.equalsIgnoreCase("COPYUID")) continue; // XXX - need to merge more than one response for MOVE? long uidvalidity = r.readLong(); String src = r.readAtom(); String dst = r.readAtom(); return new CopyUID(uidvalidity, UIDSet.parseUIDSets(src), UIDSet.parseUIDSets(dst)); } return null; } public void storeFlags(MessageSet[] msgsets, Flags flags, boolean set) throws ProtocolException { storeFlags(MessageSet.toString(msgsets), flags, set); } public void storeFlags(int start, int end, Flags flags, boolean set) throws ProtocolException { storeFlags(String.valueOf(start) + ":" + String.valueOf(end), flags, set); } /** * Set the specified flags on this message. * * @param msg the message number * @param flags the flags * @param set true to set, false to clear * @exception ProtocolException for protocol failures */ public void storeFlags(int msg, Flags flags, boolean set) throws ProtocolException { storeFlags(String.valueOf(msg), flags, set); } private void storeFlags(String msgset, Flags flags, boolean set) throws ProtocolException { 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]); } /** * Creates an IMAP flag_list from the given Flags object. * * @param flags the flags * @return the IMAP flag_list * @since JavaMail 1.5.4 */ protected String createFlagList(Flags flags) { StringBuilder sb = new StringBuilder("("); // 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(); } /** * 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. * @exception ProtocolException for protocol failures * @exception SearchException for search failures */ public int[] search(MessageSet[] msgsets, SearchTerm term) throws ProtocolException, SearchException { return search(MessageSet.toString(msgsets), 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. * @exception ProtocolException for protocol failures * @exception SearchException for search failures */ public int[] search(SearchTerm term) throws ProtocolException, SearchException { return search("ALL", term); } /* * Apply the given SearchTerm on the specified sequence. * Returns array of matching sequence numbers. Note that an empty * array is returned for no matches. */ private int[] search(String msgSequence, SearchTerm term) throws ProtocolException, SearchException { // Check if the search "text" terms contain only ASCII chars, // or if utf8 support has been enabled (in which case CHARSET // is not allowed; see RFC 6855, section 3, last paragraph) if (supportsUtf8() || SearchSequence.isAscii(term)) { try { return issueSearch(msgSequence, term, null); } catch (IOException ioex) { /* will not happen */ } } /* * The search "text" terms do contain non-ASCII chars and utf8 * support has not been enabled. 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"); } /* Apply the given SearchTerm on the specified sequence, using the * given charset. <p> * Returns array of matching sequence numbers. Note that an empty * array is returned for no matches. */ private int[] issueSearch(String msgSequence, SearchTerm term, String charset) throws ProtocolException, SearchException, IOException { // Generate a search-sequence with the given charset Argument args = getSearchSequence().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 List<Integer> v = new ArrayList<>(); 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.add(Integer.valueOf(num)); r[i] = null; } } // Copy the list into 'matches' int vsize = v.size(); matches = new int[vsize]; for (int i = 0; i < vsize; i++) matches[i] = v.get(i).intValue(); } // dispatch remaining untagged responses notifyResponseHandlers(r); handleResult(response); return matches; } /** * Get the SearchSequence object. * The SearchSequence object instance is saved in the searchSequence * field. Subclasses of IMAPProtocol may override this method to * return a subclass of SearchSequence, in order to add support for * product-specific search terms. * * @return the SearchSequence * @since JavaMail 1.4.6 */ protected SearchSequence getSearchSequence() { if (searchSequence == null) searchSequence = new SearchSequence(this); return searchSequence; } /** * Sort messages in the folder according to the specified sort criteria. * If the search term is not null, limit the sort to only the messages * that match the search term. * Returns an array of sorted sequence numbers. An empty array * is returned if no matches are found. * * @param term sort criteria * @param sterm SearchTerm * @return array of matching sequence numbers. * @exception ProtocolException for protocol failures * @exception SearchException for search failures * * @see "RFC 5256" * @since JavaMail 1.4.4 */ public int[] sort(SortTerm[] term, SearchTerm sterm) throws ProtocolException, SearchException { if (!hasCapability("SORT*")) throw new BadCommandException("SORT not supported"); if (term == null || term.length == 0) throw new BadCommandException("Must have at least one sort term"); Argument args = new Argument(); Argument sargs = new Argument(); for (int i = 0; i < term.length; i++) sargs.writeAtom(term[i].toString()); args.writeArgument(sargs); // sort criteria args.writeAtom("UTF-8"); // charset specification if (sterm != null) { try { args.append( getSearchSequence().generateSequence(sterm, "UTF-8")); } catch (IOException ioex) { // should never happen throw new SearchException(ioex.toString()); } } else args.writeAtom("ALL"); Response[] r = command("SORT", args); Response response = r[r.length-1]; int[] matches = null; // Grab all SORT responses if (response.isOK()) { // command succesful List<Integer> v = new ArrayList<>(); int num; for (int i = 0, len = r.length; i < len; i++) { if (!(r[i] instanceof IMAPResponse)) continue; IMAPResponse ir = (IMAPResponse)r[i]; if (ir.keyEquals("SORT")) { while ((num = ir.readNumber()) != -1) v.add(Integer.valueOf(num)); r[i] = null; } } // Copy the list into 'matches' int vsize = v.size(); matches = new int[vsize]; for (int i = 0; i < vsize; i++) matches[i] = v.get(i).intValue(); } // dispatch remaining untagged responses notifyResponseHandlers(r); handleResult(response); return matches; } /** * NAMESPACE Command. * * @return the namespaces * @exception ProtocolException for protocol failures * @see "RFC2342" */ public Namespaces namespace() throws ProtocolException { 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; } /** * GETQUOTAROOT Command. * * Returns an array of Quota objects, representing the quotas * for this mailbox and, indirectly, the quotaroots for this * mailbox. * * @param mbox the mailbox * @return array of Quota objects * @exception ProtocolException for protocol failures * @see "RFC2087" */ public Quota[] getQuotaRoot(String mbox) throws ProtocolException { if (!hasCapability("QUOTA")) throw new BadCommandException("GETQUOTAROOT not supported"); Argument args = new Argument(); writeMailboxName(args, mbox); Response[] r = command("GETQUOTAROOT", args); Response response = r[r.length-1]; Map<String, Quota> tab = new HashMap<>(); // 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 && root.length() > 0) tab.put(root, new Quota(root)); r[i] = null; } else if (ir.keyEquals("QUOTA")) { Quota quota = parseQuota(ir); Quota q = tab.get(quota.quotaRoot); if (q != null && q.resources != null) { // merge resources int newl = q.resources.length + quota.resources.length; Quota.Resource[] newr = new Quota.Resource[newl]; System.arraycopy(q.resources, 0, newr, 0, q.resources.length); System.arraycopy(quota.resources, 0, newr, q.resources.length, quota.resources.length); quota.resources = newr; } tab.put(quota.quotaRoot, quota); r[i] = null; } } } // dispatch remaining untagged responses notifyResponseHandlers(r); handleResult(response); return tab.values().toArray(new Quota[tab.size()]); } /** * GETQUOTA Command. * * Returns an array of Quota objects, representing the quotas * for this quotaroot. * * @param root the quotaroot * @return the quotas * @exception ProtocolException for protocol failures * @see "RFC2087" */ public Quota[] getQuota(String root) throws ProtocolException { if (!hasCapability("QUOTA")) throw new BadCommandException("QUOTA not supported"); Argument args = new Argument(); args.writeString(root); // XXX - could be UTF-8? Response[] r = command("GETQUOTA", args); Quota quota = null; List<Quota> v = new ArrayList<>(); 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.add(quota); r[i] = null; } } } // dispatch remaining untagged responses notifyResponseHandlers(r); handleResult(response); return v.toArray(new Quota[v.size()]); } /** * SETQUOTA Command. * * Set the indicated quota on the corresponding quotaroot. * * @param quota the quota to set * @exception ProtocolException for protocol failures * @see "RFC2087" */ public void setQuota(Quota quota) throws ProtocolException { if (!hasCapability("QUOTA")) throw new BadCommandException("QUOTA not supported"); Argument args = new Argument(); args.writeString(quota.quotaRoot); // XXX - could be UTF-8? 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; List<Quota> v = new ArrayList<Quota>(); // 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.add(quota); r[i] = null; } } } */ // dispatch remaining untagged responses notifyResponseHandlers(r); handleResult(response); /* return v.toArray(new Quota[v.size()]); */ } /** * Parse a QUOTA response. */ private Quota parseQuota(Response r) throws ParsingException { // 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"); List<Quota.Resource> v = new ArrayList<>(); while (!r.isNextNonSpace(')')) { // 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.add(res); } } q.resources = v.toArray(new Quota.Resource[v.size()]); return q; } /** * SETACL Command. * * @param mbox the mailbox * @param modifier the ACL modifier * @param acl the ACL * @exception ProtocolException for protocol failures * @see "RFC2086" */ public void setACL(String mbox, char modifier, ACL acl) throws ProtocolException { if (!hasCapability("ACL")) throw new BadCommandException("ACL not supported"); Argument args = new Argument(); writeMailboxName(args, 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); } /** * DELETEACL Command. * * @param mbox the mailbox * @param user the user * @exception ProtocolException for protocol failures * @see "RFC2086" */ public void deleteACL(String mbox, String user) throws ProtocolException { if (!hasCapability("ACL")) throw new BadCommandException("ACL not supported"); Argument args = new Argument(); writeMailboxName(args, mbox); args.writeString(user); // XXX - could be UTF-8? Response[] r = command("DELETEACL", args); Response response = r[r.length-1]; // dispatch untagged responses notifyResponseHandlers(r); handleResult(response); } /** * GETACL Command. * * @param mbox the mailbox * @return the ACL array * @exception ProtocolException for protocol failures * @see "RFC2086" */ public ACL[] getACL(String mbox) throws ProtocolException { if (!hasCapability("ACL")) throw new BadCommandException("ACL not supported"); Argument args = new Argument(); writeMailboxName(args, mbox); Response[] r = command("GETACL", args); Response response = r[r.length-1]; // Grab all ACL responses List<ACL> v = new ArrayList<>(); 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.add(acl); } r[i] = null; } } } // dispatch remaining untagged responses notifyResponseHandlers(r); handleResult(response); return v.toArray(new ACL[v.size()]); } /** * LISTRIGHTS Command. * * @param mbox the mailbox * @param user the user rights to return * @return the rights array * @exception ProtocolException for protocol failures * @see "RFC2086" */ public Rights[] listRights(String mbox, String user) throws ProtocolException { if (!hasCapability("ACL")) throw new BadCommandException("ACL not supported"); Argument args = new Argument(); writeMailboxName(args, mbox); args.writeString(user); // XXX - could be UTF-8? Response[] r = command("LISTRIGHTS", args); Response response = r[r.length-1]; // Grab LISTRIGHTS response List<Rights> v = new ArrayList<>(); 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.add(new Rights(rights)); r[i] = null; } } } // dispatch remaining untagged responses notifyResponseHandlers(r); handleResult(response); return v.toArray(new Rights[v.size()]); } /** * MYRIGHTS Command. * * @param mbox the mailbox * @return the rights * @exception ProtocolException for protocol failures * @see "RFC2086" */ public Rights myRights(String mbox) throws ProtocolException { if (!hasCapability("ACL")) throw new BadCommandException("ACL not supported"); Argument args = new Argument(); writeMailboxName(args, 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; } /* * The tag used on the IDLE command. Set by idleStart() and * used in processIdleResponse() to determine if the response * is the matching end tag. */ private volatile String idleTag; /** * IDLE Command. <p> * * 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. <p> * * 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. * * @exception ProtocolException for protocol failures * @see "RFC2177" * @since JavaMail 1.4.1 */ public synchronized void idleStart() throws ProtocolException { if (!hasCapability("IDLE")) throw new BadCommandException("IDLE not supported"); List<Response> v = new ArrayList<>(); boolean done = false; Response r = null; // write the command try { idleTag = writeCommand("IDLE", null); } catch (LiteralException lex) { v.add(lex.getResponse()); done = true; } catch (Exception ex) { // Convert this into a BYE response v.add(Response.byeResponse(ex)); done = true; } while (!done) { try { r = readResponse(); } catch (IOException ioex) { // convert this into a BYE response r = Response.byeResponse(ioex); } catch (ProtocolException pex) { continue; // skip this response } v.add(r); if (r.isContinuation() || r.isBYE()) done = true; } Response[] responses = v.toArray(new Response[v.size()]); r = responses[responses.length-1]; // dispatch remaining untagged responses notifyResponseHandlers(responses); if (!r.isContinuation()) handleResult(r); } /** * 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. * * @return the response * @since JavaMail 1.4.1 */ public synchronized Response readIdleResponse() { if (idleTag == null) return null; // IDLE not in progress Response r = null; try { r = readResponse(); } 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; } /** * Process a response returned by readIdleResponse(). * This method will be called with appropriate locks * held so that the processing of the response is safe. * * @param r the response * @return true if IDLE is done * @exception ProtocolException for protocol failures * @since JavaMail 1.4.1 */ public boolean processIdleResponse(Response r) throws ProtocolException { 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; } // the DONE command to break out of IDLE private static final byte[] DONE = { 'D', 'O', 'N', 'E', '\r', '\n' }; /** * 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 */ public void idleAbort() { OutputStream os = getOutputStream(); try { os.write(DONE); os.flush(); } catch (Exception ex) { // nothing to do, hope to detect it again later logger.log(Level.FINEST, "Exception aborting IDLE", ex); } } /** * ID Command. * * @param clientParams map of names and values * @return map of names and values from server * @exception ProtocolException for protocol failures * @see "RFC 2971" * @since JavaMail 1.5.1 */ public Map<String, String> id(Map<String, String> clientParams) throws ProtocolException { if (!hasCapability("ID")) throw new BadCommandException("ID not supported"); Response[] r = command("ID", ID.getArgumentList(clientParams)); ID id = null; Response response = r[r.length-1]; // Grab ID 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("ID")) { if (id == null) id = new ID(ir); r[i] = null; } } } // dispatch remaining untagged responses notifyResponseHandlers(r); handleResult(response); return id == null ? null : id.getServerParams(); } }
⏎ com/sun/mail/imap/protocol/IMAPProtocol.java
Or download all of them as a single archive file:
File name: javax.mail-1.6.2-sources.jar File size: 851487 bytes Release date: 2018-08-29 Download
⇒ Download and Install javax.mail-1.5.4.jar
⇐ Download and Install javax.mail-1.6.2.jar
2016-01-07, 9856👍, 0💬
Popular Posts:
commons-lang-1.0.1.jar is the JAR file for Apache Commons Lang 1.0.1, which provides a host of helpe...
JDK 11 jdk.hotspot.agent.jmod is the JMOD file for JDK 11 Hotspot Agent module. JDK 11 Hotspot Agent...
What Is javamail-1_2.zip? javamail-1_2.zip is the binary package of JavaMail API 1.2 in ZIP format. ...
Apache BCEL Source Code Files are inside the Apache BCEL source package file like bcel-6.5.0-src.zip...
JDK 11 java.xml.jmod is the JMOD file for JDK 11 XML (eXtensible Markup Language) module. JDK 11 XML...