blob: 537b4427d776747268ff5c8993b2441ef77d8208 [file] [log] [blame]
/*
* Copyright (C) 2008-2009 Marc Blank
* Licensed to The Android Open Source Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.exchange;
import android.net.Uri;
import com.android.emailcommon.utility.EmailClientConnectionManager;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
/**
* Encapsulate a response to an HTTP POST
*/
public class EasResponse {
// MSFT's custom HTTP result code indicating the need to provision
static private final int HTTP_NEED_PROVISIONING = 449;
// Microsoft-defined HTTP response indicating a redirect to a "better" server.
// Why is this a 4xx instead of 3xx? Because EAS considers this a "Device misconfigured" error.
static private final int HTTP_REDIRECT = 451;
final HttpResponse mResponse;
private final HttpEntity mEntity;
private final int mLength;
private InputStream mInputStream;
private boolean mClosed;
/**
* Whether or not a certificate was requested by the server and missing.
* If this is set, it is essentially a 403 whereby the failure was due
*/
private boolean mClientCertRequested = false;
private EasResponse(HttpResponse response) {
mResponse = response;
mEntity = (response == null) ? null : mResponse.getEntity();
if (mEntity != null) {
mLength = (int) mEntity.getContentLength();
} else {
mLength = 0;
}
}
public static EasResponse fromHttpRequest(
EmailClientConnectionManager connManager, HttpClient client, HttpUriRequest request)
throws IOException {
long reqTime = System.currentTimeMillis();
HttpResponse response = client.execute(request);
EasResponse result = new EasResponse(response);
if (isAuthError(response.getStatusLine().getStatusCode())
&& connManager.hasDetectedUnsatisfiedCertReq(reqTime)) {
result.mClientCertRequested = true;
result.mClosed = true;
}
return result;
}
/**
* Determine whether an HTTP code represents an authentication error
* @param code the HTTP code returned by the server
* @return whether or not the code represents an authentication error
*/
public static boolean isAuthError(int code) {
return (code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN);
}
/**
* Determine whether an HTTP code represents a provisioning error
* @param code the HTTP code returned by the server
* @return whether or not the code represents an provisioning error
*/
public static boolean isProvisionError(int code) {
return (code == HTTP_NEED_PROVISIONING) || (code == HttpStatus.SC_FORBIDDEN);
}
/**
* Determine whether an HTTP code represents a redirect error.
* @param code the HTTP code returned by the server
* @return whether or not the code represents a redirect error
*/
public static boolean isRedirectError(final int code) {
return (code == HTTP_REDIRECT);
}
/**
* Read the redirect address from this response, if it's present.
* @return The new host address, or null if it's not there.
*/
public String getRedirectAddress() {
final Header locHeader = getHeader("X-MS-Location");
if (locHeader != null) {
return Uri.parse(locHeader.getValue()).getHost();
}
return null;
}
/**
* Return an appropriate input stream for the response, either a GZIPInputStream, for
* compressed data, or a generic InputStream otherwise
* @return the input stream for the response
*/
public InputStream getInputStream() {
if (mInputStream != null || mClosed) {
throw new IllegalStateException("Can't reuse stream or get closed stream");
} else if (mEntity == null) {
throw new IllegalStateException("Can't get input stream without entity");
}
InputStream is = null;
try {
// Get the default input stream for the entity
is = mEntity.getContent();
Header ceHeader = mResponse.getFirstHeader("Content-Encoding");
if (ceHeader != null) {
String encoding = ceHeader.getValue();
// If we're gzip encoded, wrap appropriately
if (encoding.toLowerCase().equals("gzip")) {
is = new GZIPInputStream(is);
}
}
} catch (IllegalStateException e1) {
} catch (IOException e1) {
}
mInputStream = is;
return is;
}
public boolean isEmpty() {
return mLength == 0;
}
public int getStatus() {
return mClientCertRequested
? HttpStatus.SC_UNAUTHORIZED
: mResponse.getStatusLine().getStatusCode();
}
public boolean isMissingCertificate() {
return mClientCertRequested;
}
public Header getHeader(String name) {
return (mResponse == null) ? null : mResponse.getFirstHeader(name);
}
public int getLength() {
return mLength;
}
public void close() {
if (!mClosed) {
if (mEntity != null) {
try {
mEntity.consumeContent();
} catch (IOException e) {
// No harm, no foul
}
}
if (mInputStream instanceof GZIPInputStream) {
try {
mInputStream.close();
} catch (IOException e) {
// We tried
}
}
mClosed = true;
}
}
}