mirror of
https://github.com/etesync/android
synced 2025-01-20 04:30:54 +00:00
197 lines
6.8 KiB
Java
197 lines
6.8 KiB
Java
/*
|
||
* Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||
* All rights reserved. This program and the accompanying materials
|
||
* are made available under the terms of the GNU Public License v3.0
|
||
* which accompanies this distribution, and is available at
|
||
* http://www.gnu.org/licenses/gpl.html
|
||
*/
|
||
|
||
package at.bitfire.davdroid;
|
||
|
||
import android.content.Context;
|
||
import android.os.Build;
|
||
|
||
import com.squareup.okhttp.Authenticator;
|
||
import com.squareup.okhttp.CertificatePinner;
|
||
import com.squareup.okhttp.Credentials;
|
||
import com.squareup.okhttp.Interceptor;
|
||
import com.squareup.okhttp.OkHttpClient;
|
||
import com.squareup.okhttp.Request;
|
||
import com.squareup.okhttp.Response;
|
||
import com.squareup.okhttp.internal.tls.OkHostnameVerifier;
|
||
import com.squareup.okhttp.logging.HttpLoggingInterceptor;
|
||
|
||
import java.io.IOException;
|
||
import java.net.Proxy;
|
||
import java.security.KeyManagementException;
|
||
import java.security.NoSuchAlgorithmException;
|
||
import java.text.SimpleDateFormat;
|
||
import java.util.List;
|
||
import java.util.concurrent.TimeUnit;
|
||
|
||
import javax.net.ssl.HostnameVerifier;
|
||
import javax.net.ssl.SSLContext;
|
||
import javax.net.ssl.X509TrustManager;
|
||
|
||
import at.bitfire.dav4android.HttpUtils;
|
||
import de.duenndns.ssl.MemorizingTrustManager;
|
||
import lombok.RequiredArgsConstructor;
|
||
|
||
public class HttpClient extends OkHttpClient {
|
||
|
||
protected static final String HEADER_AUTHORIZATION = "Authorization";
|
||
|
||
final static UserAgentInterceptor userAgentInterceptor = new UserAgentInterceptor();
|
||
final static HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
|
||
@Override
|
||
public void log(String message) {
|
||
Constants.log.trace(message);
|
||
}
|
||
});
|
||
static {
|
||
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||
}
|
||
|
||
static final String userAgent;
|
||
static {
|
||
String date = new SimpleDateFormat("yyyy/MM/dd").format(BuildConfig.buildTime);
|
||
userAgent = "DAVdroid/" + BuildConfig.VERSION_NAME + " (" + date + "; dav4android) Android/" + Build.VERSION.RELEASE;
|
||
}
|
||
|
||
final Context context;
|
||
protected String username, password;
|
||
|
||
|
||
public HttpClient() {
|
||
super();
|
||
context = null;
|
||
initialize();
|
||
}
|
||
|
||
public HttpClient(Context context, String username, String password, boolean preemptive) {
|
||
super();
|
||
this.context = context;
|
||
|
||
initialize();
|
||
|
||
// authentication
|
||
this.username = username;
|
||
this.password = password;
|
||
if (preemptive)
|
||
networkInterceptors().add(new PreemptiveAuthenticationInterceptor(username, password));
|
||
else
|
||
setAuthenticator(new DavAuthenticator(null, username, password));
|
||
}
|
||
|
||
/**
|
||
* Creates a new HttpClient (based on another one) which can be used to download external resources:
|
||
* 1. it does not use preemptive authentication
|
||
* 2. it only authenticates against a given host
|
||
* @param client user name and password from this client will be used
|
||
* @param host authentication will be restricted to this host
|
||
*/
|
||
public HttpClient(HttpClient client, String host) {
|
||
super();
|
||
context = client.context;
|
||
|
||
initialize();
|
||
|
||
username = client.username;
|
||
password = client.password;
|
||
setAuthenticator(new DavAuthenticator(host, username, password));
|
||
}
|
||
|
||
|
||
protected void initialize() {
|
||
if (context != null) {
|
||
// use MemorizingTrustManager to manage self-signed certificates
|
||
MemorizingTrustManager mtm = new MemorizingTrustManager(context);
|
||
try {
|
||
SSLContext sc = SSLContext.getInstance("TLS");
|
||
sc.init(null, new X509TrustManager[] { mtm }, null);
|
||
setSslSocketFactory(sc.getSocketFactory());
|
||
setHostnameVerifier(mtm.wrapHostnameVerifier(OkHostnameVerifier.INSTANCE));
|
||
} catch (NoSuchAlgorithmException e) {
|
||
Constants.log.error("Couldn't get SSL Context for MemorizingTrustManager", e);
|
||
} catch (KeyManagementException e) {
|
||
Constants.log.error("Key management error while initializing MemorizingTrustManager", e);
|
||
}
|
||
}
|
||
|
||
// set timeouts
|
||
setConnectTimeout(30, TimeUnit.SECONDS);
|
||
setWriteTimeout(15, TimeUnit.SECONDS);
|
||
setReadTimeout(45, TimeUnit.SECONDS);
|
||
|
||
// add User-Agent to every request
|
||
networkInterceptors().add(userAgentInterceptor);
|
||
|
||
// enable logs
|
||
enableLogs();
|
||
}
|
||
|
||
protected void enableLogs() {
|
||
interceptors().add(loggingInterceptor);
|
||
}
|
||
|
||
|
||
static class UserAgentInterceptor implements Interceptor {
|
||
@Override
|
||
public Response intercept(Chain chain) throws IOException {
|
||
Request request = chain.request().newBuilder()
|
||
.header("User-Agent", userAgent)
|
||
.build();
|
||
return chain.proceed(request);
|
||
}
|
||
}
|
||
|
||
|
||
@RequiredArgsConstructor
|
||
static class PreemptiveAuthenticationInterceptor implements Interceptor {
|
||
final String username, password;
|
||
|
||
@Override
|
||
public Response intercept(Chain chain) throws IOException {
|
||
Request request = chain.request().newBuilder()
|
||
.header("Authorization", Credentials.basic(username, password))
|
||
.build();
|
||
return chain.proceed(request);
|
||
}
|
||
}
|
||
|
||
@RequiredArgsConstructor
|
||
public static class DavAuthenticator implements Authenticator {
|
||
final String host, username, password;
|
||
|
||
@Override
|
||
public Request authenticate(Proxy proxy, Response response) throws IOException {
|
||
Request request = response.request();
|
||
|
||
if (host != null && !request.httpUrl().host().equalsIgnoreCase(host)) {
|
||
Constants.log.warn("Not authenticating against " + host + " for security reasons!");
|
||
return null;
|
||
}
|
||
|
||
// check whether this is the first authentication try with our credentials
|
||
Response priorResponse = response.priorResponse();
|
||
boolean triedBefore = priorResponse != null ? priorResponse.request().header(HEADER_AUTHORIZATION) != null : false;
|
||
if (triedBefore)
|
||
// credentials didn't work last time, and they won't work now → stop here
|
||
return null;
|
||
|
||
//List<HttpUtils.AuthScheme> schemes = HttpUtils.parseWwwAuthenticate(response.headers("WWW-Authenticate"));
|
||
// TODO Digest auth
|
||
|
||
return request.newBuilder()
|
||
.header(HEADER_AUTHORIZATION, Credentials.basic(username, password))
|
||
.build();
|
||
}
|
||
|
||
@Override
|
||
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
}
|