mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-26 14:21:16 +00:00
Replaced deprecated http APIs
This commit is contained in:
parent
b38a152f6a
commit
63e8c22129
21 changed files with 332 additions and 632 deletions
|
@ -28,7 +28,6 @@ dependencies {
|
|||
compile 'org.mindrot:jbcrypt:0.3m'
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.robolectric:robolectric:3.1.2'
|
||||
androidTestCompile 'com.squareup.okhttp:mockwebserver:2.4.0'
|
||||
}
|
||||
|
||||
project.archivesBaseName = 'syncthing'
|
||||
|
|
|
@ -6,12 +6,13 @@ import android.content.Context;
|
|||
import android.support.annotation.NonNull;
|
||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MockRestApi extends RestApi {
|
||||
|
||||
public MockRestApi(Context context, String url, String apiKey,
|
||||
public MockRestApi(Context context, URL url, String apiKey,
|
||||
OnApiAvailableListener listener) {
|
||||
super(context, url, apiKey, listener, null);
|
||||
}
|
||||
|
@ -76,7 +77,7 @@ public class MockRestApi extends RestApi {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean editFolder(Folder folder, boolean create, Activity activity) {
|
||||
public void editFolder(Folder folder, boolean create, Activity activity) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.os.IBinder;
|
|||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class MockSyncthingService extends SyncthingService {
|
||||
|
@ -67,7 +68,7 @@ public class MockSyncthingService extends SyncthingService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getWebGuiUrl() {
|
||||
public URL getWebGuiUrl() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.test.syncthing;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.nutomic.syncthingandroid.syncthing.GetTask;
|
||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||
import com.squareup.okhttp.mockwebserver.RecordedRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GetTaskTest extends AndroidTestCase {
|
||||
|
||||
private MockWebServer mServer;
|
||||
|
||||
private static final String RESPONSE = "the response";
|
||||
|
||||
private static final String API_KEY = "the key";
|
||||
|
||||
private static final String PARAM_KEY_ONE = "first-param";
|
||||
|
||||
private static final String PARAM_VALUE_ONE = "first param value";
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mServer = new MockWebServer();
|
||||
mServer.enqueue(new MockResponse().setBody(RESPONSE));
|
||||
mServer.play();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
|
||||
// TODO: causes problems, see https://github.com/square/okhttp/issues/1033
|
||||
//mServer.shutdown();
|
||||
}
|
||||
|
||||
public void testGetNoParams() throws IOException, InterruptedException {
|
||||
new GetTask("") {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
assertEquals(RESPONSE, s);
|
||||
}
|
||||
}.execute(mServer.getUrl("").toString(), GetTask.URI_CONFIG, API_KEY);
|
||||
RecordedRequest request = mServer.takeRequest();
|
||||
assertEquals(API_KEY, request.getHeader(RestApi.HEADER_API_KEY));
|
||||
Uri uri = Uri.parse(request.getPath());
|
||||
assertEquals(GetTask.URI_CONFIG, uri.getPath());
|
||||
}
|
||||
|
||||
public void testGetParams() throws IOException, InterruptedException {
|
||||
new GetTask("") {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
assertEquals(RESPONSE, s);
|
||||
}
|
||||
}.execute(mServer.getUrl("").toString(), GetTask.URI_CONFIG, API_KEY, PARAM_KEY_ONE,
|
||||
PARAM_VALUE_ONE);
|
||||
RecordedRequest request = mServer.takeRequest();
|
||||
assertEquals(API_KEY, request.getHeader(RestApi.HEADER_API_KEY));
|
||||
Uri uri = Uri.parse(request.getPath());
|
||||
assertEquals(GetTask.URI_CONFIG, uri.getPath());
|
||||
assertEquals(PARAM_VALUE_ONE, uri.getQueryParameter(PARAM_KEY_ONE));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.test.syncthing;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.nutomic.syncthingandroid.syncthing.PollWebGuiAvailableTask;
|
||||
import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable;
|
||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||
import com.nutomic.syncthingandroid.test.MockContext;
|
||||
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PollWebGuiAvailableTaskTest extends AndroidTestCase {
|
||||
|
||||
private ConfigXml mConfig;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mConfig = new ConfigXml(new MockContext(getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
|
||||
ConfigXml.getConfigFile(new MockContext(getContext())).delete();
|
||||
}
|
||||
|
||||
public void testPolling() throws InterruptedException {
|
||||
new SyncthingRunnable(new MockContext(getContext()), SyncthingRunnable.Command.main);
|
||||
|
||||
String httpsCertPath = getContext().getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE;
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
new PollWebGuiAvailableTask(httpsCertPath) {
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
latch.countDown();
|
||||
}
|
||||
}.execute(mConfig.getWebGuiUrl());
|
||||
latch.await(1, TimeUnit.SECONDS);
|
||||
|
||||
// TODO: Unit tests fail when Syncthing is killed SyncthingRunnable.killSyncthing();
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ package com.nutomic.syncthingandroid.test.syncthing;
|
|||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.nutomic.syncthingandroid.syncthing.PollWebGuiAvailableTask;
|
||||
import com.nutomic.syncthingandroid.http.PollWebGuiAvailableTask;
|
||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||
import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable;
|
||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||
|
@ -28,13 +28,13 @@ public class RestApiTest extends AndroidTestCase {
|
|||
String httpsCertPath = getContext().getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE;
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(2);
|
||||
new PollWebGuiAvailableTask(httpsCertPath) {
|
||||
new PollWebGuiAvailableTask(config.getWebGuiUrl(), httpsCertPath, config.getApiKey()) {
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
mApi.onWebGuiAvailable();
|
||||
latch.countDown();
|
||||
}
|
||||
}.execute(config.getWebGuiUrl());
|
||||
}.execute();
|
||||
mApi = new RestApi(getContext(), config.getWebGuiUrl(), config.getApiKey(),
|
||||
latch::countDown, null);
|
||||
latch.await(1, TimeUnit.SECONDS);
|
||||
|
|
|
@ -29,7 +29,7 @@ public class ConfigXmlTest extends AndroidTestCase {
|
|||
}
|
||||
|
||||
public void testGetWebGuiUrl() {
|
||||
assertTrue(mConfig.getWebGuiUrl().startsWith("https://127.0.0.1:"));
|
||||
assertTrue(mConfig.getWebGuiUrl().toString().startsWith("https://127.0.0.1:"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ public class WebGuiActivity extends SyncthingActivity
|
|||
|
||||
@Override
|
||||
public void onWebGuiAvailable() {
|
||||
mWebView.loadUrl(getService().getWebGuiUrl());
|
||||
mWebView.loadUrl(getService().getWebGuiUrl().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
57
src/main/java/com/nutomic/syncthingandroid/http/GetTask.java
Normal file
57
src/main/java/com/nutomic/syncthingandroid/http/GetTask.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
package com.nutomic.syncthingandroid.http;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Performs a GET request to the Syncthing API
|
||||
* Performs a GET request with no parameters to the URL in uri[0] with the path in uri[1] and
|
||||
* returns the result as a String.
|
||||
*/
|
||||
public class GetTask extends RestTask<String, Void, String> {
|
||||
|
||||
private static final String TAG = "GetTask";
|
||||
|
||||
public static final String URI_CONFIG = "/rest/system/config";
|
||||
public static final String URI_VERSION = "/rest/system/version";
|
||||
public static final String URI_SYSTEM = "/rest/system/status";
|
||||
public static final String URI_CONNECTIONS = "/rest/system/connections";
|
||||
public static final String URI_MODEL = "/rest/db/status";
|
||||
public static final String URI_DEVICEID = "/rest/svc/deviceid";
|
||||
public static final String URI_REPORT = "/rest/svc/report";
|
||||
public static final String URI_EVENTS = "/rest/events";
|
||||
|
||||
public GetTask(URL url, String path, String httpsCertPath, String apiKey) {
|
||||
super(url, path, httpsCertPath, apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param params Keys and values for the query string.
|
||||
*/
|
||||
@Override
|
||||
protected String doInBackground(String... params) {
|
||||
try {
|
||||
HttpsURLConnection connection = openConnection(params);
|
||||
Log.v(TAG, "Calling Rest API at " + connection.getURL());
|
||||
connection.connect();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
|
||||
String line;
|
||||
String result = "";
|
||||
while ((line = br.readLine()) != null) {
|
||||
result += line;
|
||||
}
|
||||
br.close();
|
||||
Log.v(TAG, "API call result: " + result);
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to call rest api", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.nutomic.syncthingandroid.http;
|
||||
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Polls to load the web interface, until we receive http status 200.
|
||||
*/
|
||||
public abstract class PollWebGuiAvailableTask extends RestTask<Void, Void, Void> {
|
||||
|
||||
private static final String TAG = "PollWebGuiAvailableTask";
|
||||
|
||||
/**
|
||||
* Interval in ms, at which connections to the web gui are performed on first start
|
||||
* to find out if it's online.
|
||||
*/
|
||||
private static final long WEB_GUI_POLL_INTERVAL = 100;
|
||||
|
||||
public PollWebGuiAvailableTask(URL url, String httpsCertPath, String apiKey) {
|
||||
super(url, "", httpsCertPath, apiKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... aVoid) {
|
||||
int status = 0;
|
||||
do {
|
||||
try {
|
||||
HttpsURLConnection connection = openConnection();
|
||||
connection.connect();
|
||||
status = connection.getResponseCode();
|
||||
} catch (IOException e) {
|
||||
// We catch this in every call, as long as the service is not online, so we ignore and continue.
|
||||
try {
|
||||
Thread.sleep(WEB_GUI_POLL_INTERVAL);
|
||||
} catch (InterruptedException e2) {
|
||||
Log.w(TAG, "Failed to sleep", e2);
|
||||
}
|
||||
}
|
||||
} while (status != HttpsURLConnection.HTTP_OK);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.nutomic.syncthingandroid.http;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Sends a POST request to the Syncthing API.
|
||||
*/
|
||||
public class PostTask extends RestTask<String, Void, Boolean> {
|
||||
|
||||
private static final String TAG = "PostTask";
|
||||
|
||||
public static final String URI_CONFIG = "/rest/system/config";
|
||||
public static final String URI_SCAN = "/rest/db/scan";
|
||||
|
||||
public PostTask(URL url, String path, String httpsCertPath, String apiKey) {
|
||||
super(url, path, httpsCertPath, apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* For {@link #URI_CONFIG}, params[0] must contain the config.
|
||||
*
|
||||
* For {@link #URI_SCAN}, params[0] must contain the folder, and params[1] the subfolder.
|
||||
*/
|
||||
@Override
|
||||
protected Boolean doInBackground(String... params) {
|
||||
try {
|
||||
HttpsURLConnection connection = (mPath.equals(URI_SCAN))
|
||||
? openConnection("folder", params[0], "sub", params[1])
|
||||
: openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
Log.v(TAG, "Calling Rest API at " + connection.getURL());
|
||||
|
||||
if (mPath.equals(URI_CONFIG)) {
|
||||
OutputStream os = connection.getOutputStream();
|
||||
os.write(params[0].getBytes("UTF-8"));
|
||||
}
|
||||
connection.connect();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
|
||||
String line;
|
||||
String result = "";
|
||||
while ((line = br.readLine()) != null) {
|
||||
result += line;
|
||||
}
|
||||
br.close();
|
||||
Log.v(TAG, "API call result: " + result);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to call rest api", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
111
src/main/java/com/nutomic/syncthingandroid/http/RestTask.java
Normal file
111
src/main/java/com/nutomic/syncthingandroid/http/RestTask.java
Normal file
|
@ -0,0 +1,111 @@
|
|||
package com.nutomic.syncthingandroid.http;
|
||||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public abstract class RestTask<A, B, C> extends AsyncTask<A, B, C> {
|
||||
|
||||
private static final String TAG = "RestTask";
|
||||
|
||||
private final URL mUrl;
|
||||
protected final String mPath;
|
||||
private final String mHttpsCertPath;
|
||||
private final String mApiKey;
|
||||
|
||||
public RestTask(URL url, String path, String httpsCertPath, String apiKey) {
|
||||
mUrl = url;
|
||||
mPath = path;
|
||||
mHttpsCertPath = httpsCertPath;
|
||||
mApiKey = apiKey;
|
||||
}
|
||||
|
||||
protected HttpsURLConnection openConnection(String... params) throws IOException {
|
||||
Uri.Builder uriBuilder = Uri.parse(mUrl.toString())
|
||||
.buildUpon()
|
||||
.path(mPath);
|
||||
for (int paramCounter = 0; paramCounter + 1 < params.length; ) {
|
||||
uriBuilder.appendQueryParameter(params[paramCounter++], params[paramCounter++]);
|
||||
}
|
||||
URL url = new URL(uriBuilder.build().toString());
|
||||
|
||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.setRequestProperty(RestApi.HEADER_API_KEY, mApiKey);
|
||||
connection.setHostnameVerifier((h, s) -> true);
|
||||
connection.setSSLSocketFactory(getSslSocketFactory());
|
||||
return connection;
|
||||
}
|
||||
|
||||
private SSLSocketFactory getSslSocketFactory() {
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, new TrustManager[]{new SyncthingTrustManager()},
|
||||
new SecureRandom());
|
||||
return sslContext.getSocketFactory();
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TrustManager checking against the local Syncthing instance's https public key.
|
||||
*
|
||||
* Based on http://stackoverflow.com/questions/16719959#16759793
|
||||
*/
|
||||
private class SyncthingTrustManager implements X509TrustManager {
|
||||
|
||||
private static final String TAG = "SyncthingTrustManager";
|
||||
|
||||
@Override
|
||||
@SuppressLint("TrustAllX509TrustManager")
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies certs against public key of the local syncthing instance
|
||||
*/
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] certs,
|
||||
String authType) throws CertificateException {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(mHttpsCertPath);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate ca = (X509Certificate) cf.generateCertificate(is);
|
||||
for (X509Certificate cert : certs) {
|
||||
cert.verify(ca.getPublicKey());
|
||||
}
|
||||
} catch (FileNotFoundException | NoSuchAlgorithmException | InvalidKeyException |
|
||||
NoSuchProviderException | SignatureException e) {
|
||||
throw new CertificateException("Untrusted Certificate!", e);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null)
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.syncthing;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nutomic.syncthingandroid.util.Https;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Performs a GET request with no parameters to the URL in uri[0] with the path in uri[1] and
|
||||
* returns the result as a String.
|
||||
*/
|
||||
public class GetTask extends AsyncTask<String, Void, String> {
|
||||
|
||||
private static final String TAG = "GetTask";
|
||||
|
||||
public static final String URI_CONFIG = "/rest/system/config";
|
||||
public static final String URI_VERSION = "/rest/system/version";
|
||||
public static final String URI_SYSTEM = "/rest/system/status";
|
||||
public static final String URI_CONNECTIONS = "/rest/system/connections";
|
||||
public static final String URI_MODEL = "/rest/db/status";
|
||||
public static final String URI_DEVICEID = "/rest/svc/deviceid";
|
||||
public static final String URI_REPORT = "/rest/svc/report";
|
||||
public static final String URI_EVENTS = "/rest/events";
|
||||
|
||||
private final String mHttpsCertPath;
|
||||
|
||||
public GetTask(String httpsCertPath) {
|
||||
mHttpsCertPath = httpsCertPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* params[0] Syncthing hostname
|
||||
* params[1] URI to call
|
||||
* params[2] Syncthing API key
|
||||
* params[3] optional parameter key
|
||||
* params[4] optional parameter value
|
||||
*/
|
||||
@Override
|
||||
protected String doInBackground(String... params) {
|
||||
String fullUri = params[0] + params[1];
|
||||
Log.v(TAG, "Calling Rest API at " + fullUri);
|
||||
|
||||
if (params.length >= 5) {
|
||||
LinkedList<NameValuePair> urlParams = new LinkedList<>();
|
||||
for (int paramCounter = 3; paramCounter + 1 < params.length; ) {
|
||||
urlParams.add(new BasicNameValuePair(params[paramCounter++], params[paramCounter++]));
|
||||
}
|
||||
fullUri += "?" + URLEncodedUtils.format(urlParams, HTTP.UTF_8);
|
||||
}
|
||||
|
||||
// Retry at most 5 times before failing
|
||||
for (int i = 0; i < 5; i++) {
|
||||
HttpClient httpclient = Https.createHttpsClient(mHttpsCertPath);
|
||||
HttpGet get = new HttpGet(fullUri);
|
||||
get.addHeader(new BasicHeader(RestApi.HEADER_API_KEY, params[2]));
|
||||
|
||||
if (isCancelled())
|
||||
return null;
|
||||
|
||||
try {
|
||||
HttpResponse response = httpclient.execute(get);
|
||||
HttpEntity entity = response.getEntity();
|
||||
|
||||
if (entity != null) {
|
||||
InputStream is = entity.getContent();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
||||
String line;
|
||||
String result = "";
|
||||
while ((line = br.readLine()) != null) {
|
||||
result += line;
|
||||
}
|
||||
br.close();
|
||||
Log.v(TAG, "API call result: " + result);
|
||||
return result;
|
||||
}
|
||||
} catch (IOException|IllegalArgumentException e) {
|
||||
Log.w(TAG, "Failed to call Rest API at " + fullUri);
|
||||
}
|
||||
try {
|
||||
// Don't push the API too hard
|
||||
Thread.sleep(500 * i);
|
||||
} catch (InterruptedException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
Log.w(TAG, "Retrying GetTask Rest API call (" + (i + 1) + "/5)");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.syncthing;
|
||||
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.nutomic.syncthingandroid.util.Https;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpHead;
|
||||
import org.apache.http.conn.HttpHostConnectException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Polls SYNCTHING_URL until it returns HTTP status OK, then calls all listeners
|
||||
* in mOnWebGuiAvailableListeners and clears it.
|
||||
*/
|
||||
public abstract class PollWebGuiAvailableTask extends AsyncTask<String, Void, Void> {
|
||||
|
||||
private static final String TAG = "PollWebGuiAvailableTask";
|
||||
|
||||
/**
|
||||
* Interval in ms, at which connections to the web gui are performed on first start
|
||||
* to find out if it's online.
|
||||
*/
|
||||
private static final long WEB_GUI_POLL_INTERVAL = 100;
|
||||
|
||||
private final String mHttpsCertPath;
|
||||
|
||||
public PollWebGuiAvailableTask(String httpsCertPath) {
|
||||
mHttpsCertPath = httpsCertPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url The URL of the web GUI (eg 127.0.0.1:8384).
|
||||
*/
|
||||
@Override
|
||||
protected Void doInBackground(String... url) {
|
||||
int status = 0;
|
||||
HttpClient httpclient = Https.createHttpsClient(mHttpsCertPath);
|
||||
HttpHead head = new HttpHead(url[0]);
|
||||
do {
|
||||
try {
|
||||
Thread.sleep(WEB_GUI_POLL_INTERVAL);
|
||||
HttpResponse response = httpclient.execute(head);
|
||||
// NOTE: status is not really needed, as HttpHostConnectException is thrown
|
||||
// earlier.
|
||||
status = response.getStatusLine().getStatusCode();
|
||||
} catch (HttpHostConnectException e) {
|
||||
// We catch this in every call, as long as the service is not online,
|
||||
// so we ignore and continue.
|
||||
} catch (IOException|InterruptedException|IllegalArgumentException e) {
|
||||
//Log.w(TAG, "Failed to poll for web interface", e);
|
||||
}
|
||||
} while (status != HttpStatus.SC_OK && status != HttpStatus.SC_UNAUTHORIZED);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.syncthing;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nutomic.syncthingandroid.util.Https;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Sends a new config to {@link #URI_CONFIG}.
|
||||
*/
|
||||
public class PostConfigTask extends AsyncTask<String, Void, Boolean> {
|
||||
|
||||
private static final String TAG = "PostConfigTask";
|
||||
|
||||
public static final String URI_CONFIG = "/rest/system/config";
|
||||
|
||||
private final String mHttpsCertPath;
|
||||
|
||||
public PostConfigTask(String httpsCertPath) {
|
||||
mHttpsCertPath = httpsCertPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* params[0] Syncthing hostname
|
||||
* params[1] Syncthing API key
|
||||
* params[2] The new config
|
||||
*/
|
||||
@Override
|
||||
protected Boolean doInBackground(String... params) {
|
||||
String fullUri = params[0] + URI_CONFIG;
|
||||
Log.v(TAG, "Calling Rest API at " + fullUri);
|
||||
|
||||
HttpClient httpclient = Https.createHttpsClient(mHttpsCertPath);
|
||||
HttpPost post = new HttpPost(fullUri);
|
||||
post.addHeader(new BasicHeader(RestApi.HEADER_API_KEY, params[1]));
|
||||
|
||||
try {
|
||||
post.setEntity(new StringEntity(params[2], HTTP.UTF_8));
|
||||
Log.v(TAG, "API call parameters: " + params[2]);
|
||||
httpclient.execute(post);
|
||||
} catch (IOException|IllegalArgumentException e) {
|
||||
Log.w(TAG, "Failed to call Rest API at " + fullUri, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.syncthing;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nutomic.syncthingandroid.util.Https;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Performs a POST request to {@link #URI_SCAN} to notify Syncthing of a changed file or folder.
|
||||
*/
|
||||
public class PostScanTask extends AsyncTask<String, Void, Void> {
|
||||
|
||||
private static final String TAG = "PostScanTask";
|
||||
|
||||
public static final String URI_SCAN = "/rest/db/scan";
|
||||
|
||||
private final String mHttpsCertPath;
|
||||
|
||||
public PostScanTask(String httpsCertPath) {
|
||||
mHttpsCertPath = httpsCertPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* params[0] Syncthing hostname
|
||||
* params[1] Syncthing API key
|
||||
* params[2] folder parameter (the Syncthing folder to update)
|
||||
* params[3] sub parameter (the subfolder to update
|
||||
*/
|
||||
@Override
|
||||
protected Void doInBackground(String... params) {
|
||||
String fullUri = params[0] + URI_SCAN;
|
||||
|
||||
LinkedList<NameValuePair> urlParams = new LinkedList<>();
|
||||
urlParams.add(new BasicNameValuePair("folder", params[2]));
|
||||
urlParams.add(new BasicNameValuePair("sub", params[3]));
|
||||
fullUri += "?" + URLEncodedUtils.format(urlParams, HTTP.UTF_8);
|
||||
Log.v(TAG, "Calling Rest API at " + fullUri);
|
||||
|
||||
// Retry at most 5 times before failing
|
||||
for (int i = 0; i < 5; i++) {
|
||||
HttpClient httpclient = Https.createHttpsClient(mHttpsCertPath);
|
||||
HttpPost post = new HttpPost(fullUri);
|
||||
post.addHeader(new BasicHeader(RestApi.HEADER_API_KEY, params[1]));
|
||||
|
||||
if (isCancelled())
|
||||
return null;
|
||||
|
||||
try {
|
||||
HttpResponse response = httpclient.execute(post);
|
||||
if (response.getEntity() != null)
|
||||
return null;
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Log.w(TAG, "Failed to call Rest API at " + fullUri);
|
||||
}
|
||||
try {
|
||||
// Don't push the API too hard
|
||||
Thread.sleep(500 * i);
|
||||
} catch (InterruptedException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
Log.w(TAG, "Retrying GetTask Rest API call (" + (i + 1) + "/5)");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -11,26 +11,23 @@ import android.support.annotation.NonNull;
|
|||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.nutomic.syncthingandroid.BuildConfig;
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.activities.RestartActivity;
|
||||
import com.nutomic.syncthingandroid.http.GetTask;
|
||||
import com.nutomic.syncthingandroid.http.PostTask;
|
||||
import com.nutomic.syncthingandroid.util.FolderObserver;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
|
@ -169,7 +166,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
|
||||
private String mVersion;
|
||||
|
||||
private final String mUrl;
|
||||
private final URL mUrl;
|
||||
|
||||
private final String mApiKey;
|
||||
|
||||
|
@ -203,7 +200,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
*/
|
||||
private final Map<String, String> mCacheFolderPathLookup = new HashMap<>();
|
||||
|
||||
public RestApi(Context context, String url, String apiKey, OnApiAvailableListener apiListener,
|
||||
public RestApi(Context context, URL url, String apiKey, OnApiAvailableListener apiListener,
|
||||
OnConfigChangedListener configListener) {
|
||||
mContext = context;
|
||||
mUrl = url;
|
||||
|
@ -237,7 +234,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
@Override
|
||||
public void onWebGuiAvailable() {
|
||||
mAvailableCount.set(0);
|
||||
new GetTask(mHttpsCertPath) {
|
||||
new GetTask(mUrl, GetTask.URI_VERSION, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
if (s == null)
|
||||
|
@ -253,8 +250,8 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
Log.w(TAG, "Failed to parse config", e);
|
||||
}
|
||||
}
|
||||
}.execute(mUrl, GetTask.URI_VERSION, mApiKey);
|
||||
new GetTask(mHttpsCertPath) {
|
||||
}.execute();
|
||||
new GetTask(mUrl, GetTask.URI_CONFIG, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(String config) {
|
||||
try {
|
||||
|
@ -264,7 +261,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
Log.w(TAG, "Failed to parse config", e);
|
||||
}
|
||||
}
|
||||
}.execute(mUrl, GetTask.URI_CONFIG, mApiKey);
|
||||
}.execute();
|
||||
getSystemInfo(info -> {
|
||||
mLocalDeviceId = info.myID;
|
||||
tryIsAvailable();
|
||||
|
@ -367,7 +364,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
*/
|
||||
public void requireRestart(Activity activity) {
|
||||
if (mRestartPostponed) {
|
||||
new PostConfigTask(mHttpsCertPath).execute(mUrl, mApiKey, mConfig.toString());
|
||||
new PostTask(mUrl, PostTask.URI_CONFIG, mHttpsCertPath, mApiKey).execute(mConfig.toString());
|
||||
} else {
|
||||
activity.startActivity(new Intent(mContext, RestartActivity.class));
|
||||
}
|
||||
|
@ -380,13 +377,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
* This executes a restart immediately, and does not show a dialog.
|
||||
*/
|
||||
public void updateConfig() {
|
||||
new PostConfigTask(mHttpsCertPath) {
|
||||
new PostTask(mUrl, PostTask.URI_CONFIG, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(Boolean aBoolean) {
|
||||
protected void onPostExecute(Boolean b) {
|
||||
mContext.startService(new Intent(mContext, SyncthingService.class)
|
||||
.setAction(SyncthingService.ACTION_RESTART));
|
||||
}
|
||||
}.execute(mUrl, mApiKey, mConfig.toString());
|
||||
}.execute(mConfig.toString());
|
||||
|
||||
}
|
||||
|
||||
|
@ -445,7 +442,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
* @param listener Callback invoked when the result is received.
|
||||
*/
|
||||
public void getSystemInfo(final OnReceiveSystemInfoListener listener) {
|
||||
new GetTask(mHttpsCertPath) {
|
||||
new GetTask(mUrl, GetTask.URI_SYSTEM, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
if (s == null)
|
||||
|
@ -472,7 +469,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
Log.w(TAG, "Failed to read system info", e);
|
||||
}
|
||||
}
|
||||
}.execute(mUrl, GetTask.URI_SYSTEM, mApiKey);
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -481,7 +478,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
* @param listener Callback invoked when the result is received.
|
||||
*/
|
||||
public void getSystemVersion(final OnReceiveSystemVersionListener listener) {
|
||||
new GetTask(mHttpsCertPath) {
|
||||
new GetTask(mUrl, GetTask.URI_VERSION, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(String response) {
|
||||
if (response == null) {
|
||||
|
@ -495,7 +492,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
Log.w(TAG, "Failed to read system info", e);
|
||||
}
|
||||
}
|
||||
}.execute(mUrl, GetTask.URI_VERSION, mApiKey);
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -594,7 +591,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
* Use the key {@link #TOTAL_STATS} to get connection info for the local device.
|
||||
*/
|
||||
public void getConnections(final OnReceiveConnectionsListener listener) {
|
||||
new GetTask(mHttpsCertPath) {
|
||||
new GetTask(mUrl, GetTask.URI_CONNECTIONS, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
if (s == null)
|
||||
|
@ -650,7 +647,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
Log.w(TAG, "Failed to parse connections", e);
|
||||
}
|
||||
}
|
||||
}.execute(mUrl, GetTask.URI_CONNECTIONS, mApiKey);
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -720,7 +717,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
* Returns status information about the folder with the given id.
|
||||
*/
|
||||
public void getModel(final String folderId, final OnReceiveModelListener listener) {
|
||||
new GetTask(mHttpsCertPath) {
|
||||
new GetTask(mUrl, GetTask.URI_MODEL, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
if (s == null)
|
||||
|
@ -748,7 +745,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
Log.w(TAG, "Failed to read folder info", e);
|
||||
}
|
||||
}
|
||||
}.execute(mUrl, GetTask.URI_MODEL, mApiKey, "folder", folderId);
|
||||
}.execute("folder", folderId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -779,7 +776,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
* The OnReceiveEventListeners onEvent method is called for each event.
|
||||
*/
|
||||
public final void getEvents(final long sinceId, final long limit, final OnReceiveEventListener listener) {
|
||||
new GetTask(mHttpsCertPath) {
|
||||
new GetTask(mUrl, GetTask.URI_EVENTS, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
if (s == null)
|
||||
|
@ -815,7 +812,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
Log.w(TAG, "Failed to read events", e);
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mUrl, GetTask.URI_EVENTS, mApiKey,
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
"since", String.valueOf(sinceId), "limit", String.valueOf(limit));
|
||||
}
|
||||
|
||||
|
@ -998,7 +995,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
* Normalizes a given device ID.
|
||||
*/
|
||||
public void normalizeDeviceId(final String id, final OnDeviceIdNormalizedListener listener) {
|
||||
new GetTask(mHttpsCertPath) {
|
||||
new GetTask(mUrl, GetTask.URI_DEVICEID, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
super.onPostExecute(s);
|
||||
|
@ -1016,7 +1013,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
}
|
||||
listener.onDeviceIdNormalized(normalized, error);
|
||||
}
|
||||
}.execute(mUrl, GetTask.URI_DEVICEID, mApiKey, "id", id);
|
||||
}.execute("id", id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1050,7 +1047,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
*/
|
||||
@Override
|
||||
public void onFolderFileChange(String folderId, String relativePath) {
|
||||
new PostScanTask(mHttpsCertPath).execute(mUrl, mApiKey, folderId, relativePath);
|
||||
new PostTask(mUrl, PostTask.URI_SCAN, mHttpsCertPath, mApiKey).execute(folderId, relativePath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1109,7 +1106,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
* Returns prettyfied usage report.
|
||||
*/
|
||||
public void getUsageReport(final OnReceiveUsageReportListener listener) {
|
||||
new GetTask(mHttpsCertPath) {
|
||||
new GetTask(mUrl, GetTask.URI_REPORT, mHttpsCertPath, mApiKey) {
|
||||
@Override
|
||||
protected void onPostExecute(String s) {
|
||||
try {
|
||||
|
@ -1121,7 +1118,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
|||
throw new RuntimeException("Failed to prettify usage report", e);
|
||||
}
|
||||
}
|
||||
}.execute(mUrl, GetTask.URI_REPORT, mApiKey);
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.widget.Toast;
|
|||
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.activities.MainActivity;
|
||||
import com.nutomic.syncthingandroid.http.PollWebGuiAvailableTask;
|
||||
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||
import com.nutomic.syncthingandroid.util.FolderObserver;
|
||||
import com.nutomic.syncthingandroid.util.PRNGFixes;
|
||||
|
@ -30,6 +31,7 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -226,8 +228,8 @@ public class SyncthingService extends Service implements
|
|||
registerOnWebGuiAvailableListener(mApi);
|
||||
if (mEventProcessor != null)
|
||||
registerOnWebGuiAvailableListener(mEventProcessor);
|
||||
new PollWebGuiAvailableTaskImpl(getFilesDir() + "/" + HTTPS_CERT_FILE)
|
||||
.execute(mConfig.getWebGuiUrl());
|
||||
new PollWebGuiAvailableTaskImpl(getWebGuiUrl(), getFilesDir() + "/" + HTTPS_CERT_FILE, mConfig.getApiKey())
|
||||
.execute();
|
||||
mRunnable = new SyncthingRunnable(this, SyncthingRunnable.Command.main);
|
||||
new Thread(mRunnable).start();
|
||||
updateNotification();
|
||||
|
@ -321,10 +323,10 @@ public class SyncthingService extends Service implements
|
|||
* version, and reads syncthing URL and API key (these are passed internally as
|
||||
* {@code Pair<String, String>}.
|
||||
*/
|
||||
private class StartupTask extends AsyncTask<Void, Void, Pair<String, String>> {
|
||||
private class StartupTask extends AsyncTask<Void, Void, Pair<URL, String>> {
|
||||
|
||||
@Override
|
||||
protected Pair<String, String> doInBackground(Void... voids) {
|
||||
protected Pair<URL, String> doInBackground(Void... voids) {
|
||||
try {
|
||||
mConfig = new ConfigXml(SyncthingService.this);
|
||||
return new Pair<>(mConfig.getWebGuiUrl(), mConfig.getApiKey());
|
||||
|
@ -334,7 +336,7 @@ public class SyncthingService extends Service implements
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Pair<String, String> urlAndKey) {
|
||||
protected void onPostExecute(Pair<URL, String> urlAndKey) {
|
||||
if (urlAndKey == null) {
|
||||
Toast.makeText(SyncthingService.this, R.string.config_create_failed,
|
||||
Toast.LENGTH_LONG).show();
|
||||
|
@ -478,8 +480,8 @@ public class SyncthingService extends Service implements
|
|||
|
||||
private class PollWebGuiAvailableTaskImpl extends PollWebGuiAvailableTask {
|
||||
|
||||
public PollWebGuiAvailableTaskImpl(String httpsCertPath) {
|
||||
super(httpsCertPath);
|
||||
public PollWebGuiAvailableTaskImpl(URL url, String httpsCertPath, String apiKey) {
|
||||
super(url, httpsCertPath, apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -528,7 +530,7 @@ public class SyncthingService extends Service implements
|
|||
}
|
||||
}
|
||||
|
||||
public String getWebGuiUrl() {
|
||||
public URL getWebGuiUrl() {
|
||||
return mConfig.getWebGuiUrl();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ import org.xml.sax.SAXException;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
@ -96,8 +98,12 @@ public class ConfigXml {
|
|||
return new File(context.getFilesDir(), CONFIG_FILE);
|
||||
}
|
||||
|
||||
public String getWebGuiUrl() {
|
||||
return "https://" + getGuiElement().getElementsByTagName("address").item(0).getTextContent();
|
||||
public URL getWebGuiUrl() {
|
||||
try {
|
||||
return new URL("https://" + getGuiElement().getElementsByTagName("address").item(0).getTextContent());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException("Failed to parse web interface URL", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Socket;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SignatureException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
|
||||
/*
|
||||
* TrustManager allowing the Syncthing https.pem CA
|
||||
*
|
||||
* Based on http://stackoverflow.com/questions/16719959#16759793
|
||||
*
|
||||
*/
|
||||
public class CustomX509TrustManager implements X509TrustManager {
|
||||
|
||||
private static final String TAG = "CustomX509TrustManager";
|
||||
|
||||
/**
|
||||
* Taken from: http://janis.peisenieks.lv/en/76/english-making-an-ssl-connection-via-android/
|
||||
*
|
||||
*/
|
||||
public static class CustomSSLSocketFactory extends SSLSocketFactory {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
|
||||
public CustomSSLSocketFactory(SSLContext context)
|
||||
throws KeyManagementException, NoSuchAlgorithmException,
|
||||
KeyStoreException, UnrecoverableKeyException {
|
||||
super(null);
|
||||
sslContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket socket, String host, int port,
|
||||
boolean autoClose) throws IOException {
|
||||
return sslContext.getSocketFactory().createSocket(socket, host, port,
|
||||
autoClose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
return sslContext.getSocketFactory().createSocket();
|
||||
}
|
||||
}
|
||||
|
||||
private final String mHttpsCertPath;
|
||||
|
||||
public CustomX509TrustManager(String httpsCertPath) {
|
||||
mHttpsCertPath = httpsCertPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("TrustAllX509TrustManager")
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies certs against public key of the local syncthing instance
|
||||
*/
|
||||
@Override
|
||||
public void checkServerTrusted(java.security.cert.X509Certificate[] certs,
|
||||
String authType) throws CertificateException {
|
||||
InputStream inStream = null;
|
||||
try {
|
||||
inStream = new FileInputStream(mHttpsCertPath);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate ca = (X509Certificate)
|
||||
cf.generateCertificate(inStream);
|
||||
for (X509Certificate cert : certs) {
|
||||
cert.verify(ca.getPublicKey());
|
||||
}
|
||||
} catch (FileNotFoundException |NoSuchAlgorithmException|InvalidKeyException|
|
||||
NoSuchProviderException |SignatureException e) {
|
||||
throw new CertificateException("Untrusted Certificate!", e);
|
||||
} finally {
|
||||
try {
|
||||
if (inStream != null)
|
||||
inStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.conn.ClientConnectionManager;
|
||||
import org.apache.http.conn.scheme.Scheme;
|
||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
/*
|
||||
* Wrapper for HTTPS Clients allowing the Syncthing https.pem CA
|
||||
*
|
||||
*/
|
||||
public class Https {
|
||||
|
||||
private static final String TAG = "HTTPS";
|
||||
|
||||
/**
|
||||
* Create a HTTPClient that verifies a custom PEM certificate
|
||||
*
|
||||
* @param httpsCertPath refers to the filepath of a SSL/TLS PEM certificate.
|
||||
*/
|
||||
public static HttpClient createHttpsClient(String httpsCertPath) {
|
||||
try {
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
ctx.init(null, new TrustManager[] { new CustomX509TrustManager(httpsCertPath) },
|
||||
new SecureRandom());
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
SSLSocketFactory ssf = new CustomX509TrustManager.CustomSSLSocketFactory(ctx);
|
||||
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||
ClientConnectionManager ccm = client.getConnectionManager();
|
||||
SchemeRegistry sr = ccm.getSchemeRegistry();
|
||||
sr.register(new Scheme("https", ssf, 443));
|
||||
return new DefaultHttpClient(ccm,
|
||||
client.getParams());
|
||||
} catch (NoSuchAlgorithmException|KeyManagementException|KeyStoreException|
|
||||
UnrecoverableKeyException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue