196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/* 296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * you may not use this file except in compliance with the License. 696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * You may obtain a copy of the License at 796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * See the License for the specific language governing permissions and 1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * limitations under the License. 1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage com.android.email.mail.transport; 1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 19bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Email; 20bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.mail.Transport; 2131d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.Logging; 222193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.CertificateValidationException; 232193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.MessagingException; 243a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blankimport com.android.emailcommon.utility.SSLUtils; 2596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 26bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.util.Log; 27bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.BufferedInputStream; 2996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.BufferedOutputStream; 3096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException; 3196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream; 3296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.OutputStream; 33f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onukiimport java.net.InetAddress; 3496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.net.InetSocketAddress; 3596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.net.Socket; 3696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.net.SocketAddress; 3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.net.SocketException; 3896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 39fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.HostnameVerifier; 40fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.HttpsURLConnection; 4196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport javax.net.ssl.SSLException; 42fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.SSLPeerUnverifiedException; 43fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.SSLSession; 44fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.SSLSocket; 4596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 46bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/** 47bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * This class implements the common aspects of "transport", one layer below the 48bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * specific wire protocols such as POP3, IMAP, or SMTP. 49bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 50bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpublic class MailTransport implements Transport { 518d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 5296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // TODO protected eventually 5396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /*protected*/ public static final int SOCKET_CONNECT_TIMEOUT = 10000; 5496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /*protected*/ public static final int SOCKET_READ_TIMEOUT = 60000; 5596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 56fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler private static final HostnameVerifier HOSTNAME_VERIFIER = 57fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler HttpsURLConnection.getDefaultHostnameVerifier(); 58fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler 59bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private String mDebugLabel; 60bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 61bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private String mHost; 62bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private int mPort; 63bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private String[] mUserInfoParts; 64bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 65bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 66bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * One of the {@code Transport.CONNECTION_SECURITY_*} values. 67bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 68bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private int mConnectionSecurity; 69bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 70bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 71bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Whether or not to trust all server certificates (i.e. skip host verification) in SSL 72bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * handshakes 73bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 74bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private boolean mTrustCertificates; 7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private Socket mSocket; 7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private InputStream mIn; 7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private OutputStream mOut; 7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 80bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 81bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Simple constructor for starting from scratch. Call setUri() and setSecurity() to 82bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * complete the configuration. 83bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param debugLabel Label used for Log.d calls 84bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 85bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public MailTransport(String debugLabel) { 8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project super(); 8796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mDebugLabel = debugLabel; 8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 898d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 90bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 91ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy * Returns a new transport, using the current transport as a model. The new transport is 92ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy * configured identically (as if {@link #setSecurity(int, boolean)}, {@link #setPort(int)} 93ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy * and {@link #setHost(String)} were invoked), but not opened or connected in any way. 9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 95ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy @Override 96bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public Transport clone() { 97bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook MailTransport newObject = new MailTransport(mDebugLabel); 98bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 99bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook newObject.mDebugLabel = mDebugLabel; 100bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook newObject.mHost = mHost; 101bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook newObject.mPort = mPort; 102bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (mUserInfoParts != null) { 103bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook newObject.mUserInfoParts = mUserInfoParts.clone(); 104bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 105bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook newObject.mConnectionSecurity = mConnectionSecurity; 106bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook newObject.mTrustCertificates = mTrustCertificates; 107bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return newObject; 108bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 109bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 110bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 111bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void setHost(String host) { 112bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mHost = host; 113bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 114bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 115bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 116bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void setPort(int port) { 117bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mPort = port; 118a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy } 119a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy 120bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public String getHost() { 122bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return mHost; 12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 125bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 12696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public int getPort() { 127bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return mPort; 128bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 129bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 130bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 131bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void setSecurity(int connectionSecurity, boolean trustAllCertificates) { 132bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mConnectionSecurity = connectionSecurity; 133bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mTrustCertificates = trustAllCertificates; 13496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 136bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 137bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public int getSecurity() { 138bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return mConnectionSecurity; 139bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 140bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 141bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public boolean canTrySslSecurity() { 143bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return mConnectionSecurity == Transport.CONNECTION_SECURITY_SSL; 14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 1458d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 146bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 14796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public boolean canTryTlsSecurity() { 148bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return mConnectionSecurity == Transport.CONNECTION_SECURITY_TLS; 14996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 1508d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 151bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 152e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler public boolean canTrustAllCertificates() { 153bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return mTrustCertificates; 154e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler } 155e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler 15696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 15796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Attempts to open a connection using the Uri supplied for connection parameters. Will attempt 15896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * an SSL connection if indicated. 15996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 160bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 16196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void open() throws MessagingException, CertificateValidationException { 162bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 16331d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.d(Logging.LOG_TAG, "*** " + mDebugLabel + " open " + 16496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project getHost() + ":" + String.valueOf(getPort())); 16596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 16796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project SocketAddress socketAddress = new InetSocketAddress(getHost(), getPort()); 16996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (canTrySslSecurity()) { 170bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mSocket = SSLUtils.getSSLSocketFactory(canTrustAllCertificates()).createSocket(); 17196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 17296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket = new Socket(); 17396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 17496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); 175fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // After the socket connects to an SSL server, confirm that the hostname is as expected 176fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler if (canTrySslSecurity() && !canTrustAllCertificates()) { 177fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler verifyHostname(mSocket, getHost()); 178fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler } 17996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mIn = new BufferedInputStream(mSocket.getInputStream(), 1024); 18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512); 1818d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 18296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (SSLException e) { 183bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 18431d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.d(Logging.LOG_TAG, e.toString()); 18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 18696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project throw new CertificateValidationException(e.getMessage(), e); 18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (IOException ioe) { 188bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 18931d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.d(Logging.LOG_TAG, ioe.toString()); 19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project throw new MessagingException(MessagingException.IOERROR, ioe.toString()); 19296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 19596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 19696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Attempts to reopen a TLS connection using the Uri supplied for connection parameters. 19796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 198fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * NOTE: No explicit hostname verification is required here, because it's handled automatically 199fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * by the call to createSocket(). 200fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * 20196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * TODO should we explicitly close the old socket? This seems funky to abandon it. 20296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 203bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 20496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void reopenTls() throws MessagingException { 20596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 206bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mSocket = SSLUtils.getSSLSocketFactory(canTrustAllCertificates()) 207c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki .createSocket(mSocket, getHost(), getPort(), true); 20896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket.setSoTimeout(SOCKET_READ_TIMEOUT); 20996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mIn = new BufferedInputStream(mSocket.getInputStream(), 1024); 21096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512); 21196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 212e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler } catch (SSLException e) { 213bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 21431d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.d(Logging.LOG_TAG, e.toString()); 215e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler } 216e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler throw new CertificateValidationException(e.getMessage(), e); 21796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (IOException ioe) { 218bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 21931d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.d(Logging.LOG_TAG, ioe.toString()); 22096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project throw new MessagingException(MessagingException.IOERROR, ioe.toString()); 22296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 224fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler 225fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler /** 226fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * Lightweight version of SSLCertificateSocketFactory.verifyHostname, which provides this 227fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * service but is not in the public API. 228fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * 229fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * Verify the hostname of the certificate used by the other end of a 230fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * connected socket. You MUST call this if you did not supply a hostname 231fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * to SSLCertificateSocketFactory.createSocket(). It is harmless to call this method 232fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * redundantly if the hostname has already been verified. 233fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * 234fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * <p>Wildcard certificates are allowed to verify any matching hostname, 235fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * so "foo.bar.example.com" is verified if the peer has a certificate 236fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * for "*.example.com". 237fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * 238fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * @param socket An SSL socket which has been connected to a server 239fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * @param hostname The expected hostname of the remote server 240fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * @throws IOException if something goes wrong handshaking with the server 241fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * @throws SSLPeerUnverifiedException if the server cannot prove its identity 242fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler */ 243fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler private void verifyHostname(Socket socket, String hostname) throws IOException { 244fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // The code at the start of OpenSSLSocketImpl.startHandshake() 245fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // ensures that the call is idempotent, so we can safely call it. 246fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler SSLSocket ssl = (SSLSocket) socket; 247fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler ssl.startHandshake(); 248fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler 249fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler SSLSession session = ssl.getSession(); 250fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler if (session == null) { 251fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler throw new SSLException("Cannot verify SSL socket without session"); 252fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler } 253fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // TODO: Instead of reporting the name of the server we think we're connecting to, 254fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // we should be reporting the bad name in the certificate. Unfortunately this is buried 255fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // in the verifier code and is not available in the verifier API, and extracting the 256fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // CN & alts is beyond the scope of this patch. 257fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler if (!HOSTNAME_VERIFIER.verify(hostname, session)) { 258fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler throw new SSLPeerUnverifiedException( 259fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler "Certificate hostname not useable for server: " + hostname); 260fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler } 261fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler } 262fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler 26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Set the socket timeout. 26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param timeoutMilliseconds the read timeout value if greater than {@code 0}, or 26696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * {@code 0} for an infinite timeout. 26796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 268bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 26996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void setSoTimeout(int timeoutMilliseconds) throws SocketException { 27096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket.setSoTimeout(timeoutMilliseconds); 27196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 27296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 273bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public boolean isOpen() { 2758d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy return (mIn != null && mOut != null && 27696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket != null && mSocket.isConnected() && !mSocket.isClosed()); 27796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 27896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 28096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Close the connection. MUST NOT return any exceptions - must be "best effort" and safe. 28196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 282bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 28396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void close() { 28496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 28596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mIn.close(); 28696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (Exception e) { 28796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // May fail if the connection is already closed. 28896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 28996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 29096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mOut.close(); 29196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (Exception e) { 29296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // May fail if the connection is already closed. 29396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 29496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 29596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket.close(); 29696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (Exception e) { 29796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // May fail if the connection is already closed. 29896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 29996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mIn = null; 30096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mOut = null; 30196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket = null; 30296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 304bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 30596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public InputStream getInputStream() { 30696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return mIn; 30796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 309bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 31096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public OutputStream getOutputStream() { 31196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return mOut; 31296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 3138d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 31496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 31596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Writes a single line to the server using \r\n termination. 31696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 317bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 31896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void writeLine(String s, String sensitiveReplacement) throws IOException { 319bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 320bfac9f2e8a13f6c719608a6948203bbef921c99fMakoto Onuki if (sensitiveReplacement != null && !Logging.DEBUG_SENSITIVE) { 32131d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.d(Logging.LOG_TAG, ">>> " + sensitiveReplacement); 32296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 32331d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.d(Logging.LOG_TAG, ">>> " + s); 32496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 32596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 3268d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 32796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project OutputStream out = getOutputStream(); 32896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.write(s.getBytes()); 32996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.write('\r'); 33096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.write('\n'); 33196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.flush(); 33296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 3338d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 33496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 33596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Reads a single line from the server, using either \r\n or \n as the delimiter. The 33696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * delimiter char(s) are not included in the result. 33796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 338bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 339bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public String readLine() throws IOException { 34096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project StringBuffer sb = new StringBuffer(); 34196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project InputStream in = getInputStream(); 34296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int d; 34396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project while ((d = in.read()) != -1) { 34496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (((char)d) == '\r') { 34596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project continue; 34696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else if (((char)d) == '\n') { 34796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project break; 34896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 34996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project sb.append((char)d); 35096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 35196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 352bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (d == -1 && Email.DEBUG) { 35331d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.d(Logging.LOG_TAG, "End of stream reached while trying to read line."); 35496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 35596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String ret = sb.toString(); 356bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 35731d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank Log.d(Logging.LOG_TAG, "<<< " + ret); 35896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 35996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return ret; 36096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 36196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 362bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 363f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki public InetAddress getLocalAddress() { 364f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki if (isOpen()) { 365f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki return mSocket.getLocalAddress(); 366f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki } else { 367f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki return null; 368f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki } 369f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki } 37096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project} 371