Skip to content

Commit

Permalink
Relax URL validation (closes #16), do validation before closing prefe…
Browse files Browse the repository at this point in the history
…rence dialog
  • Loading branch information
bfabiszewski committed May 11, 2017
1 parent 10b44ab commit d4f3a47
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 73 deletions.
25 changes: 0 additions & 25 deletions app/src/main/java/net/fabiszewski/ulogger/SettingsActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.util.Patterns;
import android.widget.Toast;

/**
Expand All @@ -44,32 +43,12 @@ protected void onCreate(Bundle savedInstanceState) {
@SuppressWarnings("deprecation")
private void onCreatePreferenceActivity() {
addPreferencesFromResource(R.xml.preferences);
final Preference prefHost = findPreference("prefHost");
if (prefHost != null) {
prefHost.setOnPreferenceChangeListener(hostChanged);
}
final Preference prefLiveSync = findPreference("prefLiveSync");
if (prefLiveSync != null) {
prefLiveSync.setOnPreferenceChangeListener(liveSyncChanged);
}
}

/**
* On change listener to validate server url
*/
private final static Preference.OnPreferenceChangeListener hostChanged = new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (Patterns.WEB_URL.matcher(newValue.toString().trim()).matches()) {
return true;
} else {
Toast.makeText(preference.getContext(), R.string.provide_valid_url, Toast.LENGTH_LONG).show();
return false;
}
}

};

/**
* On change listener to validate whether live synchronization is allowed
*/
Expand Down Expand Up @@ -114,10 +93,6 @@ public static class MyPreferenceFragment extends PreferenceFragment {
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
final Preference prefHost = findPreference("prefHost");
if (prefHost != null) {
prefHost.setOnPreferenceChangeListener(hostChanged);
}
final Preference prefLiveSync = findPreference("prefLiveSync");
if (prefLiveSync != null) {
prefLiveSync.setOnPreferenceChangeListener(liveSyncChanged);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2017 Bartek Fabiszewski
* http://www.fabiszewski.net
*
* This file is part of μlogger-android.
* Licensed under GPL, either version 3, or any later.
* See <http://www.gnu.org/licenses/>
*/

package net.fabiszewski.ulogger;

import android.content.Context;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.support.annotation.RequiresApi;
import android.app.AlertDialog;
import android.util.AttributeSet;
import android.view.View;

/**
* URL edit text preference
* Validates and trims URL
*/

class UrlEditTextPreference extends EditTextPreference {

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public UrlEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}

public UrlEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

public UrlEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}

public UrlEditTextPreference(Context context) {
super(context);
}

@Override
public void setText(String text) {
super.setText(text.trim());
}

@Override
protected void showDialog(Bundle state) {
super.showDialog(state);
getEditText().setError(null);
final AlertDialog dialog = (AlertDialog) getDialog();
View positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onPositiveButtonClicked();
}
});
}

private void onPositiveButtonClicked() {
if (WebHelper.isValidURL(getEditText().getText().toString().trim())) {
getEditText().setError(null);
onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
getDialog().dismiss();
} else {
getEditText().setError(getContext().getString(R.string.provide_valid_url));
}
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/net/fabiszewski/ulogger/WebHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,14 @@ void authorize() throws IOException, WebAuthException, JSONException {
}
}

/**
* Check whether given url is valid.
* Uses relaxed pattern (@see WebPatterns#WEB_URL_RELAXED)
* @param url URL
* @return True if valid, false otherwise
*/
static boolean isValidURL(String url) {
return WebPatterns.WEB_URL_RELAXED.matcher(url).matches();
}

}
96 changes: 96 additions & 0 deletions app/src/main/java/net/fabiszewski/ulogger/WebPatterns.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2017 Bartek Fabiszewski
* http://www.fabiszewski.net
*
* This file is part of μlogger-android.
* Licensed under GPL, either version 3, or any later.
* See <http://www.gnu.org/licenses/>
*/

package net.fabiszewski.ulogger;

import java.util.regex.Pattern;

/**
* This is based on java.android.util.Patterns
* with WEB_URL pattern relaxed
*/

class WebPatterns {

/**
* Protocols limited to http(s).
*/
private static final String PROTOCOL = "(?i:https?)://";

private static final String USER_INFO = "(?:[-a-zA-Z0-9$_.+!*'(),;?&=]|(?:%[a-fA-F0-9]{2})){1,64}"
+ "(?::(?:[-a-zA-Z0-9$_.+!*'(),;?&=]|(?:%[a-fA-F0-9]{2})){1,25})?@";

/**
* Valid UCS characters defined in RFC 3987. Excludes space characters.
*/
private static final String UCS_CHAR = "[" +
"\u00A0-\uD7FF" +
"\uF900-\uFDCF" +
"\uFDF0-\uFFEF" +
"\uD800\uDC00-\uD83F\uDFFD" +
"\uD840\uDC00-\uD87F\uDFFD" +
"\uD880\uDC00-\uD8BF\uDFFD" +
"\uD8C0\uDC00-\uD8FF\uDFFD" +
"\uD900\uDC00-\uD93F\uDFFD" +
"\uD940\uDC00-\uD97F\uDFFD" +
"\uD980\uDC00-\uD9BF\uDFFD" +
"\uD9C0\uDC00-\uD9FF\uDFFD" +
"\uDA00\uDC00-\uDA3F\uDFFD" +
"\uDA40\uDC00-\uDA7F\uDFFD" +
"\uDA80\uDC00-\uDABF\uDFFD" +
"\uDAC0\uDC00-\uDAFF\uDFFD" +
"\uDB00\uDC00-\uDB3F\uDFFD" +
"\uDB44\uDC00-\uDB7F\uDFFD" +
"&&[^\u00A0[\u2000-\u200A]\u2028\u2029\u202F\u3000]]";

/**
* Valid characters for IRI label defined in RFC 3987.
*/
private static final String LABEL_CHAR = "a-zA-Z0-9" + UCS_CHAR;

/**
* RFC 1035 Section 2.3.4 limits the labels to a maximum 63 octets.
*/
private static final String IRI_LABEL = "[" + LABEL_CHAR + "]"
+ "(?:[" + LABEL_CHAR + "\\-]{0,61}[" + LABEL_CHAR + "])?";

/**
* Regular expression that matches domain names without a TLD, also IP addresses
*/
private static final String RELAXED_DOMAIN_NAME =
"(?:(?:" + IRI_LABEL + "(?:\\.(?=[" + LABEL_CHAR + "]))?)+)";

private static final String PORT_NUMBER = ":\\d{1,5}";

/**
* A word boundary or end of input. This is to stop foo.sure from matching as foo.su
*/
private static final String WORD_BOUNDARY = "(?:\\b|$|^)";

/**
* Path segment, exclude repeated slashes to rule out common error (http//example.com)
*/
private static final String PATH_SEGMENT =
"/(?:(?:[" + LABEL_CHAR + ";:@&=~\\-.+!*'(),_])|(?:%[a-fA-F0-9]{2})|" + WORD_BOUNDARY + ")+";

/**
* Regular expression pattern to match most part of RFC 3987
* Internationalized URLs, aka IRIs.
* Relaxed to accept domains without a TLD.
* Will not accept query part.
* Only http and https protocols.
*/
static final Pattern WEB_URL_RELAXED = Pattern.compile(
"(?:" + PROTOCOL + "(?:" + USER_INFO + ")?" + ")?"
+ RELAXED_DOMAIN_NAME
+ "(?:" + PORT_NUMBER + ")?"
+ "(?:" + PATH_SEGMENT + ")*"
+ WORD_BOUNDARY);

}
4 changes: 2 additions & 2 deletions app/src/main/res/xml/preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
-->

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<net.fabiszewski.ulogger.TrimmedEditTextPreference
<net.fabiszewski.ulogger.UrlEditTextPreference
android:key="prefUsername"
android:title="@string/pref_username_title"
android:summary="@string/pref_username_summary"
Expand All @@ -19,7 +19,7 @@
android:title="@string/pref_pass_title"
android:summary="@string/pref_pass_summary"
android:inputType="textPassword" />
<net.fabiszewski.ulogger.TrimmedEditTextPreference
<net.fabiszewski.ulogger.UrlEditTextPreference
android:key="prefHost"
android:title="@string/pref_host_title"
android:summary="@string/pref_host_summary"
Expand Down

0 comments on commit d4f3a47

Please sign in to comment.