Loading app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java +178 −1 Original line number Diff line number Diff line Loading @@ -193,4 +193,181 @@ public class ImportAccountActivity extends AppCompatActivity { binding.progressText.setVisibility(View.GONE); }); } private void login() { if (useWebLogin) { webLogin(); } else { legacyLogin(); } } private void legacyLogin() { String url = field_url.getText().toString().trim(); String username = field_username.getText().toString(); String password = field_password.getText().toString(); if (password.isEmpty()) { password = old_password; } url = NotesClientUtil.formatURL(url); new LoginValidatorAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url, username, password); } public static X509Certificate getX509CertificateFromError(SslError error) { Bundle bundle = SslCertificate.saveState(error.getCertificate()); X509Certificate x509Certificate; byte[] bytes = bundle.getByteArray("x509-certificate"); if (bytes == null) { x509Certificate = null; } else { try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes)); x509Certificate = (X509Certificate) cert; } catch (CertificateException e) { x509Certificate = null; } } return x509Certificate; } private void webLogin() { setContentView(R.layout.activity_settings_webview); webView = findViewById(R.id.login_webview); webView.setVisibility(View.GONE); final ProgressBar progressBar = findViewById(R.id.login_webview_progress_bar); WebSettings settings = webView.getSettings(); settings.setAllowFileAccess(false); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); settings.setUserAgentString(getWebLoginUserAgent()); settings.setSaveFormData(false); settings.setSavePassword(false); Map<String, String> headers = new HashMap<>(); headers.put("OCS-APIREQUEST", "true"); webView.loadUrl(normalizeUrlSuffix(NotesClientUtil.formatURL(field_url.getText().toString())) + "index.php/login/flow", headers); webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("nc://login/")) { parseAndLoginFromWebView(url); return true; } return false; } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); progressBar.setVisibility(View.GONE); webView.setVisibility(View.VISIBLE); } @Override public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) { X509Certificate cert = getX509CertificateFromError(error); try { final boolean[] accepted = new boolean[1]; NoteServerSyncHelper.getInstance(NoteSQLiteOpenHelper.getInstance(getApplicationContext())) .checkCertificate(cert.getEncoded(), true, new IOnCertificateDecision.Stub() { @Override public void accept() { Log.d("Note", "cert accepted"); handler.proceed(); accepted[0] = true; } @Override public void reject() { Log.d("Note", "cert rejected"); handler.cancel(); killProcess(myPid()); } }); } catch (Exception e) { Log.e("Note", "Cert could not be verified"); handler.proceed(); } } }); // show snackbar after 60s to switch back to old login method new Handler().postDelayed(() -> { Snackbar.make(webView, R.string.fallback_weblogin_text, Snackbar.LENGTH_INDEFINITE) .setAction(R.string.fallback_weblogin_back, (View.OnClickListener) v -> initLegacyLogin(field_url.getText().toString())).show(); }, 45 * 1000); } private String getWebLoginUserAgent() { return Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + " " + Build.MODEL; } private void parseAndLoginFromWebView(String dataString) { String prefix = "nc://login/"; LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, dataString); if (loginUrlInfo != null) { String url = normalizeUrlSuffix(loginUrlInfo.serverAddress); new LoginValidatorAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url, loginUrlInfo.username, loginUrlInfo.password); } } /** * parses a URI string and returns a login data object with the information from the URI string. * * @param prefix URI beginning, e.g. cloud://login/ * @param dataString the complete URI * @return login data * @throws IllegalArgumentException when */ private LoginUrlInfo parseLoginDataUrl(String prefix, String dataString) throws IllegalArgumentException { if (dataString.length() < prefix.length()) { throw new IllegalArgumentException("Invalid login URL detected"); } LoginUrlInfo loginUrlInfo = new LoginUrlInfo(); // format is basically xxx://login/server:xxx&user:xxx&password while all variables are optional String data = dataString.substring(prefix.length()); // parse data String[] values = data.split("&"); if (values.length < 1 || values.length > 3) { // error illegal number of URL elements detected throw new IllegalArgumentException("Illegal number of login URL elements detected: " + values.length); } for (String value : values) { if (value.startsWith("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { loginUrlInfo.username = URLDecoder.decode( value.substring(("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length())); } else if (value.startsWith("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { loginUrlInfo.password = URLDecoder.decode( value.substring(("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length())); } else if (value.startsWith("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { loginUrlInfo.serverAddress = URLDecoder.decode( value.substring(("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length())); } else { // error illegal URL element detected throw new IllegalArgumentException("Illegal magic login URL element detected: " + value); } } return loginUrlInfo; } } No newline at end of file Loading
app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java +178 −1 Original line number Diff line number Diff line Loading @@ -193,4 +193,181 @@ public class ImportAccountActivity extends AppCompatActivity { binding.progressText.setVisibility(View.GONE); }); } private void login() { if (useWebLogin) { webLogin(); } else { legacyLogin(); } } private void legacyLogin() { String url = field_url.getText().toString().trim(); String username = field_username.getText().toString(); String password = field_password.getText().toString(); if (password.isEmpty()) { password = old_password; } url = NotesClientUtil.formatURL(url); new LoginValidatorAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url, username, password); } public static X509Certificate getX509CertificateFromError(SslError error) { Bundle bundle = SslCertificate.saveState(error.getCertificate()); X509Certificate x509Certificate; byte[] bytes = bundle.getByteArray("x509-certificate"); if (bytes == null) { x509Certificate = null; } else { try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes)); x509Certificate = (X509Certificate) cert; } catch (CertificateException e) { x509Certificate = null; } } return x509Certificate; } private void webLogin() { setContentView(R.layout.activity_settings_webview); webView = findViewById(R.id.login_webview); webView.setVisibility(View.GONE); final ProgressBar progressBar = findViewById(R.id.login_webview_progress_bar); WebSettings settings = webView.getSettings(); settings.setAllowFileAccess(false); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); settings.setUserAgentString(getWebLoginUserAgent()); settings.setSaveFormData(false); settings.setSavePassword(false); Map<String, String> headers = new HashMap<>(); headers.put("OCS-APIREQUEST", "true"); webView.loadUrl(normalizeUrlSuffix(NotesClientUtil.formatURL(field_url.getText().toString())) + "index.php/login/flow", headers); webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("nc://login/")) { parseAndLoginFromWebView(url); return true; } return false; } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); progressBar.setVisibility(View.GONE); webView.setVisibility(View.VISIBLE); } @Override public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) { X509Certificate cert = getX509CertificateFromError(error); try { final boolean[] accepted = new boolean[1]; NoteServerSyncHelper.getInstance(NoteSQLiteOpenHelper.getInstance(getApplicationContext())) .checkCertificate(cert.getEncoded(), true, new IOnCertificateDecision.Stub() { @Override public void accept() { Log.d("Note", "cert accepted"); handler.proceed(); accepted[0] = true; } @Override public void reject() { Log.d("Note", "cert rejected"); handler.cancel(); killProcess(myPid()); } }); } catch (Exception e) { Log.e("Note", "Cert could not be verified"); handler.proceed(); } } }); // show snackbar after 60s to switch back to old login method new Handler().postDelayed(() -> { Snackbar.make(webView, R.string.fallback_weblogin_text, Snackbar.LENGTH_INDEFINITE) .setAction(R.string.fallback_weblogin_back, (View.OnClickListener) v -> initLegacyLogin(field_url.getText().toString())).show(); }, 45 * 1000); } private String getWebLoginUserAgent() { return Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + " " + Build.MODEL; } private void parseAndLoginFromWebView(String dataString) { String prefix = "nc://login/"; LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, dataString); if (loginUrlInfo != null) { String url = normalizeUrlSuffix(loginUrlInfo.serverAddress); new LoginValidatorAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url, loginUrlInfo.username, loginUrlInfo.password); } } /** * parses a URI string and returns a login data object with the information from the URI string. * * @param prefix URI beginning, e.g. cloud://login/ * @param dataString the complete URI * @return login data * @throws IllegalArgumentException when */ private LoginUrlInfo parseLoginDataUrl(String prefix, String dataString) throws IllegalArgumentException { if (dataString.length() < prefix.length()) { throw new IllegalArgumentException("Invalid login URL detected"); } LoginUrlInfo loginUrlInfo = new LoginUrlInfo(); // format is basically xxx://login/server:xxx&user:xxx&password while all variables are optional String data = dataString.substring(prefix.length()); // parse data String[] values = data.split("&"); if (values.length < 1 || values.length > 3) { // error illegal number of URL elements detected throw new IllegalArgumentException("Illegal number of login URL elements detected: " + values.length); } for (String value : values) { if (value.startsWith("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { loginUrlInfo.username = URLDecoder.decode( value.substring(("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length())); } else if (value.startsWith("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { loginUrlInfo.password = URLDecoder.decode( value.substring(("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length())); } else if (value.startsWith("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { loginUrlInfo.serverAddress = URLDecoder.decode( value.substring(("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length())); } else { // error illegal URL element detected throw new IllegalArgumentException("Illegal magic login URL element detected: " + value); } } return loginUrlInfo; } } No newline at end of file