initial support for preference based proxy settings
and filtering to stop HTML5 media leakage
This commit is contained in:
parent
81a04cac1f
commit
f70d889c03
@ -449,15 +449,20 @@ public class BrowserActivity extends Activity implements BrowserController {
|
||||
getDir("icons", MODE_PRIVATE).getPath());
|
||||
}
|
||||
|
||||
|
||||
checkForTor ();
|
||||
boolean useProxy = mPreferences.getBoolean(PreferenceConstants.USE_PROXY, false);
|
||||
|
||||
if (useProxy)
|
||||
initializeTor();
|
||||
else
|
||||
checkForTor();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* If Orbot/Tor is installed, prompt the user if they want to enable proxying for this session
|
||||
*/
|
||||
public void checkForTor ()
|
||||
public boolean checkForTor ()
|
||||
{
|
||||
|
||||
OrbotHelper oh = new OrbotHelper(this);
|
||||
@ -468,11 +473,16 @@ public class BrowserActivity extends Activity implements BrowserController {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which){
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
|
||||
mPreferences.edit().putBoolean(PreferenceConstants.USE_PROXY, true).apply();
|
||||
|
||||
initializeTor ();
|
||||
|
||||
break;
|
||||
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
//No button clicked
|
||||
|
||||
mPreferences.edit().putBoolean(PreferenceConstants.USE_PROXY, false).apply();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -481,7 +491,11 @@ public class BrowserActivity extends Activity implements BrowserController {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(R.string.use_tor_prompt).setPositiveButton(android.R.string.yes, dialogClickListener)
|
||||
.setNegativeButton(android.R.string.no, dialogClickListener).show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -496,7 +510,9 @@ public class BrowserActivity extends Activity implements BrowserController {
|
||||
|
||||
WebkitProxy wkp = new WebkitProxy();
|
||||
try {
|
||||
wkp.setProxy("acr.browser.lightning.BrowserApp", getApplicationContext(), "localhost",8118);
|
||||
String host = mPreferences.getString(PreferenceConstants.USE_PROXY_HOST, "localhost");
|
||||
int port = mPreferences.getInt(PreferenceConstants.USE_PROXY_PORT, 8118);
|
||||
wkp.setProxy("acr.browser.lightning.BrowserApp", getApplicationContext(), host, port);
|
||||
} catch (Exception e) {
|
||||
Log.d("Lightning","error enabling web proxying",e);
|
||||
}
|
||||
@ -1000,7 +1016,7 @@ public class BrowserActivity extends Activity implements BrowserController {
|
||||
|
||||
private synchronized void newTab(String url, boolean show) {
|
||||
mIsNewIntent = false;
|
||||
LightningView startingTab = new LightningView(mActivity, url);
|
||||
LightningView startingTab = new LightningView(mActivity, url, mCookieManager);
|
||||
if (mIdGenerator == 0) {
|
||||
startingTab.resumeTimers();
|
||||
}
|
||||
|
@ -908,7 +908,7 @@ public class IncognitoActivity extends Activity implements BrowserController {
|
||||
|
||||
private synchronized void newTab(String url, boolean show) {
|
||||
mIsNewIntent = false;
|
||||
LightningView startingTab = new LightningView(mActivity, url);
|
||||
LightningView startingTab = new LightningView(mActivity, url, mCookieManager);
|
||||
if (mIdGenerator == 0) {
|
||||
startingTab.resumeTimers();
|
||||
}
|
||||
|
@ -4,11 +4,19 @@
|
||||
|
||||
package acr.browser.lightning;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.http.util.ByteArrayBuffer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
@ -28,10 +36,11 @@ import android.text.TextUtils;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.GeolocationPermissions;
|
||||
import android.webkit.HttpAuthHandler;
|
||||
import android.webkit.SslErrorHandler;
|
||||
@ -44,7 +53,9 @@ import android.webkit.WebSettings.PluginState;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.VideoView;
|
||||
|
||||
public class LightningView {
|
||||
|
||||
@ -62,9 +73,12 @@ public class LightningView {
|
||||
private static SharedPreferences mPreferences;
|
||||
private static boolean mWideViewPort;
|
||||
private static AdBlock mAdBlock;
|
||||
|
||||
public LightningView(Activity activity, String url) {
|
||||
private CookieManager mCookieManager;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public LightningView(Activity activity, String url, CookieManager cookieManager) {
|
||||
mActivity = activity;
|
||||
mCookieManager = cookieManager;
|
||||
mWebView = new WebView(activity);
|
||||
mTitle = new Title(activity);
|
||||
mAdBlock = new AdBlock(activity);
|
||||
@ -249,6 +263,7 @@ public class LightningView {
|
||||
return Constants.FILE + homepage;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public synchronized void initializePreferences(Context context) {
|
||||
mPreferences = context.getSharedPreferences(
|
||||
PreferenceConstants.PREFERENCES, 0);
|
||||
@ -348,7 +363,7 @@ public class LightningView {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@SuppressLint({ "SetJavaScriptEnabled", "NewApi" })
|
||||
public void initializeSettings(WebSettings settings, Context context) {
|
||||
if (API < 18) {
|
||||
settings.setAppCacheMaxSize(Long.MAX_VALUE);
|
||||
@ -357,7 +372,7 @@ public class LightningView {
|
||||
settings.setEnableSmoothTransition(true);
|
||||
}
|
||||
if (API > 16) {
|
||||
settings.setMediaPlaybackRequiresUserGesture(true);
|
||||
settings.setMediaPlaybackRequiresUserGesture(true);
|
||||
}
|
||||
if (API < 19) {
|
||||
settings.setDatabasePath(context.getFilesDir().getAbsolutePath()
|
||||
@ -450,6 +465,7 @@ public class LightningView {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public synchronized void find(String text) {
|
||||
if (mWebView != null) {
|
||||
if (API > 16) {
|
||||
@ -536,6 +552,7 @@ public class LightningView {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public class LightningWebClient extends WebViewClient {
|
||||
|
||||
Context mActivity;
|
||||
@ -548,14 +565,121 @@ public class LightningView {
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view,
|
||||
String url) {
|
||||
if (mAdBlock.isAd(url)) {
|
||||
Log.i("Blocked Domain:", url);
|
||||
ByteArrayInputStream EMPTY = new ByteArrayInputStream(
|
||||
"".getBytes());
|
||||
WebResourceResponse response = new WebResourceResponse(
|
||||
"text/plain", "utf-8", EMPTY);
|
||||
return response;
|
||||
}
|
||||
return super.shouldInterceptRequest(view, url);
|
||||
|
||||
boolean useProxy = mPreferences.getBoolean(PreferenceConstants.USE_PROXY, false);
|
||||
boolean mDoLeakHardening = false;
|
||||
|
||||
if (!useProxy)
|
||||
return null;
|
||||
|
||||
if (!mDoLeakHardening)
|
||||
return null;
|
||||
|
||||
//now we are going to proxy!
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
URL uURl = new URL(url);
|
||||
|
||||
Proxy proxy = null;
|
||||
|
||||
String host = mPreferences.getString(PreferenceConstants.USE_PROXY_HOST, "localhost");
|
||||
int port = mPreferences.getInt(PreferenceConstants.USE_PROXY_PORT, 8118);
|
||||
proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));
|
||||
|
||||
HttpURLConnection.setFollowRedirects(true);
|
||||
HttpURLConnection conn = (HttpURLConnection)uURl.openConnection(proxy);
|
||||
conn.setInstanceFollowRedirects(true);
|
||||
conn.setRequestProperty("User-Agent", mSettings.getUserAgentString());
|
||||
|
||||
//conn.setRequestProperty("Transfer-Encoding", "chunked");
|
||||
//conn.setUseCaches(false);
|
||||
|
||||
final int bufferSize = 1024 * 32;
|
||||
conn.setChunkedStreamingMode(bufferSize);
|
||||
|
||||
String cType = conn.getContentType();
|
||||
String cEnc = conn.getContentEncoding();
|
||||
int connLen = conn.getContentLength();
|
||||
|
||||
|
||||
if (cType != null)
|
||||
{
|
||||
String[] ctArray = cType.split(";");
|
||||
cType = ctArray[0].trim();
|
||||
|
||||
if (cEnc == null && ctArray.length > 1)
|
||||
{
|
||||
cEnc = ctArray[1];
|
||||
if (cEnc.indexOf('=')!=-1)
|
||||
cEnc = cEnc.split("=")[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (connLen <= 0)
|
||||
connLen = 2048;
|
||||
|
||||
if (cType != null && cType.startsWith("text"))
|
||||
{
|
||||
InputStream fStream = null;
|
||||
|
||||
BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
|
||||
ByteArrayBuffer baf = new ByteArrayBuffer(connLen);
|
||||
int read = 0;
|
||||
int bufSize = 2048;
|
||||
byte[] buffer = new byte[bufSize];
|
||||
while(true){
|
||||
read = bis.read(buffer);
|
||||
if(read==-1){
|
||||
break;
|
||||
}
|
||||
baf.append(buffer, 0, read);
|
||||
}
|
||||
byte[] plainText = baf.toByteArray();
|
||||
|
||||
fStream = new ByteArrayInputStream(plainText);
|
||||
|
||||
fStream = new ReplacingInputStream(new ByteArrayInputStream(plainText),"poster=".getBytes(),"foo=".getBytes());
|
||||
fStream = new ReplacingInputStream(fStream,"Poster=".getBytes(),"foo=".getBytes());
|
||||
fStream = new ReplacingInputStream(fStream,"Poster=".getBytes(),"foo=".getBytes());
|
||||
fStream = new ReplacingInputStream(fStream,".poster".getBytes(),".foo".getBytes());
|
||||
fStream = new ReplacingInputStream(fStream,"\"poster\"".getBytes(),"\"foo\"".getBytes());
|
||||
|
||||
WebResourceResponse response = new WebResourceResponse(
|
||||
cType, cEnc, fStream);
|
||||
|
||||
return response;
|
||||
}/**
|
||||
else if (mDoLeakHardening)
|
||||
{
|
||||
WebResourceResponse response = new WebResourceResponse(
|
||||
cType, cEnc, conn.getInputStream());
|
||||
|
||||
return response;
|
||||
|
||||
}*/
|
||||
else
|
||||
{
|
||||
return null; //let webkit handle it
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("Lightning","Error filtering stream",e);
|
||||
ByteArrayInputStream EMPTY = new ByteArrayInputStream(
|
||||
"".getBytes());
|
||||
WebResourceResponse response = new WebResourceResponse(
|
||||
"text/plain", "utf-8", EMPTY);
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -871,9 +995,24 @@ public class LightningView {
|
||||
|
||||
@Override
|
||||
public void onShowCustomView(View view, CustomViewCallback callback) {
|
||||
Activity activity = mBrowserController.getActivity();
|
||||
mBrowserController.onShowCustomView(view,
|
||||
activity.getRequestedOrientation(), callback);
|
||||
|
||||
if (view instanceof FrameLayout){
|
||||
FrameLayout frame = (FrameLayout) view;
|
||||
if (frame.getFocusedChild() instanceof VideoView){
|
||||
VideoView video = (VideoView) frame.getFocusedChild();
|
||||
video.stopPlayback();
|
||||
frame.removeView(video);
|
||||
video.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Activity activity = mBrowserController.getActivity();
|
||||
mBrowserController.onShowCustomView(view,
|
||||
activity.getRequestedOrientation(), callback);
|
||||
|
||||
}
|
||||
|
||||
super.onShowCustomView(view, callback);
|
||||
}
|
||||
|
||||
@ -881,8 +1020,23 @@ public class LightningView {
|
||||
@Deprecated
|
||||
public void onShowCustomView(View view, int requestedOrientation,
|
||||
CustomViewCallback callback) {
|
||||
mBrowserController.onShowCustomView(view, requestedOrientation,
|
||||
callback);
|
||||
|
||||
if (view instanceof FrameLayout){
|
||||
FrameLayout frame = (FrameLayout) view;
|
||||
if (frame.getFocusedChild() instanceof VideoView){
|
||||
VideoView video = (VideoView) frame.getFocusedChild();
|
||||
video.stopPlayback();
|
||||
frame.removeView(video);
|
||||
video.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mBrowserController.onShowCustomView(view, requestedOrientation,
|
||||
callback);
|
||||
|
||||
}
|
||||
|
||||
super.onShowCustomView(view, requestedOrientation, callback);
|
||||
}
|
||||
|
||||
|
@ -31,4 +31,8 @@ public class PreferenceConstants {
|
||||
public static final String USER_AGENT = "agentchoose";
|
||||
public static final String USER_AGENT_STRING = "userAgentString";
|
||||
public static final String GOOGLE_SEARCH_SUGGESTIONS = "GoogleSearchSuggestions";
|
||||
|
||||
public static final String USE_PROXY = "useProxy";
|
||||
public static final String USE_PROXY_HOST = "useProxyHost";
|
||||
public static final String USE_PROXY_PORT = "useProxyPort";
|
||||
}
|
||||
|
91
src/acr/browser/lightning/ReplacingInputStream.java
Normal file
91
src/acr/browser/lightning/ReplacingInputStream.java
Normal file
@ -0,0 +1,91 @@
|
||||
package acr.browser.lightning;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class ReplacingInputStream extends FilterInputStream {
|
||||
|
||||
LinkedList<Integer> inQueue = new LinkedList<Integer>();
|
||||
LinkedList<Integer> outQueue = new LinkedList<Integer>();
|
||||
final byte[] search, replacement;
|
||||
|
||||
protected ReplacingInputStream(InputStream in, byte[] search,
|
||||
byte[] replacement) {
|
||||
super(in);
|
||||
this.search = search;
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
private boolean isMatchFound() {
|
||||
Iterator<Integer> inIter = inQueue.iterator();
|
||||
for (int i = 0; i < search.length; i++)
|
||||
if (!inIter.hasNext() || search[i] != inIter.next())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void readAhead() throws IOException {
|
||||
// Work up some look-ahead.
|
||||
while (inQueue.size() < search.length) {
|
||||
int next = super.read();
|
||||
inQueue.offer(next);
|
||||
if (next == -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
|
||||
// Next byte already determined.
|
||||
if (outQueue.isEmpty()) {
|
||||
|
||||
readAhead();
|
||||
|
||||
if (isMatchFound()) {
|
||||
for (int i = 0; i < search.length; i++)
|
||||
inQueue.remove();
|
||||
|
||||
for (byte b : replacement)
|
||||
outQueue.offer((int) b);
|
||||
} else
|
||||
outQueue.add(inQueue.remove());
|
||||
}
|
||||
|
||||
return outQueue.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false. REFilterInputStream does not support mark() and
|
||||
* reset() methods.
|
||||
*/
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Reads from the stream into the provided array.
|
||||
* @throws IOException */
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
int i;
|
||||
int ok = 0;
|
||||
while (len-- > 0) {
|
||||
i = read();
|
||||
if (i == -1) return (ok == 0) ? -1 : ok;
|
||||
b[off++] = (byte) i;
|
||||
ok++;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer) throws IOException {
|
||||
|
||||
return read(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user