Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
Add user agent override list support. (fixes #488) (#777)
Browse files Browse the repository at this point in the history
  • Loading branch information
bluemarvin authored Nov 20, 2018
1 parent 76881bd commit 5488d24
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package org.mozilla.vrbrowser.browser;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Rect;
Expand Down Expand Up @@ -76,6 +77,7 @@ public static SessionStore get() {
private LinkedList<SessionChangeListener> mSessionChangeListeners;
private LinkedList<GeckoSession.TextInputDelegate> mTextInputListeners;
private LinkedList<GeckoSession.PromptDelegate> mPromptListeners;
private UserAgentOverride mUserAgentOverride;

public interface SessionChangeListener {
void onNewSession(GeckoSession aSession, int aId);
Expand Down Expand Up @@ -175,6 +177,10 @@ public void setContext(Context aContext, Bundle aExtras) {

mContext = aContext;
mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
if (mUserAgentOverride == null) {
mUserAgentOverride = new UserAgentOverride();
mUserAgentOverride.loadOverridesFromAssets((Activity)aContext, aContext.getString(R.string.user_agent_override_file));
}
}

public void dumpAllState(Integer sessionId) {
Expand Down Expand Up @@ -858,6 +864,7 @@ public void onCanGoForward(GeckoSession aSession, boolean aCanGoForward) {
@Override
public @Nullable GeckoResult<AllowOrDeny> onLoadRequest(@NonNull GeckoSession aSession, @NonNull LoadRequest aRequest) {
final GeckoResult<AllowOrDeny> result = new GeckoResult<>();
aSession.getSettings().setString(GeckoSessionSettings.USER_AGENT_OVERRIDE, mUserAgentOverride.lookupOverride(aRequest.uri));
if (PRIVATE_BROWSING_URI.equalsIgnoreCase(aRequest.uri)) {
switchPrivateMode();
result.complete(AllowOrDeny.ALLOW);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package org.mozilla.vrbrowser.browser;

import android.app.Activity;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class UserAgentOverride {
private final static String LOGTAG = "VRB";
private static final String NO_OVERRIDE_FOUND = "NO OVERRIDE USER AGENT FOUND";
private ArrayMap<String, String> mOverrideMap;
private ArrayMap<String, String> mOverrideCache;
public UserAgentOverride() {
mOverrideMap = new ArrayMap<>();
mOverrideCache = new ArrayMap<>();
}

public void loadOverridesFromAssets(Activity aActivity, String aFileName) {
String json = null;
try (InputStream is = aActivity.getAssets().open(aFileName)) {
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
json = new String(buffer, "UTF-8");
importJSONData(json);
} catch (IOException e) {
Log.e(LOGTAG, "Failed reading user agent override file: " + aFileName + " Error: " + e.getMessage());
}
}

public String lookupOverride(final String aUri) {
Log.d(LOGTAG, "lookupOverride for: " + aUri);
URI uri = URI.create(aUri);
String fullDomain = uri.getHost();
if (fullDomain == null) {
return null;
}

fullDomain = fullDomain.toLowerCase();

String override = mOverrideCache.get(fullDomain);

if (override != null) {
Log.d(LOGTAG, "Found override: " + override);
return override.equals(NO_OVERRIDE_FOUND) ? null : override;
}

List<String> domains = Arrays.asList(fullDomain.split("\\."));
final int domainCount = domains.size();
String[] checkedDomains = new String[domainCount];

for (int ix = 0; ix < domainCount; ix++) {
String domain = TextUtils.join(".", domains.subList(ix, domainCount));
checkedDomains[ix] = domain;
override = mOverrideCache.get(domain);
if (override != null) {
Log.d(LOGTAG, "found cached override: " + override);
addToCache(checkedDomains, override);
return override.equals(NO_OVERRIDE_FOUND) ? null : override;
}
String domainHash = hashDomain(domain);
if (domainHash == null) {
Log.d(LOGTAG, "Failed to hash domain: " + domain);
return null;
}
Log.d(LOGTAG, "hash: " + domainHash);
override = mOverrideMap.get(domainHash);
if (override != null) {
Log.d(LOGTAG, "found override from hash: " + override);
addToCache(checkedDomains, override);
return override;
}
}
addToCache(checkedDomains, NO_OVERRIDE_FOUND);
return null;
}

private void addToCache(String[] aDomains, String aOverride) {
for (String domain: aDomains) {
if (domain == null) {
Log.d(LOGTAG, "Found null domain in checked list");
continue;
} else {
Log.d(LOGTAG, domain + " override: " + aOverride);
}
mOverrideCache.put(domain, aOverride);
}
}

private String hashDomain(String aDomain) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] digest = md.digest(aDomain.getBytes());
StringBuilder sb = new StringBuilder();
for (int value: digest) {
sb.append(Integer.toString((value & 0xff) + 0x100, 16).substring(1));
}

return sb.toString();
} catch (NoSuchAlgorithmException e) {
Log.e(LOGTAG, "Error while trying to hash domain: " + e.getMessage());
}
return null;
}

private void importJSONData(final String aData) {
try {
JSONObject json = new JSONObject(aData);
Iterator<String> iter = json.keys();
while (iter.hasNext()) {
String key = iter.next();
try {
String value = json.getString(key);
Log.d(LOGTAG, "User agent override: " + key + " -> " + value);
mOverrideMap.put(key, value);
} catch (JSONException e) {
Log.e(LOGTAG, "Failed to find UA Override while parsing file for key: " + key);
}
}

} catch (JSONException e) {
Log.e(LOGTAG, "Failed to import user agent override JSON data: " + e.getMessage());
}
}
}
5 changes: 5 additions & 0 deletions app/src/main/assets/userAgentOverride.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ae0755740e4354ac67025056e775ad06d8a529ae4f37244fbb02d72199e2c780311e47aa9895079b980ec4bfa676f1f39c4ab41ea995c524e52bde9a73623da2": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12) AppleWebKit/602.1.21 (KHTML, like Gecko) Version/9.2 Safari/602.1.21",
"e6137b4c2f49a3917c2c90a50fb270a5eebb962f2c72344ae2e29e321bb21891e5ca4fec06cae78e14f4a8510473e934234e9ec3f60e8415f5f6da754c55b9b1": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12) AppleWebKit/602.1.21 (KHTML, like Gecko) Version/9.2 Safari/602.1.21",
"a5593b10e239d568ecb4a1f5f9980cc00da2d6b14ef6696760481eeda8933811a96a7a5ecc5d8eebc40c427ae9aeda9025925db07217e375875f0fb1297216a5": "Mozilla/5.0 (X11; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0 SamsungBrowser"
}
1 change: 1 addition & 0 deletions app/src/main/res/values/non_L10n.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<string name="app_permission_name" translatable="false">org.mozilla.vrbrowser.CRASH_RECEIVER_PERMISSION</string>
<string name="developer_options_by" translatable="false">x</string>
<string name="crash_app_name" translatable="false">FirefoxReality</string>
<string name="user_agent_override_file" translatable="false">userAgentOverride.json</string>

<!-- SEARCH ENGINES -->
<string name="geolocation_api_url" translatable="false">https://location.services.mozilla.com/v1/country</string>
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ buildscript {
ext.geckoNightly = [
// GeckoView versions can be found here:
// https://maven.mozilla.org/?prefix=maven2/org/mozilla/geckoview/
version: '65.0.20181110100141'
version: '65.0.20181119100115'
]

// Android components version
Expand Down

0 comments on commit 5488d24

Please sign in to comment.