@Test public void open_withXoauth2Extension_shouldInvalidateAndRetryOnMissingStatusJsonResponse() throws Exception { MockSmtpServer server = new MockSmtpServer(); server.output("220 localhost Simple Mail Transfer Service Ready"); server.expect("EHLO localhost"); server.output("250-localhost Hello client.localhost"); server.output("250 AUTH XOAUTH2"); server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG9sZFRva2VuAQE="); server.output("334 "+ XOAuth2ChallengeParserTest.MISSING_STATUS_RESPONSE); server.expect(""); server.output("535-5.7.1 Username and Password not accepted. Learn more at"); server.output("535 5.7.1 http: server.expect("AUTH XOAUTH2 dXNlcj11c2VyAWF1dGg9QmVhcmVyIG5ld1Rva2VuAQE="); server.output("235 2.7.0 Authentication successful"); SmtpTransport transport = startServerAndCreateSmtpTransport(server, AuthType.XOAUTH2, ConnectionSecurity.NONE); transport.open(); InOrder inOrder = inOrder(oAuth2TokenProvider); inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyInt()); inOrder.verify(oAuth2TokenProvider).invalidateToken(USERNAME); inOrder.verify(oAuth2TokenProvider).getToken(eq(USERNAME), anyInt()); server.verifyConnectionStillOpen(); server.verifyInteractionCompleted(); }
@Override public void open() throws MessagingException { try { boolean secureConnection = false; InetAddress[] addresses = InetAddress.getAllByName(host); for (int i = 0; i < addresses.length; i++) { try { SocketAddress socketAddress = new InetSocketAddress(addresses[i], port); if (connectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) { socket = trustedSocketFactory.createSocket(null, host, port, clientCertificateAlias); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); secureConnection = true; } else { socket = new Socket(); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); } } catch (SocketException e) { if (i < (addresses.length - 1)) { continue; } throw new MessagingException("Cannot connect to host", e); } break; } socket.setSoTimeout(SOCKET_READ_TIMEOUT); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); executeCommand(null); InetAddress localAddress = socket.getLocalAddress(); String localHost = getCanonicalHostName(localAddress); String ipAddr = localAddress.getHostAddress(); if (localHost.equals("") || localHost.equals(ipAddr) || localHost.contains("_")) { if (!ipAddr.equals("")) { if (localAddress instanceof Inet6Address) { localHost = "[IPv6:" + ipAddr + "]"; } else { localHost = "[" + ipAddr + "]"; } } else { localHost = "android"; } } Map<String, String> extensions = sendHello(localHost); is8bitEncodingAllowed = extensions.containsKey("8BITMIME"); isEnhancedStatusCodesProvided = extensions.containsKey("ENHANCEDSTATUSCODES"); isPipeliningSupported = extensions.containsKey("PIPELINING"); if (connectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) { if (extensions.containsKey("STARTTLS")) { executeCommand("STARTTLS"); socket = trustedSocketFactory.createSocket( socket, host, port, clientCertificateAlias); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); extensions = sendHello(localHost); secureConnection = true; } else { throw new CertificateValidationException( "STARTTLS connection security not available"); } } boolean authLoginSupported = false; boolean authPlainSupported = false; boolean authCramMD5Supported = false; boolean authExternalSupported = false; boolean authXoauth2Supported = false; if (extensions.containsKey("AUTH")) { List<String> saslMech = Arrays.asList(extensions.get("AUTH").split(" ")); authLoginSupported = saslMech.contains("LOGIN"); authPlainSupported = saslMech.contains("PLAIN"); authCramMD5Supported = saslMech.contains("CRAM-MD5"); authExternalSupported = saslMech.contains("EXTERNAL"); authXoauth2Supported = saslMech.contains("XOAUTH2"); } parseOptionalSizeValue(extensions); if (!TextUtils.isEmpty(username) && (!TextUtils.isEmpty(password) || AuthType.EXTERNAL == authType || AuthType.XOAUTH2 == authType)) { switch (authType) { case LOGIN: case PLAIN: if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else { throw new MessagingException( "Authentication methods SASL PLAIN and LOGIN are unavailable."); } break; case CRAM_MD5: if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("Authentication method CRAM-MD5 is unavailable."); } break; case XOAUTH2: if (authXoauth2Supported && oauthTokenProvider != null) { saslXoauth2(); } else { throw new MessagingException("Authentication method XOAUTH2 is unavailable."); } break; case EXTERNAL: if (authExternalSupported) { saslAuthExternal(); } else { throw new CertificateValidationException(MissingCapability); } break; case AUTOMATIC: if (secureConnection) { if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("No supported authentication methods available."); } } else { if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException( "Update your outgoing server authentication setting. AUTOMATIC auth. is unavailable."); } } break; default: throw new MessagingException( "Unhandled authentication method found in the server settings (bug)."); } } } catch (MessagingException e) { close(); throw e; } catch (SSLException e) { close(); throw new CertificateValidationException(e.getMessage(), e); } catch (GeneralSecurityException gse) { close(); throw new MessagingException( "Unable to open connection to SMTP server due to security error.", gse); } catch (IOException ioe) { close(); throw new MessagingException("Unable to open connection to SMTP server.", ioe); } }
SmtpTransport extends Transport { @Override public void open() throws MessagingException { try { boolean secureConnection = false; InetAddress[] addresses = InetAddress.getAllByName(host); for (int i = 0; i < addresses.length; i++) { try { SocketAddress socketAddress = new InetSocketAddress(addresses[i], port); if (connectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) { socket = trustedSocketFactory.createSocket(null, host, port, clientCertificateAlias); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); secureConnection = true; } else { socket = new Socket(); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); } } catch (SocketException e) { if (i < (addresses.length - 1)) { continue; } throw new MessagingException("Cannot connect to host", e); } break; } socket.setSoTimeout(SOCKET_READ_TIMEOUT); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); executeCommand(null); InetAddress localAddress = socket.getLocalAddress(); String localHost = getCanonicalHostName(localAddress); String ipAddr = localAddress.getHostAddress(); if (localHost.equals("") || localHost.equals(ipAddr) || localHost.contains("_")) { if (!ipAddr.equals("")) { if (localAddress instanceof Inet6Address) { localHost = "[IPv6:" + ipAddr + "]"; } else { localHost = "[" + ipAddr + "]"; } } else { localHost = "android"; } } Map<String, String> extensions = sendHello(localHost); is8bitEncodingAllowed = extensions.containsKey("8BITMIME"); isEnhancedStatusCodesProvided = extensions.containsKey("ENHANCEDSTATUSCODES"); isPipeliningSupported = extensions.containsKey("PIPELINING"); if (connectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) { if (extensions.containsKey("STARTTLS")) { executeCommand("STARTTLS"); socket = trustedSocketFactory.createSocket( socket, host, port, clientCertificateAlias); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); extensions = sendHello(localHost); secureConnection = true; } else { throw new CertificateValidationException( "STARTTLS connection security not available"); } } boolean authLoginSupported = false; boolean authPlainSupported = false; boolean authCramMD5Supported = false; boolean authExternalSupported = false; boolean authXoauth2Supported = false; if (extensions.containsKey("AUTH")) { List<String> saslMech = Arrays.asList(extensions.get("AUTH").split(" ")); authLoginSupported = saslMech.contains("LOGIN"); authPlainSupported = saslMech.contains("PLAIN"); authCramMD5Supported = saslMech.contains("CRAM-MD5"); authExternalSupported = saslMech.contains("EXTERNAL"); authXoauth2Supported = saslMech.contains("XOAUTH2"); } parseOptionalSizeValue(extensions); if (!TextUtils.isEmpty(username) && (!TextUtils.isEmpty(password) || AuthType.EXTERNAL == authType || AuthType.XOAUTH2 == authType)) { switch (authType) { case LOGIN: case PLAIN: if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else { throw new MessagingException( "Authentication methods SASL PLAIN and LOGIN are unavailable."); } break; case CRAM_MD5: if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("Authentication method CRAM-MD5 is unavailable."); } break; case XOAUTH2: if (authXoauth2Supported && oauthTokenProvider != null) { saslXoauth2(); } else { throw new MessagingException("Authentication method XOAUTH2 is unavailable."); } break; case EXTERNAL: if (authExternalSupported) { saslAuthExternal(); } else { throw new CertificateValidationException(MissingCapability); } break; case AUTOMATIC: if (secureConnection) { if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("No supported authentication methods available."); } } else { if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException( "Update your outgoing server authentication setting. AUTOMATIC auth. is unavailable."); } } break; default: throw new MessagingException( "Unhandled authentication method found in the server settings (bug)."); } } } catch (MessagingException e) { close(); throw e; } catch (SSLException e) { close(); throw new CertificateValidationException(e.getMessage(), e); } catch (GeneralSecurityException gse) { close(); throw new MessagingException( "Unable to open connection to SMTP server due to security error.", gse); } catch (IOException ioe) { close(); throw new MessagingException("Unable to open connection to SMTP server.", ioe); } } }
SmtpTransport extends Transport { @Override public void open() throws MessagingException { try { boolean secureConnection = false; InetAddress[] addresses = InetAddress.getAllByName(host); for (int i = 0; i < addresses.length; i++) { try { SocketAddress socketAddress = new InetSocketAddress(addresses[i], port); if (connectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) { socket = trustedSocketFactory.createSocket(null, host, port, clientCertificateAlias); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); secureConnection = true; } else { socket = new Socket(); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); } } catch (SocketException e) { if (i < (addresses.length - 1)) { continue; } throw new MessagingException("Cannot connect to host", e); } break; } socket.setSoTimeout(SOCKET_READ_TIMEOUT); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); executeCommand(null); InetAddress localAddress = socket.getLocalAddress(); String localHost = getCanonicalHostName(localAddress); String ipAddr = localAddress.getHostAddress(); if (localHost.equals("") || localHost.equals(ipAddr) || localHost.contains("_")) { if (!ipAddr.equals("")) { if (localAddress instanceof Inet6Address) { localHost = "[IPv6:" + ipAddr + "]"; } else { localHost = "[" + ipAddr + "]"; } } else { localHost = "android"; } } Map<String, String> extensions = sendHello(localHost); is8bitEncodingAllowed = extensions.containsKey("8BITMIME"); isEnhancedStatusCodesProvided = extensions.containsKey("ENHANCEDSTATUSCODES"); isPipeliningSupported = extensions.containsKey("PIPELINING"); if (connectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) { if (extensions.containsKey("STARTTLS")) { executeCommand("STARTTLS"); socket = trustedSocketFactory.createSocket( socket, host, port, clientCertificateAlias); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); extensions = sendHello(localHost); secureConnection = true; } else { throw new CertificateValidationException( "STARTTLS connection security not available"); } } boolean authLoginSupported = false; boolean authPlainSupported = false; boolean authCramMD5Supported = false; boolean authExternalSupported = false; boolean authXoauth2Supported = false; if (extensions.containsKey("AUTH")) { List<String> saslMech = Arrays.asList(extensions.get("AUTH").split(" ")); authLoginSupported = saslMech.contains("LOGIN"); authPlainSupported = saslMech.contains("PLAIN"); authCramMD5Supported = saslMech.contains("CRAM-MD5"); authExternalSupported = saslMech.contains("EXTERNAL"); authXoauth2Supported = saslMech.contains("XOAUTH2"); } parseOptionalSizeValue(extensions); if (!TextUtils.isEmpty(username) && (!TextUtils.isEmpty(password) || AuthType.EXTERNAL == authType || AuthType.XOAUTH2 == authType)) { switch (authType) { case LOGIN: case PLAIN: if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else { throw new MessagingException( "Authentication methods SASL PLAIN and LOGIN are unavailable."); } break; case CRAM_MD5: if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("Authentication method CRAM-MD5 is unavailable."); } break; case XOAUTH2: if (authXoauth2Supported && oauthTokenProvider != null) { saslXoauth2(); } else { throw new MessagingException("Authentication method XOAUTH2 is unavailable."); } break; case EXTERNAL: if (authExternalSupported) { saslAuthExternal(); } else { throw new CertificateValidationException(MissingCapability); } break; case AUTOMATIC: if (secureConnection) { if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("No supported authentication methods available."); } } else { if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException( "Update your outgoing server authentication setting. AUTOMATIC auth. is unavailable."); } } break; default: throw new MessagingException( "Unhandled authentication method found in the server settings (bug)."); } } } catch (MessagingException e) { close(); throw e; } catch (SSLException e) { close(); throw new CertificateValidationException(e.getMessage(), e); } catch (GeneralSecurityException gse) { close(); throw new MessagingException( "Unable to open connection to SMTP server due to security error.", gse); } catch (IOException ioe) { close(); throw new MessagingException("Unable to open connection to SMTP server.", ioe); } } SmtpTransport(StoreConfig storeConfig, TrustedSocketFactory trustedSocketFactory,
            OAuth2TokenProvider oauthTokenProvider); }
SmtpTransport extends Transport { @Override public void open() throws MessagingException { try { boolean secureConnection = false; InetAddress[] addresses = InetAddress.getAllByName(host); for (int i = 0; i < addresses.length; i++) { try { SocketAddress socketAddress = new InetSocketAddress(addresses[i], port); if (connectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) { socket = trustedSocketFactory.createSocket(null, host, port, clientCertificateAlias); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); secureConnection = true; } else { socket = new Socket(); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); } } catch (SocketException e) { if (i < (addresses.length - 1)) { continue; } throw new MessagingException("Cannot connect to host", e); } break; } socket.setSoTimeout(SOCKET_READ_TIMEOUT); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); executeCommand(null); InetAddress localAddress = socket.getLocalAddress(); String localHost = getCanonicalHostName(localAddress); String ipAddr = localAddress.getHostAddress(); if (localHost.equals("") || localHost.equals(ipAddr) || localHost.contains("_")) { if (!ipAddr.equals("")) { if (localAddress instanceof Inet6Address) { localHost = "[IPv6:" + ipAddr + "]"; } else { localHost = "[" + ipAddr + "]"; } } else { localHost = "android"; } } Map<String, String> extensions = sendHello(localHost); is8bitEncodingAllowed = extensions.containsKey("8BITMIME"); isEnhancedStatusCodesProvided = extensions.containsKey("ENHANCEDSTATUSCODES"); isPipeliningSupported = extensions.containsKey("PIPELINING"); if (connectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) { if (extensions.containsKey("STARTTLS")) { executeCommand("STARTTLS"); socket = trustedSocketFactory.createSocket( socket, host, port, clientCertificateAlias); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); extensions = sendHello(localHost); secureConnection = true; } else { throw new CertificateValidationException( "STARTTLS connection security not available"); } } boolean authLoginSupported = false; boolean authPlainSupported = false; boolean authCramMD5Supported = false; boolean authExternalSupported = false; boolean authXoauth2Supported = false; if (extensions.containsKey("AUTH")) { List<String> saslMech = Arrays.asList(extensions.get("AUTH").split(" ")); authLoginSupported = saslMech.contains("LOGIN"); authPlainSupported = saslMech.contains("PLAIN"); authCramMD5Supported = saslMech.contains("CRAM-MD5"); authExternalSupported = saslMech.contains("EXTERNAL"); authXoauth2Supported = saslMech.contains("XOAUTH2"); } parseOptionalSizeValue(extensions); if (!TextUtils.isEmpty(username) && (!TextUtils.isEmpty(password) || AuthType.EXTERNAL == authType || AuthType.XOAUTH2 == authType)) { switch (authType) { case LOGIN: case PLAIN: if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else { throw new MessagingException( "Authentication methods SASL PLAIN and LOGIN are unavailable."); } break; case CRAM_MD5: if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("Authentication method CRAM-MD5 is unavailable."); } break; case XOAUTH2: if (authXoauth2Supported && oauthTokenProvider != null) { saslXoauth2(); } else { throw new MessagingException("Authentication method XOAUTH2 is unavailable."); } break; case EXTERNAL: if (authExternalSupported) { saslAuthExternal(); } else { throw new CertificateValidationException(MissingCapability); } break; case AUTOMATIC: if (secureConnection) { if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("No supported authentication methods available."); } } else { if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException( "Update your outgoing server authentication setting. AUTOMATIC auth. is unavailable."); } } break; default: throw new MessagingException( "Unhandled authentication method found in the server settings (bug)."); } } } catch (MessagingException e) { close(); throw e; } catch (SSLException e) { close(); throw new CertificateValidationException(e.getMessage(), e); } catch (GeneralSecurityException gse) { close(); throw new MessagingException( "Unable to open connection to SMTP server due to security error.", gse); } catch (IOException ioe) { close(); throw new MessagingException("Unable to open connection to SMTP server.", ioe); } } SmtpTransport(StoreConfig storeConfig, TrustedSocketFactory trustedSocketFactory,
            OAuth2TokenProvider oauthTokenProvider); @Override void open(); @Override void sendMessage(Message message); @Override void close(); }
SmtpTransport extends Transport { @Override public void open() throws MessagingException { try { boolean secureConnection = false; InetAddress[] addresses = InetAddress.getAllByName(host); for (int i = 0; i < addresses.length; i++) { try { SocketAddress socketAddress = new InetSocketAddress(addresses[i], port); if (connectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) { socket = trustedSocketFactory.createSocket(null, host, port, clientCertificateAlias); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); secureConnection = true; } else { socket = new Socket(); socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); } } catch (SocketException e) { if (i < (addresses.length - 1)) { continue; } throw new MessagingException("Cannot connect to host", e); } break; } socket.setSoTimeout(SOCKET_READ_TIMEOUT); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); executeCommand(null); InetAddress localAddress = socket.getLocalAddress(); String localHost = getCanonicalHostName(localAddress); String ipAddr = localAddress.getHostAddress(); if (localHost.equals("") || localHost.equals(ipAddr) || localHost.contains("_")) { if (!ipAddr.equals("")) { if (localAddress instanceof Inet6Address) { localHost = "[IPv6:" + ipAddr + "]"; } else { localHost = "[" + ipAddr + "]"; } } else { localHost = "android"; } } Map<String, String> extensions = sendHello(localHost); is8bitEncodingAllowed = extensions.containsKey("8BITMIME"); isEnhancedStatusCodesProvided = extensions.containsKey("ENHANCEDSTATUSCODES"); isPipeliningSupported = extensions.containsKey("PIPELINING"); if (connectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) { if (extensions.containsKey("STARTTLS")) { executeCommand("STARTTLS"); socket = trustedSocketFactory.createSocket( socket, host, port, clientCertificateAlias); inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024)); outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024); extensions = sendHello(localHost); secureConnection = true; } else { throw new CertificateValidationException( "STARTTLS connection security not available"); } } boolean authLoginSupported = false; boolean authPlainSupported = false; boolean authCramMD5Supported = false; boolean authExternalSupported = false; boolean authXoauth2Supported = false; if (extensions.containsKey("AUTH")) { List<String> saslMech = Arrays.asList(extensions.get("AUTH").split(" ")); authLoginSupported = saslMech.contains("LOGIN"); authPlainSupported = saslMech.contains("PLAIN"); authCramMD5Supported = saslMech.contains("CRAM-MD5"); authExternalSupported = saslMech.contains("EXTERNAL"); authXoauth2Supported = saslMech.contains("XOAUTH2"); } parseOptionalSizeValue(extensions); if (!TextUtils.isEmpty(username) && (!TextUtils.isEmpty(password) || AuthType.EXTERNAL == authType || AuthType.XOAUTH2 == authType)) { switch (authType) { case LOGIN: case PLAIN: if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else { throw new MessagingException( "Authentication methods SASL PLAIN and LOGIN are unavailable."); } break; case CRAM_MD5: if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("Authentication method CRAM-MD5 is unavailable."); } break; case XOAUTH2: if (authXoauth2Supported && oauthTokenProvider != null) { saslXoauth2(); } else { throw new MessagingException("Authentication method XOAUTH2 is unavailable."); } break; case EXTERNAL: if (authExternalSupported) { saslAuthExternal(); } else { throw new CertificateValidationException(MissingCapability); } break; case AUTOMATIC: if (secureConnection) { if (authPlainSupported) { saslAuthPlain(); } else if (authLoginSupported) { saslAuthLogin(); } else if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException("No supported authentication methods available."); } } else { if (authCramMD5Supported) { saslAuthCramMD5(); } else { throw new MessagingException( "Update your outgoing server authentication setting. AUTOMATIC auth. is unavailable."); } } break; default: throw new MessagingException( "Unhandled authentication method found in the server settings (bug)."); } } } catch (MessagingException e) { close(); throw e; } catch (SSLException e) { close(); throw new CertificateValidationException(e.getMessage(), e); } catch (GeneralSecurityException gse) { close(); throw new MessagingException( "Unable to open connection to SMTP server due to security error.", gse); } catch (IOException ioe) { close(); throw new MessagingException("Unable to open connection to SMTP server.", ioe); } } SmtpTransport(StoreConfig storeConfig, TrustedSocketFactory trustedSocketFactory,
            OAuth2TokenProvider oauthTokenProvider); @Override void open(); @Override void sendMessage(Message message); @Override void close();  }