Package: IMAPProtocol
IMAPProtocol
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
IMAPProtocol(InputStream, PrintStream, Properties, boolean) |
|
|
|
|
|
||||||||||||||||||||
IMAPProtocol(String, String, int, Properties, boolean, MailLogger) |
|
|
|
|
|
||||||||||||||||||||
append(String, Flags, Date, Literal) |
|
|
|
|
|
||||||||||||||||||||
appenduid(String, Flags, Date, Literal) |
|
|
|
|
|
||||||||||||||||||||
appenduid(String, Flags, Date, Literal, boolean) |
|
|
|
|
|
||||||||||||||||||||
authlogin(String, String) |
|
|
|
|
|
||||||||||||||||||||
authntlm(String, String, String) |
|
|
|
|
|
||||||||||||||||||||
authoauth2(String, String) |
|
|
|
|
|
||||||||||||||||||||
authplain(String, String, String) |
|
|
|
|
|
||||||||||||||||||||
capability() |
|
|
|
|
|
||||||||||||||||||||
check() |
|
|
|
|
|
||||||||||||||||||||
checkReferral(Response) |
|
|
|
|
|
||||||||||||||||||||
close() |
|
|
|
|
|
||||||||||||||||||||
compress() |
|
|
|
|
|
||||||||||||||||||||
copy(MessageSet[], String) |
|
|
|
|
|
||||||||||||||||||||
copy(int, int, String) |
|
|
|
|
|
||||||||||||||||||||
copyuid(MessageSet[], String) |
|
|
|
|
|
||||||||||||||||||||
copyuid(String, String, boolean) |
|
|
|
|
|
||||||||||||||||||||
copyuid(int, int, String) |
|
|
|
|
|
||||||||||||||||||||
create(String) |
|
|
|
|
|
||||||||||||||||||||
createFlagList(Flags) |
|
|
|
|
|
||||||||||||||||||||
delete(String) |
|
|
|
|
|
||||||||||||||||||||
deleteACL(String, String) |
|
|
|
|
|
||||||||||||||||||||
disconnect() |
|
|
|
|
|
||||||||||||||||||||
doList(String, String, String) |
|
|
|
|
|
||||||||||||||||||||
enable(String) |
|
|
|
|
|
||||||||||||||||||||
examine(String) |
|
|
|
|
|
||||||||||||||||||||
examine(String, ResyncData) |
|
|
|
|
|
||||||||||||||||||||
expunge() |
|
|
|
|
|
||||||||||||||||||||
fetch(MessageSet[], String) |
|
|
|
|
|
||||||||||||||||||||
fetch(String, String, boolean) |
|
|
|
|
|
||||||||||||||||||||
fetch(int, String) |
|
|
|
|
|
||||||||||||||||||||
fetch(int, int, String) |
|
|
|
|
|
||||||||||||||||||||
fetchBody(int, String) |
|
|
|
|
|
||||||||||||||||||||
fetchBody(int, String, boolean) |
|
|
|
|
|
||||||||||||||||||||
fetchBody(int, String, int, int) |
|
|
|
|
|
||||||||||||||||||||
fetchBody(int, String, int, int, ByteArray) |
|
|
|
|
|
||||||||||||||||||||
fetchBody(int, String, int, int, boolean, ByteArray) |
|
|
|
|
|
||||||||||||||||||||
fetchBodyStructure(int) |
|
|
|
|
|
||||||||||||||||||||
fetchFlags(int) |
|
|
|
|
|
||||||||||||||||||||
fetchMODSEQ(int) |
|
|
|
|
|
||||||||||||||||||||
fetchRFC822(int, String) |
|
|
|
|
|
||||||||||||||||||||
fetchSectionBody(int, String, String) |
|
|
|
|
|
||||||||||||||||||||
fetchSequenceNumber(long) |
|
|
|
|
|
||||||||||||||||||||
fetchSequenceNumbers(long, long) |
|
|
|
|
|
||||||||||||||||||||
fetchSequenceNumbers(long[]) |
|
|
|
|
|
||||||||||||||||||||
fetchUID(int) |
|
|
|
|
|
||||||||||||||||||||
getACL(String) |
|
|
|
|
|
||||||||||||||||||||
getAppendUID(Response) |
|
|
|
|
|
||||||||||||||||||||
getCapabilities() |
|
|
|
|
|
||||||||||||||||||||
getCopyUID(Response[]) |
|
|
|
|
|
||||||||||||||||||||
getFetchItems() |
|
|
|
|
|
||||||||||||||||||||
getIMAPOutputStream() |
|
|
|
|
|
||||||||||||||||||||
getProxyAuthUser() |
|
|
|
|
|
||||||||||||||||||||
getQuota(String) |
|
|
|
|
|
||||||||||||||||||||
getQuotaRoot(String) |
|
|
|
|
|
||||||||||||||||||||
getResponseBuffer() |
|
|
|
|
|
||||||||||||||||||||
getSearchSequence() |
|
|
|
|
|
||||||||||||||||||||
handleCapabilityResponse(Response[]) |
|
|
|
|
|
||||||||||||||||||||
handleLoginResult(Response) |
|
|
|
|
|
||||||||||||||||||||
hasCapability(String) |
|
|
|
|
|
||||||||||||||||||||
id(Map) |
|
|
|
|
|
||||||||||||||||||||
id(String) |
|
|
|
|
|
||||||||||||||||||||
idleAbort() |
|
|
|
|
|
||||||||||||||||||||
idleStart() |
|
|
|
|
|
||||||||||||||||||||
isAuthenticated() |
|
|
|
|
|
||||||||||||||||||||
isEnabled(String) |
|
|
|
|
|
||||||||||||||||||||
isREV1() |
|
|
|
|
|
||||||||||||||||||||
issueSearch(String, SearchTerm, String) |
|
|
|
|
|
||||||||||||||||||||
list(String, String) |
|
|
|
|
|
||||||||||||||||||||
listRights(String, String) |
|
|
|
|
|
||||||||||||||||||||
login(String, String) |
|
|
|
|
|
||||||||||||||||||||
logout() |
|
|
|
|
|
||||||||||||||||||||
lsub(String, String) |
|
|
|
|
|
||||||||||||||||||||
move(MessageSet[], String) |
|
|
|
|
|
||||||||||||||||||||
move(int, int, String) |
|
|
|
|
|
||||||||||||||||||||
moveuid(MessageSet[], String) |
|
|
|
|
|
||||||||||||||||||||
moveuid(String, String, boolean) |
|
|
|
|
|
||||||||||||||||||||
moveuid(int, int, String) |
|
|
|
|
|
||||||||||||||||||||
myRights(String) |
|
|
|
|
|
||||||||||||||||||||
namespace() |
|
|
|
|
|
||||||||||||||||||||
noop() |
|
|
|
|
|
||||||||||||||||||||
parseCapabilities(Response) |
|
|
|
|
|
||||||||||||||||||||
parseQuota(Response) |
|
|
|
|
|
||||||||||||||||||||
peekBody(int, String) |
|
|
|
|
|
||||||||||||||||||||
peekBody(int, String, int, int) |
|
|
|
|
|
||||||||||||||||||||
peekBody(int, String, int, int, ByteArray) |
|
|
|
|
|
||||||||||||||||||||
processGreeting(Response) |
|
|
|
|
|
||||||||||||||||||||
processIdleResponse(Response) |
|
|
|
|
|
||||||||||||||||||||
proxyauth(String) |
|
|
|
|
|
||||||||||||||||||||
readIdleResponse() |
|
|
|
|
|
||||||||||||||||||||
readResponse() |
|
|
|
|
|
||||||||||||||||||||
rename(String, String) |
|
|
|
|
|
||||||||||||||||||||
resyncArgs(ResyncData) |
|
|
|
|
|
||||||||||||||||||||
sasllogin(String[], String, String, String, String) |
|
|
|
|
|
||||||||||||||||||||
search(MessageSet[], SearchTerm) |
|
|
|
|
|
||||||||||||||||||||
search(SearchTerm) |
|
|
|
|
|
||||||||||||||||||||
search(String, SearchTerm) |
|
|
|
|
|
||||||||||||||||||||
select(String) |
|
|
|
|
|
||||||||||||||||||||
select(String, ResyncData) |
|
|
|
|
|
||||||||||||||||||||
setACL(String, char, ACL) |
|
|
|
|
|
||||||||||||||||||||
setCapabilities(Response) |
|
|
|
|
|
||||||||||||||||||||
setQuota(Quota) |
|
|
|
|
|
||||||||||||||||||||
sort(SortTerm[], SearchTerm) |
|
|
|
|
|
||||||||||||||||||||
startTLS() |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
||||||||||||||||||||
status(String, String[]) |
|
|
|
|
|
||||||||||||||||||||
storeFlags(MessageSet[], Flags, boolean) |
|
|
|
|
|
||||||||||||||||||||
storeFlags(String, Flags, boolean) |
|
|
|
|
|
||||||||||||||||||||
storeFlags(int, Flags, boolean) |
|
|
|
|
|
||||||||||||||||||||
storeFlags(int, int, Flags, boolean) |
|
|
|
|
|
||||||||||||||||||||
subscribe(String) |
|
|
|
|
|
||||||||||||||||||||
supportsNonSyncLiterals() |
|
|
|
|
|
||||||||||||||||||||
supportsUtf8() |
|
|
|
|
|
||||||||||||||||||||
uidexpunge(UIDSet[]) |
|
|
|
|
|
||||||||||||||||||||
uidfetchChangedSince(long, long, long) |
|
|
|
|
|
||||||||||||||||||||
unauthenticate() |
|
|
|
|
|
||||||||||||||||||||
unselect() |
|
|
|
|
|
||||||||||||||||||||
unsubscribe(String) |
|
|
|
|
|
||||||||||||||||||||
writeMailboxName(Argument, String) |
|
|
|
|
|
Coverage
1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package org.eclipse.angus.mail.imap.protocol;
18:
19: import jakarta.mail.Flags;
20: import jakarta.mail.Folder;
21: import jakarta.mail.Quota;
22: import jakarta.mail.UIDFolder;
23: import jakarta.mail.internet.MimeUtility;
24: import jakarta.mail.search.SearchException;
25: import jakarta.mail.search.SearchTerm;
26:
27: import java.io.ByteArrayOutputStream;
28: import java.io.IOException;
29: import java.io.InputStream;
30: import java.io.OutputStream;
31: import java.io.PrintStream;
32: import java.lang.reflect.Constructor;
33: import java.nio.charset.StandardCharsets;
34: import java.util.ArrayList;
35: import java.util.Base64;
36: import java.util.Date;
37: import java.util.HashMap;
38: import java.util.HashSet;
39: import java.util.Iterator;
40: import java.util.List;
41: import java.util.Locale;
42: import java.util.Map;
43: import java.util.Properties;
44: import java.util.Set;
45: import java.util.logging.Level;
46:
47: import org.eclipse.angus.mail.auth.Ntlm;
48: import org.eclipse.angus.mail.iap.Argument;
49: import org.eclipse.angus.mail.iap.BadCommandException;
50: import org.eclipse.angus.mail.iap.ByteArray;
51: import org.eclipse.angus.mail.iap.CommandFailedException;
52: import org.eclipse.angus.mail.iap.ConnectionException;
53: import org.eclipse.angus.mail.iap.Literal;
54: import org.eclipse.angus.mail.iap.LiteralException;
55: import org.eclipse.angus.mail.iap.ParsingException;
56: import org.eclipse.angus.mail.iap.Protocol;
57: import org.eclipse.angus.mail.iap.ProtocolException;
58: import org.eclipse.angus.mail.iap.Response;
59: import org.eclipse.angus.mail.imap.ACL;
60: import org.eclipse.angus.mail.imap.AppendUID;
61: import org.eclipse.angus.mail.imap.CopyUID;
62: import org.eclipse.angus.mail.imap.ResyncData;
63: import org.eclipse.angus.mail.imap.Rights;
64: import org.eclipse.angus.mail.imap.SortTerm;
65: import org.eclipse.angus.mail.imap.Utility;
66: import org.eclipse.angus.mail.util.BASE64EncoderStream;
67: import org.eclipse.angus.mail.util.ASCIIUtility;
68: import org.eclipse.angus.mail.util.MailLogger;
69: import org.eclipse.angus.mail.util.PropUtil;
70:
71: /**
72: * This class extends the iap.Protocol object and implements IMAP
73: * semantics. In general, there is a method corresponding to each
74: * IMAP protocol command. The typical implementation issues the
75: * appropriate protocol command, collects all responses, processes
76: * those responses that are specific to this command and then
77: * dispatches the rest (the unsolicited ones) to the dispatcher
78: * using the <code>notifyResponseHandlers(r)</code>.
79: *
80: * @author John Mani
81: * @author Bill Shannon
82: */
83:
84: public class IMAPProtocol extends Protocol {
85:
86: private boolean connected = false;        // did constructor succeed?
87: private boolean rev1 = false;        // REV1 server ?
88: private boolean referralException;        // throw exception for IMAP REFERRAL?
89: private boolean noauthdebug = true;        // hide auth info in debug output
90: private boolean authenticated;        // authenticated?
91: // WARNING: authenticated may be set to true in superclass
92: //                constructor, don't initialize it here.
93:
94: private Map<String, String> capabilities;
95: // WARNING: capabilities may be initialized as a result of superclass
96: //                constructor, don't initialize it here.
97: private List<String> authmechs;
98: // WARNING: authmechs may be initialized as a result of superclass
99: //                constructor, don't initialize it here.
100: private boolean utf8;                // UTF-8 support enabled?
101:
102: protected SearchSequence searchSequence;
103: protected String[] searchCharsets;         // array of search charsets
104:
105: protected Set<String> enabled;        // enabled capabilities - RFC 5161
106:
107: private String name;
108: private SaslAuthenticator saslAuthenticator;        // if SASL is being used
109: private String proxyAuthUser;        // user name used with PROXYAUTH
110:
111: private ByteArray ba;                // a buffer for fetchBody
112:
113: private static final byte[] CRLF = { (byte)'\r', (byte)'\n'};
114:
115: private static final FetchItem[] fetchItems = { };
116:
117: /**
118: * Constructor.
119: * Opens a connection to the given host at given port.
120: *
121: * @param        name        the protocol name
122: * @param        host        host to connect to
123: * @param        port        port number to connect to
124: * @param        props        Properties object used by this protocol
125: * @param        isSSL        true if SSL should be used
126: * @param        logger        the MailLogger to use for debug output
127: * @exception        IOException        for I/O errors
128: * @exception ProtocolException for protocol failures
129: */
130: public IMAPProtocol(String name, String host, int port,
131:                         Properties props, boolean isSSL, MailLogger logger)
132:                         throws IOException, ProtocolException {
133:         super(host, port, props, "mail." + name, isSSL, logger);
134:
135:         try {
136:          this.name = name;
137:          noauthdebug =
138:•                !PropUtil.getBooleanProperty(props, "mail.debug.auth", false);
139:
140:          // in case it was not initialized in processGreeting
141:          referralException = PropUtil.getBooleanProperty(props,
142:                                 prefix + ".referralexception", false);
143:
144:•         if (capabilities == null)
145:                 capability();
146:
147:•         if (hasCapability("IMAP4rev1"))
148:                 rev1 = true;
149:
150:          searchCharsets = new String[2]; // 2, for now.
151:          searchCharsets[0] = "UTF-8";
152:          searchCharsets[1] = MimeUtility.mimeCharset(
153:                                  MimeUtility.getDefaultJavaCharset()
154:                                 );
155:
156:          connected = true;        // must be last statement in constructor
157:         } finally {
158:          /*
159:          * If we get here because an exception was thrown, we need
160:          * to disconnect to avoid leaving a connected socket that
161:          * no one will be able to use because this object was never
162:          * completely constructed.
163:          */
164:•         if (!connected)
165:                 disconnect();
166:         }
167: }
168:
169: /**
170: * Constructor for debugging.
171: *
172: * @param        in        the InputStream from which to read
173: * @param        out        the PrintStream to which to write
174: * @param        props        Properties object used by this protocol
175: * @param        debug        true to enable debugging output
176: * @exception        IOException        for I/O errors
177: */
178: public IMAPProtocol(InputStream in, PrintStream out,
179:                         Properties props, boolean debug)
180:                         throws IOException {
181:         super(in, out, props, debug);
182:
183:         this.name = "imap";
184:         noauthdebug =
185:•         !PropUtil.getBooleanProperty(props, "mail.debug.auth", false);
186:
187:•        if (capabilities == null)
188:          capabilities = new HashMap<>();
189:
190:         searchCharsets = new String[2]; // 2, for now.
191:         searchCharsets[0] = "UTF-8";
192:         searchCharsets[1] = MimeUtility.mimeCharset(
193:                                 MimeUtility.getDefaultJavaCharset()
194:                          );
195:
196:         connected = true;        // must be last statement in constructor
197: }
198:
199: /**
200: * Return an array of FetchItem objects describing the
201: * FETCH items supported by this protocol. Subclasses may
202: * override this method to combine their FetchItems with
203: * the FetchItems returned by the superclass.
204: *
205: * @return        an array of FetchItem objects
206: * @since JavaMail 1.4.6
207: */
208: public FetchItem[] getFetchItems() {
209:         return fetchItems;
210: }
211:
212: /**
213: * CAPABILITY command.
214: *
215: * @exception        ProtocolException        for protocol failures
216: * @see "RFC2060, section 6.1.1"
217: */
218: public void capability() throws ProtocolException {
219:         // Check CAPABILITY
220:         Response[] r = command("CAPABILITY", null);
221:         Response response = r[r.length-1];
222:
223:•        if (response.isOK())
224:          handleCapabilityResponse(r);
225:         handleResult(response);
226: }
227:
228: /**
229: * Handle any untagged CAPABILITY response in the Response array.
230: *
231: * @param        r        the responses
232: */
233: public void handleCapabilityResponse(Response[] r) {
234:         boolean first = true;
235:•        for (int i = 0, len = r.length; i < len; i++) {
236:•         if (!(r[i] instanceof IMAPResponse))
237:                 continue;
238:
239:          IMAPResponse ir = (IMAPResponse)r[i];
240:
241:          // Handle *all* untagged CAPABILITY responses.
242:          // Though the spec seemingly states that only
243:          // one CAPABILITY response string is allowed (6.1.1),
244:          // some server vendors claim otherwise.
245:•         if (ir.keyEquals("CAPABILITY")) {
246:•                if (first) {
247:                  // clear out current when first response seen
248:                  capabilities = new HashMap<>(10);
249:                  authmechs = new ArrayList<>(5);
250:                  first = false;
251:                 }
252:                 parseCapabilities(ir);
253:          }
254:         }
255: }
256:
257: /**
258: * If the response contains a CAPABILITY response code, extract
259: * it and save the capabilities.
260: *
261: * @param        r        the response
262: */
263: protected void setCapabilities(Response r) {
264:         byte b;
265:•        while ((b = r.readByte()) > 0 && b != (byte)'[')
266:          ;
267:•        if (b == 0)
268:          return;
269:         String s;
270:         s = r.readAtom();
271:•        if (!s.equalsIgnoreCase("CAPABILITY"))
272:          return;
273:         capabilities = new HashMap<>(10);
274:         authmechs = new ArrayList<>(5);
275:         parseCapabilities(r);
276: }
277:
278: /**
279: * Parse the capabilities from a CAPABILITY response or from
280: * a CAPABILITY response code attached to (e.g.) an OK response.
281: *
282: * @param        r        the CAPABILITY response
283: */
284: protected void parseCapabilities(Response r) {
285:         String s;
286:•        while ((s = r.readAtom()) != null) {
287:•         if (s.length() == 0) {
288:•                if (r.peekByte() == (byte)']')
289:                  break;
290:                 /*
291:                  * Probably found something here that's not an atom.
292:                  * Rather than loop forever or fail completely, we'll
293:                  * try to skip this bogus capability. This is known
294:                  * to happen with:
295:                  * Netscape Messaging Server 4.03 (built Apr 27 1999)
296:                  * that returns:
297:                  * * CAPABILITY * CAPABILITY IMAP4 IMAP4rev1 ...
298:                  * The "*" in the middle of the capability list causes
299:                  * us to loop forever here.
300:                  */
301:                 r.skipToken();
302:          } else {
303:                 capabilities.put(s.toUpperCase(Locale.ENGLISH), s);
304:•                if (s.regionMatches(true, 0, "AUTH=", 0, 5)) {
305:                  authmechs.add(s.substring(5));
306:•                 if (logger.isLoggable(Level.FINE))
307:                         logger.fine("AUTH: " + s.substring(5));
308:                 }
309:          }
310:         }
311: }
312:
313: /**
314: * Check the greeting when first connecting; look for PREAUTH response.
315: *
316: * @param        r        the greeting response
317: * @exception        ProtocolException        for protocol failures
318: */
319: @Override
320: protected void processGreeting(Response r) throws ProtocolException {
321:•        if (r.isBYE()) {
322:          checkReferral(r);        // may throw exception
323:          throw new ConnectionException(this, r);
324:         }
325:•        if (r.isOK()) {                        // check if it's OK
326:          // XXX - is a REFERRAL response code really allowed here?
327:          // XXX - referralException hasn't been initialized in c'tor yet
328:          referralException = PropUtil.getBooleanProperty(props,
329:                                 prefix + ".referralexception", false);
330:•         if (referralException)
331:                 checkReferral(r);
332:          setCapabilities(r);
333:          return;
334:         }
335:         // only other choice is PREAUTH
336:•        assert r instanceof IMAPResponse;
337:         IMAPResponse ir = (IMAPResponse)r;
338:•        if (ir.keyEquals("PREAUTH")) {
339:          authenticated = true;
340:          setCapabilities(r);
341:         } else {
342:          disconnect();
343:          throw new ConnectionException(this, r);
344:         }
345: }
346:
347: /**
348: * Check for an IMAP login REFERRAL response code.
349: *
350: * @exception        IMAPReferralException        if REFERRAL response code found
351: * @see "RFC 2221"
352: */
353: private void checkReferral(Response r) throws IMAPReferralException {
354:         String s = r.getRest();        // get the text after the response
355:•        if (s.startsWith("[")) {        // a response code
356:          int i = s.indexOf(' ');
357:•         if (i > 0 && s.substring(1, i).equalsIgnoreCase("REFERRAL")) {
358:                 String url, msg;
359:                 int j = s.indexOf(']');
360:•                if (j > 0) {        // should always be true;
361:                  url = s.substring(i + 1, j);
362:                  msg = s.substring(j + 1).trim();
363:                 } else {
364:                  url = s.substring(i + 1);
365:                  msg = "";
366:                 }
367:•                if (r.isBYE())
368:                  disconnect();
369:                 throw new IMAPReferralException(msg, url);
370:          }
371:         }
372: }
373:
374: /**
375: * Returns <code>true</code> if the connection has been authenticated,
376: * either due to a successful login, or due to a PREAUTH greeting response.
377: *
378: * @return        true if the connection has been authenticated
379: */
380: public boolean isAuthenticated() {
381:         return authenticated;
382: }
383:
384: /**
385: * Returns <code>true</code> if this is an IMAP4rev1 server
386: *
387: * @return        true if this is an IMAP4rev1 server
388: */
389: public boolean isREV1() {
390:         return rev1;
391: }
392:
393: /**
394: * Returns whether this Protocol supports non-synchronizing literals.
395: *
396: * @return        true if non-synchronizing literals are supported
397: */
398: @Override
399: protected boolean supportsNonSyncLiterals() {
400:         return hasCapability("LITERAL+");
401: }
402:
403: /**
404: * Read a response from the server.
405: *
406: * @return        the response
407: * @exception        IOException        for I/O errors
408: * @exception        ProtocolException        for protocol failures
409: */
410: @Override
411: public Response readResponse() throws IOException, ProtocolException {
412:         // assert Thread.holdsLock(this);
413:         // can't assert because it's called from constructor
414:         IMAPResponse r = new IMAPResponse(this);
415:•        if (r.keyEquals("FETCH"))
416:          r = new FetchResponse(r, getFetchItems());
417:         return r;
418: }
419:
420: /**
421: * Check whether the given capability is supported by
422: * this server. Returns <code>true</code> if so, otherwise
423: * returns false.
424: *
425: * @param        c        the capability name
426: * @return                true if the server has the capability
427: */
428: public boolean hasCapability(String c) {
429:•        if (c.endsWith("*")) {
430:          c = c.substring(0, c.length() - 1).toUpperCase(Locale.ENGLISH);
431:          Iterator<String> it = capabilities.keySet().iterator();
432:•         while (it.hasNext()) {
433:•                if (it.next().startsWith(c))
434:                  return true;
435:          }
436:          return false;
437:         }
438:         return capabilities.containsKey(c.toUpperCase(Locale.ENGLISH));
439: }
440:
441: /**
442: * Return the map of capabilities returned by the server.
443: *
444: * @return        the Map of capabilities
445: * @since        JavaMail 1.4.1
446: */
447: public Map<String, String> getCapabilities() {
448:         return capabilities;
449: }
450:
451: /**
452: * Does the server support UTF-8?
453: *
454: * @since JavaMail 1.6.0
455: */
456: public boolean supportsUtf8() {
457:         return utf8;
458: }
459:
460: /**
461: * Close socket connection.
462: *
463: * This method just makes the Protocol.disconnect() method
464: * public.
465: */
466: @Override
467: public void disconnect() {
468:         super.disconnect();
469:         authenticated = false;        // just in case
470: }
471:
472: /**
473: * The NOOP command.
474: *
475: * @exception        ProtocolException        for protocol failures
476: * @see "RFC2060, section 6.1.2"
477: */
478: public void noop() throws ProtocolException {
479:         logger.fine("IMAPProtocol noop");
480:         simpleCommand("NOOP", null);
481: }
482:
483: /**
484: * LOGOUT Command.
485: *
486: * @exception        ProtocolException        for protocol failures
487: * @see "RFC2060, section 6.1.3"
488: */
489: public void logout() throws ProtocolException {
490:         try {
491:          Response[] r = command("LOGOUT", null);
492:
493:          authenticated = false;
494:          // dispatch any unsolicited responses.
495:          // NOTE that the BYE response is dispatched here as well
496:          notifyResponseHandlers(r);
497:         } finally {
498:          disconnect();
499:         }
500: }
501:
502: /**
503: * LOGIN Command.
504: *
505: * @param u                the username
506: * @param p                the password
507: * @throws ProtocolException as thrown by {@link Protocol#handleResult}.
508: * @see "RFC2060, section 6.2.2"
509: */
510: public void login(String u, String p) throws ProtocolException {
511:         Argument args = new Argument();
512:         args.writeString(u);
513:         args.writeString(p);
514:
515:         Response[] r = null;
516:         try {
517:•         if (noauthdebug && isTracing()) {
518:                 logger.fine("LOGIN command trace suppressed");
519:                 suspendTracing();
520:          }
521:          r = command("LOGIN", args);
522:         } finally {
523:          resumeTracing();
524:         }
525:
526:         // handle an illegal but not uncommon untagged CAPABILTY response
527:         handleCapabilityResponse(r);
528:
529:         // dispatch untagged responses
530:         notifyResponseHandlers(r);
531:
532:         // Handle result of this command
533:•        if (noauthdebug && isTracing())
534:          logger.fine("LOGIN command result: " + r[r.length-1]);
535:         handleLoginResult(r[r.length-1]);
536:         // If the response includes a CAPABILITY response code, process it
537:         setCapabilities(r[r.length-1]);
538:         // if we get this far without an exception, we're authenticated
539:         authenticated = true;
540: }
541:
542: /**
543: * The AUTHENTICATE command with AUTH=LOGIN authenticate scheme
544: *
545: * @param u                the username
546: * @param p                the password
547: * @throws ProtocolException as thrown by {@link Protocol#handleResult}.
548: * @see "RFC2060, section 6.2.1"
549: */
550: public synchronized void authlogin(String u, String p)
551:                                 throws ProtocolException {
552:         List<Response> v = new ArrayList<>();
553:         String tag = null;
554:         Response r = null;
555:         boolean done = false;
556:
557:         try {
558:
559:•        if (noauthdebug && isTracing()) {
560:          logger.fine("AUTHENTICATE LOGIN command trace suppressed");
561:          suspendTracing();
562:         }
563:
564:         try {
565:          tag = writeCommand("AUTHENTICATE LOGIN", null);
566:         } catch (Exception ex) {
567:          // Convert this into a BYE response
568:          r = Response.byeResponse(ex);
569:          done = true;
570:         }
571:
572:         OutputStream os = getOutputStream(); // stream to IMAP server
573:
574:         /* Wrap a BASE64Encoder around a ByteArrayOutputstream
575:          * to craft b64 encoded username and password strings
576:          *
577:          * Note that the encoded bytes should be sent "as-is" to the
578:          * server, *not* as literals or quoted-strings.
579:          *
580:          * Also note that unlike the B64 definition in MIME, CRLFs
581:          * should *not* be inserted during the encoding process. So, I
582:          * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine,
583:          * which should be sufficiently large !
584:          *
585:          * Finally, format the line in a buffer so it can be sent as
586:          * a single packet, to avoid triggering a bug in SUN's SIMS 2.0
587:          * server caused by patch 105346.
588:          */
589:
590:         ByteArrayOutputStream bos = new ByteArrayOutputStream();
591:         OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE);
592:         boolean first = true;
593:
594:•        while (!done) { // loop till we are done
595:          try {
596:                 r = readResponse();
597:•                 if (r.isContinuation()) {
598:                  // Server challenge ..
599:                  String s;
600:•                 if (first) { // Send encoded username
601:                         s = u;
602:                         first = false;
603:                  } else         // Send encoded password
604:                         s = p;
605:                 
606:                  // obtain b64 encoded bytes
607:                  b64os.write(s.getBytes(StandardCharsets.UTF_8));
608:                  b64os.flush();         // complete the encoding
609:
610:                  bos.write(CRLF);         // CRLF termination
611:                  os.write(bos.toByteArray()); // write out line
612:                  os.flush();         // flush the stream
613:                  bos.reset();         // reset buffer
614:•                } else if (r.isTagged() && r.getTag().equals(tag))
615:                  // Ah, our tagged response
616:                  done = true;
617:•                else if (r.isBYE()) // outta here
618:                  done = true;
619:                 // hmm .. unsolicited response here ?!
620:          } catch (Exception ioex) {
621:                 // convert this into a BYE response
622:                 r = Response.byeResponse(ioex);
623:                 done = true;
624:          }
625:          v.add(r);
626:         }
627:
628:         } finally {
629:          resumeTracing();
630:         }
631:
632:         Response[] responses = v.toArray(new Response[v.size()]);
633:
634:         // handle an illegal but not uncommon untagged CAPABILTY response
635:         handleCapabilityResponse(responses);
636:
637:         /*
638:          * Dispatch untagged responses.
639:          * NOTE: in our current upper level IMAP classes, we add the
640:          * responseHandler to the Protocol object only *after* the
641:          * connection has been authenticated. So, for now, the below
642:          * code really ends up being just a no-op.
643:          */
644:         notifyResponseHandlers(responses);
645:
646:         // Handle the final OK, NO, BAD or BYE response
647:•        if (noauthdebug && isTracing())
648:          logger.fine("AUTHENTICATE LOGIN command result: " + r);
649:         handleLoginResult(r);
650:         // If the response includes a CAPABILITY response code, process it
651:         setCapabilities(r);
652:         // if we get this far without an exception, we're authenticated
653:         authenticated = true;
654: }
655:
656:
657: /**
658: * The AUTHENTICATE command with AUTH=PLAIN authentication scheme.
659: * This is based heavly on the {@link #authlogin} method.
660: *
661: * @param authzid                the authorization id
662: * @param u                the username
663: * @param p                the password
664: * @throws ProtocolException as thrown by {@link Protocol#handleResult}.
665: * @see "RFC3501, section 6.2.2"
666: * @see "RFC2595, section 6"
667: * @since JavaMail 1.3.2
668: */
669: public synchronized void authplain(String authzid, String u, String p)
670:                                 throws ProtocolException {
671:         List<Response> v = new ArrayList<>();
672:         String tag = null;
673:         Response r = null;
674:         boolean done = false;
675:
676:         try {
677:
678:•        if (noauthdebug && isTracing()) {
679:          logger.fine("AUTHENTICATE PLAIN command trace suppressed");
680:          suspendTracing();
681:         }
682:
683:         try {
684:          tag = writeCommand("AUTHENTICATE PLAIN", null);
685:         } catch (Exception ex) {
686:          // Convert this into a BYE response
687:          r = Response.byeResponse(ex);
688:          done = true;
689:         }
690:
691:         OutputStream os = getOutputStream(); // stream to IMAP server
692:
693:         /* Wrap a BASE64Encoder around a ByteArrayOutputstream
694:          * to craft b64 encoded username and password strings
695:          *
696:          * Note that the encoded bytes should be sent "as-is" to the
697:          * server, *not* as literals or quoted-strings.
698:          *
699:          * Also note that unlike the B64 definition in MIME, CRLFs
700:          * should *not* be inserted during the encoding process. So, I
701:          * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine,
702:          * which should be sufficiently large !
703:          *
704:          * Finally, format the line in a buffer so it can be sent as
705:          * a single packet, to avoid triggering a bug in SUN's SIMS 2.0
706:          * server caused by patch 105346.
707:          */
708:
709:         ByteArrayOutputStream bos = new ByteArrayOutputStream();
710:         OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE);
711:
712:•        while (!done) { // loop till we are done
713:          try {
714:                 r = readResponse();
715:•                if (r.isContinuation()) {
716:                  // Server challenge ..
717:                  final String nullByte = "\0";
718:•                 String s = (authzid == null ? "" : authzid) +
719:                                  nullByte + u + nullByte + p;
720:
721:                  // obtain b64 encoded bytes
722:                  b64os.write(s.getBytes(StandardCharsets.UTF_8));
723:                  b64os.flush();         // complete the encoding
724:
725:                  bos.write(CRLF);         // CRLF termination
726:                  os.write(bos.toByteArray()); // write out line
727:                  os.flush();         // flush the stream
728:                  bos.reset();         // reset buffer
729:•                } else if (r.isTagged() && r.getTag().equals(tag))
730:                  // Ah, our tagged response
731:                  done = true;
732:•                else if (r.isBYE()) // outta here
733:                  done = true;
734:                 // hmm .. unsolicited response here ?!
735:          } catch (Exception ioex) {
736:                 // convert this into a BYE response
737:                 r = Response.byeResponse(ioex);
738:                 done = true;
739:          }
740:          v.add(r);
741:         }
742:
743:         } finally {
744:          resumeTracing();
745:         }
746:
747:         Response[] responses = v.toArray(new Response[v.size()]);
748:
749:         // handle an illegal but not uncommon untagged CAPABILTY response
750:         handleCapabilityResponse(responses);
751:
752:         /*
753:          * Dispatch untagged responses.
754:          * NOTE: in our current upper level IMAP classes, we add the
755:          * responseHandler to the Protocol object only *after* the
756:          * connection has been authenticated. So, for now, the below
757:          * code really ends up being just a no-op.
758:          */
759:         notifyResponseHandlers(responses);
760:
761:         // Handle the final OK, NO, BAD or BYE response
762:•        if (noauthdebug && isTracing())
763:          logger.fine("AUTHENTICATE PLAIN command result: " + r);
764:         handleLoginResult(r);
765:         // If the response includes a CAPABILITY response code, process it
766:         setCapabilities(r);
767:         // if we get this far without an exception, we're authenticated
768:         authenticated = true;
769: }
770:
771: /**
772: * The AUTHENTICATE command with AUTH=NTLM authentication scheme.
773: * This is based heavly on the {@link #authlogin} method.
774: *
775: * @param authzid                the authorization id
776: * @param u                the username
777: * @param p                the password
778: * @throws ProtocolException as thrown by {@link Protocol#handleResult}.
779: * @see "RFC3501, section 6.2.2"
780: * @see "RFC2595, section 6"
781: * @since JavaMail 1.4.3
782: */
783: public synchronized void authntlm(String authzid, String u, String p)
784:                                 throws ProtocolException {
785:         List<Response> v = new ArrayList<>();
786:         String tag = null;
787:         Response r = null;
788:         boolean done = false;
789:
790:         int flags = PropUtil.getIntProperty(props,
791:          "mail." + name + ".auth.ntlm.flags", 0);
792:         boolean v2 = PropUtil.getBooleanProperty(props,
793:          "mail." + name + ".auth.ntlm.v2", true);
794:         String domain = props.getProperty(
795:          "mail." + name + ".auth.ntlm.domain", "");
796:         Ntlm ntlm = new Ntlm(domain, getLocalHost(), u, p, logger);
797:
798:         try {
799:
800:•        if (noauthdebug && isTracing()) {
801:          logger.fine("AUTHENTICATE NTLM command trace suppressed");
802:          suspendTracing();
803:         }
804:
805:         try {
806:          tag = writeCommand("AUTHENTICATE NTLM", null);
807:         } catch (Exception ex) {
808:          // Convert this into a BYE response
809:          r = Response.byeResponse(ex);
810:          done = true;
811:         }
812:
813:         OutputStream os = getOutputStream(); // stream to IMAP server
814:         boolean first = true;
815:
816:•        while (!done) { // loop till we are done
817:          try {
818:                 r = readResponse();
819:•                 if (r.isContinuation()) {
820:                  // Server challenge ..
821:                  String s;
822:•                 if (first) {
823:                         s = ntlm.generateType1Msg(flags, v2);
824:                         first = false;
825:                  } else {
826:                         s = ntlm.generateType3Msg(r.getRest());
827:                  }
828:
829:                  os.write(s.getBytes(StandardCharsets.UTF_8));
830:                  os.write(CRLF);         // CRLF termination
831:                  os.flush();         // flush the stream
832:•                } else if (r.isTagged() && r.getTag().equals(tag))
833:                  // Ah, our tagged response
834:                  done = true;
835:•                else if (r.isBYE()) // outta here
836:                  done = true;
837:                 // hmm .. unsolicited response here ?!
838:          } catch (Exception ioex) {
839:                 // convert this into a BYE response
840:                 r = Response.byeResponse(ioex);
841:                 done = true;
842:          }
843:          v.add(r);
844:         }
845:
846:         } finally {
847:          resumeTracing();
848:         }
849:
850:         Response[] responses = v.toArray(new Response[v.size()]);
851:
852:         // handle an illegal but not uncommon untagged CAPABILTY response
853:         handleCapabilityResponse(responses);
854:
855:         /*
856:          * Dispatch untagged responses.
857:          * NOTE: in our current upper level IMAP classes, we add the
858:          * responseHandler to the Protocol object only *after* the
859:          * connection has been authenticated. So, for now, the below
860:          * code really ends up being just a no-op.
861:          */
862:         notifyResponseHandlers(responses);
863:
864:         // Handle the final OK, NO, BAD or BYE response
865:•        if (noauthdebug && isTracing())
866:          logger.fine("AUTHENTICATE NTLM command result: " + r);
867:         handleLoginResult(r);
868:         // If the response includes a CAPABILITY response code, process it
869:         setCapabilities(r);
870:         // if we get this far without an exception, we're authenticated
871:         authenticated = true;
872: }
873:
874: /**
875: * The AUTHENTICATE command with AUTH=XOAUTH2 authentication scheme.
876: * This is based heavly on the {@link #authlogin} method.
877: *
878: * @param u                the username
879: * @param p                the password
880: * @throws ProtocolException as thrown by {@link Protocol#handleResult}.
881: * @see "RFC3501, section 6.2.2"
882: * @see "RFC2595, section 6"
883: * @since JavaMail 1.5.5
884: */
885: public synchronized void authoauth2(String u, String p)
886:                                 throws ProtocolException {
887:         List<Response> v = new ArrayList<>();
888:         String tag = null;
889:         Response r = null;
890:         boolean done = false;
891:
892:         try {
893:
894:•        if (noauthdebug && isTracing()) {
895:          logger.fine("AUTHENTICATE XOAUTH2 command trace suppressed");
896:          suspendTracing();
897:         }
898:
899:         try {
900:          Argument args = new Argument();
901:          args.writeAtom("XOAUTH2");
902:•         if (hasCapability("SASL-IR")) {
903:                 String resp = "user=" + u + "\001auth=Bearer " + p + "\001\001";
904:                 byte[] ba = Base64.getEncoder().encode(
905:                                  resp.getBytes(StandardCharsets.UTF_8));
906:                 String irs = ASCIIUtility.toString(ba, 0, ba.length);
907:                 args.writeAtom(irs);
908:          }
909:          tag = writeCommand("AUTHENTICATE", args);
910:         } catch (Exception ex) {
911:          // Convert this into a BYE response
912:          r = Response.byeResponse(ex);
913:          done = true;
914:         }
915:
916:         OutputStream os = getOutputStream(); // stream to IMAP server
917:
918:•        while (!done) { // loop till we are done
919:          try {
920:                 r = readResponse();
921:•                if (r.isContinuation()) {
922:                  // Server challenge ..
923:                  String resp = "user=" + u + "\001auth=Bearer " +
924:                                  p + "\001\001";
925:                  byte[] b = Base64.getEncoder().encode(
926:                                  resp.getBytes(StandardCharsets.UTF_8));
927:                  os.write(b);        // write out response
928:                  os.write(CRLF);         // CRLF termination
929:                  os.flush();         // flush the stream
930:•                } else if (r.isTagged() && r.getTag().equals(tag))
931:                  // Ah, our tagged response
932:                  done = true;
933:•                else if (r.isBYE()) // outta here
934:                  done = true;
935:                 // hmm .. unsolicited response here ?!
936:          } catch (Exception ioex) {
937:                 // convert this into a BYE response
938:                 r = Response.byeResponse(ioex);
939:                 done = true;
940:          }
941:          v.add(r);
942:         }
943:
944:         } finally {
945:          resumeTracing();
946:         }
947:
948:         Response[] responses = v.toArray(new Response[v.size()]);
949:
950:         // handle an illegal but not uncommon untagged CAPABILTY response
951:         handleCapabilityResponse(responses);
952:
953:         /*
954:          * Dispatch untagged responses.
955:          * NOTE: in our current upper level IMAP classes, we add the
956:          * responseHandler to the Protocol object only *after* the
957:          * connection has been authenticated. So, for now, the below
958:          * code really ends up being just a no-op.
959:          */
960:         notifyResponseHandlers(responses);
961:
962:         // Handle the final OK, NO, BAD or BYE response
963:•        if (noauthdebug && isTracing())
964:          logger.fine("AUTHENTICATE XOAUTH2 command result: " + r);
965:         handleLoginResult(r);
966:         // If the response includes a CAPABILITY response code, process it
967:         setCapabilities(r);
968:         // if we get this far without an exception, we're authenticated
969:         authenticated = true;
970: }
971:
972: /**
973: * SASL-based login.
974: *
975: * @param        allowed        the SASL mechanisms we're allowed to use
976: * @param        realm        the SASL realm
977: * @param        authzid        the authorization id
978: * @param        u        the username
979: * @param        p        the password
980: * @exception        ProtocolException        for protocol failures
981: */
982: public void sasllogin(String[] allowed, String realm, String authzid,
983:                                 String u, String p) throws ProtocolException {
984:         boolean useCanonicalHostName = PropUtil.getBooleanProperty(props,
985:                  "mail." + name + ".sasl.usecanonicalhostname", false);
986:         String serviceHost;
987:•        if (useCanonicalHostName)
988:          serviceHost = getInetAddress().getCanonicalHostName();
989:         else
990:          serviceHost = host;
991:•        if (saslAuthenticator == null) {
992:          try {
993:                 Class<?> sac = Class.forName(
994: "org.eclipse.angus.mail.imap.protocol.IMAPSaslAuthenticator");
995:                 Constructor<?> c = sac.getConstructor(new Class<?>[] {
996:                                         IMAPProtocol.class,
997:                                         String.class,
998:                                         Properties.class,
999:                                         MailLogger.class,
1000:                                         String.class
1001:                                         });
1002:                 saslAuthenticator = (SaslAuthenticator)c.newInstance(
1003:                                         new Object[] {
1004:                                         this,
1005:                                         name,
1006:                                         props,
1007:                                         logger,
1008:                                         serviceHost
1009:                                         });
1010:          } catch (Exception ex) {
1011:                 logger.log(Level.FINE, "Can't load SASL authenticator", ex);
1012:                 // probably because we're running on a system without SASL
1013:                 return;        // not authenticated, try without SASL
1014:          }
1015:         }
1016:
1017:         // were any allowed mechanisms specified?
1018:         List<String> v;
1019:•        if (allowed != null && allowed.length > 0) {
1020:          // remove anything not supported by the server
1021:          v = new ArrayList<>(allowed.length);
1022:•         for (int i = 0; i < allowed.length; i++)
1023:•                if (authmechs.contains(allowed[i]))        // XXX - case must match
1024:                  v.add(allowed[i]);
1025:         } else {
1026:          // everything is allowed
1027:          v = authmechs;
1028:         }
1029:         String[] mechs = v.toArray(new String[v.size()]);
1030:
1031:         try {
1032:
1033:•         if (noauthdebug && isTracing()) {
1034:                 logger.fine("SASL authentication command trace suppressed");
1035:                 suspendTracing();
1036:          }
1037:
1038:•         if (saslAuthenticator.authenticate(mechs, realm, authzid, u, p)) {
1039:•                if (noauthdebug && isTracing())
1040:                  logger.fine("SASL authentication succeeded");
1041:                 authenticated = true;
1042:          } else {
1043:•                if (noauthdebug && isTracing())
1044:                  logger.fine("SASL authentication failed");
1045:          }
1046:         } finally {
1047:          resumeTracing();
1048:         }
1049: }
1050:
1051: // XXX - for IMAPSaslAuthenticator access to protected method
1052: OutputStream getIMAPOutputStream() {
1053:         return getOutputStream();
1054: }
1055:
1056: /**
1057: * Handle the result response for a LOGIN or AUTHENTICATE command.
1058: * Look for IMAP login REFERRAL.
1059: *
1060: * @param        r        the response
1061: * @exception        ProtocolException        for protocol failures
1062: * @since        JavaMail 1.5.5
1063: */
1064: protected void handleLoginResult(Response r) throws ProtocolException {
1065:•        if (hasCapability("LOGIN-REFERRALS") &&
1066:•                (!r.isOK() || referralException))
1067:          checkReferral(r);
1068:         handleResult(r);
1069: }
1070:
1071: /**
1072: * PROXYAUTH Command.
1073: *
1074: * @param        u        the PROXYAUTH user name
1075: * @exception        ProtocolException        for protocol failures
1076: * @see "Netscape/iPlanet/SunONE Messaging Server extension"
1077: */
1078: public void proxyauth(String u) throws ProtocolException {
1079:         Argument args = new Argument();
1080:         args.writeString(u);
1081:
1082:         simpleCommand("PROXYAUTH", args);
1083:         proxyAuthUser = u;
1084: }
1085:
1086: /**
1087: * Get the user name used with the PROXYAUTH command.
1088: * Returns null if PROXYAUTH was not used.
1089: *
1090: * @return        the PROXYAUTH user name
1091: * @since        JavaMail 1.5.1
1092: */
1093: public String getProxyAuthUser() {
1094:         return proxyAuthUser;
1095: }
1096:
1097: /**
1098: * UNAUTHENTICATE Command.
1099: *
1100: * @exception        ProtocolException        for protocol failures
1101: * @see "Netscape/iPlanet/SunONE Messaging Server extension"
1102: * @since        JavaMail 1.5.1
1103: */
1104: public void unauthenticate() throws ProtocolException {
1105:•        if (!hasCapability("X-UNAUTHENTICATE"))
1106:          throw new BadCommandException("UNAUTHENTICATE not supported");
1107:         simpleCommand("UNAUTHENTICATE", null);
1108:         authenticated = false;
1109: }
1110:
1111: /**
1112: * ID Command, for Yahoo! Mail IMAP server.
1113: *
1114: * @param        guid        the GUID
1115: * @exception        ProtocolException        for protocol failures
1116: * @deprecated As of JavaMail 1.5.1, replaced by
1117: *                {@link #id(Map) id(Map<String,String>)}
1118: * @since JavaMail 1.4.4
1119: */
1120: @Deprecated
1121: public void id(String guid) throws ProtocolException {
1122:         // support this for now, but remove it soon
1123:         Map<String,String> gmap = new HashMap<>();
1124:         gmap.put("GUID", guid);
1125:         id(gmap);
1126: }
1127:
1128: /**
1129: * STARTTLS Command.
1130: *
1131: * @exception        ProtocolException        for protocol failures
1132: * @see "RFC3501, section 6.2.1"
1133: */
1134: public void startTLS() throws ProtocolException {
1135:         try {
1136:          super.startTLS("STARTTLS");
1137:         } catch (ProtocolException pex) {
1138:          logger.log(Level.FINE, "STARTTLS ProtocolException", pex);
1139:          // ProtocolException just means the command wasn't recognized,
1140:          // or failed. This should never happen if we check the
1141:          // CAPABILITY first.
1142:          throw pex;
1143:         } catch (Exception ex) {
1144:          logger.log(Level.FINE, "STARTTLS Exception", ex);
1145:          // any other exception means we have to shut down the connection
1146:          // generate an artificial BYE response and disconnect
1147:          Response[] r = { Response.byeResponse(ex) };
1148:          notifyResponseHandlers(r);
1149:          disconnect();
1150:          throw new ProtocolException("STARTTLS failure", ex);
1151:         }
1152: }
1153:
1154: /**
1155: * COMPRESS Command. Only supports DEFLATE.
1156: *
1157: * @exception        ProtocolException        for protocol failures
1158: * @see "RFC 4978"
1159: */
1160: public void compress() throws ProtocolException {
1161:         try {
1162:          super.startCompression("COMPRESS DEFLATE");
1163:         } catch (ProtocolException pex) {
1164:          logger.log(Level.FINE, "COMPRESS ProtocolException", pex);
1165:          // ProtocolException just means the command wasn't recognized,
1166:          // or failed. This should never happen if we check the
1167:          // CAPABILITY first.
1168:          throw pex;
1169:         } catch (Exception ex) {
1170:          logger.log(Level.FINE, "COMPRESS Exception", ex);
1171:          // any other exception means we have to shut down the connection
1172:          // generate an artificial BYE response and disconnect
1173:          Response[] r = { Response.byeResponse(ex) };
1174:          notifyResponseHandlers(r);
1175:          disconnect();
1176:          throw new ProtocolException("COMPRESS failure", ex);
1177:         }
1178: }
1179:
1180: /**
1181: * Encode a mailbox name appropriately depending on whether or not
1182: * the server supports UTF-8, and add the encoded name to the
1183: * Argument.
1184: *
1185: * @param        args        the arguments
1186: * @param        name        the name to encode
1187: * @since        JavaMail 1.6.0
1188: */
1189: protected void writeMailboxName(Argument args, String name) {
1190:•        if (utf8)
1191:          args.writeString(name, StandardCharsets.UTF_8);
1192:         else
1193:          // encode the mbox as per RFC2060
1194:          args.writeString(BASE64MailboxEncoder.encode(name));
1195: }
1196:
1197: /**
1198: * SELECT Command.
1199: *
1200: * @param        mbox        the mailbox name
1201: * @return                MailboxInfo if successful
1202: * @exception        ProtocolException        for protocol failures
1203: * @see "RFC2060, section 6.3.1"
1204: */
1205: public MailboxInfo select(String mbox) throws ProtocolException {
1206:         return select(mbox, null);
1207: }
1208:
1209: /**
1210: * SELECT Command with QRESYNC data.
1211: *
1212: * @param        mbox        the mailbox name
1213: * @param        rd        the ResyncData
1214: * @return                MailboxInfo if successful
1215: * @exception        ProtocolException        for protocol failures
1216: * @see "RFC2060, section 6.3.1"
1217: * @see "RFC5162, section 3.1"
1218: * @since        JavaMail 1.5.1
1219: */
1220: public MailboxInfo select(String mbox, ResyncData rd)
1221:                                 throws ProtocolException {
1222:         Argument args = new Argument();        
1223:         writeMailboxName(args, mbox);
1224:
1225:•        if (rd != null) {
1226:•         if (rd == ResyncData.CONDSTORE) {
1227:•                if (!hasCapability("CONDSTORE"))
1228:                  throw new BadCommandException("CONDSTORE not supported");
1229:                 args.writeArgument(new Argument().writeAtom("CONDSTORE"));
1230:          } else {
1231:•                if (!hasCapability("QRESYNC"))
1232:                  throw new BadCommandException("QRESYNC not supported");
1233:                 args.writeArgument(resyncArgs(rd));
1234:          }
1235:         }
1236:
1237:         Response[] r = command("SELECT", args);
1238:
1239:         // Note that MailboxInfo also removes those responses
1240:         // it knows about
1241:         MailboxInfo minfo = new MailboxInfo(r);
1242:         
1243:         // dispatch any remaining untagged responses
1244:         notifyResponseHandlers(r);
1245:
1246:         Response response = r[r.length-1];
1247:
1248:•        if (response.isOK()) { // command succesful
1249:•         if (response.toString().indexOf("READ-ONLY") != -1)
1250:                 minfo.mode = Folder.READ_ONLY;
1251:          else
1252:                 minfo.mode = Folder.READ_WRITE;
1253:         }
1254:         
1255:         handleResult(response);
1256:         return minfo;
1257: }
1258:
1259: /**
1260: * EXAMINE Command.
1261: *
1262: * @param        mbox        the mailbox name
1263: * @return                MailboxInfo if successful
1264: * @exception        ProtocolException        for protocol failures
1265: * @see "RFC2060, section 6.3.2"
1266: */
1267: public MailboxInfo examine(String mbox) throws ProtocolException {
1268:         return examine(mbox, null);
1269: }
1270:
1271: /**
1272: * EXAMINE Command with QRESYNC data.
1273: *
1274: * @param        mbox        the mailbox name
1275: * @param        rd        the ResyncData
1276: * @return                MailboxInfo if successful
1277: * @exception        ProtocolException        for protocol failures
1278: * @see "RFC2060, section 6.3.2"
1279: * @see "RFC5162, section 3.1"
1280: * @since        JavaMail 1.5.1
1281: */
1282: public MailboxInfo examine(String mbox, ResyncData rd)
1283:                                 throws ProtocolException {
1284:         Argument args = new Argument();        
1285:         writeMailboxName(args, mbox);
1286:
1287:•        if (rd != null) {
1288:•         if (rd == ResyncData.CONDSTORE) {
1289:•                if (!hasCapability("CONDSTORE"))
1290:                  throw new BadCommandException("CONDSTORE not supported");
1291:                 args.writeArgument(new Argument().writeAtom("CONDSTORE"));
1292:          } else {
1293:•                if (!hasCapability("QRESYNC"))
1294:                  throw new BadCommandException("QRESYNC not supported");
1295:                 args.writeArgument(resyncArgs(rd));
1296:          }
1297:         }
1298:
1299:         Response[] r = command("EXAMINE", args);
1300:
1301:         // Note that MailboxInfo also removes those responses
1302:         // it knows about
1303:         MailboxInfo minfo = new MailboxInfo(r);
1304:         minfo.mode = Folder.READ_ONLY; // Obviously
1305:
1306:         // dispatch any remaining untagged responses
1307:         notifyResponseHandlers(r);
1308:
1309:         handleResult(r[r.length-1]);
1310:         return minfo;
1311: }
1312:
1313: /**
1314: * Generate a QRESYNC argument list based on the ResyncData.
1315: */
1316: private static Argument resyncArgs(ResyncData rd) {
1317:         Argument cmd = new Argument();
1318:         cmd.writeAtom("QRESYNC");
1319:         Argument args = new Argument();
1320:         args.writeNumber(rd.getUIDValidity());
1321:         args.writeNumber(rd.getModSeq());
1322:         UIDSet[] uids = Utility.getResyncUIDSet(rd);
1323:•        if (uids != null)
1324:          args.writeString(UIDSet.toString(uids));
1325:         cmd.writeArgument(args);
1326:         return cmd;
1327: }
1328:
1329: /**
1330: * ENABLE Command.
1331: *
1332: * @param        cap        the name of the capability to enable
1333: * @exception        ProtocolException        for protocol failures
1334: * @see "RFC 5161"
1335: * @since        JavaMail 1.5.1
1336: */
1337: public void enable(String cap) throws ProtocolException {
1338:•        if (!hasCapability("ENABLE"))
1339:          throw new BadCommandException("ENABLE not supported");
1340:         Argument args = new Argument();
1341:         args.writeAtom(cap);
1342:         simpleCommand("ENABLE", args);
1343:•        if (enabled == null)
1344:          enabled = new HashSet<>();
1345:         enabled.add(cap.toUpperCase(Locale.ENGLISH));
1346:
1347:         // update the utf8 flag
1348:         utf8 = isEnabled("UTF8=ACCEPT");
1349: }
1350:
1351: /**
1352: * Is the capability/extension enabled?
1353: *
1354: * @param        cap        the capability name
1355: * @return                true if enabled
1356: * @see "RFC 5161"
1357: * @since        JavaMail 1.5.1
1358: */
1359: public boolean isEnabled(String cap) {
1360:•        if (enabled == null)
1361:          return false;
1362:         else
1363:          return enabled.contains(cap.toUpperCase(Locale.ENGLISH));
1364: }
1365:
1366: /**
1367: * UNSELECT Command.
1368: *
1369: * @exception        ProtocolException        for protocol failures
1370: * @see "RFC 3691"
1371: * @since        JavaMail 1.4.4
1372: */
1373: public void unselect() throws ProtocolException {
1374:•        if (!hasCapability("UNSELECT"))
1375:          throw new BadCommandException("UNSELECT not supported");
1376:         simpleCommand("UNSELECT", null);
1377: }
1378:
1379: /**
1380: * STATUS Command.
1381: *
1382: * @param        mbox        the mailbox
1383: * @param        items        the STATUS items to request
1384: * @return                STATUS results
1385: * @exception        ProtocolException        for protocol failures
1386: * @see "RFC2060, section 6.3.10"
1387: */
1388: public Status status(String mbox, String[] items)
1389:                 throws ProtocolException {
1390:•        if (!isREV1() && !hasCapability("IMAP4SUNVERSION"))
1391:          // STATUS is rev1 only, however the non-rev1 SIMS2.0
1392:          // does support this.
1393:          throw new BadCommandException("STATUS not supported");
1394:
1395:         Argument args = new Argument();        
1396:         writeMailboxName(args, mbox);
1397:
1398:         Argument itemArgs = new Argument();
1399:•        if (items == null)
1400:          items = Status.standardItems;
1401:
1402:•        for (int i = 0, len = items.length; i < len; i++)
1403:          itemArgs.writeAtom(items[i]);
1404:         args.writeArgument(itemArgs);
1405:
1406:         Response[] r = command("STATUS", args);
1407:
1408:         Status status = null;
1409:         Response response = r[r.length-1];
1410:
1411:         // Grab all STATUS responses
1412:•        if (response.isOK()) { // command succesful
1413:•         for (int i = 0, len = r.length; i < len; i++) {
1414:•                if (!(r[i] instanceof IMAPResponse))
1415:                  continue;
1416:
1417:                 IMAPResponse ir = (IMAPResponse)r[i];
1418:•                if (ir.keyEquals("STATUS")) {
1419:•                 if (status == null)
1420:                         status = new Status(ir);
1421:                  else // collect 'em all
1422:                         Status.add(status, new Status(ir));
1423:                  r[i] = null;
1424:                 }
1425:          }
1426:         }
1427:
1428:         // dispatch remaining untagged responses
1429:         notifyResponseHandlers(r);
1430:         handleResult(response);
1431:         return status;
1432: }
1433:
1434: /**
1435: * CREATE Command.
1436: *
1437: * @param        mbox        the mailbox to create
1438: * @exception        ProtocolException        for protocol failures
1439: * @see "RFC2060, section 6.3.3"
1440: */
1441: public void create(String mbox) throws ProtocolException {
1442:         Argument args = new Argument();        
1443:         writeMailboxName(args, mbox);
1444:
1445:         simpleCommand("CREATE", args);
1446: }
1447:
1448: /**
1449: * DELETE Command.
1450: *
1451: * @param        mbox        the mailbox to delete
1452: * @exception        ProtocolException        for protocol failures
1453: * @see "RFC2060, section 6.3.4"
1454: */
1455: public void delete(String mbox) throws ProtocolException {
1456:         Argument args = new Argument();        
1457:         writeMailboxName(args, mbox);
1458:
1459:         simpleCommand("DELETE", args);
1460: }
1461:
1462: /**
1463: * RENAME Command.
1464: *
1465: * @param        o        old mailbox name
1466: * @param        n        new mailbox name
1467: * @exception        ProtocolException        for protocol failures
1468: * @see "RFC2060, section 6.3.5"
1469: */
1470: public void rename(String o, String n) throws ProtocolException {
1471:         Argument args = new Argument();        
1472:         writeMailboxName(args, o);
1473:         writeMailboxName(args, n);
1474:
1475:         simpleCommand("RENAME", args);
1476: }
1477:
1478: /**
1479: * SUBSCRIBE Command.
1480: *
1481: * @param        mbox        the mailbox
1482: * @exception        ProtocolException        for protocol failures
1483: * @see "RFC2060, section 6.3.6"
1484: */
1485: public void subscribe(String mbox) throws ProtocolException {
1486:         Argument args = new Argument();        
1487:         writeMailboxName(args, mbox);
1488:
1489:         simpleCommand("SUBSCRIBE", args);
1490: }
1491:
1492: /**
1493: * UNSUBSCRIBE Command.
1494: *
1495: * @param        mbox        the mailbox
1496: * @exception        ProtocolException        for protocol failures
1497: * @see "RFC2060, section 6.3.7"
1498: */
1499: public void unsubscribe(String mbox) throws ProtocolException {
1500:         Argument args = new Argument();        
1501:         writeMailboxName(args, mbox);
1502:
1503:         simpleCommand("UNSUBSCRIBE", args);
1504: }
1505:
1506: /**
1507: * LIST Command.
1508: *
1509: * @param        ref        reference string
1510: * @param        pattern        pattern to list
1511: * @return                LIST results
1512: * @exception        ProtocolException        for protocol failures
1513: * @see "RFC2060, section 6.3.8"
1514: */
1515: public ListInfo[] list(String ref, String pattern)
1516:                         throws ProtocolException {
1517:         return doList("LIST", ref, pattern);
1518: }
1519:
1520: /**
1521: * LSUB Command.
1522: *
1523: * @param        ref        reference string
1524: * @param        pattern        pattern to list
1525: * @return                LSUB results
1526: * @exception        ProtocolException        for protocol failures
1527: * @see "RFC2060, section 6.3.9"
1528: */
1529: public ListInfo[] lsub(String ref, String pattern)
1530:                         throws ProtocolException {
1531:         return doList("LSUB", ref, pattern);
1532: }
1533:
1534: /**
1535: * Execute the specified LIST-like command (e.g., "LIST" or "LSUB"),
1536: * using the reference and pattern.
1537: *
1538: * @param        cmd        the list command
1539: * @param        ref        the reference string
1540: * @param        pat        the pattern
1541: * @return                array of ListInfo results
1542: * @exception        ProtocolException        for protocol failures
1543: * @since JavaMail 1.4.6
1544: */
1545: protected ListInfo[] doList(String cmd, String ref, String pat)
1546:                         throws ProtocolException {
1547:         Argument args = new Argument();        
1548:         writeMailboxName(args, ref);
1549:         writeMailboxName(args, pat);
1550:
1551:         Response[] r = command(cmd, args);
1552:
1553:         ListInfo[] linfo = null;
1554:         Response response = r[r.length-1];
1555:
1556:•        if (response.isOK()) { // command succesful
1557:          List<ListInfo> v = new ArrayList<>(1);
1558:•         for (int i = 0, len = r.length; i < len; i++) {
1559:•                if (!(r[i] instanceof IMAPResponse))
1560:                  continue;
1561:
1562:                 IMAPResponse ir = (IMAPResponse)r[i];
1563:•                if (ir.keyEquals(cmd)) {
1564:                  v.add(new ListInfo(ir));
1565:                  r[i] = null;
1566:                 }
1567:          }
1568:•         if (v.size() > 0) {
1569:                 linfo = v.toArray(new ListInfo[v.size()]);
1570:          }
1571:         }
1572:         
1573:         // Dispatch remaining untagged responses
1574:         notifyResponseHandlers(r);
1575:         handleResult(response);
1576:         return linfo;
1577: }
1578:                 
1579: /**
1580: * APPEND Command.
1581: *
1582: * @param        mbox        the mailbox
1583: * @param        f        the message Flags
1584: * @param        d        the message date
1585: * @param        data        the message data
1586: * @exception        ProtocolException        for protocol failures
1587: * @see "RFC2060, section 6.3.11"
1588: */
1589: public void append(String mbox, Flags f, Date d,
1590:                         Literal data) throws ProtocolException {
1591:         appenduid(mbox, f, d, data, false);        // ignore return value
1592: }
1593:
1594: /**
1595: * APPEND Command, return uid from APPENDUID response code.
1596: *
1597: * @param        mbox        the mailbox
1598: * @param        f        the message Flags
1599: * @param        d        the message date
1600: * @param        data        the message data
1601: * @return                APPENDUID data
1602: * @exception        ProtocolException        for protocol failures
1603: * @see "RFC2060, section 6.3.11"
1604: */
1605: public AppendUID appenduid(String mbox, Flags f, Date d,
1606: Literal data) throws ProtocolException {
1607:         return appenduid(mbox, f, d, data, true);
1608: }
1609:
1610: public AppendUID appenduid(String mbox, Flags f, Date d,
1611:                         Literal data, boolean uid) throws ProtocolException {
1612:         Argument args = new Argument();        
1613:         writeMailboxName(args, mbox);
1614:
1615:•        if (f != null) { // set Flags in appended message
1616:          // can't set the \Recent flag in APPEND
1617:•         if (f.contains(Flags.Flag.RECENT)) {
1618:                 f = new Flags(f);                // copy, don't modify orig
1619:                 f.remove(Flags.Flag.RECENT);        // remove RECENT from copy
1620:          }
1621:
1622:          /*
1623:          * HACK ALERT: We want the flag_list to be written out
1624:          * without any checking/processing of the bytes in it. If
1625:          * I use writeString(), the flag_list will end up being
1626:          * quoted since it contains "illegal" characters. So I
1627:          * am depending on implementation knowledge that writeAtom()
1628:          * does not do any checking/processing - it just writes out
1629:          * the bytes. What we really need is a writeFoo() that just
1630:          * dumps out its argument.
1631:          */
1632:          args.writeAtom(createFlagList(f));
1633:         }
1634:•        if (d != null) // set INTERNALDATE in appended message
1635:          args.writeString(INTERNALDATE.format(d));
1636:
1637:         args.writeBytes(data);
1638:
1639:         Response[] r = command("APPEND", args);
1640:
1641:         // dispatch untagged responses
1642:         notifyResponseHandlers(r);
1643:
1644:         // Handle result of this command
1645:         handleResult(r[r.length-1]);
1646:
1647:•        if (uid)
1648:          return getAppendUID(r[r.length-1]);
1649:         else
1650:          return null;
1651: }
1652:
1653: /**
1654: * If the response contains an APPENDUID response code, extract
1655: * it and return an AppendUID object with the information.
1656: */
1657: private AppendUID getAppendUID(Response r) {
1658:•        if (!r.isOK())
1659:          return null;
1660:         byte b;
1661:•        while ((b = r.readByte()) > 0 && b != (byte)'[')
1662:          ;
1663:•        if (b == 0)
1664:          return null;
1665:         String s;
1666:         s = r.readAtom();
1667:•        if (!s.equalsIgnoreCase("APPENDUID"))
1668:          return null;
1669:
1670:         long uidvalidity = r.readLong();
1671:         long uid = r.readLong();
1672:         return new AppendUID(uidvalidity, uid);
1673: }
1674:
1675: /**
1676: * CHECK Command.
1677: *
1678: * @exception        ProtocolException        for protocol failures
1679: * @see "RFC2060, section 6.4.1"
1680: */
1681: public void check() throws ProtocolException {
1682:         simpleCommand("CHECK", null);
1683: }
1684:
1685: /**
1686: * CLOSE Command.
1687: *
1688: * @exception        ProtocolException        for protocol failures
1689: * @see "RFC2060, section 6.4.2"
1690: */
1691: public void close() throws ProtocolException {
1692:         simpleCommand("CLOSE", null);
1693: }
1694:
1695: /**
1696: * EXPUNGE Command.
1697: *
1698: * @exception        ProtocolException        for protocol failures
1699: * @see "RFC2060, section 6.4.3"
1700: */
1701: public void expunge() throws ProtocolException {
1702:         simpleCommand("EXPUNGE", null);
1703: }
1704:
1705: /**
1706: * UID EXPUNGE Command.
1707: *
1708: * @param        set        UIDs to expunge
1709: * @exception        ProtocolException        for protocol failures
1710: * @see "RFC4315, section 2"
1711: */
1712: public void uidexpunge(UIDSet[] set) throws ProtocolException {
1713:•        if (!hasCapability("UIDPLUS"))
1714:          throw new BadCommandException("UID EXPUNGE not supported");
1715:         simpleCommand("UID EXPUNGE " + UIDSet.toString(set), null);
1716: }
1717:
1718: /**
1719: * Fetch the BODYSTRUCTURE of the specified message.
1720: *
1721: * @param        msgno        the message number
1722: * @return                the BODYSTRUCTURE item
1723: * @exception        ProtocolException        for protocol failures
1724: */
1725: public BODYSTRUCTURE fetchBodyStructure(int msgno)
1726:                         throws ProtocolException {
1727:         Response[] r = fetch(msgno, "BODYSTRUCTURE");
1728:         notifyResponseHandlers(r);
1729:
1730:         Response response = r[r.length-1];
1731:•        if (response.isOK())
1732:          return FetchResponse.getItem(r, msgno, BODYSTRUCTURE.class);
1733:•        else if (response.isNO())
1734:          return null;
1735:         else {
1736:          handleResult(response);
1737:          return null;
1738:         }
1739: }
1740:
1741: /**
1742: * Fetch given BODY section, without marking the message
1743: * as SEEN.
1744: *
1745: * @param        msgno        the message number
1746: * @param        section        the body section
1747: * @return                the BODY item
1748: * @exception        ProtocolException        for protocol failures
1749: */
1750: public BODY peekBody(int msgno, String section)
1751:                         throws ProtocolException {
1752:         return fetchBody(msgno, section, true);
1753: }
1754:
1755: /**
1756: * Fetch given BODY section.
1757: *
1758: * @param        msgno        the message number
1759: * @param        section        the body section
1760: * @return                the BODY item
1761: * @exception        ProtocolException        for protocol failures
1762: */
1763: public BODY fetchBody(int msgno, String section)
1764:                         throws ProtocolException {
1765:         return fetchBody(msgno, section, false);
1766: }
1767:
1768: protected BODY fetchBody(int msgno, String section, boolean peek)
1769:                         throws ProtocolException {
1770:         Response[] r;
1771:
1772:•        if (section == null)
1773:          section = "";
1774:•        String body = (peek ? "BODY.PEEK[" : "BODY[") + section + "]";
1775:         return fetchSectionBody(msgno, section, body);
1776: }
1777:
1778: /**
1779: * Partial FETCH of given BODY section, without setting SEEN flag.
1780: *
1781: * @param        msgno        the message number
1782: * @param        section        the body section
1783: * @param        start        starting byte count
1784: * @param        size        number of bytes to fetch
1785: * @return                the BODY item
1786: * @exception        ProtocolException        for protocol failures
1787: */
1788: public BODY peekBody(int msgno, String section, int start, int size)
1789:                         throws ProtocolException {
1790:         return fetchBody(msgno, section, start, size, true, null);
1791: }
1792:
1793: /**
1794: * Partial FETCH of given BODY section.
1795: *
1796: * @param        msgno        the message number
1797: * @param        section        the body section
1798: * @param        start        starting byte count
1799: * @param        size        number of bytes to fetch
1800: * @return                the BODY item
1801: * @exception        ProtocolException        for protocol failures
1802: */
1803: public BODY fetchBody(int msgno, String section, int start, int size)
1804:                         throws ProtocolException {
1805:         return fetchBody(msgno, section, start, size, false, null);
1806: }
1807:
1808: /**
1809: * Partial FETCH of given BODY section, without setting SEEN flag.
1810: *
1811: * @param        msgno        the message number
1812: * @param        section        the body section
1813: * @param        start        starting byte count
1814: * @param        size        number of bytes to fetch
1815: * @param        ba        the buffer into which to read the response
1816: * @return                the BODY item
1817: * @exception        ProtocolException        for protocol failures
1818: */
1819: public BODY peekBody(int msgno, String section, int start, int size,
1820:                                 ByteArray ba) throws ProtocolException {
1821:         return fetchBody(msgno, section, start, size, true, ba);
1822: }
1823:
1824: /**
1825: * Partial FETCH of given BODY section.
1826: *
1827: * @param        msgno        the message number
1828: * @param        section        the body section
1829: * @param        start        starting byte count
1830: * @param        size        number of bytes to fetch
1831: * @param        ba        the buffer into which to read the response
1832: * @return                the BODY item
1833: * @exception        ProtocolException        for protocol failures
1834: */
1835: public BODY fetchBody(int msgno, String section, int start, int size,
1836:                                 ByteArray ba) throws ProtocolException {
1837:         return fetchBody(msgno, section, start, size, false, ba);
1838: }
1839:
1840: protected BODY fetchBody(int msgno, String section, int start, int size,
1841:                         boolean peek, ByteArray ba) throws ProtocolException {
1842:         this.ba = ba;        // save for later use by getResponseBuffer
1843:•        if (section == null)
1844:          section = "";
1845:•        String body = (peek ? "BODY.PEEK[" : "BODY[") + section + "]<" +
1846:                         String.valueOf(start) + "." +
1847:                         String.valueOf(size) + ">";
1848:         return fetchSectionBody(msgno, section, body);
1849: }
1850:
1851: /**
1852: * Fetch the given body section of the given message, using the
1853: * body string "body".
1854: *
1855: * @param        msgno        the message number
1856: * @param        section        the body section
1857: * @param        body        the body string
1858: * @return                the BODY item
1859: * @exception        ProtocolException        for protocol failures
1860: */
1861: protected BODY fetchSectionBody(int msgno, String section, String body)
1862:                         throws ProtocolException {
1863:         Response[] r;
1864:
1865:         r = fetch(msgno, body);
1866:         notifyResponseHandlers(r);
1867:
1868:         Response response = r[r.length-1];
1869:•        if (response.isOK()) {
1870:          List<BODY> bl = FetchResponse.getItems(r, msgno, BODY.class);
1871:•         if (bl.size() == 1)
1872:                 return bl.get(0);        // the common case
1873:•         if (logger.isLoggable(Level.FINEST))
1874:                 logger.finest("got " + bl.size() +
1875:                                 " BODY responses for section " + section);
1876:          // more then one BODY response, have to find the right one
1877:•         for (BODY br : bl) {
1878:•                if (logger.isLoggable(Level.FINEST))
1879:                  logger.finest("got BODY section " + br.getSection());
1880:•                if (br.getSection().equalsIgnoreCase(section))
1881:                  return br;        // that's the one!
1882:          }
1883:          return null;        // couldn't find it
1884:•        } else if (response.isNO())
1885:          return null;
1886:         else {
1887:          handleResult(response);
1888:          return null;
1889:         }
1890: }
1891:
1892: /**
1893: * Return a buffer to read a response into.
1894: * The buffer is provided by fetchBody and is
1895: * used only once.
1896: *
1897: * @return        the buffer to use
1898: */
1899: @Override
1900: protected ByteArray getResponseBuffer() {
1901:         ByteArray ret = ba;
1902:         ba = null;
1903:         return ret;
1904: }
1905:
1906: /**
1907: * Fetch the specified RFC822 Data item. 'what' names
1908: * the item to be fetched. 'what' can be <code>null</code>
1909: * to fetch the whole message.
1910: *
1911: * @param        msgno        the message number
1912: * @param        what        the item to fetch
1913: * @return                the RFC822DATA item
1914: * @exception        ProtocolException        for protocol failures
1915: */
1916: public RFC822DATA fetchRFC822(int msgno, String what)
1917:                         throws ProtocolException {
1918:         Response[] r = fetch(msgno,
1919:•                         what == null ? "RFC822" : "RFC822." + what
1920:                          );
1921:
1922:         // dispatch untagged responses
1923:         notifyResponseHandlers(r);
1924:
1925:         Response response = r[r.length-1];
1926:•        if (response.isOK())
1927:          return FetchResponse.getItem(r, msgno, RFC822DATA.class);
1928:•        else if (response.isNO())
1929:          return null;
1930:         else {
1931:          handleResult(response);
1932:          return null;
1933:         }
1934: }
1935:
1936: /**
1937: * Fetch the FLAGS for the given message.
1938: *
1939: * @param        msgno        the message number
1940: * @return                the Flags
1941: * @exception        ProtocolException        for protocol failures
1942: */
1943: public Flags fetchFlags(int msgno) throws ProtocolException {
1944:         Flags flags = null;
1945:         Response[] r = fetch(msgno, "FLAGS");
1946:
1947:         // Search for our FLAGS response
1948:•        for (int i = 0, len = r.length; i < len; i++) {
1949:•         if (r[i] == null ||
1950:                 !(r[i] instanceof FetchResponse) ||
1951:•                ((FetchResponse)r[i]).getNumber() != msgno)
1952:                 continue;                
1953:         
1954:          FetchResponse fr = (FetchResponse)r[i];
1955:•         if ((flags = fr.getItem(FLAGS.class)) != null) {
1956:                 r[i] = null; // remove this response
1957:                 break;
1958:          }
1959:         }
1960:
1961:         // dispatch untagged responses
1962:         notifyResponseHandlers(r);
1963:         handleResult(r[r.length-1]);
1964:         return flags;
1965: }
1966:
1967: /**
1968: * Fetch the IMAP UID for the given message.
1969: *
1970: * @param        msgno        the message number
1971: * @return                the UID
1972: * @exception        ProtocolException        for protocol failures
1973: */
1974: public UID fetchUID(int msgno) throws ProtocolException {
1975:         Response[] r = fetch(msgno, "UID");
1976:
1977:         // dispatch untagged responses
1978:         notifyResponseHandlers(r);
1979:
1980:         Response response = r[r.length-1];
1981:•        if (response.isOK())
1982:          return FetchResponse.getItem(r, msgno, UID.class);
1983:•        else if (response.isNO()) // XXX: Issue NOOP ?
1984:          return null;
1985:         else {
1986:          handleResult(response);
1987:          return null; // NOTREACHED
1988:         }
1989: }
1990:
1991: /**
1992: * Fetch the IMAP MODSEQ for the given message.
1993: *
1994: * @param        msgno        the message number
1995: * @return                the MODSEQ
1996: * @exception        ProtocolException        for protocol failures
1997: * @since        JavaMail 1.5.1
1998: */
1999: public MODSEQ fetchMODSEQ(int msgno) throws ProtocolException {
2000:         Response[] r = fetch(msgno, "MODSEQ");
2001:
2002:         // dispatch untagged responses
2003:         notifyResponseHandlers(r);
2004:
2005:         Response response = r[r.length-1];
2006:•        if (response.isOK())
2007:          return FetchResponse.getItem(r, msgno, MODSEQ.class);
2008:•        else if (response.isNO()) // XXX: Issue NOOP ?
2009:          return null;
2010:         else {
2011:          handleResult(response);
2012:          return null; // NOTREACHED
2013:         }
2014: }
2015:                 
2016: /**
2017: * Get the sequence number for the given UID. Nothing is returned;
2018: * the FETCH UID response must be handled by the reponse handler,
2019: * along with any possible EXPUNGE responses, to ensure that the
2020: * UID is matched with the correct sequence number.
2021: *
2022: * @param        uid        the UID
2023: * @exception        ProtocolException        for protocol failures
2024: * @since        JavaMail 1.5.3
2025: */
2026: public void fetchSequenceNumber(long uid) throws ProtocolException {
2027:         Response[] r = fetch(String.valueOf(uid), "UID", true);        
2028:
2029:         notifyResponseHandlers(r);
2030:         handleResult(r[r.length-1]);
2031: }
2032:
2033: /**
2034: * Get the sequence numbers for UIDs ranging from start till end.
2035: * Since the range may be large and sparse, an array of the UIDs actually
2036: * found is returned. The caller must map these to messages after
2037: * the FETCH UID responses have been handled by the reponse handler,
2038: * along with any possible EXPUNGE responses, to ensure that the
2039: * UIDs are matched with the correct sequence numbers.
2040: *
2041: * @param        start        first UID
2042: * @param        end        last UID
2043: * @return                array of sequence numbers
2044: * @exception        ProtocolException        for protocol failures
2045: * @since        JavaMail 1.5.3
2046: */
2047: public long[] fetchSequenceNumbers(long start, long end)
2048:                         throws ProtocolException {
2049:         Response[] r = fetch(String.valueOf(start) + ":" +
2050:•                                (end == UIDFolder.LASTUID ? "*" :
2051:                                 String.valueOf(end)),
2052:                          "UID", true);        
2053:
2054:         UID u;
2055:         List<UID> v = new ArrayList<>();
2056:•        for (int i = 0, len = r.length; i < len; i++) {
2057:•         if (r[i] == null || !(r[i] instanceof FetchResponse))
2058:                 continue;
2059:         
2060:          FetchResponse fr = (FetchResponse)r[i];
2061:•         if ((u = fr.getItem(UID.class)) != null)
2062:                 v.add(u);
2063:         }
2064:                 
2065:         notifyResponseHandlers(r);
2066:         handleResult(r[r.length-1]);
2067:
2068:         long[] lv = new long[v.size()];
2069:•        for (int i = 0; i < v.size(); i++)
2070:          lv[i] = v.get(i).uid;
2071:         return lv;
2072: }
2073:
2074: /**
2075: * Get the sequence numbers for UIDs specified in the array.
2076: * Nothing is returned. The caller must map the UIDs to messages after
2077: * the FETCH UID responses have been handled by the reponse handler,
2078: * along with any possible EXPUNGE responses, to ensure that the
2079: * UIDs are matched with the correct sequence numbers.
2080: *
2081: * @param        uids        the UIDs
2082: * @exception        ProtocolException        for protocol failures
2083: * @since        JavaMail 1.5.3
2084: */
2085: public void fetchSequenceNumbers(long[] uids) throws ProtocolException {
2086:         StringBuilder sb = new StringBuilder();
2087:•        for (int i = 0; i < uids.length; i++) {
2088:•         if (i > 0)
2089:                 sb.append(",");
2090:          sb.append(String.valueOf(uids[i]));
2091:         }
2092:
2093:         Response[] r = fetch(sb.toString(), "UID", true);        
2094:
2095:         notifyResponseHandlers(r);
2096:         handleResult(r[r.length-1]);
2097: }
2098:
2099: /**
2100: * Get the sequence numbers for messages changed since the given
2101: * modseq and with UIDs ranging from start till end.
2102: * Also, prefetch the flags for the returned messages.
2103: *
2104: * @param        start        first UID
2105: * @param        end        last UID
2106: * @param        modseq        the MODSEQ
2107: * @return                array of sequence numbers
2108: * @exception        ProtocolException        for protocol failures
2109: * @see        "RFC 4551"
2110: * @since        JavaMail 1.5.1
2111: */
2112: public int[] uidfetchChangedSince(long start, long end, long modseq)
2113:                         throws ProtocolException {
2114:         String msgSequence = String.valueOf(start) + ":" +
2115:•                                (end == UIDFolder.LASTUID ? "*" :
2116:                                 String.valueOf(end));
2117:         Response[] r = command("UID FETCH " + msgSequence +
2118:                 " (FLAGS) (CHANGEDSINCE " + String.valueOf(modseq) + ")", null);
2119:
2120:         List<Integer> v = new ArrayList<>();
2121:•        for (int i = 0, len = r.length; i < len; i++) {
2122:•         if (r[i] == null || !(r[i] instanceof FetchResponse))
2123:                 continue;
2124:
2125:          FetchResponse fr = (FetchResponse)r[i];
2126:          v.add(Integer.valueOf(fr.getNumber()));
2127:         }
2128:                 
2129:         notifyResponseHandlers(r);
2130:         handleResult(r[r.length-1]);
2131:
2132:         // Copy the list into 'matches'
2133:         int vsize = v.size();
2134:         int[] matches = new int[vsize];
2135:•        for (int i = 0; i < vsize; i++)
2136:          matches[i] = v.get(i).intValue();
2137:         return matches;
2138: }
2139:
2140: public Response[] fetch(MessageSet[] msgsets, String what)
2141:                         throws ProtocolException {
2142:         return fetch(MessageSet.toString(msgsets), what, false);
2143: }
2144:
2145: public Response[] fetch(int start, int end, String what)
2146:                         throws ProtocolException {
2147:         return fetch(String.valueOf(start) + ":" + String.valueOf(end),
2148:                  what, false);
2149: }
2150:
2151: public Response[] fetch(int msg, String what)
2152:                         throws ProtocolException {
2153:         return fetch(String.valueOf(msg), what, false);
2154: }
2155:
2156: private Response[] fetch(String msgSequence, String what, boolean uid)
2157:                         throws ProtocolException {
2158:•        if (uid)
2159:          return command("UID FETCH " + msgSequence +" (" + what + ")",null);
2160:         else
2161:          return command("FETCH " + msgSequence + " (" + what + ")", null);
2162: }
2163:
2164: /**
2165: * COPY command.
2166: *
2167: * @param        msgsets        the messages to copy
2168: * @param        mbox        the mailbox to copy them to
2169: * @exception        ProtocolException        for protocol failures
2170: */
2171: public void copy(MessageSet[] msgsets, String mbox)
2172:                         throws ProtocolException {
2173:         copyuid(MessageSet.toString(msgsets), mbox, false);
2174: }
2175:
2176: /**
2177: * COPY command.
2178: *
2179: * @param        start        start message number
2180: * @param        end        end message number
2181: * @param        mbox        the mailbox to copy them to
2182: * @exception        ProtocolException        for protocol failures
2183: */
2184: public void copy(int start, int end, String mbox)
2185:                         throws ProtocolException {
2186:         copyuid(String.valueOf(start) + ":" + String.valueOf(end),
2187:                  mbox, false);
2188: }
2189:
2190: /**
2191: * COPY command, return uid from COPYUID response code.
2192: *
2193: * @param        msgsets        the messages to copy
2194: * @param        mbox        the mailbox to copy them to
2195: * @return                COPYUID response data
2196: * @exception        ProtocolException        for protocol failures
2197: * @see "RFC 4315, section 3"
2198: */
2199: public CopyUID copyuid(MessageSet[] msgsets, String mbox)
2200:                         throws ProtocolException {
2201:         return copyuid(MessageSet.toString(msgsets), mbox, true);
2202: }
2203:
2204: /**
2205: * COPY command, return uid from COPYUID response code.
2206: *
2207: * @param        start        start message number
2208: * @param        end        end message number
2209: * @param        mbox        the mailbox to copy them to
2210: * @return                COPYUID response data
2211: * @exception        ProtocolException        for protocol failures
2212: * @see "RFC 4315, section 3"
2213: */
2214: public CopyUID copyuid(int start, int end, String mbox)
2215:                         throws ProtocolException {
2216:         return copyuid(String.valueOf(start) + ":" + String.valueOf(end),
2217:                  mbox, true);
2218: }
2219:
2220: private CopyUID copyuid(String msgSequence, String mbox, boolean uid)
2221:                                 throws ProtocolException {
2222:•        if (uid && !hasCapability("UIDPLUS"))
2223:          throw new BadCommandException("UIDPLUS not supported");
2224:
2225:         Argument args = new Argument();        
2226:         args.writeAtom(msgSequence);
2227:         writeMailboxName(args, mbox);
2228:
2229:         Response[] r = command("COPY", args);
2230:
2231:         // dispatch untagged responses
2232:         notifyResponseHandlers(r);
2233:
2234:         // Handle result of this command
2235:         handleResult(r[r.length-1]);
2236:
2237:•        if (uid)
2238:          return getCopyUID(r);
2239:         else
2240:          return null;
2241: }
2242:
2243: /**
2244: * MOVE command.
2245: *
2246: * @param        msgsets        the messages to move
2247: * @param        mbox        the mailbox to move them to
2248: * @exception        ProtocolException        for protocol failures
2249: * @see "RFC 6851"
2250: * @since        JavaMail 1.5.4
2251: */
2252: public void move(MessageSet[] msgsets, String mbox)
2253:                         throws ProtocolException {
2254:         moveuid(MessageSet.toString(msgsets), mbox, false);
2255: }
2256:
2257: /**
2258: * MOVE command.
2259: *
2260: * @param        start        start message number
2261: * @param        end        end message number
2262: * @param        mbox        the mailbox to move them to
2263: * @exception        ProtocolException        for protocol failures
2264: * @see "RFC 6851"
2265: * @since        JavaMail 1.5.4
2266: */
2267: public void move(int start, int end, String mbox)
2268:                         throws ProtocolException {
2269:         moveuid(String.valueOf(start) + ":" + String.valueOf(end),
2270:                  mbox, false);
2271: }
2272:
2273: /**
2274: * MOVE Command, return uid from COPYUID response code.
2275: *
2276: * @param        msgsets        the messages to move
2277: * @param        mbox        the mailbox to move them to
2278: * @return                COPYUID response data
2279: * @exception        ProtocolException        for protocol failures
2280: * @see "RFC 6851"
2281: * @see "RFC 4315, section 3"
2282: * @since        JavaMail 1.5.4
2283: */
2284: public CopyUID moveuid(MessageSet[] msgsets, String mbox)
2285:                         throws ProtocolException {
2286:         return moveuid(MessageSet.toString(msgsets), mbox, true);
2287: }
2288:
2289: /**
2290: * MOVE Command, return uid from COPYUID response code.
2291: *
2292: * @param        start        start message number
2293: * @param        end        end message number
2294: * @param        mbox        the mailbox to move them to
2295: * @return                COPYUID response data
2296: * @exception        ProtocolException        for protocol failures
2297: * @see "RFC 6851"
2298: * @see "RFC 4315, section 3"
2299: * @since        JavaMail 1.5.4
2300: */
2301: public CopyUID moveuid(int start, int end, String mbox)
2302:                         throws ProtocolException {
2303:         return moveuid(String.valueOf(start) + ":" + String.valueOf(end),
2304:                  mbox, true);
2305: }
2306:
2307: /**
2308: * MOVE Command, return uid from COPYUID response code.
2309: *
2310: * @see "RFC 6851"
2311: * @see "RFC 4315, section 3"
2312: * @since        JavaMail 1.5.4
2313: */
2314: private CopyUID moveuid(String msgSequence, String mbox, boolean uid)
2315:                                 throws ProtocolException {
2316:•        if (!hasCapability("MOVE"))
2317:          throw new BadCommandException("MOVE not supported");
2318:•        if (uid && !hasCapability("UIDPLUS"))
2319:          throw new BadCommandException("UIDPLUS not supported");
2320:
2321:         Argument args = new Argument();        
2322:         args.writeAtom(msgSequence);
2323:         writeMailboxName(args, mbox);
2324:
2325:         Response[] r = command("MOVE", args);
2326:
2327:         // dispatch untagged responses
2328:         notifyResponseHandlers(r);
2329:
2330:         // Handle result of this command
2331:         handleResult(r[r.length-1]);
2332:
2333:•        if (uid)
2334:          return getCopyUID(r);
2335:         else
2336:          return null;
2337: }
2338:
2339: /**
2340: * If the response contains a COPYUID response code, extract
2341: * it and return a CopyUID object with the information.
2342: *
2343: * @param        rr        the responses to examine
2344: * @return                the COPYUID response code data, or null if not found
2345: * @since        JavaMail 1.5.4
2346: */
2347: protected CopyUID getCopyUID(Response[] rr) {
2348:         // most likely in the last response, so start there and work backward
2349:•        for (int i = rr.length - 1; i >= 0; i--) {
2350:          Response r = rr[i];
2351:•         if (r == null || !r.isOK())
2352:                 continue;
2353:          byte b;
2354:•         while ((b = r.readByte()) > 0 && b != (byte)'[')
2355:                
2356:•         if (b == 0)
2357:                 continue;
2358:          String s;
2359:          s = r.readAtom();
2360:•         if (!s.equalsIgnoreCase("COPYUID"))
2361:                 continue;
2362:
2363:          // XXX - need to merge more than one response for MOVE?
2364:          long uidvalidity = r.readLong();
2365:          String src = r.readAtom();
2366:          String dst = r.readAtom();
2367:          return new CopyUID(uidvalidity,
2368:                          UIDSet.parseUIDSets(src), UIDSet.parseUIDSets(dst));
2369:         }
2370:         return null;
2371: }
2372:
2373: public void storeFlags(MessageSet[] msgsets, Flags flags, boolean set)
2374:                         throws ProtocolException {
2375:         storeFlags(MessageSet.toString(msgsets), flags, set);
2376: }
2377:
2378: public void storeFlags(int start, int end, Flags flags, boolean set)
2379:                         throws ProtocolException {
2380:         storeFlags(String.valueOf(start) + ":" + String.valueOf(end),
2381:                  flags, set);
2382: }
2383:
2384: /**
2385: * Set the specified flags on this message.
2386: *
2387: * @param        msg        the message number
2388: * @param        flags        the flags
2389: * @param        set        true to set, false to clear
2390: * @exception        ProtocolException        for protocol failures
2391: */
2392: public void storeFlags(int msg, Flags flags, boolean set)
2393:                         throws ProtocolException {
2394:         storeFlags(String.valueOf(msg), flags, set);
2395: }
2396:
2397: private void storeFlags(String msgset, Flags flags, boolean set)
2398:                         throws ProtocolException {
2399:         Response[] r;
2400:•        if (set)
2401:          r = command("STORE " + msgset + " +FLAGS " +
2402:                          createFlagList(flags), null);
2403:         else
2404:          r = command("STORE " + msgset + " -FLAGS " +
2405:                         createFlagList(flags), null);
2406:         
2407:         // Dispatch untagged responses
2408:         notifyResponseHandlers(r);
2409:         handleResult(r[r.length-1]);
2410: }
2411:
2412: /**
2413: * Creates an IMAP flag_list from the given Flags object.
2414: *
2415: * @param        flags        the flags
2416: * @return                the IMAP flag_list
2417: * @since        JavaMail 1.5.4
2418: */
2419: protected String createFlagList(Flags flags) {
2420:         StringBuilder sb = new StringBuilder("("); // start of flag_list
2421:
2422:         Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags
2423:         boolean first = true;
2424:•        for (int i = 0; i < sf.length; i++) {
2425:          String s;
2426:          Flags.Flag f = sf[i];
2427:•         if (f == Flags.Flag.ANSWERED)
2428:                 s = "\\Answered";
2429:•         else if (f == Flags.Flag.DELETED)
2430:                 s = "\\Deleted";
2431:•         else if (f == Flags.Flag.DRAFT)
2432:                 s = "\\Draft";
2433:•         else if (f == Flags.Flag.FLAGGED)
2434:                 s = "\\Flagged";
2435:•         else if (f == Flags.Flag.RECENT)
2436:                 s = "\\Recent";
2437:•         else if (f == Flags.Flag.SEEN)
2438:                 s = "\\Seen";
2439:          else
2440:                 continue;        // skip it
2441:•         if (first)
2442:                 first = false;
2443:          else
2444:                 sb.append(' ');
2445:          sb.append(s);
2446:         }
2447:
2448:         String[] uf = flags.getUserFlags(); // get the user flag strings
2449:•        for (int i = 0; i < uf.length; i++) {
2450:•         if (first)
2451:                 first = false;
2452:          else
2453:                 sb.append(' ');
2454:          sb.append(uf[i]);
2455:         }
2456:
2457:         sb.append(")"); // terminate flag_list
2458:         return sb.toString();
2459: }
2460:
2461: /**
2462: * Issue the given search criterion on the specified message sets.
2463: * Returns array of matching sequence numbers. An empty array
2464: * is returned if no matches are found.
2465: *
2466: * @param        msgsets        array of MessageSets
2467: * @param        term        SearchTerm
2468: * @return                array of matching sequence numbers.
2469: * @exception        ProtocolException        for protocol failures
2470: * @exception        SearchException        for search failures
2471: */
2472: public int[] search(MessageSet[] msgsets, SearchTerm term)
2473:                         throws ProtocolException, SearchException {
2474:         return search(MessageSet.toString(msgsets), term);
2475: }
2476:
2477: /**
2478: * Issue the given search criterion on all messages in this folder.
2479: * Returns array of matching sequence numbers. An empty array
2480: * is returned if no matches are found.
2481: *
2482: * @param        term        SearchTerm
2483: * @return                array of matching sequence numbers.
2484: * @exception        ProtocolException        for protocol failures
2485: * @exception        SearchException        for search failures
2486: */
2487: public int[] search(SearchTerm term)
2488:                         throws ProtocolException, SearchException {
2489:         return search("ALL", term);
2490: }
2491:
2492: /*
2493: * Apply the given SearchTerm on the specified sequence.
2494: * Returns array of matching sequence numbers. Note that an empty
2495: * array is returned for no matches.
2496: */
2497: private int[] search(String msgSequence, SearchTerm term)
2498:                         throws ProtocolException, SearchException {
2499:         // Check if the search "text" terms contain only ASCII chars,
2500:         // or if utf8 support has been enabled (in which case CHARSET
2501:         // is not allowed; see RFC 6855, section 3, last paragraph)
2502:•        if (supportsUtf8() || SearchSequence.isAscii(term)) {
2503:          try {
2504:                 return issueSearch(msgSequence, term, null);
2505:          } catch (IOException ioex) { /* will not happen */ }
2506:         }
2507:
2508:         /*
2509:          * The search "text" terms do contain non-ASCII chars and utf8
2510:          * support has not been enabled. We need to use:
2511:          * "SEARCH CHARSET <charset> ..."
2512:          * The charsets we try to use are UTF-8 and the locale's
2513:          * default charset. If the server supports UTF-8, great,
2514:          * always use it. Else we try to use the default charset.
2515:          */
2516:
2517:         // Cycle thru the list of charsets
2518:•        for (int i = 0; i < searchCharsets.length; i++) {
2519:•         if (searchCharsets[i] == null)
2520:                 continue;
2521:
2522:          try {
2523:                 return issueSearch(msgSequence, term, searchCharsets[i]);
2524:          } catch (CommandFailedException cfx) {
2525:                 /*
2526:                  * Server returned NO. For now, I'll just assume that
2527:                  * this indicates that this charset is unsupported.
2528:                  * We can check the BADCHARSET response code once
2529:                  * that's spec'd into the IMAP RFC ..
2530:                  */
2531:                 searchCharsets[i] = null;
2532:                 continue;
2533:          } catch (IOException ioex) {
2534:                 /* Charset conversion failed. Try the next one */
2535:                 continue;
2536:          } catch (ProtocolException pex) {
2537:                 throw pex;
2538:          } catch (SearchException sex) {
2539:                 throw sex;
2540:          }
2541:         }
2542:
2543:         // No luck.
2544:         throw new SearchException("Search failed");
2545: }
2546:
2547: /* Apply the given SearchTerm on the specified sequence, using the
2548: * given charset. <p>
2549: * Returns array of matching sequence numbers. Note that an empty
2550: * array is returned for no matches.
2551: */
2552: private int[] issueSearch(String msgSequence, SearchTerm term,
2553:                          String charset)
2554:          throws ProtocolException, SearchException, IOException {
2555:
2556:         // Generate a search-sequence with the given charset
2557:         Argument args = getSearchSequence().generateSequence(term,
2558:•                         charset == null ? null :
2559:                                          MimeUtility.javaCharset(charset)
2560:                         );
2561:         args.writeAtom(msgSequence);
2562:
2563:         Response[] r;
2564:
2565:•        if (charset == null) // text is all US-ASCII
2566:          r = command("SEARCH", args);
2567:         else
2568:          r = command("SEARCH CHARSET " + charset, args);
2569:
2570:         Response response = r[r.length-1];
2571:         int[] matches = null;
2572:
2573:         // Grab all SEARCH responses
2574:•        if (response.isOK()) { // command succesful
2575:          List<Integer> v = new ArrayList<>();
2576:          int num;
2577:•         for (int i = 0, len = r.length; i < len; i++) {
2578:•                if (!(r[i] instanceof IMAPResponse))
2579:                  continue;
2580:
2581:                 IMAPResponse ir = (IMAPResponse)r[i];
2582:                 // There *will* be one SEARCH response.
2583:•                if (ir.keyEquals("SEARCH")) {
2584:•                 while ((num = ir.readNumber()) != -1)
2585:                         v.add(Integer.valueOf(num));
2586:                  r[i] = null;
2587:                 }
2588:          }
2589:
2590:          // Copy the list into 'matches'
2591:          int vsize = v.size();
2592:          matches = new int[vsize];
2593:•         for (int i = 0; i < vsize; i++)
2594:                 matches[i] = v.get(i).intValue();
2595:         }
2596:
2597:         // dispatch remaining untagged responses
2598:         notifyResponseHandlers(r);
2599:         handleResult(response);
2600:         return matches;
2601: }
2602:
2603: /**
2604: * Get the SearchSequence object.
2605: * The SearchSequence object instance is saved in the searchSequence
2606: * field. Subclasses of IMAPProtocol may override this method to
2607: * return a subclass of SearchSequence, in order to add support for
2608: * product-specific search terms.
2609: *
2610: * @return        the SearchSequence
2611: * @since JavaMail 1.4.6
2612: */
2613: protected SearchSequence getSearchSequence() {
2614:•        if (searchSequence == null)
2615:          searchSequence = new SearchSequence(this);
2616:         return searchSequence;
2617: }
2618:
2619: /**
2620: * Sort messages in the folder according to the specified sort criteria.
2621: * If the search term is not null, limit the sort to only the messages
2622: * that match the search term.
2623: * Returns an array of sorted sequence numbers. An empty array
2624: * is returned if no matches are found.
2625: *
2626: * @param        term        sort criteria
2627: * @param        sterm        SearchTerm
2628: * @return                array of matching sequence numbers.
2629: * @exception        ProtocolException        for protocol failures
2630: * @exception        SearchException        for search failures
2631: *
2632: * @see        "RFC 5256"
2633: * @since        JavaMail 1.4.4
2634: */
2635: public int[] sort(SortTerm[] term, SearchTerm sterm)
2636:                         throws ProtocolException, SearchException {
2637:•        if (!hasCapability("SORT*"))
2638:          throw new BadCommandException("SORT not supported");
2639:
2640:•        if (term == null || term.length == 0)
2641:          throw new BadCommandException("Must have at least one sort term");
2642:
2643:         Argument args = new Argument();
2644:         Argument sargs = new Argument();
2645:•        for (int i = 0; i < term.length; i++)
2646:          sargs.writeAtom(term[i].toString());
2647:         args.writeArgument(sargs);        // sort criteria
2648:
2649:         args.writeAtom("UTF-8");        // charset specification
2650:•        if (sterm != null) {
2651:          try {
2652:                 args.append(
2653:                  getSearchSequence().generateSequence(sterm, "UTF-8"));
2654:          } catch (IOException ioex) {
2655:                 // should never happen
2656:                 throw new SearchException(ioex.toString());
2657:          }
2658:         } else
2659:          args.writeAtom("ALL");
2660:
2661:         Response[] r = command("SORT", args);
2662:         Response response = r[r.length-1];
2663:         int[] matches = null;
2664:
2665:         // Grab all SORT responses
2666:•        if (response.isOK()) { // command succesful
2667:          List<Integer> v = new ArrayList<>();
2668:          int num;
2669:•         for (int i = 0, len = r.length; i < len; i++) {
2670:•                if (!(r[i] instanceof IMAPResponse))
2671:                  continue;
2672:
2673:                 IMAPResponse ir = (IMAPResponse)r[i];
2674:•                if (ir.keyEquals("SORT")) {
2675:•                 while ((num = ir.readNumber()) != -1)
2676:                         v.add(Integer.valueOf(num));
2677:                  r[i] = null;
2678:                 }
2679:          }
2680:
2681:          // Copy the list into 'matches'
2682:          int vsize = v.size();
2683:          matches = new int[vsize];
2684:•         for (int i = 0; i < vsize; i++)
2685:                 matches[i] = v.get(i).intValue();
2686:         }
2687:
2688:         // dispatch remaining untagged responses
2689:         notifyResponseHandlers(r);
2690:         handleResult(response);
2691:         return matches;
2692: }
2693:
2694: /**
2695: * NAMESPACE Command.
2696: *
2697: * @return        the namespaces
2698: * @exception        ProtocolException        for protocol failures
2699: * @see "RFC2342"
2700: */
2701: public Namespaces namespace() throws ProtocolException {
2702:•        if (!hasCapability("NAMESPACE"))
2703:          throw new BadCommandException("NAMESPACE not supported");
2704:
2705:         Response[] r = command("NAMESPACE", null);
2706:
2707:         Namespaces namespace = null;
2708:         Response response = r[r.length-1];
2709:
2710:         // Grab NAMESPACE response
2711:•        if (response.isOK()) { // command succesful
2712:•         for (int i = 0, len = r.length; i < len; i++) {
2713:•                if (!(r[i] instanceof IMAPResponse))
2714:                  continue;
2715:
2716:                 IMAPResponse ir = (IMAPResponse)r[i];
2717:•                if (ir.keyEquals("NAMESPACE")) {
2718:•                 if (namespace == null)
2719:                         namespace = new Namespaces(ir);
2720:                  r[i] = null;
2721:                 }
2722:          }
2723:         }
2724:
2725:         // dispatch remaining untagged responses
2726:         notifyResponseHandlers(r);
2727:         handleResult(response);
2728:         return namespace;
2729: }
2730:
2731: /**
2732: * GETQUOTAROOT Command.
2733: *
2734: * Returns an array of Quota objects, representing the quotas
2735: * for this mailbox and, indirectly, the quotaroots for this
2736: * mailbox.
2737: *
2738: * @param        mbox        the mailbox
2739: * @return                array of Quota objects
2740: * @exception        ProtocolException        for protocol failures
2741: * @see "RFC2087"
2742: */
2743: public Quota[] getQuotaRoot(String mbox) throws ProtocolException {
2744:•        if (!hasCapability("QUOTA"))
2745:          throw new BadCommandException("GETQUOTAROOT not supported");
2746:
2747:         Argument args = new Argument();        
2748:         writeMailboxName(args, mbox);
2749:
2750:         Response[] r = command("GETQUOTAROOT", args);
2751:
2752:         Response response = r[r.length-1];
2753:
2754:         Map<String, Quota> tab = new HashMap<>();
2755:
2756:         // Grab all QUOTAROOT and QUOTA responses
2757:•        if (response.isOK()) { // command succesful
2758:•         for (int i = 0, len = r.length; i < len; i++) {
2759:•                if (!(r[i] instanceof IMAPResponse))
2760:                  continue;
2761:
2762:                 IMAPResponse ir = (IMAPResponse)r[i];
2763:•                if (ir.keyEquals("QUOTAROOT")) {
2764:                  // quotaroot_response
2765:                  //                 ::= "QUOTAROOT" SP astring *(SP astring)
2766:
2767:                  // read name of mailbox and throw away
2768:                  ir.readAtomString();
2769:                  // for each quotaroot add a placeholder quota
2770:                  String root = null;
2771:•                 while ((root = ir.readAtomString()) != null &&
2772:•                         root.length() > 0)
2773:                         tab.put(root, new Quota(root));
2774:                  r[i] = null;
2775:•                } else if (ir.keyEquals("QUOTA")) {
2776:                  Quota quota = parseQuota(ir);
2777:                  Quota q = tab.get(quota.quotaRoot);
2778:•                 if (q != null && q.resources != null) {
2779:                         // merge resources
2780:                         int newl = q.resources.length + quota.resources.length;
2781:                         Quota.Resource[] newr = new Quota.Resource[newl];
2782:                         System.arraycopy(q.resources, 0, newr, 0,
2783:                                                         q.resources.length);
2784:                         System.arraycopy(quota.resources, 0,
2785:                          newr, q.resources.length, quota.resources.length);
2786:                         quota.resources = newr;
2787:                  }
2788:                  tab.put(quota.quotaRoot, quota);
2789:                  r[i] = null;
2790:                 }
2791:          }
2792:         }
2793:
2794:         // dispatch remaining untagged responses
2795:         notifyResponseHandlers(r);
2796:         handleResult(response);
2797:
2798:         return tab.values().toArray(new Quota[tab.size()]);
2799: }
2800:
2801: /**
2802: * GETQUOTA Command.
2803: *
2804: * Returns an array of Quota objects, representing the quotas
2805: * for this quotaroot.
2806: *
2807: * @param        root        the quotaroot
2808: * @return                the quotas
2809: * @exception        ProtocolException        for protocol failures
2810: * @see "RFC2087"
2811: */
2812: public Quota[] getQuota(String root) throws ProtocolException {
2813:•        if (!hasCapability("QUOTA"))
2814:          throw new BadCommandException("QUOTA not supported");
2815:
2816:         Argument args = new Argument();        
2817:         args.writeString(root);                // XXX - could be UTF-8?
2818:
2819:         Response[] r = command("GETQUOTA", args);
2820:
2821:         Quota quota = null;
2822:         List<Quota> v = new ArrayList<>();
2823:         Response response = r[r.length-1];
2824:
2825:         // Grab all QUOTA responses
2826:•        if (response.isOK()) { // command succesful
2827:•         for (int i = 0, len = r.length; i < len; i++) {
2828:•                if (!(r[i] instanceof IMAPResponse))
2829:                  continue;
2830:
2831:                 IMAPResponse ir = (IMAPResponse)r[i];
2832:•                if (ir.keyEquals("QUOTA")) {
2833:                  quota = parseQuota(ir);
2834:                  v.add(quota);
2835:                  r[i] = null;
2836:                 }
2837:          }
2838:         }
2839:
2840:         // dispatch remaining untagged responses
2841:         notifyResponseHandlers(r);
2842:         handleResult(response);
2843:         return v.toArray(new Quota[v.size()]);
2844: }
2845:
2846: /**
2847: * SETQUOTA Command.
2848: *
2849: * Set the indicated quota on the corresponding quotaroot.
2850: *
2851: * @param        quota        the quota to set
2852: * @exception        ProtocolException        for protocol failures
2853: * @see "RFC2087"
2854: */
2855: public void setQuota(Quota quota) throws ProtocolException {
2856:•        if (!hasCapability("QUOTA"))
2857:          throw new BadCommandException("QUOTA not supported");
2858:
2859:         Argument args = new Argument();        
2860:         args.writeString(quota.quotaRoot);        // XXX - could be UTF-8?
2861:         Argument qargs = new Argument();        
2862:•        if (quota.resources != null) {
2863:•         for (int i = 0; i < quota.resources.length; i++) {
2864:                 qargs.writeAtom(quota.resources[i].name);
2865:                 qargs.writeNumber(quota.resources[i].limit);
2866:          }
2867:         }
2868:         args.writeArgument(qargs);
2869:
2870:         Response[] r = command("SETQUOTA", args);
2871:         Response response = r[r.length-1];
2872:
2873:         // XXX - It's not clear from the RFC whether the SETQUOTA command
2874:         // will provoke untagged QUOTA responses. If it does, perhaps
2875:         // we should grab them here and return them?
2876:
2877:         /*
2878:         Quota quota = null;
2879:         List<Quota> v = new ArrayList<Quota>();
2880:
2881:         // Grab all QUOTA responses
2882:         if (response.isOK()) { // command succesful
2883:          for (int i = 0, len = r.length; i < len; i++) {
2884:                 if (!(r[i] instanceof IMAPResponse))
2885:                  continue;
2886:
2887:                 IMAPResponse ir = (IMAPResponse)r[i];
2888:                 if (ir.keyEquals("QUOTA")) {
2889:                  quota = parseQuota(ir);
2890:                  v.add(quota);
2891:                  r[i] = null;
2892:                 }
2893:          }
2894:         }
2895:         */
2896:
2897:         // dispatch remaining untagged responses
2898:         notifyResponseHandlers(r);
2899:         handleResult(response);
2900:         /*
2901:         return v.toArray(new Quota[v.size()]);
2902:         */
2903: }
2904:
2905: /**
2906: * Parse a QUOTA response.
2907: */
2908: private Quota parseQuota(Response r) throws ParsingException {
2909:         // quota_response ::= "QUOTA" SP astring SP quota_list
2910:         String quotaRoot = r.readAtomString();        // quotaroot ::= astring
2911:         Quota q = new Quota(quotaRoot);
2912:         r.skipSpaces();
2913:         // quota_list ::= "(" #quota_resource ")"
2914:•        if (r.readByte() != '(')
2915:          throw new ParsingException("parse error in QUOTA");
2916:
2917:         List<Quota.Resource> v = new ArrayList<>();
2918:•        while (!r.isNextNonSpace(')')) {
2919:          // quota_resource ::= atom SP number SP number
2920:          String name = r.readAtom();
2921:•         if (name != null) {
2922:                 long usage = r.readLong();
2923:                 long limit = r.readLong();
2924:                 Quota.Resource res = new Quota.Resource(name, usage, limit);
2925:                 v.add(res);
2926:          }
2927:         }
2928:         q.resources = v.toArray(new Quota.Resource[v.size()]);
2929:         return q;
2930: }
2931:
2932:
2933: /**
2934: * SETACL Command.
2935: *
2936: * @param        mbox        the mailbox
2937: * @param        modifier        the ACL modifier
2938: * @param        acl        the ACL
2939: * @exception        ProtocolException        for protocol failures
2940: * @see "RFC2086"
2941: */
2942: public void setACL(String mbox, char modifier, ACL acl)
2943:                                 throws ProtocolException {
2944:•        if (!hasCapability("ACL"))
2945:          throw new BadCommandException("ACL not supported");
2946:
2947:         Argument args = new Argument();        
2948:         writeMailboxName(args, mbox);
2949:         args.writeString(acl.getName());
2950:         String rights = acl.getRights().toString();
2951:•        if (modifier == '+' || modifier == '-')
2952:          rights = modifier + rights;
2953:         args.writeString(rights);
2954:
2955:         Response[] r = command("SETACL", args);
2956:         Response response = r[r.length-1];
2957:
2958:         // dispatch untagged responses
2959:         notifyResponseHandlers(r);
2960:         handleResult(response);
2961: }
2962:
2963: /**
2964: * DELETEACL Command.
2965: *
2966: * @param        mbox        the mailbox
2967: * @param        user        the user
2968: * @exception        ProtocolException        for protocol failures
2969: * @see "RFC2086"
2970: */
2971: public void deleteACL(String mbox, String user) throws ProtocolException {
2972:•        if (!hasCapability("ACL"))
2973:          throw new BadCommandException("ACL not supported");
2974:
2975:         Argument args = new Argument();        
2976:         writeMailboxName(args, mbox);
2977:         args.writeString(user);                // XXX - could be UTF-8?
2978:
2979:         Response[] r = command("DELETEACL", args);
2980:         Response response = r[r.length-1];
2981:
2982:         // dispatch untagged responses
2983:         notifyResponseHandlers(r);
2984:         handleResult(response);
2985: }
2986:
2987: /**
2988: * GETACL Command.
2989: *
2990: * @param        mbox        the mailbox
2991: * @return                the ACL array
2992: * @exception        ProtocolException        for protocol failures
2993: * @see "RFC2086"
2994: */
2995: public ACL[] getACL(String mbox) throws ProtocolException {
2996:•        if (!hasCapability("ACL"))
2997:          throw new BadCommandException("ACL not supported");
2998:
2999:         Argument args = new Argument();        
3000:         writeMailboxName(args, mbox);
3001:
3002:         Response[] r = command("GETACL", args);
3003:         Response response = r[r.length-1];
3004:
3005:         // Grab all ACL responses
3006:         List<ACL> v = new ArrayList<>();
3007:•        if (response.isOK()) { // command succesful
3008:•         for (int i = 0, len = r.length; i < len; i++) {
3009:•                if (!(r[i] instanceof IMAPResponse))
3010:                  continue;
3011:
3012:                 IMAPResponse ir = (IMAPResponse)r[i];
3013:•                if (ir.keyEquals("ACL")) {
3014:                  // acl_data ::= "ACL" SPACE mailbox
3015:                  //                *(SPACE identifier SPACE rights)
3016:                  // read name of mailbox and throw away
3017:                  ir.readAtomString();
3018:                  String name = null;
3019:•                 while ((name = ir.readAtomString()) != null) {
3020:                         String rights = ir.readAtomString();
3021:•                        if (rights == null)
3022:                          break;
3023:                         ACL acl = new ACL(name, new Rights(rights));
3024:                         v.add(acl);
3025:                  }
3026:                  r[i] = null;
3027:                 }
3028:          }
3029:         }
3030:
3031:         // dispatch remaining untagged responses
3032:         notifyResponseHandlers(r);
3033:         handleResult(response);
3034:         return v.toArray(new ACL[v.size()]);
3035: }
3036:
3037: /**
3038: * LISTRIGHTS Command.
3039: *
3040: * @param        mbox        the mailbox
3041: * @param        user        the user rights to return
3042: * @return                the rights array
3043: * @exception        ProtocolException        for protocol failures
3044: * @see "RFC2086"
3045: */
3046: public Rights[] listRights(String mbox, String user)
3047:                                 throws ProtocolException {
3048:•        if (!hasCapability("ACL"))
3049:          throw new BadCommandException("ACL not supported");
3050:
3051:         Argument args = new Argument();        
3052:         writeMailboxName(args, mbox);
3053:         args.writeString(user);                // XXX - could be UTF-8?
3054:
3055:         Response[] r = command("LISTRIGHTS", args);
3056:         Response response = r[r.length-1];
3057:
3058:         // Grab LISTRIGHTS response
3059:         List<Rights> v = new ArrayList<>();
3060:•        if (response.isOK()) { // command succesful
3061:•         for (int i = 0, len = r.length; i < len; i++) {
3062:•                if (!(r[i] instanceof IMAPResponse))
3063:                  continue;
3064:
3065:                 IMAPResponse ir = (IMAPResponse)r[i];
3066:•                if (ir.keyEquals("LISTRIGHTS")) {
3067:                  // listrights_data ::= "LISTRIGHTS" SPACE mailbox
3068:                  //                SPACE identifier SPACE rights *(SPACE rights)
3069:                  // read name of mailbox and throw away
3070:                  ir.readAtomString();
3071:                  // read identifier and throw away
3072:                  ir.readAtomString();
3073:                  String rights;
3074:•                 while ((rights = ir.readAtomString()) != null)
3075:                         v.add(new Rights(rights));
3076:                  r[i] = null;
3077:                 }
3078:          }
3079:         }
3080:
3081:         // dispatch remaining untagged responses
3082:         notifyResponseHandlers(r);
3083:         handleResult(response);
3084:         return v.toArray(new Rights[v.size()]);
3085: }
3086:
3087: /**
3088: * MYRIGHTS Command.
3089: *
3090: * @param        mbox        the mailbox
3091: * @return                the rights
3092: * @exception        ProtocolException        for protocol failures
3093: * @see "RFC2086"
3094: */
3095: public Rights myRights(String mbox) throws ProtocolException {
3096:•        if (!hasCapability("ACL"))
3097:          throw new BadCommandException("ACL not supported");
3098:
3099:         Argument args = new Argument();        
3100:         writeMailboxName(args, mbox);
3101:
3102:         Response[] r = command("MYRIGHTS", args);
3103:         Response response = r[r.length-1];
3104:
3105:         // Grab MYRIGHTS response
3106:         Rights rights = null;
3107:•        if (response.isOK()) { // command succesful
3108:•         for (int i = 0, len = r.length; i < len; i++) {
3109:•                if (!(r[i] instanceof IMAPResponse))
3110:                  continue;
3111:
3112:                 IMAPResponse ir = (IMAPResponse)r[i];
3113:•                if (ir.keyEquals("MYRIGHTS")) {
3114:                  // myrights_data ::= "MYRIGHTS" SPACE mailbox SPACE rights
3115:                  // read name of mailbox and throw away
3116:                  ir.readAtomString();
3117:                  String rs = ir.readAtomString();
3118:•                 if (rights == null)
3119:                         rights = new Rights(rs);
3120:                  r[i] = null;
3121:                 }
3122:          }
3123:         }
3124:
3125:         // dispatch remaining untagged responses
3126:         notifyResponseHandlers(r);
3127:         handleResult(response);
3128:         return rights;
3129: }
3130:
3131: /*
3132: * The tag used on the IDLE command. Set by idleStart() and
3133: * used in processIdleResponse() to determine if the response
3134: * is the matching end tag.
3135: */
3136: private volatile String idleTag;
3137:
3138: /**
3139: * IDLE Command. <p>
3140: *
3141: * If the server supports the IDLE command extension, the IDLE
3142: * command is issued and this method blocks until a response has
3143: * been received. Once the first response has been received, the
3144: * IDLE command is terminated and all responses are collected and
3145: * handled and this method returns. <p>
3146: *
3147: * Note that while this method is blocked waiting for a response,
3148: * no other threads may issue any commands to the server that would
3149: * use this same connection.
3150: *
3151: * @exception        ProtocolException        for protocol failures
3152: * @see "RFC2177"
3153: * @since        JavaMail 1.4.1
3154: */
3155: public synchronized void idleStart() throws ProtocolException {
3156:•        if (!hasCapability("IDLE"))
3157:          throw new BadCommandException("IDLE not supported");
3158:
3159:         List<Response> v = new ArrayList<>();
3160:         boolean done = false;
3161:         Response r = null;
3162:
3163:         // write the command
3164:         try {
3165:          idleTag = writeCommand("IDLE", null);
3166:         } catch (LiteralException lex) {
3167:          v.add(lex.getResponse());
3168:          done = true;
3169:         } catch (Exception ex) {
3170:          // Convert this into a BYE response
3171:          v.add(Response.byeResponse(ex));
3172:          done = true;
3173:         }
3174:
3175:•        while (!done) {
3176:          try {
3177:                 r = readResponse();
3178:          } catch (IOException ioex) {
3179:                 // convert this into a BYE response
3180:                 r = Response.byeResponse(ioex);
3181:          } catch (ProtocolException pex) {
3182:                 continue; // skip this response
3183:          }
3184:
3185:          v.add(r);
3186:
3187:•         if (r.isContinuation() || r.isBYE())
3188:                 done = true;
3189:         }
3190:
3191:         Response[] responses = v.toArray(new Response[v.size()]);
3192:         r = responses[responses.length-1];
3193:
3194:         // dispatch remaining untagged responses
3195:         notifyResponseHandlers(responses);
3196:•        if (!r.isContinuation())
3197:          handleResult(r);
3198: }
3199:
3200: /**
3201: * While an IDLE command is in progress, read a response
3202: * sent from the server. The response is read with no locks
3203: * held so that when the read blocks waiting for the response
3204: * from the server it's not holding locks that would prevent
3205: * other threads from interrupting the IDLE command.
3206: *
3207: * @return        the response
3208: * @since        JavaMail 1.4.1
3209: */
3210: public synchronized Response readIdleResponse() {
3211:•        if (idleTag == null)
3212:          return null;        // IDLE not in progress
3213:         Response r = null;
3214:         try {
3215:          r = readResponse();
3216:         } catch (IOException ioex) {
3217:          // convert this into a BYE response
3218:          r = Response.byeResponse(ioex);
3219:         } catch (ProtocolException pex) {
3220:          // convert this into a BYE response
3221:          r = Response.byeResponse(pex);
3222:         }
3223:         return r;
3224: }
3225:
3226: /**
3227: * Process a response returned by readIdleResponse().
3228: * This method will be called with appropriate locks
3229: * held so that the processing of the response is safe.
3230: *
3231: * @param        r        the response
3232: * @return                true if IDLE is done
3233: * @exception        ProtocolException        for protocol failures
3234: * @since        JavaMail 1.4.1
3235: */
3236: public boolean processIdleResponse(Response r) throws ProtocolException {
3237:         Response[] responses = new Response[1];
3238:         responses[0] = r;
3239:         boolean done = false;                // done reading responses?
3240:         notifyResponseHandlers(responses);
3241:
3242:•        if (r.isBYE()) // shouldn't wait for command completion response
3243:          done = true;
3244:
3245:         // If this is a matching command completion response, we are done
3246:•        if (r.isTagged() && r.getTag().equals(idleTag))
3247:          done = true;
3248:
3249:•        if (done)
3250:          idleTag = null;        // no longer in IDLE
3251:
3252:         handleResult(r);
3253:•        return !done;
3254: }
3255:
3256: // the DONE command to break out of IDLE
3257: private static final byte[] DONE = { 'D', 'O', 'N', 'E', '\r', '\n' };
3258:
3259: /**
3260: * Abort an IDLE command. While one thread is blocked in
3261: * readIdleResponse(), another thread will use this method
3262: * to abort the IDLE command, which will cause the server
3263: * to send the closing tag for the IDLE command, which
3264: * readIdleResponse() and processIdleResponse() will see
3265: * and terminate the IDLE state.
3266: *
3267: * @since        JavaMail 1.4.1
3268: */
3269: public void idleAbort() {
3270:         OutputStream os = getOutputStream();
3271:         try {
3272:          os.write(DONE);
3273:          os.flush();
3274:         } catch (Exception ex) {
3275:          // nothing to do, hope to detect it again later
3276:          logger.log(Level.FINEST, "Exception aborting IDLE", ex);
3277:         }
3278: }
3279:
3280: /**
3281: * ID Command.
3282: *
3283: * @param        clientParams        map of names and values
3284: * @return                        map of names and values from server
3285: * @exception        ProtocolException        for protocol failures
3286: * @see "RFC 2971"
3287: * @since        JavaMail 1.5.1
3288: */
3289: public Map<String, String> id(Map<String, String> clientParams)
3290:                                 throws ProtocolException {
3291:•        if (!hasCapability("ID"))
3292:          throw new BadCommandException("ID not supported");
3293:
3294:         Response[] r = command("ID", ID.getArgumentList(clientParams));
3295:
3296:         ID id = null;
3297:         Response response = r[r.length-1];
3298:
3299:         // Grab ID response
3300:•        if (response.isOK()) { // command succesful
3301:•         for (int i = 0, len = r.length; i < len; i++) {
3302:•                if (!(r[i] instanceof IMAPResponse))
3303:                  continue;
3304:
3305:                 IMAPResponse ir = (IMAPResponse)r[i];
3306:•                if (ir.keyEquals("ID")) {
3307:•                 if (id == null)
3308:                         id = new ID(ir);
3309:                  r[i] = null;
3310:                 }
3311:          }
3312:         }
3313:
3314:         // dispatch remaining untagged responses
3315:         notifyResponseHandlers(r);
3316:         handleResult(response);
3317:•        return id == null ? null : id.getServerParams();
3318: }
3319: }