mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-22 20:31:16 +00:00
More refactoring
This commit is contained in:
parent
be1be9746e
commit
e6c9960d0b
12 changed files with 98 additions and 73 deletions
|
@ -334,7 +334,7 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
mUseRoot.setChecked(false);
|
mUseRoot.setChecked(false);
|
||||||
new TestRootTask().execute();
|
new TestRootTask().execute();
|
||||||
} else {
|
} else {
|
||||||
new Thread(new ChownFilesRunnable()).start();
|
new Thread(() -> Util.fixAppDataPermissions(getActivity())).start();
|
||||||
mSyncthingService.getApi().showRestartDialog(getActivity());
|
mSyncthingService.getApi().showRestartDialog(getActivity());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -409,16 +409,5 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the owner of syncthing files so they can be accessed without root.
|
|
||||||
*/
|
|
||||||
private class ChownFilesRunnable implements Runnable {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Util.fixAppDataPermissions(getActivity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import com.nutomic.syncthingandroid.service.RestApi;
|
||||||
import com.nutomic.syncthingandroid.service.SyncthingService;
|
import com.nutomic.syncthingandroid.service.SyncthingService;
|
||||||
import com.nutomic.syncthingandroid.util.Util;
|
import com.nutomic.syncthingandroid.util.Util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -196,7 +197,7 @@ public class DrawerFragment extends Fragment implements View.OnClickListener {
|
||||||
|
|
||||||
private void showQrCode() {
|
private void showQrCode() {
|
||||||
//The QRCode request takes one paramteer called "text", which is the text to be converted to a QRCode.
|
//The QRCode request takes one paramteer called "text", which is the text to be converted to a QRCode.
|
||||||
String httpsCertPath = mActivity.getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE;
|
File httpsCertPath = new File(mActivity.getFilesDir(), SyncthingService.HTTPS_CERT_FILE);
|
||||||
String apiKey = mActivity.getApi().getGui().apiKey;
|
String apiKey = mActivity.getApi().getGui().apiKey;
|
||||||
String deviceId = mActivity.getApi().getLocalDevice().deviceID;
|
String deviceId = mActivity.getApi().getLocalDevice().deviceID;
|
||||||
URL url = mActivity.getApi().getUrl();
|
URL url = mActivity.getApi().getUrl();
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.android.volley.toolbox.Volley;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -78,10 +79,10 @@ public abstract class ApiRequest {
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final URL mUrl;
|
private final URL mUrl;
|
||||||
private final String mPath;
|
private final String mPath;
|
||||||
private final String mHttpsCertPath;
|
private final File mHttpsCertPath;
|
||||||
private final String mApiKey;
|
private final String mApiKey;
|
||||||
|
|
||||||
public ApiRequest(Context context, URL url, String path, String httpsCertPath, String apiKey) {
|
public ApiRequest(Context context, URL url, String path, File httpsCertPath, String apiKey) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mUrl = url;
|
mUrl = url;
|
||||||
mPath = path;
|
mPath = path;
|
||||||
|
@ -176,7 +177,7 @@ public abstract class ApiRequest {
|
||||||
private SSLSocketFactory getSslSocketFactory() {
|
private SSLSocketFactory getSslSocketFactory() {
|
||||||
try {
|
try {
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
sslContext.init(null, new TrustManager[]{new SyncthingTrustManager()},
|
sslContext.init(null, new TrustManager[]{new SyncthingTrustManager(mHttpsCertPath)},
|
||||||
new SecureRandom());
|
new SecureRandom());
|
||||||
return sslContext.getSocketFactory();
|
return sslContext.getSocketFactory();
|
||||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||||
|
@ -184,51 +185,4 @@ public abstract class ApiRequest {
|
||||||
return null;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.support.annotation.Nullable;
|
||||||
import com.android.volley.Request;
|
import com.android.volley.Request;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -25,7 +26,7 @@ public class GetRequest extends ApiRequest {
|
||||||
public static final String URI_REPORT = "/rest/svc/report";
|
public static final String URI_REPORT = "/rest/svc/report";
|
||||||
public static final String URI_EVENTS = "/rest/events";
|
public static final String URI_EVENTS = "/rest/events";
|
||||||
|
|
||||||
public GetRequest(Context context, URL url, String path, String httpsCertPath, String apiKey,
|
public GetRequest(Context context, URL url, String path, File httpsCertPath, String apiKey,
|
||||||
@Nullable Map<String, String> params, OnSuccessListener listener) {
|
@Nullable Map<String, String> params, OnSuccessListener listener) {
|
||||||
super(context, url, path, httpsCertPath, apiKey);
|
super(context, url, path, httpsCertPath, apiKey);
|
||||||
Map<String, String> safeParams = Optional.fromNullable(params).or(Collections.emptyMap());
|
Map<String, String> safeParams = Optional.fromNullable(params).or(Collections.emptyMap());
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -14,7 +15,7 @@ public class ImageGetRequest extends ApiRequest {
|
||||||
|
|
||||||
public static final String QR_CODE_GENERATOR = "/qr/";
|
public static final String QR_CODE_GENERATOR = "/qr/";
|
||||||
|
|
||||||
public ImageGetRequest(Context context, URL url, String path, String httpsCertPath, String apiKey,
|
public ImageGetRequest(Context context, URL url, String path, File httpsCertPath, String apiKey,
|
||||||
@Nullable Map<String, String> params, OnImageSuccessListener onSuccessListener, OnErrorListener onErrorListener) {
|
@Nullable Map<String, String> params, OnImageSuccessListener onSuccessListener, OnErrorListener onErrorListener) {
|
||||||
super(context, url, path, httpsCertPath, apiKey);
|
super(context, url, path, httpsCertPath, apiKey);
|
||||||
Map<String, String> safeParams = Optional.fromNullable(params).or(Collections.emptyMap());
|
Map<String, String> safeParams = Optional.fromNullable(params).or(Collections.emptyMap());
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.os.Handler;
|
||||||
import com.android.volley.Request;
|
import com.android.volley.Request;
|
||||||
import com.android.volley.VolleyError;
|
import com.android.volley.VolleyError;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ public class PollWebGuiAvailableTask extends ApiRequest {
|
||||||
private final OnSuccessListener mListener;
|
private final OnSuccessListener mListener;
|
||||||
private final Handler mHandler = new Handler();
|
private final Handler mHandler = new Handler();
|
||||||
|
|
||||||
public PollWebGuiAvailableTask(Context context, URL url, String httpsCertPath, String apiKey,
|
public PollWebGuiAvailableTask(Context context, URL url, File httpsCertPath, String apiKey,
|
||||||
OnSuccessListener listener) {
|
OnSuccessListener listener) {
|
||||||
super(context, url, "", httpsCertPath, apiKey);
|
super(context, url, "", httpsCertPath, apiKey);
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.net.Uri;
|
||||||
|
|
||||||
import com.android.volley.Request;
|
import com.android.volley.Request;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ public class PostConfigRequest extends ApiRequest {
|
||||||
|
|
||||||
private static final String URI_CONFIG = "/rest/system/config";
|
private static final String URI_CONFIG = "/rest/system/config";
|
||||||
|
|
||||||
public PostConfigRequest(Context context, URL url, String httpsCertPath, String apiKey, String config,
|
public PostConfigRequest(Context context, URL url, File httpsCertPath, String apiKey, String config,
|
||||||
OnSuccessListener listener) {
|
OnSuccessListener listener) {
|
||||||
super(context, url, URI_CONFIG, httpsCertPath, apiKey);
|
super(context, url, URI_CONFIG, httpsCertPath, apiKey);
|
||||||
Uri uri = buildUri(Collections.emptyMap());
|
Uri uri = buildUri(Collections.emptyMap());
|
||||||
|
|
|
@ -6,13 +6,14 @@ import android.net.Uri;
|
||||||
import com.android.volley.Request;
|
import com.android.volley.Request;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
public class PostScanRequest extends ApiRequest {
|
public class PostScanRequest extends ApiRequest {
|
||||||
|
|
||||||
private static final String URI_SCAN = "/rest/db/scan";
|
private static final String URI_SCAN = "/rest/db/scan";
|
||||||
|
|
||||||
public PostScanRequest(Context context, URL url, String httpsCertPath, String apiKey,
|
public PostScanRequest(Context context, URL url, File httpsCertPath, String apiKey,
|
||||||
String folder, String sub) {
|
String folder, String sub) {
|
||||||
super(context, url, URI_SCAN, httpsCertPath, apiKey);
|
super(context, url, URI_SCAN, httpsCertPath, apiKey);
|
||||||
Uri uri = buildUri(ImmutableMap.of("folder", folder, "sub", sub));
|
Uri uri = buildUri(ImmutableMap.of("folder", folder, "sub", sub));
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package com.nutomic.syncthingandroid.http;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TrustManager checking against the local Syncthing instance's https public key.
|
||||||
|
*
|
||||||
|
* Based on http://stackoverflow.com/questions/16719959#16759793
|
||||||
|
*/
|
||||||
|
class SyncthingTrustManager implements X509TrustManager {
|
||||||
|
|
||||||
|
private static final String TAG = "SyncthingTrustManager";
|
||||||
|
|
||||||
|
private final File mHttpsCertPath;
|
||||||
|
|
||||||
|
SyncthingTrustManager(File 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(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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ import com.nutomic.syncthingandroid.model.SystemInfo;
|
||||||
import com.nutomic.syncthingandroid.model.SystemVersion;
|
import com.nutomic.syncthingandroid.model.SystemVersion;
|
||||||
import com.nutomic.syncthingandroid.util.FolderObserver;
|
import com.nutomic.syncthingandroid.util.FolderObserver;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -75,7 +76,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final URL mUrl;
|
private final URL mUrl;
|
||||||
private final String mApiKey;
|
private final String mApiKey;
|
||||||
private final String mHttpsCertPath;
|
private final File mHttpsCertPath;
|
||||||
|
|
||||||
private String mVersion;
|
private String mVersion;
|
||||||
private Config mConfig;
|
private Config mConfig;
|
||||||
|
@ -104,7 +105,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mUrl = url;
|
mUrl = url;
|
||||||
mApiKey = apiKey;
|
mApiKey = apiKey;
|
||||||
mHttpsCertPath = mContext.getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE;
|
mHttpsCertPath = new File(mContext.getFilesDir(), SyncthingService.HTTPS_CERT_FILE);
|
||||||
mOnApiAvailableListener = apiListener;
|
mOnApiAvailableListener = apiListener;
|
||||||
mOnConfigChangedListener = configListener;
|
mOnConfigChangedListener = configListener;
|
||||||
}
|
}
|
||||||
|
@ -151,7 +152,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increments mAvailableCount by one, and, if it reached TOTAL_STARTUP_CALLS,
|
* Increments mAvailableCount by one, and, if it reached TOTAL_STARTUP_CALLS,
|
||||||
* calls {@link SyncthingService#onApiChange()}.
|
* calls {@link SyncthingService#onApiChange}.
|
||||||
*/
|
*/
|
||||||
private void tryIsAvailable() {
|
private void tryIsAvailable() {
|
||||||
int value = mAvailableCount.incrementAndGet();
|
int value = mAvailableCount.incrementAndGet();
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.LineNumberReader;
|
import java.io.LineNumberReader;
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
@ -85,7 +86,7 @@ public class SyncthingRunnable implements Runnable {
|
||||||
mCommand = new String[]{ mSyncthingBinary, "-home", mContext.getFilesDir().toString(), "-reset" };
|
mCommand = new String[]{ mSyncthingBinary, "-home", mContext.getFilesDir().toString(), "-reset" };
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.w(TAG, "Unknown command option");
|
throw new InvalidParameterException("Unknown command option");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,6 +343,9 @@ public class SyncthingRunnable implements Runnable {
|
||||||
* Only keep last {@link #LOG_FILE_MAX_LINES} lines in log file, to avoid bloat.
|
* Only keep last {@link #LOG_FILE_MAX_LINES} lines in log file, to avoid bloat.
|
||||||
*/
|
*/
|
||||||
private void trimLogFile() {
|
private void trimLogFile() {
|
||||||
|
if (!mLogFile.exists())
|
||||||
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LineNumberReader lnr = new LineNumberReader(new FileReader(mLogFile));
|
LineNumberReader lnr = new LineNumberReader(new FileReader(mLogFile));
|
||||||
lnr.skip(Long.MAX_VALUE);
|
lnr.skip(Long.MAX_VALUE);
|
||||||
|
|
|
@ -471,7 +471,7 @@ public class SyncthingService extends Service implements
|
||||||
* for SyncthingService.onDestroy for details.
|
* for SyncthingService.onDestroy for details.
|
||||||
*/
|
*/
|
||||||
private void pollWebGui() {
|
private void pollWebGui() {
|
||||||
new PollWebGuiAvailableTask(this, getWebGuiUrl(), getFilesDir() + "/" + HTTPS_CERT_FILE,
|
new PollWebGuiAvailableTask(this, getWebGuiUrl(), new File(getFilesDir(), HTTPS_CERT_FILE),
|
||||||
mConfig.getApiKey(), result -> {
|
mConfig.getApiKey(), result -> {
|
||||||
synchronized (stateLock) {
|
synchronized (stateLock) {
|
||||||
if (mStopScheduled) {
|
if (mStopScheduled) {
|
||||||
|
|
Loading…
Reference in a new issue