From e6c9960d0b7ddd466c9118df388cd90620fe8952 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Mon, 2 Oct 2017 15:40:10 +0900 Subject: [PATCH] More refactoring --- .../activities/SettingsActivity.java | 13 +--- .../fragments/DrawerFragment.java | 3 +- .../syncthingandroid/http/ApiRequest.java | 54 ++------------ .../syncthingandroid/http/GetRequest.java | 3 +- .../http/ImageGetRequest.java | 3 +- .../http/PollWebGuiAvailableTask.java | 3 +- .../http/PostConfigRequest.java | 3 +- .../http/PostScanRequest.java | 3 +- .../http/SyncthingTrustManager.java | 71 +++++++++++++++++++ .../syncthingandroid/service/RestApi.java | 7 +- .../service/SyncthingRunnable.java | 6 +- .../service/SyncthingService.java | 2 +- 12 files changed, 98 insertions(+), 73 deletions(-) create mode 100644 src/main/java/com/nutomic/syncthingandroid/http/SyncthingTrustManager.java diff --git a/src/main/java/com/nutomic/syncthingandroid/activities/SettingsActivity.java b/src/main/java/com/nutomic/syncthingandroid/activities/SettingsActivity.java index d2e249d1..6b3e9c69 100644 --- a/src/main/java/com/nutomic/syncthingandroid/activities/SettingsActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/activities/SettingsActivity.java @@ -334,7 +334,7 @@ public class SettingsActivity extends SyncthingActivity { mUseRoot.setChecked(false); new TestRootTask().execute(); } else { - new Thread(new ChownFilesRunnable()).start(); + new Thread(() -> Util.fixAppDataPermissions(getActivity())).start(); mSyncthingService.getApi().showRestartDialog(getActivity()); } 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()); - } - } - } } diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/DrawerFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/DrawerFragment.java index 00441d94..2da6cbef 100644 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/DrawerFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/DrawerFragment.java @@ -24,6 +24,7 @@ import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.SyncthingService; import com.nutomic.syncthingandroid.util.Util; +import java.io.File; import java.net.URL; import java.text.NumberFormat; import java.util.Locale; @@ -196,7 +197,7 @@ public class DrawerFragment extends Fragment implements View.OnClickListener { private void showQrCode() { //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 deviceId = mActivity.getApi().getLocalDevice().deviceID; URL url = mActivity.getApi().getUrl(); diff --git a/src/main/java/com/nutomic/syncthingandroid/http/ApiRequest.java b/src/main/java/com/nutomic/syncthingandroid/http/ApiRequest.java index a5dc623b..babba5d2 100644 --- a/src/main/java/com/nutomic/syncthingandroid/http/ApiRequest.java +++ b/src/main/java/com/nutomic/syncthingandroid/http/ApiRequest.java @@ -19,6 +19,7 @@ import com.android.volley.toolbox.Volley; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -78,10 +79,10 @@ public abstract class ApiRequest { private final Context mContext; private final URL mUrl; private final String mPath; - private final String mHttpsCertPath; + private final File mHttpsCertPath; 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; mUrl = url; mPath = path; @@ -176,7 +177,7 @@ public abstract class ApiRequest { private SSLSocketFactory getSslSocketFactory() { try { SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, new TrustManager[]{new SyncthingTrustManager()}, + sslContext.init(null, new TrustManager[]{new SyncthingTrustManager(mHttpsCertPath)}, new SecureRandom()); return sslContext.getSocketFactory(); } catch (NoSuchAlgorithmException | KeyManagementException e) { @@ -184,51 +185,4 @@ public abstract class ApiRequest { 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; - } - } - } diff --git a/src/main/java/com/nutomic/syncthingandroid/http/GetRequest.java b/src/main/java/com/nutomic/syncthingandroid/http/GetRequest.java index a8a55d3c..18e0d35b 100644 --- a/src/main/java/com/nutomic/syncthingandroid/http/GetRequest.java +++ b/src/main/java/com/nutomic/syncthingandroid/http/GetRequest.java @@ -7,6 +7,7 @@ import android.support.annotation.Nullable; import com.android.volley.Request; import com.google.common.base.Optional; +import java.io.File; import java.net.URL; import java.util.Collections; 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_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 params, OnSuccessListener listener) { super(context, url, path, httpsCertPath, apiKey); Map safeParams = Optional.fromNullable(params).or(Collections.emptyMap()); diff --git a/src/main/java/com/nutomic/syncthingandroid/http/ImageGetRequest.java b/src/main/java/com/nutomic/syncthingandroid/http/ImageGetRequest.java index 333877f7..8f263d81 100644 --- a/src/main/java/com/nutomic/syncthingandroid/http/ImageGetRequest.java +++ b/src/main/java/com/nutomic/syncthingandroid/http/ImageGetRequest.java @@ -6,6 +6,7 @@ import android.support.annotation.Nullable; import com.google.common.base.Optional; +import java.io.File; import java.net.URL; import java.util.Collections; import java.util.Map; @@ -14,7 +15,7 @@ public class ImageGetRequest extends ApiRequest { 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 params, OnImageSuccessListener onSuccessListener, OnErrorListener onErrorListener) { super(context, url, path, httpsCertPath, apiKey); Map safeParams = Optional.fromNullable(params).or(Collections.emptyMap()); diff --git a/src/main/java/com/nutomic/syncthingandroid/http/PollWebGuiAvailableTask.java b/src/main/java/com/nutomic/syncthingandroid/http/PollWebGuiAvailableTask.java index 83a2c6d1..cec8f992 100644 --- a/src/main/java/com/nutomic/syncthingandroid/http/PollWebGuiAvailableTask.java +++ b/src/main/java/com/nutomic/syncthingandroid/http/PollWebGuiAvailableTask.java @@ -8,6 +8,7 @@ import android.os.Handler; import com.android.volley.Request; import com.android.volley.VolleyError; +import java.io.File; import java.net.URL; import java.util.Collections; @@ -25,7 +26,7 @@ public class PollWebGuiAvailableTask extends ApiRequest { private final OnSuccessListener mListener; 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) { super(context, url, "", httpsCertPath, apiKey); mListener = listener; diff --git a/src/main/java/com/nutomic/syncthingandroid/http/PostConfigRequest.java b/src/main/java/com/nutomic/syncthingandroid/http/PostConfigRequest.java index d8f36474..eb3db445 100644 --- a/src/main/java/com/nutomic/syncthingandroid/http/PostConfigRequest.java +++ b/src/main/java/com/nutomic/syncthingandroid/http/PostConfigRequest.java @@ -5,6 +5,7 @@ import android.net.Uri; import com.android.volley.Request; +import java.io.File; import java.net.URL; import java.util.Collections; @@ -12,7 +13,7 @@ public class PostConfigRequest extends ApiRequest { 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) { super(context, url, URI_CONFIG, httpsCertPath, apiKey); Uri uri = buildUri(Collections.emptyMap()); diff --git a/src/main/java/com/nutomic/syncthingandroid/http/PostScanRequest.java b/src/main/java/com/nutomic/syncthingandroid/http/PostScanRequest.java index 266b9487..29200601 100644 --- a/src/main/java/com/nutomic/syncthingandroid/http/PostScanRequest.java +++ b/src/main/java/com/nutomic/syncthingandroid/http/PostScanRequest.java @@ -6,13 +6,14 @@ import android.net.Uri; import com.android.volley.Request; import com.google.common.collect.ImmutableMap; +import java.io.File; import java.net.URL; public class PostScanRequest extends ApiRequest { 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) { super(context, url, URI_SCAN, httpsCertPath, apiKey); Uri uri = buildUri(ImmutableMap.of("folder", folder, "sub", sub)); diff --git a/src/main/java/com/nutomic/syncthingandroid/http/SyncthingTrustManager.java b/src/main/java/com/nutomic/syncthingandroid/http/SyncthingTrustManager.java new file mode 100644 index 00000000..544999d9 --- /dev/null +++ b/src/main/java/com/nutomic/syncthingandroid/http/SyncthingTrustManager.java @@ -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; + } +} diff --git a/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java b/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java index 979c35c0..0f1ca7b2 100644 --- a/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java +++ b/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java @@ -32,6 +32,7 @@ import com.nutomic.syncthingandroid.model.SystemInfo; import com.nutomic.syncthingandroid.model.SystemVersion; import com.nutomic.syncthingandroid.util.FolderObserver; +import java.io.File; import java.lang.reflect.Type; import java.net.URL; import java.util.Collections; @@ -75,7 +76,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, private final Context mContext; private final URL mUrl; private final String mApiKey; - private final String mHttpsCertPath; + private final File mHttpsCertPath; private String mVersion; private Config mConfig; @@ -104,7 +105,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, mContext = context; mUrl = url; mApiKey = apiKey; - mHttpsCertPath = mContext.getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE; + mHttpsCertPath = new File(mContext.getFilesDir(), SyncthingService.HTTPS_CERT_FILE); mOnApiAvailableListener = apiListener; mOnConfigChangedListener = configListener; } @@ -151,7 +152,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, /** * Increments mAvailableCount by one, and, if it reached TOTAL_STARTUP_CALLS, - * calls {@link SyncthingService#onApiChange()}. + * calls {@link SyncthingService#onApiChange}. */ private void tryIsAvailable() { int value = mAvailableCount.incrementAndGet(); diff --git a/src/main/java/com/nutomic/syncthingandroid/service/SyncthingRunnable.java b/src/main/java/com/nutomic/syncthingandroid/service/SyncthingRunnable.java index c4c6ea7b..d77ff386 100644 --- a/src/main/java/com/nutomic/syncthingandroid/service/SyncthingRunnable.java +++ b/src/main/java/com/nutomic/syncthingandroid/service/SyncthingRunnable.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; +import java.security.InvalidParameterException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @@ -85,7 +86,7 @@ public class SyncthingRunnable implements Runnable { mCommand = new String[]{ mSyncthingBinary, "-home", mContext.getFilesDir().toString(), "-reset" }; break; 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. */ private void trimLogFile() { + if (!mLogFile.exists()) + return; + try { LineNumberReader lnr = new LineNumberReader(new FileReader(mLogFile)); lnr.skip(Long.MAX_VALUE); diff --git a/src/main/java/com/nutomic/syncthingandroid/service/SyncthingService.java b/src/main/java/com/nutomic/syncthingandroid/service/SyncthingService.java index 2e725bec..697e8fbe 100644 --- a/src/main/java/com/nutomic/syncthingandroid/service/SyncthingService.java +++ b/src/main/java/com/nutomic/syncthingandroid/service/SyncthingService.java @@ -471,7 +471,7 @@ public class SyncthingService extends Service implements * for SyncthingService.onDestroy for details. */ private void pollWebGui() { - new PollWebGuiAvailableTask(this, getWebGuiUrl(), getFilesDir() + "/" + HTTPS_CERT_FILE, + new PollWebGuiAvailableTask(this, getWebGuiUrl(), new File(getFilesDir(), HTTPS_CERT_FILE), mConfig.getApiKey(), result -> { synchronized (stateLock) { if (mStopScheduled) {