/**
* Android ownCloud News
*
* @author David Luhmer
* @copyright 2013 David Luhmer david-dev@live.de
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.luhmer.owncloudnewsreader.reader;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import de.luhmer.owncloudnewsreader.SettingsActivity;
import de.luhmer.owncloudnewsreader.model.Tuple;
import de.luhmer.owncloudnewsreader.ssl.MemorizingTrustManager;
import de.luhmer.owncloudnewsreader.ssl.TLSSocketFactory;
import okhttp3.Credentials;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class HttpJsonRequest {
@SuppressWarnings("unused")
private static final String TAG = "HttpJsonRequest";
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private static HttpJsonRequest instance;
public static void init(Context context) {
instance = new HttpJsonRequest(context);
}
public static HttpJsonRequest getInstance() {
if(instance == null)
throw new IllegalStateException("Must be initialized first");
return instance;
}
private final OkHttpClient client;
private final OkHttpClient imageClient;
private String credentials;
private HttpUrl oc_root_url;
private X509TrustManager systemDefaultTrustManager() {
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}
}
private HttpJsonRequest(Context context) {
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
// set location of the keystore
MemorizingTrustManager.setKeyStoreFile("private", "sslkeys.bks");
// register MemorizingTrustManager for HTTPS
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, MemorizingTrustManager.getInstanceList(context), new java.security.SecureRandom());
// enables TLSv1.1/1.2 for Jelly Bean Devices
TLSSocketFactory tlsSocketFactory = new TLSSocketFactory(sc);
clientBuilder.sslSocketFactory(tlsSocketFactory, systemDefaultTrustManager());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
clientBuilder.connectTimeout(10, TimeUnit.SECONDS);
clientBuilder.readTimeout(120, TimeUnit.SECONDS);
// disable hostname verification, when preference is set
// (this still shows a certification dialog, which requires user interaction!)
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
if(sp.getBoolean(SettingsActivity.CB_DISABLE_HOSTNAME_VERIFICATION_STRING, false))
clientBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
clientBuilder.interceptors().add(new AuthorizationInterceptor());
imageClient = clientBuilder.build();
client = clientBuilder.build();
setCredentials(sp.getString(SettingsActivity.EDT_USERNAME_STRING, null), sp.getString(SettingsActivity.EDT_PASSWORD_STRING, null), sp.getString(SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING, null));
}
public void setCredentials(final String username, final String password, final String oc_root_path) {
if(username != null)
credentials = Credentials.basic(username, password);
else
credentials = null;
if(oc_root_path != null) {
// Add empty path segment to ensure trailing slash
oc_root_url = HttpUrl.parse(oc_root_path).newBuilder().addPathSegment("").build();
}
}
public HttpUrl getRootUrl() {
return oc_root_url;
}
private class AuthorizationInterceptor implements Interceptor {
public AuthorizationInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// only add Authorization header for urls on the configured owncloud host
if(oc_root_url.host().equals(request.url().host()))
request = request.newBuilder()
.addHeader("Authorization",credentials)
.build();
return chain.proceed(request);
}
}
public OkHttpClient getImageClient() {
return imageClient;
}
public InputStream PerformJsonRequest(HttpUrl url) throws Exception
{
Request request = new Request.Builder()
.url(url)
.get()
.build();
//http://nelenkov.blogspot.de/2011/12/using-custom-certificate-trust-store-on.html
//http://stackoverflow.com/questions/5947162/https-and-self-signed-certificate-issue
//http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e537
//http://stackoverflow.com/questions/859111/how-do-i-accept-a-self-signed-certificate-with-a-java-httpsurlconnection
//http://developer.android.com/training/articles/security-ssl.html
// CookieHandler.setDefault(new CookieManager());
Response response = client.newCall(request).execute();
if(response.isSuccessful()) {
return response.body().byteStream();
} else {
throw new Exception(response.message());
}
}
public int performCreateFeedRequest(HttpUrl url, String feedUrlString, long folderId) throws Exception {
HttpUrl feedUrl = url.newBuilder()
.setQueryParameter("url", feedUrlString)
.setQueryParameter("folderId", String.valueOf(folderId))
.build();
Request request = new Request.Builder()
.url(feedUrl)
.post(RequestBody.create(JSON, ""))
.build();
Response response = client.newCall(request).execute();
return response.code();
}
public int performRemoveFeedRequest(HttpUrl url, long feedId) throws Exception {
HttpUrl feedUrl = url.newBuilder()
.addPathSegment(String.valueOf(feedId))
.build();
Request request = new Request.Builder()
.url(feedUrl)
.delete()
.build();
Response response = client.newCall(request).execute();
return response.code();
}
public int performRenameFeedRequest(HttpUrl url, long feedId, String newFeedName) throws Exception {
HttpUrl feedUrl = url.newBuilder()
.addPathSegment(String.valueOf(feedId))
.addPathSegment("rename")
.build();
Request request = new Request.Builder()
.url(feedUrl)
.put(RequestBody.create(JSON, new JSONObject().put("feedTitle", newFeedName).toString()))
.build();
Response response = client.newCall(request).execute();
return response.code();
}
public int performTagChangeRequest(HttpUrl url, String content) throws Exception
{
Request request = new Request.Builder()
.url(url)
.put(RequestBody.create(JSON, content))
.build();
Response response = client.newCall(request).execute();
return response.code();
}
public Tuple<Integer, String> performCreateFolderRequest(HttpUrl url, String folderName) throws Exception {
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(JSON, new JSONObject().put("name", folderName).toString()))
.build();
Response response = client.newCall(request).execute();
String body = response.body().string();
return new Tuple<>(response.code(), body);
}
}