1498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo/* 2498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * Copyright (C) 2008-2009 Marc Blank 3498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * Licensed to The Android Open Source Project. 4498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * 5498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * Licensed under the Apache License, Version 2.0 (the "License"); 6498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * you may not use this file except in compliance with the License. 7498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * You may obtain a copy of the License at 8498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * 9498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * http://www.apache.org/licenses/LICENSE-2.0 10498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * 11498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * Unless required by applicable law or agreed to in writing, software 12498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * distributed under the License is distributed on an "AS IS" BASIS, 13498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * See the License for the specific language governing permissions and 15498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * limitations under the License. 16498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo */ 17498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 18498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalopackage com.android.exchange; 19498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 203d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komaloimport com.android.emailcommon.utility.EmailClientConnectionManager; 210b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo 22498c903e02ef1b150d6dbd3a01d35839026db264Ben Komaloimport org.apache.http.Header; 23498c903e02ef1b150d6dbd3a01d35839026db264Ben Komaloimport org.apache.http.HttpEntity; 24498c903e02ef1b150d6dbd3a01d35839026db264Ben Komaloimport org.apache.http.HttpResponse; 250b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komaloimport org.apache.http.HttpStatus; 260b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komaloimport org.apache.http.client.HttpClient; 270b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komaloimport org.apache.http.client.methods.HttpUriRequest; 280b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo 29498c903e02ef1b150d6dbd3a01d35839026db264Ben Komaloimport java.io.IOException; 30498c903e02ef1b150d6dbd3a01d35839026db264Ben Komaloimport java.io.InputStream; 31498c903e02ef1b150d6dbd3a01d35839026db264Ben Komaloimport java.util.zip.GZIPInputStream; 32498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 33498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo/** 34498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * Encapsulate a response to an HTTP POST 35498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo */ 36498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalopublic class EasResponse { 376534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank // MSFT's custom HTTP result code indicating the need to provision 386534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank static private final int HTTP_NEED_PROVISIONING = 449; 396534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank 40498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo final HttpResponse mResponse; 41498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final HttpEntity mEntity; 42498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final int mLength; 43498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private InputStream mInputStream; 44498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private boolean mClosed; 45498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 460b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo /** 470b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo * Whether or not a certificate was requested by the server and missing. 480b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo * If this is set, it is essentially a 403 whereby the failure was due 490b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo */ 500b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo private boolean mClientCertRequested = false; 510b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo 520b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo private EasResponse(HttpResponse response) { 53498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo mResponse = response; 540b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo mEntity = (response == null) ? null : mResponse.getEntity(); 55498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo if (mEntity != null) { 563d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo mLength = (int) mEntity.getContentLength(); 57498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } else { 58498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo mLength = 0; 59498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 60498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 61498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 623d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo public static EasResponse fromHttpRequest( 633d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo EmailClientConnectionManager connManager, HttpClient client, HttpUriRequest request) 640b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo throws IOException { 653d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo 663d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo long reqTime = System.currentTimeMillis(); 673d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo HttpResponse response = client.execute(request); 683d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo EasResponse result = new EasResponse(response); 693d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo if (isAuthError(response.getStatusLine().getStatusCode()) 703d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo && connManager.hasDetectedUnsatisfiedCertReq(reqTime)) { 710b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo result.mClientCertRequested = true; 720b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo result.mClosed = true; 730b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo } 743d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo 753d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo return result; 763d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo } 773d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo 783d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo /** 793d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo * Determine whether an HTTP code represents an authentication error 803d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo * @param code the HTTP code returned by the server 813d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo * @return whether or not the code represents an authentication error 823d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo */ 833d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo public static boolean isAuthError(int code) { 843d6d254c2503e317b9b0873dc6a638c974f705f4Ben Komalo return (code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN); 850b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo } 860b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo 87498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo /** 886534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank * Determine whether an HTTP code represents a provisioning error 896534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank * @param code the HTTP code returned by the server 906534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank * @return whether or not the code represents an provisioning error 916534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank */ 926534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank public static boolean isProvisionError(int code) { 936534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank return (code == HTTP_NEED_PROVISIONING) || (code == HttpStatus.SC_FORBIDDEN); 946534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank } 956534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank 966534bd5e100625c653c9fba09243bbb67a9023c7Marc Blank /** 97498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * Return an appropriate input stream for the response, either a GZIPInputStream, for 98498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * compressed data, or a generic InputStream otherwise 99498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo * @return the input stream for the response 100498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo */ 101498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo public InputStream getInputStream() { 102498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo if (mInputStream != null || mClosed) { 103498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo throw new IllegalStateException("Can't reuse stream or get closed stream"); 104498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } else if (mEntity == null) { 105498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo throw new IllegalStateException("Can't get input stream without entity"); 106498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 107498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo InputStream is = null; 108498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo try { 109498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo // Get the default input stream for the entity 110498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo is = mEntity.getContent(); 111498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo Header ceHeader = mResponse.getFirstHeader("Content-Encoding"); 112498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo if (ceHeader != null) { 113498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo String encoding = ceHeader.getValue(); 114498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo // If we're gzip encoded, wrap appropriately 115498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo if (encoding.toLowerCase().equals("gzip")) { 116498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo is = new GZIPInputStream(is); 117498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 118498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 119498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } catch (IllegalStateException e1) { 120498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } catch (IOException e1) { 121498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 122498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo mInputStream = is; 123498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo return is; 124498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 125498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 126498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo public boolean isEmpty() { 127498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo return mLength == 0; 128498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 129498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 130498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo public int getStatus() { 1310b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo return mClientCertRequested 1320b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo ? HttpStatus.SC_UNAUTHORIZED 1330b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo : mResponse.getStatusLine().getStatusCode(); 1340b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo } 1350b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo 1360b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo public boolean isMissingCertificate() { 1370b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo return mClientCertRequested; 138498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 139498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 140498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo public Header getHeader(String name) { 1410b3a1547b4adf380dab1cc9a1af6c227c9a4e00fBen Komalo return (mResponse == null) ? null : mResponse.getFirstHeader(name); 142498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 143498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 144498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo public int getLength() { 145498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo return mLength; 146498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 147498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo 148498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo public void close() { 1496760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank if (!mClosed) { 1506760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank if (mEntity != null) { 1516760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank try { 1526760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank mEntity.consumeContent(); 1536760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank } catch (IOException e) { 1546760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank // No harm, no foul 1556760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank } 1566760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank } 1576760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank if (mInputStream instanceof GZIPInputStream) { 1586760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank try { 1596760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank mInputStream.close(); 1606760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank } catch (IOException e) { 1616760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank // We tried 1626760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank } 163498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 1646760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank mClosed = true; 165498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 166498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo } 167498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo}