1 /* 2 * Copyright (C) 2008-2009 Marc Blank 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.exchange; 19 20 import com.android.emailcommon.utility.EmailClientConnectionManager; 21 22 import org.apache.http.Header; 23 import org.apache.http.HttpEntity; 24 import org.apache.http.HttpResponse; 25 import org.apache.http.HttpStatus; 26 import org.apache.http.client.HttpClient; 27 import org.apache.http.client.methods.HttpUriRequest; 28 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.util.zip.GZIPInputStream; 32 33 /** 34 * Encapsulate a response to an HTTP POST 35 */ 36 public class EasResponse { 37 // MSFT's custom HTTP result code indicating the need to provision 38 static private final int HTTP_NEED_PROVISIONING = 449; 39 40 final HttpResponse mResponse; 41 private final HttpEntity mEntity; 42 private final int mLength; 43 private InputStream mInputStream; 44 private boolean mClosed; 45 46 /** 47 * Whether or not a certificate was requested by the server and missing. 48 * If this is set, it is essentially a 403 whereby the failure was due 49 */ 50 private boolean mClientCertRequested = false; 51 52 private EasResponse(HttpResponse response) { 53 mResponse = response; 54 mEntity = (response == null) ? null : mResponse.getEntity(); 55 if (mEntity != null) { 56 mLength = (int) mEntity.getContentLength(); 57 } else { 58 mLength = 0; 59 } 60 } 61 62 public static EasResponse fromHttpRequest( 63 EmailClientConnectionManager connManager, HttpClient client, HttpUriRequest request) 64 throws IOException { 65 66 long reqTime = System.currentTimeMillis(); 67 HttpResponse response = client.execute(request); 68 EasResponse result = new EasResponse(response); 69 if (isAuthError(response.getStatusLine().getStatusCode()) 70 && connManager.hasDetectedUnsatisfiedCertReq(reqTime)) { 71 result.mClientCertRequested = true; 72 result.mClosed = true; 73 } 74 75 return result; 76 } 77 78 /** 79 * Determine whether an HTTP code represents an authentication error 80 * @param code the HTTP code returned by the server 81 * @return whether or not the code represents an authentication error 82 */ 83 public static boolean isAuthError(int code) { 84 return (code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN); 85 } 86 87 /** 88 * Determine whether an HTTP code represents a provisioning error 89 * @param code the HTTP code returned by the server 90 * @return whether or not the code represents an provisioning error 91 */ 92 public static boolean isProvisionError(int code) { 93 return (code == HTTP_NEED_PROVISIONING) || (code == HttpStatus.SC_FORBIDDEN); 94 } 95 96 /** 97 * Return an appropriate input stream for the response, either a GZIPInputStream, for 98 * compressed data, or a generic InputStream otherwise 99 * @return the input stream for the response 100 */ 101 public InputStream getInputStream() { 102 if (mInputStream != null || mClosed) { 103 throw new IllegalStateException("Can't reuse stream or get closed stream"); 104 } else if (mEntity == null) { 105 throw new IllegalStateException("Can't get input stream without entity"); 106 } 107 InputStream is = null; 108 try { 109 // Get the default input stream for the entity 110 is = mEntity.getContent(); 111 Header ceHeader = mResponse.getFirstHeader("Content-Encoding"); 112 if (ceHeader != null) { 113 String encoding = ceHeader.getValue(); 114 // If we're gzip encoded, wrap appropriately 115 if (encoding.toLowerCase().equals("gzip")) { 116 is = new GZIPInputStream(is); 117 } 118 } 119 } catch (IllegalStateException e1) { 120 } catch (IOException e1) { 121 } 122 mInputStream = is; 123 return is; 124 } 125 126 public boolean isEmpty() { 127 return mLength == 0; 128 } 129 130 public int getStatus() { 131 return mClientCertRequested 132 ? HttpStatus.SC_UNAUTHORIZED 133 : mResponse.getStatusLine().getStatusCode(); 134 } 135 136 public boolean isMissingCertificate() { 137 return mClientCertRequested; 138 } 139 140 public Header getHeader(String name) { 141 return (mResponse == null) ? null : mResponse.getFirstHeader(name); 142 } 143 144 public int getLength() { 145 return mLength; 146 } 147 148 public void close() { 149 if (mEntity != null && !mClosed) { 150 try { 151 mEntity.consumeContent(); 152 } catch (IOException e) { 153 // No harm, no foul 154 } 155 } 156 mClosed = true; 157 } 158 }