Home | History | Annotate | Download | only in exchange
      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 (!mClosed) {
    150             if (mEntity != null) {
    151                 try {
    152                     mEntity.consumeContent();
    153                 } catch (IOException e) {
    154                     // No harm, no foul
    155                 }
    156             }
    157             if (mInputStream instanceof GZIPInputStream) {
    158                 try {
    159                     mInputStream.close();
    160                 } catch (IOException e) {
    161                     // We tried
    162                 }
    163             }
    164             mClosed = true;
    165         }
    166     }
    167 }