Skip to content

Package: Protocol

Protocol

nameinstructionbranchcomplexitylinemethod
Protocol(InputStream, PrintStream, Properties, boolean)
M: 89 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 17 C: 0
0%
M: 1 C: 0
0%
Protocol(String, int, Properties, String, boolean, MailLogger)
M: 66 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 19 C: 0
0%
M: 1 C: 0
0%
addResponseHandler(ResponseHandler)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
command(String, Argument)
M: 101 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 38 C: 0
0%
M: 1 C: 0
0%
commandEnd()
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
commandStart(String)
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
computePrefix(Properties, String)
M: 97 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 11 C: 0
0%
M: 1 C: 0
0%
disconnect()
M: 12 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
finalize()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
findSocketChannel(Socket)
M: 93 C: 0
0%
M: 18 C: 0
0%
M: 10 C: 0
0%
M: 25 C: 0
0%
M: 1 C: 0
0%
getChannel()
M: 26 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
getInetAddress()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getInputStream()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getLocalHost()
M: 86 C: 0
0%
M: 24 C: 0
0%
M: 13 C: 0
0%
M: 20 C: 0
0%
M: 1 C: 0
0%
getLocalSocketAddress()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getOutputStream()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getResponseBuffer()
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getTimestamp()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
handleResult(Response)
M: 32 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
hasResponse()
M: 11 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
initStreams()
M: 48 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
isSSL()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
isTracing()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
notifyResponseHandlers(Response[])
M: 41 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 9 C: 0
0%
M: 1 C: 0
0%
processGreeting(Response)
M: 10 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
readResponse()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
removeResponseHandler(ResponseHandler)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
resumeTracing()
M: 14 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
simpleCommand(String, Argument)
M: 17 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
startCompression(String)
M: 132 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 25 C: 0
0%
M: 1 C: 0
0%
startTLS(String)
M: 23 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 16 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
supportsNonSyncLiterals()
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
supportsUtf8()
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
suspendTracing()
M: 14 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
writeCommand(String, Argument)
M: 36 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%

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.iap;
18:
19: import java.util.Properties;
20: import java.io.*;
21: import java.nio.channels.SocketChannel;
22: import java.net.*;
23: import javax.net.ssl.SSLSocket;
24: import java.util.ArrayList;
25: import java.util.List;
26: import java.util.concurrent.CopyOnWriteArrayList;
27: import java.util.concurrent.atomic.AtomicInteger;
28: import java.util.logging.Level;
29: import java.util.zip.Deflater;
30: import java.util.zip.DeflaterOutputStream;
31: import java.util.zip.Inflater;
32: import java.util.zip.InflaterInputStream;
33: import java.lang.reflect.Field;
34:
35: import org.eclipse.angus.mail.util.MailLogger;
36: import org.eclipse.angus.mail.util.PropUtil;
37: import org.eclipse.angus.mail.util.SocketFetcher;
38: import org.eclipse.angus.mail.util.TraceInputStream;
39: import org.eclipse.angus.mail.util.TraceOutputStream;
40:
41: /**
42: * General protocol handling code for IMAP-like protocols. <p>
43: *
44: * The Protocol object is multithread safe.
45: *
46: * @author John Mani
47: * @author Max Spivak
48: * @author Bill Shannon
49: */
50:
51: public class Protocol {
52: protected String host;
53: private Socket socket;
54: // in case we turn on TLS, we'll need these later
55: protected boolean quote;
56: protected MailLogger logger;
57: protected MailLogger traceLogger;
58: protected Properties props;
59: protected String prefix;
60:
61: private TraceInputStream traceInput;        // the Tracer
62: private volatile ResponseInputStream input;
63:
64: private TraceOutputStream traceOutput;        // the Tracer
65: private volatile DataOutputStream output;
66:
67: private int tagCounter = 0;
68: private final String tagPrefix;
69:
70: private String localHostName;
71:
72: private final List<ResponseHandler> handlers
73:          = new CopyOnWriteArrayList<>();
74:
75: private volatile long timestamp;
76:
77: // package private, to allow testing
78: static final AtomicInteger tagNum = new AtomicInteger();
79:
80: private static final byte[] CRLF = { (byte)'\r', (byte)'\n'};
81:
82: /**
83: * Constructor. <p>
84: *
85: * Opens a connection to the given host at given port.
86: *
87: * @param host        host to connect to
88: * @param port        portnumber to connect to
89: * @param props Properties object used by this protocol
90: * @param prefix         Prefix to prepend to property keys
91: * @param isSSL         use SSL?
92: * @param logger         log messages here
93: * @exception        IOException        for I/O errors
94: * @exception        ProtocolException        for protocol failures
95: */
96: public Protocol(String host, int port,
97:                  Properties props, String prefix,
98:                  boolean isSSL, MailLogger logger)
99:                  throws IOException, ProtocolException {
100:         boolean connected = false;                // did constructor succeed?
101:         tagPrefix = computePrefix(props, prefix);
102:         try {
103:          this.host = host;
104:          this.props = props;
105:          this.prefix = prefix;
106:          this.logger = logger;
107:          traceLogger = logger.getSubLogger("protocol", null);
108:
109:          socket = SocketFetcher.getSocket(host, port, props, prefix, isSSL);
110:          quote = PropUtil.getBooleanProperty(props,
111:                                         "mail.debug.quote", false);
112:
113:          initStreams();
114:
115:          // Read server greeting
116:          processGreeting(readResponse());
117:
118:          timestamp = System.currentTimeMillis();
119:
120:          connected = true;        // must be last statement in constructor
121:         } finally {
122:          /*
123:          * If we get here because an exception was thrown, we need
124:          * to disconnect to avoid leaving a connected socket that
125:          * no one will be able to use because this object was never
126:          * completely constructed.
127:          */
128:•         if (!connected)
129:                 disconnect();
130:         }
131: }
132:
133: private void initStreams() throws IOException {
134:         traceInput = new TraceInputStream(socket.getInputStream(), traceLogger);
135:         traceInput.setQuote(quote);
136:         input = new ResponseInputStream(traceInput);
137:
138:         traceOutput =
139:          new TraceOutputStream(socket.getOutputStream(), traceLogger);
140:         traceOutput.setQuote(quote);
141:         output = new DataOutputStream(new BufferedOutputStream(traceOutput));
142: }
143:
144: /**
145: * Compute the tag prefix to be used for this connection.
146: * Start with "A" - "Z", then "AA" - "ZZ", and finally "AAA" - "ZZZ".
147: * Wrap around after that.
148: */
149: private String computePrefix(Properties props, String prefix) {
150:         // XXX - in case someone depends on the tag prefix
151:•        if (PropUtil.getBooleanProperty(props,
152:                                  prefix + ".reusetagprefix", false))
153:          return "A";
154:         // tag prefix, wrap around after three letters
155:         int n = tagNum.getAndIncrement() % (26*26*26 + 26*26 + 26);
156:         String tagPrefix;
157:•        if (n < 26)
158:          tagPrefix = new String(new char[] { (char)('A' + n) });
159:•        else if (n < (26*26 + 26)) {
160:          n -= 26;
161:          tagPrefix = new String(new char[] {
162:                          (char)('A' + n/26), (char)('A' + n%26) });
163:         } else {
164:          n -= (26*26 + 26);
165:          tagPrefix = new String(new char[] {
166:                 (char)('A' + n/(26*26)),
167:                 (char)('A' + (n%(26*26))/26),
168:                 (char)('A' + n%26) });
169:         }
170:         return tagPrefix;
171: }
172:
173: /**
174: * Constructor for debugging.
175: *
176: * @param in        the InputStream to read from
177: * @param out        the PrintStream to write to
178: * @param props Properties object used by this protocol
179: * @param debug        true to enable debugging output
180: * @exception        IOException        for I/O errors
181: */
182: public Protocol(InputStream in, PrintStream out, Properties props,
183:                                 boolean debug) throws IOException {
184:         this.host = "localhost";
185:         this.props = props;
186:         this.quote = false;
187:         tagPrefix = computePrefix(props, "mail.imap");
188:         logger = new MailLogger(this.getClass(), "DEBUG", debug, System.out);
189:         traceLogger = logger.getSubLogger("protocol", null);
190:
191:         // XXX - inlined initStreams, won't allow later startTLS
192:         traceInput = new TraceInputStream(in, traceLogger);
193:         traceInput.setQuote(quote);
194:         input = new ResponseInputStream(traceInput);
195:
196:         traceOutput = new TraceOutputStream(out, traceLogger);
197:         traceOutput.setQuote(quote);
198:         output = new DataOutputStream(new BufferedOutputStream(traceOutput));
199:
200: timestamp = System.currentTimeMillis();
201: }
202:
203: /**
204: * Returns the timestamp.
205: *
206: * @return        the timestamp
207: */
208: public long getTimestamp() {
209: return timestamp;
210: }
211:
212: /**
213: * Adds a response handler.
214: *
215: * @param        h        the response handler
216: */
217: public void addResponseHandler(ResponseHandler h) {
218:         handlers.add(h);
219: }
220:
221: /**
222: * Removed the specified response handler.
223: *
224: * @param        h        the response handler
225: */
226: public void removeResponseHandler(ResponseHandler h) {
227:         handlers.remove(h);
228: }
229:
230: /**
231: * Notify response handlers
232: *
233: * @param        responses        the responses
234: */
235: public void notifyResponseHandlers(Response[] responses) {
236:•        if (handlers.isEmpty()) {
237:          return;
238:         }
239:
240:•        for (Response r : responses) {
241:•         if (r != null) {
242:•                for (ResponseHandler rh : handlers) {
243:•                 if (rh != null) {
244:                         rh.handleResponse(r);
245:                  }
246:                 }
247:          }
248:         }
249: }
250:
251: protected void processGreeting(Response r) throws ProtocolException {
252:•        if (r.isBYE())
253:          throw new ConnectionException(this, r);
254: }
255:
256: /**
257: * Return the Protocol's InputStream.
258: *
259: * @return        the input stream
260: */
261: protected ResponseInputStream getInputStream() {
262:         return input;
263: }
264:
265: /**
266: * Return the Protocol's OutputStream
267: *
268: * @return        the output stream
269: */
270: protected OutputStream getOutputStream() {
271:         return output;
272: }
273:
274: /**
275: * Returns whether this Protocol supports non-synchronizing literals
276: * Default is false. Subclasses should override this if required
277: *
278: * @return        true if the server supports non-synchronizing literals
279: */
280: protected synchronized boolean supportsNonSyncLiterals() {
281:         return false;
282: }
283:
284: public Response readResponse()
285:                 throws IOException, ProtocolException {
286:         return new Response(this);
287: }
288:
289: /**
290: * Is another response available in our buffer?
291: *
292: * @return        true if another response is in the buffer
293: * @since        JavaMail 1.5.4
294: */
295: public boolean hasResponse() {
296:         /*
297:          * XXX - Really should peek ahead in the buffer to see
298:          * if there's a *complete* response available, but if there
299:          * isn't who's going to read more data into the buffer
300:          * until there is?
301:          */
302:         try {
303:•         return input.available() > 0;
304:         } catch (IOException ex) {
305:         }
306:         return false;
307: }
308:
309: /**
310: * Return a buffer to be used to read a response.
311: * The default implementation returns null, which causes
312: * a new buffer to be allocated for every response.
313: *
314: * @return        the buffer to use
315: * @since        JavaMail 1.4.1
316: */
317: protected ByteArray getResponseBuffer() {
318:         return null;
319: }
320:
321: public String writeCommand(String command, Argument args)
322:                 throws IOException, ProtocolException {
323:         // assert Thread.holdsLock(this);
324:         // can't assert because it's called from constructor
325:         String tag = tagPrefix + Integer.toString(tagCounter++); // unique tag
326:
327:         output.writeBytes(tag + " " + command);
328:
329:•        if (args != null) {
330:          output.write(' ');
331:          args.write(this);
332:         }
333:
334:         output.write(CRLF);
335:         output.flush();
336:         return tag;
337: }
338:
339: /**
340: * Send a command to the server. Collect all responses until either
341: * the corresponding command completion response or a BYE response
342: * (indicating server failure). Return all the collected responses.
343: *
344: * @param        command        the command
345: * @param        args        the arguments
346: * @return                array of Response objects returned by the server
347: */
348: public synchronized Response[] command(String command, Argument args) {
349:         commandStart(command);
350:         List<Response> v = new ArrayList<>();
351:         boolean done = false;
352:         String tag = null;
353:
354:         // write the command
355:         try {
356:          tag = writeCommand(command, args);
357:         } catch (LiteralException lex) {
358:          v.add(lex.getResponse());
359:          done = true;
360:         } catch (Exception ex) {
361:          // Convert this into a BYE response
362:          v.add(Response.byeResponse(ex));
363:          done = true;
364:         }
365:
366:         Response byeResp = null;
367:•        while (!done) {
368:          Response r = null;
369:          try {
370:                 r = readResponse();
371:          } catch (IOException ioex) {
372:•                if (byeResp == null)        // convert this into a BYE response
373:                  byeResp = Response.byeResponse(ioex);
374:                 // else, connection closed after BYE was sent
375:                 break;
376:          } catch (ProtocolException pex) {
377:                 logger.log(Level.FINE, "ignoring bad response", pex);
378:                 continue; // skip this response
379:          }
380:
381:•         if (r.isBYE()) {
382:                 byeResp = r;
383:                 continue;
384:          }
385:
386:          v.add(r);
387:
388:          // If this is a matching command completion response, we are done
389:•         if (r.isTagged() && r.getTag().equals(tag))
390:                 done = true;
391:         }
392:
393:•        if (byeResp != null)
394:                 v.add(byeResp);        // must be last
395:         Response[] responses = new Response[v.size()];
396:         v.toArray(responses);
397: timestamp = System.currentTimeMillis();
398:         commandEnd();
399:         return responses;
400: }
401:
402: /**
403: * Convenience routine to handle OK, NO, BAD and BYE responses.
404: *
405: * @param        response        the response
406: * @exception        ProtocolException        for protocol failures
407: */
408: public void handleResult(Response response) throws ProtocolException {
409:•        if (response.isOK())
410:          return;
411:•        else if (response.isNO())
412:          throw new CommandFailedException(response);
413:•        else if (response.isBAD())
414:          throw new BadCommandException(response);
415:•        else if (response.isBYE()) {
416:          disconnect();
417:          throw new ConnectionException(this, response);
418:         }
419: }
420:
421: /**
422: * Convenience routine to handle simple IAP commands
423: * that do not have responses specific to that command.
424: *
425: * @param        cmd        the command
426: * @param        args        the arguments
427: * @exception        ProtocolException        for protocol failures
428: */
429: public void simpleCommand(String cmd, Argument args)
430:                         throws ProtocolException {
431:         // Issue command
432:         Response[] r = command(cmd, args);
433:
434:         // dispatch untagged responses
435:         notifyResponseHandlers(r);
436:
437:         // Handle result of this command
438:         handleResult(r[r.length-1]);
439: }
440:
441: /**
442: * Start TLS on the current connection.
443: * <code>cmd</code> is the command to issue to start TLS negotiation.
444: * If the command succeeds, we begin TLS negotiation.
445: * If the socket is already an SSLSocket this is a nop and the command
446: * is not issued.
447: *
448: * @param        cmd        the command to issue
449: * @exception        IOException        for I/O errors
450: * @exception        ProtocolException        for protocol failures
451: */
452: public synchronized void startTLS(String cmd)
453:                                 throws IOException, ProtocolException {
454:•        if (socket instanceof SSLSocket)
455:          return;        // nothing to do
456:         simpleCommand(cmd, null);
457:         socket = SocketFetcher.startTLS(socket, host, props, prefix);
458:         initStreams();
459: }
460:
461: /**
462: * Start compression on the current connection.
463: * <code>cmd</code> is the command to issue to start compression.
464: * If the command succeeds, we begin compression.
465: *
466: * @param        cmd        the command to issue
467: * @exception        IOException        for I/O errors
468: * @exception        ProtocolException        for protocol failures
469: */
470: public synchronized void startCompression(String cmd)
471:                                 throws IOException, ProtocolException {
472:         // XXX - check whether compression is already enabled?
473:         simpleCommand(cmd, null);
474:
475:         // need to create our own Inflater and Deflater in order to set nowrap
476:         Inflater inf = new Inflater(true);
477:         traceInput = new TraceInputStream(new InflaterInputStream(
478:                          socket.getInputStream(), inf), traceLogger);
479:         traceInput.setQuote(quote);
480:         input = new ResponseInputStream(traceInput);
481:
482:         // configure the Deflater
483:         int level = PropUtil.getIntProperty(props, prefix + ".compress.level",
484:                                                 Deflater.DEFAULT_COMPRESSION);
485:         int strategy = PropUtil.getIntProperty(props,
486:                                                 prefix + ".compress.strategy",
487:                                                 Deflater.DEFAULT_STRATEGY);
488:•        if (logger.isLoggable(Level.FINE))
489:          logger.log(Level.FINE,
490:                 "Creating Deflater with compression level {0} and strategy {1}",
491:                 new Object[] { level, strategy });
492:         Deflater def = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
493:         try {
494:          def.setLevel(level);
495:         } catch (IllegalArgumentException ex) {
496:          logger.log(Level.FINE, "Ignoring bad compression level", ex);
497:         }
498:         try {
499:          def.setStrategy(strategy);
500:         } catch (IllegalArgumentException ex) {
501:          logger.log(Level.FINE, "Ignoring bad compression strategy", ex);
502:         }
503:         traceOutput = new TraceOutputStream(new DeflaterOutputStream(
504:                          socket.getOutputStream(), def, true), traceLogger);
505:         traceOutput.setQuote(quote);
506:         output = new DataOutputStream(new BufferedOutputStream(traceOutput));
507: }
508:
509: /**
510: * Is this connection using an SSL socket?
511: *
512: * @return        true if using SSL
513: * @since        JavaMail 1.4.6
514: */
515: public boolean isSSL() {
516:         return socket instanceof SSLSocket;
517: }
518:
519: /**
520: * Return the address the socket connected to.
521: *
522: * @return        the InetAddress the socket is connected to
523: * @since        JavaMail 1.5.2
524: */
525: public InetAddress getInetAddress() {
526:         return socket.getInetAddress();
527: }
528:
529: /**
530: * Return the SocketChannel associated with this connection, if any.
531: *
532: * @return        the SocketChannel
533: * @since        JavaMail 1.5.2
534: */
535: public SocketChannel getChannel() {
536: //SocketFetcher controls if a socket has a channel via
537: //usesocketchannels property. When the socket is known to not have
538: //a channel this guard ensures that the reflective search for a socket
539: //channel is avoided which can print warnings to error stream.
540: //This is assuming the session properties are not mutated after the
541: //socket has been connected.
542:• if (PropUtil.getBooleanProperty(props,
543:                                         prefix + ".usesocketchannels", false)) {
544: SocketChannel ret = socket.getChannel();
545:• if (ret == null && socket instanceof SSLSocket) {
546: ret = Protocol.findSocketChannel(socket);
547: }
548: return ret;
549: }
550: return null;
551: }
552:
553: /**
554: * Android/Conscrypt is broken and SSL wrapped sockets don't delegate
555: * the getChannel method to the wrapped Socket. This method attempts to
556: * examine the internals of the SSLSocket to locate the transport socket.
557: *
558: * @param socket a non null socket
559: * @return the SocketChannel or null if not found
560: * @throws NullPointerException if given socket is null
561: */
562: private static SocketChannel findSocketChannel(Socket socket) {
563:         //Search class hierarchy for field name socket regardless of modifier.
564: //Old versions of Android and even versions of Conscrypt use this name.
565:•        for (Class<?> k = socket.getClass(); k != Object.class; k = k.getSuperclass()) {
566: try {
567: Field f = k.getDeclaredField("socket");
568:                 f.setAccessible(true);
569:                 Socket s = (Socket) f.get(socket);
570:• if (s != socket) { //reference compare only
571: SocketChannel ret = s.getChannel();
572:• if (ret != null) {
573: return ret;
574: }
575: }
576: } catch (Exception ignore) {
577: //ignore anything that might go wrong
578: }
579:         }
580:         
581:         //Search class hierarchy for fields that can hold a Socket
582:         //or subclass regardless of modifier but ignoring synthetic fields.
583: //Fields declared as super types of Socket be ignored.
584:•        for (Class<?> k = socket.getClass(); k != Object.class; k = k.getSuperclass()) {
585: try {
586:•                for (Field f : k.getDeclaredFields()) {
587:• if (Socket.class.isAssignableFrom(f.getType())
588:• && !f.isSynthetic()) {
589: try {
590: f.setAccessible(true);
591: Socket s = (Socket) f.get(socket);
592:• if (s != socket) { //reference compare only
593: SocketChannel ret = s.getChannel();
594:• if (ret != null) {
595: return ret;
596: }
597: }
598: } catch (Exception ignore) {
599: //ignore anything that might go wrong
600: }
601: }
602:                 }
603: } catch (Exception ignore) {
604:         //ignore anything that might go wrong
605: }
606:         }
607:         return null;
608: }
609:
610: /**
611: * Return the local SocketAddress (host and port) for this
612: * end of the connection.
613: *
614: * @return        the SocketAddress
615: * @since        Jakarta Mail 1.6.4
616: */
617: public SocketAddress getLocalSocketAddress() {
618:         return socket.getLocalSocketAddress();
619: }
620:
621: /**
622: * Does the server support UTF-8?
623: * This implementation returns false.
624: * Subclasses should override as appropriate.
625: *
626: * @return        true if the server supports UTF-8
627: * @since JavaMail 1.6.0
628: */
629: public boolean supportsUtf8() {
630:         return false;
631: }
632:
633: /**
634: * Disconnect.
635: */
636: protected synchronized void disconnect() {
637:•        if (socket != null) {
638:          try {
639:                 socket.close();
640:          } catch (IOException e) {
641:                 // ignore it
642:          }
643:          socket = null;
644:         }
645: }
646:
647: /**
648: * Get the name of the local host.
649: * The property <prefix>.localhost overrides
650: * <prefix>.localaddress,
651: * which overrides what InetAddress would tell us.
652: *
653: * @return        the name of the local host
654: */
655: protected synchronized String getLocalHost() {
656:         // get our hostname and cache it for future use
657:•        if (localHostName == null || localHostName.length() <= 0)
658:          localHostName =
659:                  props.getProperty(prefix + ".localhost");
660:•        if (localHostName == null || localHostName.length() <= 0)
661:          localHostName =
662:                  props.getProperty(prefix + ".localaddress");
663:         try {
664:•         if (localHostName == null || localHostName.length() <= 0) {
665:                 InetAddress localHost = InetAddress.getLocalHost();
666:                 localHostName = localHost.getCanonicalHostName();
667:                 // if we can't get our name, use local address literal
668:•                if (localHostName == null)
669:                  // XXX - not correct for IPv6
670:                  localHostName = "[" + localHost.getHostAddress() + "]";
671:          }
672:         } catch (UnknownHostException uhex) {
673:         }
674:
675:         // last chance, try to get our address from our socket
676:•        if (localHostName == null || localHostName.length() <= 0) {
677:•         if (socket != null && socket.isBound()) {
678:                 InetAddress localHost = socket.getLocalAddress();
679:                 localHostName = localHost.getCanonicalHostName();
680:                 // if we can't get our name, use local address literal
681:•                if (localHostName == null)
682:                  // XXX - not correct for IPv6
683:                  localHostName = "[" + localHost.getHostAddress() + "]";
684:          }
685:         }
686:         return localHostName;
687: }
688:
689: /**
690: * Is protocol tracing enabled?
691: *
692: * @return        true if protocol tracing is enabled
693: */
694: protected boolean isTracing() {
695:         return traceLogger.isLoggable(Level.FINEST);
696: }
697:
698: /**
699: * Temporarily turn off protocol tracing, e.g., to prevent
700: * tracing the authentication sequence, including the password.
701: */
702: protected void suspendTracing() {
703:•        if (traceLogger.isLoggable(Level.FINEST)) {
704:          traceInput.setTrace(false);
705:          traceOutput.setTrace(false);
706:         }
707: }
708:
709: /**
710: * Resume protocol tracing, if it was enabled to begin with.
711: */
712: protected void resumeTracing() {
713:•        if (traceLogger.isLoggable(Level.FINEST)) {
714:          traceInput.setTrace(true);
715:          traceOutput.setTrace(true);
716:         }
717: }
718:
719: /**
720: * Finalizer.
721: */
722: @Override
723: protected void finalize() throws Throwable {
724:         try {
725:          disconnect();
726:         } finally {
727:          super.finalize();
728:         }
729: }
730:
731: /*
732: * Probe points for GlassFish monitoring.
733: */
734: private void commandStart(String command) { }
735: private void commandEnd() { }
736: }