bug fixes
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'maven'
|
||||
|
||||
ext {
|
||||
|
@ -44,25 +41,15 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
implementation 'com.google.android.gms:play-services-ads:17.1.1'
|
||||
implementation "cz.msebera.android:httpclient:4.4.1.2"
|
||||
implementation 'info.guardianproject.netcipher:netcipher:2.0.0-alpha1'
|
||||
implementation 'info.guardianproject.netcipher:netcipher-okhttp3:2.0.0-alpha1'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.4.2'
|
||||
implementation 'org.apache.httpcomponents:httpcore:4.4.1'
|
||||
x86Implementation "org.mozilla.geckoview:geckoview-${geckoviewChannel}-x86:${geckoviewVersion}"
|
||||
x86_64Implementation "org.mozilla.geckoview:geckoview-${geckoviewChannel}-x86_64:${geckoviewVersion}"
|
||||
armImplementation "org.mozilla.geckoview:geckoview-${geckoviewChannel}-armeabi-v7a:${geckoviewVersion}"
|
||||
aarch64Implementation "org.mozilla.geckoview:geckoview-${geckoviewChannel}-arm64-v8a:${geckoviewVersion}"
|
||||
implementation 'com.yarolegovich:lovely-dialog:1.1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<uses-library
|
||||
android:name="org.apache.http.legacy"
|
||||
android:required="false" />
|
||||
<activity android:name=".applicationController"
|
||||
<activity android:name=".application_controller"
|
||||
android:screenOrientation="portrait" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
|
|
@ -1,639 +0,0 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Color;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.constraint.ConstraintLayout;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Patterns;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.*;
|
||||
import android.widget.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Stack;
|
||||
|
||||
import com.yarolegovich.lovelydialog.LovelyInfoDialog;
|
||||
import com.yarolegovich.lovelydialog.LovelyStandardDialog;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
import org.mozilla.gecko.PrefsHelper;
|
||||
import org.mozilla.geckoview.GeckoRuntime;
|
||||
import org.mozilla.geckoview.GeckoSession;
|
||||
import org.mozilla.geckoview.GeckoView;
|
||||
|
||||
import static java.lang.Thread.sleep;
|
||||
|
||||
public class applicationController extends AppCompatActivity
|
||||
{
|
||||
|
||||
/*View Objects*/
|
||||
private WebView webView1;
|
||||
private WebView webView2;
|
||||
private GeckoView webLoader;
|
||||
private ProgressBar progressBar;
|
||||
private ConstraintLayout requestFailure;
|
||||
private ConstraintLayout splashScreen;
|
||||
private Button reloadButton;
|
||||
private ImageButton homeButton;
|
||||
private EditText searchbar;
|
||||
private LinearLayout topbar;
|
||||
private GeckoSession session1;
|
||||
private GeckoRuntime runtime1;
|
||||
|
||||
/*helper Variables*/
|
||||
String currentURL = "http://boogle.store/";
|
||||
boolean isRequestError = false;
|
||||
boolean hasApplicationLoaded = false;
|
||||
Stack urlList = new Stack<String>();
|
||||
boolean wasBackPressed = false;
|
||||
boolean isFirstLaunch = true;
|
||||
|
||||
/*Initialization*/
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.applicationView);
|
||||
initializeProxy();
|
||||
initializeView();
|
||||
initializeAds();
|
||||
}
|
||||
|
||||
public void initializeAds()
|
||||
{
|
||||
|
||||
admanager.getInstance().initialize(this);
|
||||
}
|
||||
|
||||
public void initializeProxy()
|
||||
{
|
||||
PrefsHelper.setPref("network.proxy.type",1); //manual proxy settings
|
||||
PrefsHelper.setPref("network.proxy.socks","127.0.0.1"); //manual proxy settings
|
||||
PrefsHelper.setPref("network.proxy.socks_port",9050); //manual proxy settings
|
||||
PrefsHelper.setPref("network.proxy.socks_version",5); //manual proxy settings
|
||||
PrefsHelper.setPref("network.proxy.socks_remote_dns",true); //manual proxy settings
|
||||
PrefsHelper.setPref("browser.cache.disk.enable",false);
|
||||
PrefsHelper.setPref("browser.cache.memory.enable",true);
|
||||
PrefsHelper.setPref("browser.cache.disk.capacity",0);
|
||||
PrefsHelper.setPref("general.useragent.override", "Mozilla/5.0 (Windows NT 6.1; rv:17.0) Gecko/20100101 Firefox/17.0");
|
||||
PrefsHelper.setPref("privacy.donottrackheader.enabled",false);
|
||||
PrefsHelper.setPref("privacy.donottrackheader.value",1);
|
||||
}
|
||||
|
||||
/*Initialization*/
|
||||
public void initializeView()
|
||||
{
|
||||
/*if (android.os.Build.VERSION.SDK_INT > 9)
|
||||
{
|
||||
StrictMode.ThreadPolicy policy = new
|
||||
StrictMode.ThreadPolicy.Builder().permitAll().build();
|
||||
StrictMode.setThreadPolicy(policy);
|
||||
}*/
|
||||
|
||||
webView1 = (WebView) findViewById(R.id.pageLoader1);
|
||||
webView2 = (WebView) findViewById(R.id.pageLoader2);
|
||||
progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
requestFailure = (ConstraintLayout) findViewById(R.id.requestFailure);
|
||||
splashScreen = (ConstraintLayout) findViewById(R.id.splashScreen);
|
||||
reloadButton = (Button) findViewById(R.id.reloadButton);
|
||||
homeButton = (ImageButton) findViewById(R.id.home);
|
||||
searchbar = (EditText) findViewById(R.id.search);
|
||||
topbar = (LinearLayout) findViewById(R.id.topbar);
|
||||
webRequestHandler.getInstance().initialization(webView1,webView2,progressBar,searchbar,requestFailure,this);
|
||||
webView1.bringToFront();
|
||||
progressBar.animate().alpha(0f);
|
||||
|
||||
Intent orbot = OrbotHelper.getOrbotStartIntent(getApplicationContext());
|
||||
getApplicationContext().registerReceiver(orbotStatusReceiver,new IntentFilter(OrbotHelper.ACTION_STATUS));
|
||||
getApplicationContext().sendBroadcast(orbot);
|
||||
|
||||
session1 = new GeckoSession();
|
||||
webLoader = (GeckoView) findViewById(R.id.webLoader);
|
||||
runtime1 = GeckoRuntime.create(this);
|
||||
session1.open(runtime1);
|
||||
webLoader.setSession(session1);
|
||||
|
||||
webView2.animate().setDuration(0).alpha(0f);
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
|
||||
|
||||
webView1.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
|
||||
webView1.setBackgroundColor(Color.WHITE);
|
||||
webView1.setWebViewClient(loadWebViewClient());
|
||||
webView1.getSettings().setJavaScriptEnabled(true);
|
||||
webView1.getSettings().setUseWideViewPort(true);
|
||||
|
||||
loadURLAnimate("http://boogle.store/");
|
||||
|
||||
webView2.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
|
||||
webView2.setBackgroundColor(Color.WHITE);
|
||||
webView2.setWebViewClient(loadWebViewClient());
|
||||
webView2.getSettings().setJavaScriptEnabled(true);
|
||||
webView2.getSettings().setUseWideViewPort(true);
|
||||
|
||||
requestFailure.animate().setDuration(0).alpha(0.0f);
|
||||
initializeViewClients();
|
||||
progressBar.animate().alpha(0f);
|
||||
|
||||
}
|
||||
|
||||
public void openSession()
|
||||
{
|
||||
session1 = new GeckoSession();
|
||||
webLoader = (GeckoView) findViewById(R.id.webLoader);
|
||||
GeckoRuntime runtime1 = GeckoRuntime.create(this);
|
||||
session1.open(runtime1);
|
||||
webLoader.setSession(session1);
|
||||
}
|
||||
|
||||
private void initializeViewClients()
|
||||
{
|
||||
homeButton.setOnClickListener(new View.OnClickListener(){
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
session1.stop();
|
||||
session1.close();
|
||||
session1.stop();
|
||||
progressBar.animate().alpha(0f);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.animate().setDuration(300).alpha(1f);
|
||||
loadURLAnimate("http://boogle.store/");
|
||||
isRequestError = false;
|
||||
}
|
||||
});
|
||||
|
||||
searchbar.setOnEditorActionListener(new EditText.OnEditorActionListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
|
||||
{
|
||||
try
|
||||
{
|
||||
session1.stop();
|
||||
session1.close();
|
||||
String url = v.getText().toString();
|
||||
if(!url.startsWith("www.")&& !url.startsWith("http://")&& !url.startsWith("https://")){
|
||||
url = "www."+url;
|
||||
}
|
||||
if(!url.startsWith("http://")&&!url.startsWith("https://")){
|
||||
url = "http://"+url;
|
||||
}
|
||||
|
||||
|
||||
boolean isUrlValid = Patterns.WEB_URL.matcher(url).matches();
|
||||
|
||||
URL host = new URL(url);
|
||||
if(isUrlValid && host.getHost().replace("www.","").contains("."))
|
||||
{
|
||||
if(host.getHost().contains("boogle.store")||host.getHost().contains("genesis.store"))
|
||||
{
|
||||
loadURLAnimate(v.getText().toString());
|
||||
return true;
|
||||
}
|
||||
else if(host.getHost().contains(".onion"))
|
||||
{
|
||||
if(!reinitOrbot())
|
||||
{
|
||||
session1.close();
|
||||
session1 = new GeckoSession();
|
||||
session1.open(runtime1);
|
||||
session1.setProgressDelegate(new ExamplesProgressDelegate());
|
||||
webLoader.releaseSession();
|
||||
webLoader.setSession(session1);
|
||||
|
||||
session1.loadUri(url);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
baseURLError();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
loadURLAnimate("http://boogle.store/search?q="+v.getText().toString().replaceAll(" ","+")+"&p_num=1&s_type=all");
|
||||
}
|
||||
reinitOrbot();
|
||||
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
loadURLAnimate("http://boogle.store/search?q="+v.getText().toString().replaceAll(" ","+")+"&p_num=1&s_type=all");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
reloadButton.setOnClickListener(new View.OnClickListener(){
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
progressBar.animate().alpha(0f);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.animate().setDuration(300).alpha(1f);
|
||||
loadURLAnimate(currentURL);
|
||||
isRequestError = false;
|
||||
}
|
||||
});
|
||||
|
||||
session1.setProgressDelegate(new ExamplesProgressDelegate());
|
||||
|
||||
}
|
||||
|
||||
public static String getDomainName(String url) throws URISyntaxException
|
||||
{
|
||||
URI uri = new URI(url);
|
||||
String domain = uri.getHost();
|
||||
return domain.startsWith("www.") ? domain.substring(4) : domain;
|
||||
}
|
||||
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
private void onChromeClientProgressChanged(int newProgress)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void baseURLError()
|
||||
{
|
||||
new LovelyInfoDialog(this)
|
||||
.setTopColorRes(R.color.header)
|
||||
.setIcon(R.drawable.logo)
|
||||
.setTitle("Surface Web URL Not Allowed")
|
||||
.setMessage("This software can only be used to search hidden web such as \"Onion\" and \"I2P\" for searching in Surface Web use \"Google\" or \"Bing\"")
|
||||
.show();
|
||||
}
|
||||
|
||||
public void startingOrbotInfo()
|
||||
{
|
||||
new LovelyInfoDialog(this)
|
||||
.setTopColorRes(R.color.header)
|
||||
.setIcon(R.drawable.logo)
|
||||
.setTitle("Orbot is Starting")
|
||||
.setMessage("Looks Like Orbot is Installed but not Running. Please wait while we Start Orbot for you")
|
||||
.show();
|
||||
}
|
||||
|
||||
public boolean reinitOrbot()
|
||||
{
|
||||
if(!OrbotHelper.isOrbotInstalled(this))
|
||||
{
|
||||
OrbotHelper.getOrbotInstallIntent(this);
|
||||
new LovelyStandardDialog(this)
|
||||
.setTopColorRes(R.color.header)
|
||||
.setIcon(R.drawable.logo)
|
||||
.setTitle("Orbot Proxy Not Installed")
|
||||
.setMessage("Hidden Web can only be access by Special Proxies. Please Install Orbot Proxy from Playstore")
|
||||
.setPositiveButton(android.R.string.ok, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final String appPackageName = "org.torproject.android"; // getPackageName() from Context or Activity object
|
||||
try {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName)));
|
||||
} catch (android.content.ActivityNotFoundException anfe) {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName)));
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
if(!isOrbotRunning)
|
||||
{
|
||||
OrbotHelper.get(this).init();
|
||||
startingOrbotInfo();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed()
|
||||
{
|
||||
reinitOrbot();
|
||||
if(urlList.size()>0)
|
||||
{
|
||||
searchbar.setText(urlList.peek().toString().replaceAll("boogle.store","genesis.onion"));
|
||||
if(urlList.peek().toString().contains("boogle.store"))
|
||||
{
|
||||
if(!currentURL.contains("boogle.store"))
|
||||
{
|
||||
currentURL = urlList.pop().toString();
|
||||
progressBar.animate().alpha(0f);
|
||||
webLoader.animate().setDuration(250).alpha(0);
|
||||
webLoader.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadURLAnimate(urlList.pop().toString());
|
||||
if(urlList.size()<=0)
|
||||
{
|
||||
currentURL = "http://boogle.store/";
|
||||
}
|
||||
else
|
||||
{
|
||||
currentURL = urlList.peek().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wasBackPressed = true;
|
||||
if(urlList.size()<=0 || urlList.peek().toString().contains("boogle.store"))
|
||||
{
|
||||
currentURL = "http://boogle.store/";
|
||||
webLoader.animate().setDuration(250).alpha(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
webLoader.animate().setDuration(250).alpha(1);
|
||||
currentURL = urlList.peek().toString();
|
||||
}
|
||||
urlList.pop();
|
||||
session1.stop();
|
||||
session1.close();
|
||||
session1.stop();
|
||||
session1.goBack();
|
||||
}
|
||||
if(urlList.size()==0)
|
||||
{
|
||||
searchbar.setText("http://genesis.onion/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAnimating = false;
|
||||
private WebViewClient loadWebViewClient()
|
||||
{
|
||||
WebViewClient client = new WebViewClient()
|
||||
{
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url)
|
||||
{
|
||||
searchbar.setText(url.replaceAll("boogle.store","genesis.onion"));
|
||||
|
||||
if(!url.toString().contains("boogle"))
|
||||
{
|
||||
//admanager.getInstance().showAd();
|
||||
|
||||
boolean init_status=reinitOrbot();
|
||||
if(!init_status)
|
||||
{
|
||||
session1.close();
|
||||
session1 = new GeckoSession();
|
||||
session1.open(runtime1);
|
||||
session1.setProgressDelegate(new ExamplesProgressDelegate());
|
||||
webLoader.releaseSession();
|
||||
webLoader.setSession(session1);
|
||||
|
||||
session1.loadUri(url);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(urlList.size()==0 || !currentURL.equals(urlList.peek()))
|
||||
{
|
||||
urlList.add(currentURL);
|
||||
currentURL = url;
|
||||
}
|
||||
|
||||
loadURLAnimate(url);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url)
|
||||
{
|
||||
super.onPageFinished(view, url);
|
||||
|
||||
webView1.animate().setDuration(250).alpha(1f);
|
||||
webView2.animate().setDuration(250).alpha(1f).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
datamodel.getInstance().setIsLoadingURL(false);
|
||||
requestFailure.animate().alpha(0f).setDuration(300).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
requestFailure.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}));;
|
||||
|
||||
}
|
||||
}));;
|
||||
|
||||
progressBar.animate().alpha(0f).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}));;
|
||||
|
||||
if(!hasApplicationLoaded)
|
||||
{
|
||||
try
|
||||
{
|
||||
sleep(2000);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
hasApplicationLoaded = true;
|
||||
splashScreen.animate().alpha(0.0f).setDuration(300).setListener(null).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
splashScreen.setVisibility(View.GONE);
|
||||
}
|
||||
}));
|
||||
}
|
||||
if(!isRequestError)
|
||||
{
|
||||
requestFailure.animate().alpha(0.0f).setDuration(300).setListener(null).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
requestFailure.setVisibility(View.GONE);
|
||||
//reloadButton.setEnabled(false);
|
||||
}
|
||||
}));
|
||||
}
|
||||
isRequestError = false;
|
||||
|
||||
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(searchbar.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
|
||||
{
|
||||
//reloadButton.setEnabled(true);
|
||||
System.out.println("SUP2");
|
||||
requestFailure.setVisibility(View.VISIBLE);
|
||||
requestFailure.animate().alpha(1.0f);
|
||||
isRequestError = true;
|
||||
|
||||
super.onReceivedError(view, errorCode, description, failingUrl);
|
||||
}
|
||||
};
|
||||
|
||||
if(!isNetworkAvailable())
|
||||
{
|
||||
hasApplicationLoaded = true;
|
||||
splashScreen.animate().setStartDelay(2000).alpha(0.0f).setDuration(300).setListener(null).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
splashScreen.setVisibility(View.GONE);
|
||||
}
|
||||
}));
|
||||
|
||||
requestFailure.setVisibility(View.VISIBLE);
|
||||
requestFailure.animate().alpha(1.0f);
|
||||
isRequestError = true;
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public void loadURLAnimate(String url)
|
||||
{
|
||||
if(!isAnimating)
|
||||
{
|
||||
webRequestHandler.getInstance().loadURL(url);
|
||||
}
|
||||
}
|
||||
boolean isPageLoaded = false;
|
||||
class ExamplesProgressDelegate implements GeckoSession.ProgressDelegate
|
||||
{
|
||||
@Override
|
||||
public void onPageStart(GeckoSession session, String url)
|
||||
{
|
||||
try
|
||||
{
|
||||
URL host = new URL(url);
|
||||
if(!host.getHost().contains("onion"))
|
||||
{
|
||||
session1.stop();
|
||||
session1.close();
|
||||
session1.stop();
|
||||
baseURLError();
|
||||
}
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
isPageLoaded = false;
|
||||
datamodel.getInstance().setIsLoadingURL(true);
|
||||
boolean isBlackPage = url.equals("about:blank");
|
||||
if(!isBlackPage &&!wasBackPressed)
|
||||
{
|
||||
urlList.add(currentURL);
|
||||
currentURL = url;
|
||||
}
|
||||
wasBackPressed = false;
|
||||
if(!isBlackPage)
|
||||
{
|
||||
progressBar.setAlpha(0);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.animate().setDuration(300).alpha(1f);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onPageStop(GeckoSession session, boolean success)
|
||||
{
|
||||
progressBar.animate().alpha(0f);
|
||||
datamodel.getInstance().setIsLoadingURL(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChange(GeckoSession session, int progress)
|
||||
{
|
||||
if(progress>=50 && !isPageLoaded)
|
||||
{
|
||||
isPageLoaded = true;
|
||||
webLoader.bringToFront();
|
||||
webLoader.animate().setDuration(100).alpha(1);
|
||||
webLoader.setVisibility(View.VISIBLE);
|
||||
|
||||
requestFailure.animate().alpha(0f).setDuration(300).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
requestFailure.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}));;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNetworkAvailable()
|
||||
{
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
|
||||
if (networkInfo != null && networkInfo.isConnected())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isOrbotRunning = false;
|
||||
private BroadcastReceiver orbotStatusReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (TextUtils.equals(intent.getAction(),
|
||||
OrbotHelper.ACTION_STATUS)) {
|
||||
String status = intent.getStringExtra(OrbotHelper.EXTRA_STATUS);
|
||||
if (status.equals(OrbotHelper.STATUS_ON))
|
||||
{
|
||||
isOrbotRunning = true;
|
||||
}
|
||||
else if (status.equals(OrbotHelper.STATUS_OFF))
|
||||
{
|
||||
isOrbotRunning = false;
|
||||
}
|
||||
else if (status.equals(OrbotHelper.STATUS_STARTING))
|
||||
{
|
||||
}
|
||||
else if (status.equals(OrbotHelper.STATUS_STOPPING))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,481 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Color;
|
||||
import android.support.constraint.ConstraintLayout;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Patterns;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.*;
|
||||
import android.widget.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Stack;
|
||||
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
import org.mozilla.gecko.PrefsHelper;
|
||||
import org.mozilla.geckoview.GeckoRuntime;
|
||||
import org.mozilla.geckoview.GeckoSession;
|
||||
import org.mozilla.geckoview.GeckoView;
|
||||
|
||||
|
||||
import static java.lang.Thread.sleep;
|
||||
|
||||
public class application_controller extends AppCompatActivity
|
||||
{
|
||||
|
||||
/*View Objects*/
|
||||
private WebView webView1;
|
||||
private WebView webView2;
|
||||
private GeckoView webLoader;
|
||||
private ProgressBar progressBar;
|
||||
private ConstraintLayout requestFailure;
|
||||
private ConstraintLayout splashScreen;
|
||||
private Button reloadButton;
|
||||
private ImageButton homeButton;
|
||||
private EditText searchbar;
|
||||
private LinearLayout topbar;
|
||||
private GeckoSession session1;
|
||||
private GeckoRuntime runtime1;
|
||||
private String version_code = "1.0";
|
||||
|
||||
/*helper Variables*/
|
||||
Stack traceUrlList = new Stack<String>();
|
||||
|
||||
/*-------------------------------------------------------INITIALIZATION-------------------------------------------------------*/
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.application_view);
|
||||
initializeProxy();
|
||||
initializeConnections();
|
||||
initializeOrbot();
|
||||
initializeWebViews();
|
||||
initializeView();
|
||||
initializeAds();
|
||||
}
|
||||
|
||||
public void versionChecker()
|
||||
{
|
||||
String version = preference_manager.getInstance().getString("version","none",this);
|
||||
if(!version.equals(version_code) && !version.equals("none"))
|
||||
{
|
||||
message_manager.getInstance().versionWarning(this);
|
||||
}
|
||||
if(version.equals("none"))
|
||||
{
|
||||
webRequestHandler.getInstance().getVersion(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void initializeAds()
|
||||
{
|
||||
admanager.getInstance().initialize(this);
|
||||
}
|
||||
|
||||
public void initializeProxy()
|
||||
{
|
||||
PrefsHelper.setPref("network.proxy.type",1); //manual proxy settings
|
||||
PrefsHelper.setPref("network.proxy.socks","127.0.0.1"); //manual proxy settings
|
||||
PrefsHelper.setPref("network.proxy.socks_port",9050); //manual proxy settings
|
||||
PrefsHelper.setPref("network.proxy.socks_version",5); //manual proxy settings
|
||||
PrefsHelper.setPref("network.proxy.socks_remote_dns",true); //manual proxy settings
|
||||
PrefsHelper.setPref("browser.cache.disk.enable",false);
|
||||
PrefsHelper.setPref("browser.cache.memory.enable",true);
|
||||
PrefsHelper.setPref("browser.cache.disk.capacity",0);
|
||||
PrefsHelper.setPref("general.useragent.override", "Mozilla/5.0 (Windows NT 6.1; rv:17.0) Gecko/20100101 Firefox/17.0");
|
||||
PrefsHelper.setPref("privacy.donottrackheader.enabled",false);
|
||||
PrefsHelper.setPref("privacy.donottrackheader.value",1);
|
||||
}
|
||||
|
||||
public void initializeConnections()
|
||||
{
|
||||
webView1 = findViewById(R.id.pageLoader1);
|
||||
webView2 = findViewById(R.id.pageLoader2);
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
requestFailure = findViewById(R.id.requestFailure);
|
||||
splashScreen = findViewById(R.id.splashScreen);
|
||||
reloadButton = findViewById(R.id.reloadButton);
|
||||
homeButton = findViewById(R.id.home);
|
||||
searchbar = findViewById(R.id.search);
|
||||
topbar = findViewById(R.id.topbar);
|
||||
webLoader = findViewById(R.id.webLoader);
|
||||
}
|
||||
|
||||
public void initializeOrbot()
|
||||
{
|
||||
Intent orbot = OrbotHelper.getOrbotStartIntent(getApplicationContext());
|
||||
getApplicationContext().registerReceiver(orbot_manager.getInstance().orbotStatusReceiver,new IntentFilter(OrbotHelper.ACTION_STATUS));
|
||||
getApplicationContext().sendBroadcast(orbot);
|
||||
}
|
||||
|
||||
public void initializeWebViews()
|
||||
{
|
||||
webRequestHandler.getInstance().initialization(webView1,webView2,progressBar,searchbar,requestFailure,this);
|
||||
webView1.bringToFront();
|
||||
progressBar.animate().alpha(0f);
|
||||
|
||||
session1 = new GeckoSession();
|
||||
runtime1 = GeckoRuntime.create(this);
|
||||
session1.open(runtime1);
|
||||
webLoader.setSession(session1);
|
||||
session1.setProgressDelegate(new progressDelegate());
|
||||
webLoader.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
/*Initialization*/
|
||||
public void initializeView()
|
||||
{
|
||||
webView1.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
|
||||
webView1.setBackgroundColor(Color.WHITE);
|
||||
webView1.setWebViewClient(loadWebViewClient());
|
||||
webView1.getSettings().setJavaScriptEnabled(true);
|
||||
webView1.getSettings().setUseWideViewPort(true);
|
||||
|
||||
webView2.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
|
||||
webView2.setBackgroundColor(Color.WHITE);
|
||||
webView2.setWebViewClient(loadWebViewClient());
|
||||
webView2.getSettings().setJavaScriptEnabled(true);
|
||||
webView2.getSettings().setUseWideViewPort(true);
|
||||
|
||||
webView2.animate().setDuration(0).alpha(0f);
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
requestFailure.animate().setDuration(0).alpha(0.0f);
|
||||
progressBar.animate().alpha(0f);
|
||||
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
|
||||
loadURLAnimate(constants.backendUrl);
|
||||
initializeViewClients();
|
||||
}
|
||||
|
||||
private void initializeViewClients()
|
||||
{
|
||||
searchbar.setOnEditorActionListener((v, actionId, event) -> {
|
||||
return onEditorClicked(v, actionId, event);
|
||||
});
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------WEBVIEW LISTENERS-------------------------------------------------------*/
|
||||
|
||||
private WebViewClient loadWebViewClient()
|
||||
{
|
||||
WebViewClient client = new WebViewClient()
|
||||
{
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url)
|
||||
{
|
||||
searchbar.setText(url.replaceAll("boogle.store","genesis.onion"));
|
||||
|
||||
if(!url.toString().contains("boogle"))
|
||||
{
|
||||
//admanager.getInstance().showAd();
|
||||
|
||||
boolean init_status=orbot_manager.getInstance().reinitOrbot(application_controller.this);
|
||||
if(!init_status)
|
||||
{
|
||||
session1.close();
|
||||
session1 = new GeckoSession();
|
||||
session1.open(runtime1);
|
||||
session1.setProgressDelegate(new progressDelegate());
|
||||
webLoader.releaseSession();
|
||||
webLoader.setSession(session1);
|
||||
|
||||
session1.loadUri(url);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(traceUrlList.size()==0 || !status.currentURL.equals(traceUrlList.peek()))
|
||||
{
|
||||
traceUrlList.add(status.currentURL);
|
||||
status.currentURL = url;
|
||||
}
|
||||
|
||||
loadURLAnimate(url);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url)
|
||||
{
|
||||
super.onPageFinished(view, url);
|
||||
|
||||
webView1.animate().setDuration(250).alpha(1f);
|
||||
webView2.animate().setDuration(250).alpha(1f).withEndAction((() -> {
|
||||
datamodel.getInstance().setIsLoadingURL(false);
|
||||
requestFailure.animate().alpha(0f).setDuration(300).withEndAction((() -> requestFailure.setVisibility(View.INVISIBLE)));;
|
||||
|
||||
}));;
|
||||
|
||||
progressBar.animate().alpha(0f).withEndAction((() -> progressBar.setVisibility(View.INVISIBLE)));;
|
||||
|
||||
if(!status.hasApplicationLoaded)
|
||||
{
|
||||
try
|
||||
{
|
||||
sleep(2000);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
status.hasApplicationLoaded = true;
|
||||
splashScreen.animate().alpha(0.0f).setDuration(300).setListener(null).withEndAction((() -> splashScreen.setVisibility(View.GONE)));
|
||||
versionChecker();
|
||||
}
|
||||
|
||||
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(searchbar.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
|
||||
{
|
||||
System.out.println("SUP2");
|
||||
requestFailure.setVisibility(View.VISIBLE);
|
||||
requestFailure.animate().alpha(1.0f);
|
||||
loadErrorPage();
|
||||
super.onReceivedError(view, errorCode, description, failingUrl);
|
||||
}
|
||||
};
|
||||
|
||||
if(!helperMethod.isNetworkAvailable(this))
|
||||
{
|
||||
status.hasApplicationLoaded = true;
|
||||
splashScreen.animate().setStartDelay(2000).alpha(0.0f).setDuration(300).setListener(null).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
splashScreen.setVisibility(View.GONE);
|
||||
}
|
||||
}));
|
||||
|
||||
requestFailure.setVisibility(View.VISIBLE);
|
||||
requestFailure.animate().alpha(1.0f);
|
||||
loadErrorPage();
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
class progressDelegate implements GeckoSession.ProgressDelegate
|
||||
{
|
||||
@Override
|
||||
public void onPageStart(GeckoSession session, String url)
|
||||
{
|
||||
try
|
||||
{
|
||||
URL host = new URL(url);
|
||||
if(!host.getHost().contains("onion"))
|
||||
{
|
||||
session1.stop();
|
||||
session1.close();
|
||||
session1.stop();
|
||||
message_manager.getInstance().baseURLError(application_controller.this);
|
||||
}
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
datamodel.getInstance().setIsLoadingURL(true);
|
||||
boolean isBlackPage = url.equals("about:blank");
|
||||
if(!isBlackPage)
|
||||
{
|
||||
traceUrlList.add(status.currentURL);
|
||||
status.currentURL = url;
|
||||
}
|
||||
if(!isBlackPage)
|
||||
{
|
||||
progressBar.setAlpha(0);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.animate().setDuration(300).alpha(1f);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onPageStop(GeckoSession session, boolean success)
|
||||
{
|
||||
progressBar.animate().alpha(0f);
|
||||
datamodel.getInstance().setIsLoadingURL(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChange(GeckoSession session, int progress)
|
||||
{
|
||||
if(progress>=50 && webLoader.getVisibility()==View.INVISIBLE)
|
||||
{
|
||||
webLoader.bringToFront();
|
||||
webLoader.animate().setDuration(100).alpha(1);
|
||||
webLoader.setVisibility(View.VISIBLE);
|
||||
|
||||
requestFailure.animate().alpha(0f).setDuration(300).withEndAction((() -> requestFailure.setVisibility(View.INVISIBLE)));;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
/*-------------------------------------------------------Helper Method-------------------------------------------------------*/
|
||||
|
||||
public void loadErrorPage()
|
||||
{
|
||||
requestFailure.animate().alpha(0.0f).setDuration(300).setListener(null).withEndAction((() -> {
|
||||
requestFailure.setVisibility(View.GONE);
|
||||
}));
|
||||
}
|
||||
|
||||
public void loadURLAnimate(String url)
|
||||
{
|
||||
webRequestHandler.getInstance().loadURL(url);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------EVENT LISTENERS-------------------------------------------------------*/
|
||||
|
||||
public void onHomeButtonPressed(View view)
|
||||
{
|
||||
session1.stop();
|
||||
session1.close();
|
||||
session1.stop();
|
||||
progressBar.animate().alpha(0f);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.animate().setDuration(300).alpha(1f);
|
||||
loadURLAnimate("http://boogle.store/");
|
||||
}
|
||||
|
||||
public void onReloadButtonPressed(View view)
|
||||
{
|
||||
progressBar.animate().alpha(0f);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.animate().setDuration(300).alpha(1f);
|
||||
loadURLAnimate(status.currentURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed()
|
||||
{
|
||||
orbot_manager.getInstance().reinitOrbot(this);
|
||||
if(traceUrlList.size()>0)
|
||||
{
|
||||
searchbar.setText(traceUrlList.peek().toString().replaceAll("boogle.store","genesis.onion"));
|
||||
if(traceUrlList.peek().toString().contains("boogle.store"))
|
||||
{
|
||||
if(!status.currentURL.contains("boogle.store"))
|
||||
{
|
||||
status.currentURL = traceUrlList.pop().toString();
|
||||
progressBar.animate().alpha(0f);
|
||||
webLoader.animate().setDuration(250).alpha(0);
|
||||
webLoader.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadURLAnimate(traceUrlList.pop().toString());
|
||||
if(traceUrlList.size()<=0)
|
||||
{
|
||||
status.currentURL = "http://boogle.store/";
|
||||
}
|
||||
else
|
||||
{
|
||||
status.currentURL = traceUrlList.peek().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(traceUrlList.size()<=0 || traceUrlList.peek().toString().contains("boogle.store"))
|
||||
{
|
||||
status.currentURL = "http://boogle.store/";
|
||||
webLoader.animate().setDuration(250).alpha(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
webLoader.animate().setDuration(250).alpha(1);
|
||||
status.currentURL = traceUrlList.peek().toString();
|
||||
}
|
||||
traceUrlList.pop();
|
||||
session1.stop();
|
||||
session1.close();
|
||||
session1.stop();
|
||||
session1.goBack();
|
||||
}
|
||||
if(traceUrlList.size()==0)
|
||||
{
|
||||
searchbar.setText("http://genesis.onion/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onEditorClicked(TextView v, int actionId, KeyEvent event)
|
||||
{
|
||||
try
|
||||
{
|
||||
session1.stop();
|
||||
session1.close();
|
||||
String url = v.getText().toString();
|
||||
if(!url.startsWith("www.")&& !url.startsWith("http://")&& !url.startsWith("https://")){
|
||||
url = "www."+url;
|
||||
}
|
||||
if(!url.startsWith("http://")&&!url.startsWith("https://")){
|
||||
url = "http://"+url;
|
||||
}
|
||||
|
||||
|
||||
boolean isUrlValid = Patterns.WEB_URL.matcher(url).matches();
|
||||
|
||||
URL host = new URL(url);
|
||||
if(isUrlValid && host.getHost().replace("www.","").contains("."))
|
||||
{
|
||||
if(host.getHost().contains(constants.backendUrlHost)||host.getHost().contains(constants.frontEndUrlHost))
|
||||
{
|
||||
loadURLAnimate(v.getText().toString());
|
||||
return true;
|
||||
}
|
||||
else if(host.getHost().contains(constants.allowedHost))
|
||||
{
|
||||
if(!orbot_manager.getInstance().reinitOrbot(this.getApplicationContext()))
|
||||
{
|
||||
session1.close();
|
||||
session1 = new GeckoSession();
|
||||
session1.open(runtime1);
|
||||
session1.setProgressDelegate(new application_controller.progressDelegate());
|
||||
webLoader.releaseSession();
|
||||
webLoader.setSession(session1);
|
||||
|
||||
session1.loadUri(url);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
message_manager.getInstance().baseURLError(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
loadURLAnimate("http://boogle.store/search?q="+v.getText().toString().replaceAll(" ","+")+"&p_num=1&s_type=all");
|
||||
}
|
||||
orbot_manager.getInstance().reinitOrbot(this.getApplicationContext());
|
||||
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
loadURLAnimate("http://boogle.store/search?q="+v.getText().toString().replaceAll(" ","+")+"&p_num=1&s_type=all");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
public class constants
|
||||
{
|
||||
public static String backendUrl = "http://boogle.store";
|
||||
public static String backendUrlHost = "boogle.store";
|
||||
public static String frontEndUrlHost = "genesis.store";
|
||||
public static String allowedHost = ".onion";
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
|
||||
public class helperMethod
|
||||
{
|
||||
public static boolean isNetworkAvailable(Context application_context)
|
||||
{
|
||||
ConnectivityManager cm = (ConnectivityManager) application_context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
|
||||
if (networkInfo != null && networkInfo.isConnected())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.view.View;
|
||||
import com.yarolegovich.lovelydialog.LovelyInfoDialog;
|
||||
import com.yarolegovich.lovelydialog.LovelyStandardDialog;
|
||||
|
||||
public class message_manager {
|
||||
private static final message_manager ourInstance = new message_manager();
|
||||
|
||||
public static message_manager getInstance() {
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
private message_manager() {
|
||||
}
|
||||
|
||||
public void baseURLError(Context application_context)
|
||||
{
|
||||
new LovelyInfoDialog(application_context)
|
||||
.setTopColorRes(R.color.header)
|
||||
.setIcon(R.drawable.logo)
|
||||
.setTitle("Surface Web URL Not Allowed")
|
||||
.setMessage("This software can only be used to search hidden web such as \"Onion\" and \"I2P\" for searching in Surface Web use \"Google\" or \"Bing\"")
|
||||
.show();
|
||||
}
|
||||
|
||||
public void startingOrbotInfo(Context application_context)
|
||||
{
|
||||
new LovelyInfoDialog(application_context)
|
||||
.setTopColorRes(R.color.header)
|
||||
.setIcon(R.drawable.logo)
|
||||
.setTitle("Orbot is Starting")
|
||||
.setMessage("Looks Like Orbot is Installed but not Running. Please wait while we Start Orbot for you")
|
||||
.show();
|
||||
}
|
||||
|
||||
public void versionWarning(Context application_context)
|
||||
{
|
||||
new LovelyStandardDialog(application_context)
|
||||
.setTopColorRes(R.color.header)
|
||||
.setIcon(R.drawable.logo)
|
||||
.setTitle("Update Application")
|
||||
.setMessage("A newer version is availabe please install to get better experience")
|
||||
.setPositiveButton(android.R.string.ok, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
String url = "http://boogle.store/android";
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(url));
|
||||
application_context.startActivity(i);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import com.yarolegovich.lovelydialog.LovelyStandardDialog;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
|
||||
public class orbot_manager {
|
||||
private static final orbot_manager ourInstance = new orbot_manager();
|
||||
boolean isOrbotRunning = false;
|
||||
|
||||
public static orbot_manager getInstance() {
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
private orbot_manager() {
|
||||
}
|
||||
|
||||
public BroadcastReceiver orbotStatusReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (TextUtils.equals(intent.getAction(),
|
||||
OrbotHelper.ACTION_STATUS)) {
|
||||
String status = intent.getStringExtra(OrbotHelper.EXTRA_STATUS);
|
||||
if (status.equals(OrbotHelper.STATUS_ON))
|
||||
{
|
||||
isOrbotRunning = true;
|
||||
}
|
||||
else if (status.equals(OrbotHelper.STATUS_OFF))
|
||||
{
|
||||
isOrbotRunning = false;
|
||||
}
|
||||
else if (status.equals(OrbotHelper.STATUS_STARTING))
|
||||
{
|
||||
}
|
||||
else if (status.equals(OrbotHelper.STATUS_STOPPING))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public boolean reinitOrbot(Context application_context)
|
||||
{
|
||||
if(!OrbotHelper.isOrbotInstalled(application_context))
|
||||
{
|
||||
OrbotHelper.getOrbotInstallIntent(application_context);
|
||||
new LovelyStandardDialog(application_context)
|
||||
.setTopColorRes(R.color.header)
|
||||
.setIcon(R.drawable.logo)
|
||||
.setTitle("Orbot Proxy Not Installed")
|
||||
.setMessage("Hidden Web can only be access by Special Proxies. Please Install Orbot Proxy from Playstore")
|
||||
.setPositiveButton(android.R.string.ok, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final String appPackageName = "org.torproject.android"; // getPackageName() from Context or Activity object
|
||||
try {
|
||||
application_context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName)));
|
||||
} catch (android.content.ActivityNotFoundException anfe) {
|
||||
application_context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName)));
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
if(!isOrbotRunning)
|
||||
{
|
||||
OrbotHelper.get(application_context).init();
|
||||
message_manager.getInstance().startingOrbotInfo(application_context);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class preference_manager {
|
||||
private static final preference_manager ourInstance = new preference_manager();
|
||||
|
||||
public static preference_manager getInstance() {
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
private preference_manager() {
|
||||
}
|
||||
|
||||
public void saveString(String valueKey, String value, Context applicationContext) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
|
||||
SharedPreferences.Editor edit = prefs.edit();
|
||||
edit.putString(valueKey, value);
|
||||
edit.commit();
|
||||
}
|
||||
|
||||
public String getString(String valueKey, String valueDefault,Context applicationContext) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
|
||||
return prefs.getString(valueKey, valueDefault);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,357 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014-2016 Hans-Christoph Steiner
|
||||
* Copyright 2012-2016 Nathan Freitas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.myapplication.proxy.netcipher;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import info.guardianproject.netcipher.client.TlsOnlySocketFactory;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
|
||||
public class NetCipher {
|
||||
private static final String TAG = "NetCipher";
|
||||
|
||||
private NetCipher() {
|
||||
// this is a utility class with only static methods
|
||||
}
|
||||
|
||||
public final static Proxy ORBOT_HTTP_PROXY = new Proxy(Proxy.Type.HTTP,
|
||||
new InetSocketAddress("127.0.0.1", 8118));
|
||||
|
||||
private static Proxy proxy;
|
||||
|
||||
/**
|
||||
* Set the global HTTP proxy for all new {@link HttpURLConnection}s and
|
||||
* {@link HttpsURLConnection}s that are created after this is called.
|
||||
* <p>
|
||||
* {@link #useTor()} will override this setting. Traffic must be directed
|
||||
* to Tor using the proxy settings, and Orbot has its own proxy settings
|
||||
* for connections that need proxies to work. So if "use Tor" is enabled,
|
||||
* as tested by looking for the static instance of Proxy, then no other
|
||||
* proxy settings are allowed to override the current Tor proxy.
|
||||
*
|
||||
* @param host the IP address for the HTTP proxy to use globally
|
||||
* @param port the port number for the HTTP proxy to use globally
|
||||
*/
|
||||
public static void setProxy(String host, int port) {
|
||||
if (!TextUtils.isEmpty(host) && port > 0) {
|
||||
InetSocketAddress isa = new InetSocketAddress(host, port);
|
||||
setProxy(new Proxy(Proxy.Type.HTTP, isa));
|
||||
} else if (NetCipher.proxy != ORBOT_HTTP_PROXY) {
|
||||
setProxy(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the global HTTP proxy for all new {@link HttpURLConnection}s and
|
||||
* {@link HttpsURLConnection}s that are created after this is called.
|
||||
* <p>
|
||||
* {@link #useTor()} will override this setting. Traffic must be directed
|
||||
* to Tor using the proxy settings, and Orbot has its own proxy settings
|
||||
* for connections that need proxies to work. So if "use Tor" is enabled,
|
||||
* as tested by looking for the static instance of Proxy, then no other
|
||||
* proxy settings are allowed to override the current Tor proxy.
|
||||
*
|
||||
* @param proxy the HTTP proxy to use globally
|
||||
*/
|
||||
public static void setProxy(Proxy proxy) {
|
||||
if (proxy != null && NetCipher.proxy == ORBOT_HTTP_PROXY) {
|
||||
Log.w(TAG, "useTor is enabled, ignoring new proxy settings!");
|
||||
} else {
|
||||
NetCipher.proxy = proxy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently active global HTTP {@link Proxy}.
|
||||
*
|
||||
* @return the active HTTP {@link Proxy}
|
||||
*/
|
||||
public static Proxy getProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the global HTTP proxy for all new {@link HttpURLConnection}s and
|
||||
* {@link HttpsURLConnection}s that are created after this is called. This
|
||||
* returns things to the default, proxy-less state.
|
||||
*/
|
||||
public static void clearProxy() {
|
||||
setProxy(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Orbot as the global HTTP proxy for all new {@link HttpURLConnection}
|
||||
* s and {@link HttpsURLConnection}s that are created after this is called.
|
||||
* This overrides all future calls to {@link #setProxy(Proxy)}, except to
|
||||
* clear the proxy, e.g. {@code #setProxy(null)} or {@link #clearProxy()}.
|
||||
* <p>
|
||||
* Traffic must be directed to Tor using the proxy settings, and Orbot has its
|
||||
* own proxy settings for connections that need proxies to work. So if "use
|
||||
* Tor" is enabled, as tested by looking for the static instance of Proxy,
|
||||
* then no other proxy settings are allowed to override the current Tor proxy.
|
||||
*/
|
||||
public static void useTor() {
|
||||
setProxy(ORBOT_HTTP_PROXY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link TlsOnlySocketFactory} from NetCipher.
|
||||
*
|
||||
* @see HttpsURLConnection#setDefaultSSLSocketFactory(SSLSocketFactory)
|
||||
*/
|
||||
public static TlsOnlySocketFactory getTlsOnlySocketFactory() {
|
||||
return getTlsOnlySocketFactory(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link TlsOnlySocketFactory} from NetCipher, and specify whether
|
||||
* it should use a more compatible, but less strong, suite of ciphers.
|
||||
*
|
||||
* @see HttpsURLConnection#setDefaultSSLSocketFactory(SSLSocketFactory)
|
||||
*/
|
||||
public static TlsOnlySocketFactory getTlsOnlySocketFactory(boolean compatible) {
|
||||
SSLContext sslcontext;
|
||||
try {
|
||||
sslcontext = SSLContext.getInstance("TLSv1");
|
||||
sslcontext.init(null, null, null);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (KeyManagementException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
return new TlsOnlySocketFactory(sslcontext.getSocketFactory(), compatible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpURLConnection} from a {@link URL}, and specify whether
|
||||
* it should use a more compatible, but less strong, suite of ciphers.
|
||||
*
|
||||
* @param url
|
||||
* @param compatible
|
||||
* @return the {@code url} in an instance of {@link HttpURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect
|
||||
*/
|
||||
public static HttpURLConnection getHttpURLConnection(URL url, boolean compatible)
|
||||
throws IOException {
|
||||
// .onion addresses only work via Tor, so force Tor for all of them
|
||||
Proxy proxy = NetCipher.proxy;
|
||||
if (OrbotHelper.isOnionAddress(url))
|
||||
proxy = ORBOT_HTTP_PROXY;
|
||||
|
||||
HttpURLConnection connection;
|
||||
if (proxy != null) {
|
||||
connection = (HttpURLConnection) url.openConnection(proxy);
|
||||
} else {
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
HttpsURLConnection httpsConnection = ((HttpsURLConnection) connection);
|
||||
SSLSocketFactory tlsOnly = getTlsOnlySocketFactory(compatible);
|
||||
httpsConnection.setSSLSocketFactory(tlsOnly);
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
httpsConnection.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpsURLConnection} from a URL {@link String} using the best
|
||||
* TLS configuration available on the device.
|
||||
*
|
||||
* @param urlString
|
||||
* @return the URL in an instance of {@link HttpsURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
|
||||
* or if an HTTP URL is given that does not support HTTPS
|
||||
*/
|
||||
public static HttpsURLConnection getHttpsURLConnection(String urlString) throws IOException {
|
||||
URL url = new URL(urlString.replaceFirst("^[Hh][Tt][Tt][Pp]:", "https:"));
|
||||
return getHttpsURLConnection(url, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpsURLConnection} from a {@link Uri} using the best TLS
|
||||
* configuration available on the device.
|
||||
*
|
||||
* @param uri
|
||||
* @return the {@code uri} in an instance of {@link HttpsURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
|
||||
* or if an HTTP URL is given that does not support HTTPS
|
||||
*/
|
||||
public static HttpsURLConnection getHttpsURLConnection(Uri uri) throws IOException {
|
||||
return getHttpsURLConnection(uri.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpsURLConnection} from a {@link URI} using the best TLS
|
||||
* configuration available on the device.
|
||||
*
|
||||
* @param uri
|
||||
* @return the {@code uri} in an instance of {@link HttpsURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
|
||||
* or if an HTTP URL is given that does not support HTTPS
|
||||
*/
|
||||
public static HttpsURLConnection getHttpsURLConnection(URI uri) throws IOException {
|
||||
if (TextUtils.equals(uri.getScheme(), "https"))
|
||||
return getHttpsURLConnection(uri.toURL(), false);
|
||||
else
|
||||
// otherwise force scheme to https
|
||||
return getHttpsURLConnection(uri.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpsURLConnection} from a {@link URL} using the best TLS
|
||||
* configuration available on the device.
|
||||
*
|
||||
* @param url
|
||||
* @return the {@code url} in an instance of {@link HttpsURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
|
||||
* or if an HTTP URL is given that does not support HTTPS
|
||||
*/
|
||||
public static HttpsURLConnection getHttpsURLConnection(URL url) throws IOException {
|
||||
return getHttpsURLConnection(url, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpsURLConnection} from a {@link URL} using a more
|
||||
* compatible, but less strong, suite of ciphers.
|
||||
*
|
||||
* @param url
|
||||
* @return the {@code url} in an instance of {@link HttpsURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
|
||||
* or if an HTTP URL is given that does not support HTTPS
|
||||
*/
|
||||
public static HttpsURLConnection getCompatibleHttpsURLConnection(URL url) throws IOException {
|
||||
return getHttpsURLConnection(url, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpsURLConnection} from a {@link URL}, and specify whether
|
||||
* it should use a more compatible, but less strong, suite of ciphers.
|
||||
*
|
||||
* @param url
|
||||
* @param compatible
|
||||
* @return the {@code url} in an instance of {@link HttpsURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
|
||||
* or if an HTTP URL is given that does not support HTTPS
|
||||
*/
|
||||
public static HttpsURLConnection getHttpsURLConnection(URL url, boolean compatible)
|
||||
throws IOException {
|
||||
// use default method, but enforce a HttpsURLConnection
|
||||
HttpURLConnection connection = getHttpURLConnection(url, compatible);
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
return (HttpsURLConnection) connection;
|
||||
} else {
|
||||
throw new IllegalArgumentException("not an HTTPS connection!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpURLConnection} from a {@link URL}. If the connection is
|
||||
* {@code https://}, it will use a more compatible, but less strong, TLS
|
||||
* configuration.
|
||||
*
|
||||
* @param url
|
||||
* @return the {@code url} in an instance of {@link HttpsURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect
|
||||
*/
|
||||
public static HttpURLConnection getCompatibleHttpURLConnection(URL url) throws IOException {
|
||||
return getHttpURLConnection(url, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpURLConnection} from a URL {@link String}. If it is an
|
||||
* {@code https://} link, then this will use the best TLS configuration
|
||||
* available on the device.
|
||||
*
|
||||
* @param urlString
|
||||
* @return the URL in an instance of {@link HttpURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect
|
||||
*/
|
||||
public static HttpURLConnection getHttpURLConnection(String urlString) throws IOException {
|
||||
return getHttpURLConnection(new URL(urlString));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpURLConnection} from a {@link Uri}. If it is an
|
||||
* {@code https://} link, then this will use the best TLS configuration
|
||||
* available on the device.
|
||||
*
|
||||
* @param uri
|
||||
* @return the {@code uri} in an instance of {@link HttpURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect
|
||||
*/
|
||||
public static HttpURLConnection getHttpURLConnection(Uri uri) throws IOException {
|
||||
return getHttpURLConnection(uri.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpURLConnection} from a {@link URI}. If it is an
|
||||
* {@code https://} link, then this will use the best TLS configuration
|
||||
* available on the device.
|
||||
*
|
||||
* @param uri
|
||||
* @return the {@code uri} in an instance of {@link HttpURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect
|
||||
*/
|
||||
public static HttpURLConnection getHttpURLConnection(URI uri) throws IOException {
|
||||
return getHttpURLConnection(uri.toURL());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link HttpURLConnection} from a {@link URL}. If it is an
|
||||
* {@code https://} link, then this will use the best TLS configuration
|
||||
* available on the device.
|
||||
*
|
||||
* @param url
|
||||
* @return the {@code url} in an instance of {@link HttpURLConnection}
|
||||
* @throws IOException
|
||||
* @throws IllegalArgumentException if the proxy or TLS setup is incorrect
|
||||
*/
|
||||
public static HttpURLConnection getHttpURLConnection(URL url) throws IOException {
|
||||
return (HttpURLConnection) getHttpURLConnection(url, false);
|
||||
}
|
||||
}
|
|
@ -1,754 +0,0 @@
|
|||
package com.example.myapplication.proxy.netcipher;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Proxy;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Parcelable;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import org.apache.http.HttpHost;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
public class WebkitProxy {
|
||||
|
||||
private final static String DEFAULT_HOST = "localhost";//"127.0.0.1";
|
||||
private final static int DEFAULT_PORT = 8118;
|
||||
private final static int DEFAULT_SOCKS_PORT = 9050;
|
||||
|
||||
private final static int REQUEST_CODE = 0;
|
||||
|
||||
private final static String TAG = "OrbotHelpher";
|
||||
|
||||
public static boolean setProxy(String appClass, Context ctx, WebView wView, String host, int port) throws Exception {
|
||||
|
||||
setSystemProperties(host, port);
|
||||
|
||||
boolean worked = false;
|
||||
|
||||
if (Build.VERSION.SDK_INT < 13) {
|
||||
// worked = setWebkitProxyGingerbread(ctx, host, port);
|
||||
setProxyUpToHC(wView, host, port);
|
||||
} else if (Build.VERSION.SDK_INT < 19) {
|
||||
worked = setWebkitProxyICS(ctx, host, port);
|
||||
} else if (Build.VERSION.SDK_INT < 20) {
|
||||
worked = setKitKatProxy(appClass, ctx, host, port);
|
||||
|
||||
if (!worked) //some kitkat's still use ICS browser component (like Cyanogen 11)
|
||||
worked = setWebkitProxyICS(ctx, host, port);
|
||||
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
worked = setWebkitProxyLollipop(ctx, host, port);
|
||||
|
||||
}
|
||||
|
||||
return worked;
|
||||
}
|
||||
|
||||
private static void setSystemProperties(String host, int port) {
|
||||
|
||||
System.setProperty("proxyHost", host);
|
||||
System.setProperty("proxyPort", Integer.toString(port));
|
||||
|
||||
System.setProperty("http.proxyHost", host);
|
||||
System.setProperty("http.proxyPort", Integer.toString(port));
|
||||
|
||||
System.setProperty("https.proxyHost", host);
|
||||
System.setProperty("https.proxyPort", Integer.toString(port));
|
||||
|
||||
|
||||
System.setProperty("socks.proxyHost", host);
|
||||
System.setProperty("socks.proxyPort", Integer.toString(DEFAULT_SOCKS_PORT));
|
||||
|
||||
System.setProperty("socksProxyHost", host);
|
||||
System.setProperty("socksProxyPort", Integer.toString(DEFAULT_SOCKS_PORT));
|
||||
|
||||
|
||||
/*
|
||||
ProxySelector pSelect = new ProxySelector();
|
||||
pSelect.addProxy(Proxy.Type.HTTP, host, port);
|
||||
ProxySelector.setDefault(pSelect);
|
||||
*/
|
||||
/*
|
||||
System.setProperty("http_proxy", "http://" + host + ":" + port);
|
||||
System.setProperty("proxy-server", "http://" + host + ":" + port);
|
||||
System.setProperty("host-resolver-rules","MAP * 0.0.0.0 , EXCLUDE myproxy");
|
||||
System.getProperty("networkaddress.cache.ttl", "-1");
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
private static void resetSystemProperties() {
|
||||
|
||||
System.setProperty("proxyHost", "");
|
||||
System.setProperty("proxyPort", "");
|
||||
|
||||
System.setProperty("http.proxyHost", "");
|
||||
System.setProperty("http.proxyPort", "");
|
||||
|
||||
System.setProperty("https.proxyHost", "");
|
||||
System.setProperty("https.proxyPort", "");
|
||||
|
||||
|
||||
System.setProperty("socks.proxyHost", "");
|
||||
System.setProperty("socks.proxyPort", Integer.toString(DEFAULT_SOCKS_PORT));
|
||||
|
||||
System.setProperty("socksProxyHost", "");
|
||||
System.setProperty("socksProxyPort", Integer.toString(DEFAULT_SOCKS_PORT));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Override WebKit Proxy settings
|
||||
*
|
||||
* @param ctx Android ApplicationContext
|
||||
* @param host
|
||||
* @param port
|
||||
* @return true if Proxy was successfully set
|
||||
*/
|
||||
private static boolean setWebkitProxyGingerbread(Context ctx, String host, int port)
|
||||
throws Exception {
|
||||
|
||||
boolean ret = false;
|
||||
|
||||
Object requestQueueObject = getRequestQueue(ctx);
|
||||
if (requestQueueObject != null) {
|
||||
// Create Proxy config object and set it into request Q
|
||||
HttpHost httpHost = new HttpHost(host, port, "http");
|
||||
setDeclaredField(requestQueueObject, "mProxyHost", httpHost);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set Proxy for Android 3.2 and below.
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
private static boolean setProxyUpToHC(WebView webview, String host, int port) {
|
||||
Log.d(TAG, "Setting proxy with <= 3.2 API.");
|
||||
|
||||
HttpHost proxyServer = new HttpHost(host, port);
|
||||
// Getting network
|
||||
Class networkClass = null;
|
||||
Object network = null;
|
||||
try {
|
||||
networkClass = Class.forName("android.webkit.Network");
|
||||
if (networkClass == null) {
|
||||
Log.e(TAG, "failed to get class for android.webkit.Network");
|
||||
return false;
|
||||
}
|
||||
Method getInstanceMethod = networkClass.getMethod("getInstance", Context.class);
|
||||
if (getInstanceMethod == null) {
|
||||
Log.e(TAG, "failed to get getInstance method");
|
||||
}
|
||||
network = getInstanceMethod.invoke(networkClass, new Object[]{webview.getContext()});
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "error getting network: " + ex);
|
||||
return false;
|
||||
}
|
||||
if (network == null) {
|
||||
Log.e(TAG, "error getting network: network is null");
|
||||
return false;
|
||||
}
|
||||
Object requestQueue = null;
|
||||
try {
|
||||
Field requestQueueField = networkClass
|
||||
.getDeclaredField("mRequestQueue");
|
||||
requestQueue = getFieldValueSafely(requestQueueField, network);
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "error getting field value");
|
||||
return false;
|
||||
}
|
||||
if (requestQueue == null) {
|
||||
Log.e(TAG, "Request queue is null");
|
||||
return false;
|
||||
}
|
||||
Field proxyHostField = null;
|
||||
try {
|
||||
Class requestQueueClass = Class.forName("android.net.http.RequestQueue");
|
||||
proxyHostField = requestQueueClass
|
||||
.getDeclaredField("mProxyHost");
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "error getting proxy host field");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean temp = proxyHostField.isAccessible();
|
||||
try {
|
||||
proxyHostField.setAccessible(true);
|
||||
proxyHostField.set(requestQueue, proxyServer);
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "error setting proxy host");
|
||||
} finally {
|
||||
proxyHostField.setAccessible(temp);
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting proxy with <= 3.2 API successful!");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static Object getFieldValueSafely(Field field, Object classInstance) throws IllegalArgumentException, IllegalAccessException {
|
||||
boolean oldAccessibleValue = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
Object result = field.get(classInstance);
|
||||
field.setAccessible(oldAccessibleValue);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean setWebkitProxyICS(Context ctx, String host, int port) {
|
||||
|
||||
// PSIPHON: added support for Android 4.x WebView proxy
|
||||
try {
|
||||
Class webViewCoreClass = Class.forName("android.webkit.WebViewCore");
|
||||
|
||||
Class proxyPropertiesClass = Class.forName("android.net.ProxyProperties");
|
||||
if (webViewCoreClass != null && proxyPropertiesClass != null) {
|
||||
Method m = webViewCoreClass.getDeclaredMethod("sendStaticMessage", Integer.TYPE,
|
||||
Object.class);
|
||||
Constructor c = proxyPropertiesClass.getConstructor(String.class, Integer.TYPE,
|
||||
String.class);
|
||||
|
||||
if (m != null && c != null) {
|
||||
m.setAccessible(true);
|
||||
c.setAccessible(true);
|
||||
Object properties = c.newInstance(host, port, null);
|
||||
|
||||
// android.webkit.WebViewCore.EventHub.PROXY_CHANGED = 193;
|
||||
m.invoke(null, 193, properties);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("ProxySettings",
|
||||
"Exception setting WebKit proxy through android.net.ProxyProperties: "
|
||||
+ e.toString());
|
||||
} catch (Error e) {
|
||||
Log.e("ProxySettings",
|
||||
"Exception setting WebKit proxy through android.webkit.Network: "
|
||||
+ e.toString());
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
public static boolean resetKitKatProxy(String appClass, Context appContext) {
|
||||
|
||||
return setKitKatProxy(appClass, appContext, null, 0);
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
private static boolean setKitKatProxy(String appClass, Context appContext, String host, int port) {
|
||||
//Context appContext = webView.getContext().getApplicationContext();
|
||||
|
||||
try {
|
||||
Class applictionCls = Class.forName(appClass);
|
||||
Field loadedApkField = applictionCls.getField("mLoadedApk");
|
||||
loadedApkField.setAccessible(true);
|
||||
Object loadedApk = loadedApkField.get(appContext);
|
||||
Class loadedApkCls = Class.forName("android.app.LoadedApk");
|
||||
Field receiversField = loadedApkCls.getDeclaredField("mReceivers");
|
||||
receiversField.setAccessible(true);
|
||||
ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
|
||||
for (Object receiverMap : receivers.values()) {
|
||||
for (Object rec : ((ArrayMap) receiverMap).keySet()) {
|
||||
Class clazz = rec.getClass();
|
||||
if (clazz.getName().contains("ProxyChangeListener")) {
|
||||
Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class);
|
||||
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
|
||||
|
||||
if (host != null) {
|
||||
/*********** optional, may be need in future *************/
|
||||
final String CLASS_NAME = "android.net.ProxyProperties";
|
||||
Class cls = Class.forName(CLASS_NAME);
|
||||
Constructor constructor = cls.getConstructor(String.class, Integer.TYPE, String.class);
|
||||
constructor.setAccessible(true);
|
||||
Object proxyProperties = constructor.newInstance(host, port, null);
|
||||
intent.putExtra("proxy", (Parcelable) proxyProperties);
|
||||
/*********** optional, may be need in future *************/
|
||||
}
|
||||
|
||||
onReceiveMethod.invoke(rec, appContext, intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
String exceptionAsString = sw.toString();
|
||||
Log.v(TAG, e.getMessage());
|
||||
Log.v(TAG, exceptionAsString);
|
||||
} catch (NoSuchFieldException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
String exceptionAsString = sw.toString();
|
||||
Log.v(TAG, e.getMessage());
|
||||
Log.v(TAG, exceptionAsString);
|
||||
} catch (IllegalAccessException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
String exceptionAsString = sw.toString();
|
||||
Log.v(TAG, e.getMessage());
|
||||
Log.v(TAG, exceptionAsString);
|
||||
} catch (IllegalArgumentException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
String exceptionAsString = sw.toString();
|
||||
Log.v(TAG, e.getMessage());
|
||||
Log.v(TAG, exceptionAsString);
|
||||
} catch (NoSuchMethodException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
String exceptionAsString = sw.toString();
|
||||
Log.v(TAG, e.getMessage());
|
||||
Log.v(TAG, exceptionAsString);
|
||||
} catch (InvocationTargetException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
String exceptionAsString = sw.toString();
|
||||
Log.v(TAG, e.getMessage());
|
||||
Log.v(TAG, exceptionAsString);
|
||||
} catch (InstantiationException e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
String exceptionAsString = sw.toString();
|
||||
Log.v(TAG, e.getMessage());
|
||||
Log.v(TAG, exceptionAsString);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
public static boolean resetLollipopProxy(String appClass, Context appContext) {
|
||||
|
||||
return setWebkitProxyLollipop(appContext, null, 0);
|
||||
}
|
||||
|
||||
// http://stackanswers.com/questions/25272393/android-webview-set-proxy-programmatically-on-android-l
|
||||
@TargetApi(21) // for android.util.ArrayMap methods
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static boolean setWebkitProxyLollipop(Context appContext, String host, int port) {
|
||||
|
||||
try {
|
||||
Class applictionClass = Class.forName("android.app.Application");
|
||||
Field mLoadedApkField = applictionClass.getDeclaredField("mLoadedApk");
|
||||
mLoadedApkField.setAccessible(true);
|
||||
Object mloadedApk = mLoadedApkField.get(appContext);
|
||||
Class loadedApkClass = Class.forName("android.app.LoadedApk");
|
||||
Field mReceiversField = loadedApkClass.getDeclaredField("mReceivers");
|
||||
mReceiversField.setAccessible(true);
|
||||
ArrayMap receivers = (ArrayMap) mReceiversField.get(mloadedApk);
|
||||
for (Object receiverMap : receivers.values()) {
|
||||
for (Object receiver : ((ArrayMap) receiverMap).keySet()) {
|
||||
Class clazz = receiver.getClass();
|
||||
if (clazz.getName().contains("ProxyChangeListener")) {
|
||||
Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class);
|
||||
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
|
||||
Object proxyInfo = null;
|
||||
if (host != null) {
|
||||
final String CLASS_NAME = "android.net.ProxyInfo";
|
||||
Class cls = Class.forName(CLASS_NAME);
|
||||
Method buildDirectProxyMethod = cls.getMethod("buildDirectProxy", String.class, Integer.TYPE);
|
||||
proxyInfo = buildDirectProxyMethod.invoke(cls, host, port);
|
||||
}
|
||||
intent.putExtra("proxy", (Parcelable) proxyInfo);
|
||||
onReceiveMethod.invoke(receiver, appContext, intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.d("ProxySettings", "Exception setting WebKit proxy on Lollipop through ProxyChangeListener: " + e.toString());
|
||||
} catch (NoSuchFieldException e) {
|
||||
Log.d("ProxySettings", "Exception setting WebKit proxy on Lollipop through ProxyChangeListener: " + e.toString());
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.d("ProxySettings", "Exception setting WebKit proxy on Lollipop through ProxyChangeListener: " + e.toString());
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.d("ProxySettings", "Exception setting WebKit proxy on Lollipop through ProxyChangeListener: " + e.toString());
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.d("ProxySettings", "Exception setting WebKit proxy on Lollipop through ProxyChangeListener: " + e.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean sendProxyChangedIntent(Context ctx, String host, int port) {
|
||||
|
||||
try {
|
||||
Class proxyPropertiesClass = Class.forName("android.net.ProxyProperties");
|
||||
if (proxyPropertiesClass != null) {
|
||||
Constructor c = proxyPropertiesClass.getConstructor(String.class, Integer.TYPE,
|
||||
String.class);
|
||||
|
||||
if (c != null) {
|
||||
c.setAccessible(true);
|
||||
Object properties = c.newInstance(host, port, null);
|
||||
|
||||
Intent intent = new Intent(android.net.Proxy.PROXY_CHANGE_ACTION);
|
||||
intent.putExtra("proxy", (Parcelable) properties);
|
||||
ctx.sendBroadcast(intent);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("ProxySettings",
|
||||
"Exception sending Intent ", e);
|
||||
} catch (Error e) {
|
||||
Log.e("ProxySettings",
|
||||
"Exception sending Intent ", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
private static boolean setKitKatProxy0(Context ctx, String host, int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class cmClass = Class.forName("android.net.ConnectivityManager");
|
||||
Class proxyPropertiesClass = Class.forName("android.net.ProxyProperties");
|
||||
if (cmClass != null && proxyPropertiesClass != null)
|
||||
{
|
||||
Constructor c = proxyPropertiesClass.getConstructor(String.class, Integer.TYPE,
|
||||
String.class);
|
||||
if (c != null)
|
||||
{
|
||||
c.setAccessible(true);
|
||||
Object proxyProps = c.newInstance(host, port, null);
|
||||
ConnectivityManager cm =
|
||||
(ConnectivityManager)ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
Method mSetGlobalProxy = cmClass.getDeclaredMethod("setGlobalProxy", proxyPropertiesClass);
|
||||
mSetGlobalProxy.invoke(cm, proxyProps);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
Log.e("ProxySettings",
|
||||
"ConnectivityManager.setGlobalProxy ",e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
//CommandLine.initFromFile(COMMAND_LINE_FILE);
|
||||
|
||||
/**
|
||||
* private static boolean setKitKatProxy2 (Context ctx, String host, int port)
|
||||
* {
|
||||
* <p>
|
||||
* String commandLinePath = "/data/local/tmp/orweb.conf";
|
||||
* try
|
||||
* {
|
||||
* Class webViewCoreClass = Class.forName("org.chromium.content.common.CommandLine");
|
||||
* <p>
|
||||
* if (webViewCoreClass != null)
|
||||
* {
|
||||
* for (Method method : webViewCoreClass.getDeclaredMethods())
|
||||
* {
|
||||
* Log.d("Orweb","Proxy methods: " + method.getName());
|
||||
* }
|
||||
* <p>
|
||||
* Method m = webViewCoreClass.getDeclaredMethod("initFromFile",
|
||||
* String.class);
|
||||
* <p>
|
||||
* if (m != null)
|
||||
* {
|
||||
* m.setAccessible(true);
|
||||
* m.invoke(null, commandLinePath);
|
||||
* return true;
|
||||
* }
|
||||
* else
|
||||
* return false;
|
||||
* }
|
||||
* } catch (Exception e)
|
||||
* {
|
||||
* Log.e("ProxySettings",
|
||||
* "Exception setting WebKit proxy through android.net.ProxyProperties: "
|
||||
* + e.toString());
|
||||
* } catch (Error e)
|
||||
* {
|
||||
* Log.e("ProxySettings",
|
||||
* "Exception setting WebKit proxy through android.webkit.Network: "
|
||||
* + e.toString());
|
||||
* }
|
||||
* <p>
|
||||
* return false;
|
||||
* }
|
||||
* <p>
|
||||
* /**
|
||||
* private static boolean setKitKatProxy (Context ctx, String host, int port)
|
||||
* {
|
||||
* <p>
|
||||
* try
|
||||
* {
|
||||
* Class webViewCoreClass = Class.forName("android.net.Proxy");
|
||||
* <p>
|
||||
* Class proxyPropertiesClass = Class.forName("android.net.ProxyProperties");
|
||||
* if (webViewCoreClass != null && proxyPropertiesClass != null)
|
||||
* {
|
||||
* for (Method method : webViewCoreClass.getDeclaredMethods())
|
||||
* {
|
||||
* Log.d("Orweb","Proxy methods: " + method.getName());
|
||||
* }
|
||||
* <p>
|
||||
* Method m = webViewCoreClass.getDeclaredMethod("setHttpProxySystemProperty",
|
||||
* proxyPropertiesClass);
|
||||
* Constructor c = proxyPropertiesClass.getConstructor(String.class, Integer.TYPE,
|
||||
* String.class);
|
||||
* <p>
|
||||
* if (m != null && c != null)
|
||||
* {
|
||||
* m.setAccessible(true);
|
||||
* c.setAccessible(true);
|
||||
* Object properties = c.newInstance(host, port, null);
|
||||
* <p>
|
||||
* m.invoke(null, properties);
|
||||
* return true;
|
||||
* }
|
||||
* else
|
||||
* return false;
|
||||
* }
|
||||
* } catch (Exception e)
|
||||
* {
|
||||
* Log.e("ProxySettings",
|
||||
* "Exception setting WebKit proxy through android.net.ProxyProperties: "
|
||||
* + e.toString());
|
||||
* } catch (Error e)
|
||||
* {
|
||||
* Log.e("ProxySettings",
|
||||
* "Exception setting WebKit proxy through android.webkit.Network: "
|
||||
* + e.toString());
|
||||
* }
|
||||
* <p>
|
||||
* return false;
|
||||
* }
|
||||
* <p>
|
||||
* private static boolean resetProxyForKitKat ()
|
||||
* {
|
||||
* <p>
|
||||
* try
|
||||
* {
|
||||
* Class webViewCoreClass = Class.forName("android.net.Proxy");
|
||||
* <p>
|
||||
* Class proxyPropertiesClass = Class.forName("android.net.ProxyProperties");
|
||||
* if (webViewCoreClass != null && proxyPropertiesClass != null)
|
||||
* {
|
||||
* for (Method method : webViewCoreClass.getDeclaredMethods())
|
||||
* {
|
||||
* Log.d("Orweb","Proxy methods: " + method.getName());
|
||||
* }
|
||||
* <p>
|
||||
* Method m = webViewCoreClass.getDeclaredMethod("setHttpProxySystemProperty",
|
||||
* proxyPropertiesClass);
|
||||
* <p>
|
||||
* if (m != null)
|
||||
* {
|
||||
* m.setAccessible(true);
|
||||
* <p>
|
||||
* m.invoke(null, null);
|
||||
* return true;
|
||||
* }
|
||||
* else
|
||||
* return false;
|
||||
* }
|
||||
* } catch (Exception e)
|
||||
* {
|
||||
* Log.e("ProxySettings",
|
||||
* "Exception setting WebKit proxy through android.net.ProxyProperties: "
|
||||
* + e.toString());
|
||||
* } catch (Error e)
|
||||
* {
|
||||
* Log.e("ProxySettings",
|
||||
* "Exception setting WebKit proxy through android.webkit.Network: "
|
||||
* + e.toString());
|
||||
* }
|
||||
* <p>
|
||||
* return false;
|
||||
* }
|
||||
**/
|
||||
|
||||
public static void resetProxy(String appClass, Context ctx) throws Exception {
|
||||
|
||||
resetSystemProperties();
|
||||
|
||||
if (Build.VERSION.SDK_INT < 14) {
|
||||
resetProxyForGingerBread(ctx);
|
||||
} else if (Build.VERSION.SDK_INT < 19) {
|
||||
resetProxyForICS();
|
||||
} else if (Build.VERSION.SDK_INT < 20) {
|
||||
resetKitKatProxy(appClass, ctx);
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
resetLollipopProxy(appClass, ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void resetProxyForICS() throws Exception {
|
||||
try {
|
||||
Class webViewCoreClass = Class.forName("android.webkit.WebViewCore");
|
||||
Class proxyPropertiesClass = Class.forName("android.net.ProxyProperties");
|
||||
if (webViewCoreClass != null && proxyPropertiesClass != null) {
|
||||
Method m = webViewCoreClass.getDeclaredMethod("sendStaticMessage", Integer.TYPE,
|
||||
Object.class);
|
||||
|
||||
if (m != null) {
|
||||
m.setAccessible(true);
|
||||
|
||||
// android.webkit.WebViewCore.EventHub.PROXY_CHANGED = 193;
|
||||
m.invoke(null, 193, null);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("ProxySettings",
|
||||
"Exception setting WebKit proxy through android.net.ProxyProperties: "
|
||||
+ e.toString());
|
||||
throw e;
|
||||
} catch (Error e) {
|
||||
Log.e("ProxySettings",
|
||||
"Exception setting WebKit proxy through android.webkit.Network: "
|
||||
+ e.toString());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetProxyForGingerBread(Context ctx) throws Exception {
|
||||
Object requestQueueObject = getRequestQueue(ctx);
|
||||
if (requestQueueObject != null) {
|
||||
setDeclaredField(requestQueueObject, "mProxyHost", null);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getRequestQueue(Context ctx) throws Exception {
|
||||
Object ret = null;
|
||||
Class networkClass = Class.forName("android.webkit.Network");
|
||||
if (networkClass != null) {
|
||||
Object networkObj = invokeMethod(networkClass, "getInstance", new Object[]{
|
||||
ctx
|
||||
}, Context.class);
|
||||
if (networkObj != null) {
|
||||
ret = getDeclaredField(networkObj, "mRequestQueue");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static Object getDeclaredField(Object obj, String name)
|
||||
throws SecurityException, NoSuchFieldException,
|
||||
IllegalArgumentException, IllegalAccessException {
|
||||
Field f = obj.getClass().getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
Object out = f.get(obj);
|
||||
// System.out.println(obj.getClass().getName() + "." + name + " = "+
|
||||
// out);
|
||||
return out;
|
||||
}
|
||||
|
||||
private static void setDeclaredField(Object obj, String name, Object value)
|
||||
throws SecurityException, NoSuchFieldException,
|
||||
IllegalArgumentException, IllegalAccessException {
|
||||
Field f = obj.getClass().getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
f.set(obj, value);
|
||||
}
|
||||
|
||||
private static Object invokeMethod(Object object, String methodName, Object[] params,
|
||||
Class... types) throws Exception {
|
||||
Object out = null;
|
||||
Class c = object instanceof Class ? (Class) object : object.getClass();
|
||||
if (types != null) {
|
||||
Method method = c.getMethod(methodName, types);
|
||||
out = method.invoke(object, params);
|
||||
} else {
|
||||
Method method = c.getMethod(methodName);
|
||||
out = method.invoke(object);
|
||||
}
|
||||
// System.out.println(object.getClass().getName() + "." + methodName +
|
||||
// "() = "+ out);
|
||||
return out;
|
||||
}
|
||||
|
||||
public static Socket getSocket(Context context, String proxyHost, int proxyPort)
|
||||
throws IOException {
|
||||
Socket sock = new Socket();
|
||||
|
||||
sock.connect(new InetSocketAddress(proxyHost, proxyPort), 10000);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
public static Socket getSocket(Context context) throws IOException {
|
||||
return getSocket(context, DEFAULT_HOST, DEFAULT_SOCKS_PORT);
|
||||
|
||||
}
|
||||
|
||||
public static AlertDialog initOrbot(Activity activity,
|
||||
CharSequence stringTitle,
|
||||
CharSequence stringMessage,
|
||||
CharSequence stringButtonYes,
|
||||
CharSequence stringButtonNo,
|
||||
CharSequence stringDesiredBarcodeFormats) {
|
||||
Intent intentScan = new Intent("org.torproject.android.START_TOR");
|
||||
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
|
||||
try {
|
||||
activity.startActivityForResult(intentScan, REQUEST_CODE);
|
||||
return null;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
return showDownloadDialog(activity, stringTitle, stringMessage, stringButtonYes,
|
||||
stringButtonNo);
|
||||
}
|
||||
}
|
||||
|
||||
private static AlertDialog showDownloadDialog(final Activity activity,
|
||||
CharSequence stringTitle,
|
||||
CharSequence stringMessage,
|
||||
CharSequence stringButtonYes,
|
||||
CharSequence stringButtonNo) {
|
||||
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
|
||||
downloadDialog.setTitle(stringTitle);
|
||||
downloadDialog.setMessage(stringMessage);
|
||||
downloadDialog.setPositiveButton(stringButtonYes, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
Uri uri = Uri.parse("market://search?q=pname:org.torproject.android");
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
});
|
||||
downloadDialog.setNegativeButton(stringButtonNo, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
}
|
||||
});
|
||||
return downloadDialog.show();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 CommonsWare, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package info.guardianproject.netcipher.client;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
public interface StrongBuilder<T extends StrongBuilder, C> {
|
||||
/**
|
||||
* Callback to get a connection handed to you for use,
|
||||
* already set up for NetCipher.
|
||||
*
|
||||
* @param <C> the type of connection created by this builder
|
||||
*/
|
||||
interface Callback<C> {
|
||||
/**
|
||||
* Called when the NetCipher-enhanced connection is ready
|
||||
* for use.
|
||||
*
|
||||
* @param connection the connection
|
||||
*/
|
||||
void onConnected(C connection);
|
||||
|
||||
/**
|
||||
* Called if we tried to connect through to Orbot but failed
|
||||
* for some reason
|
||||
*
|
||||
* @param e the reason
|
||||
*/
|
||||
void onConnectionException(Exception e);
|
||||
|
||||
/**
|
||||
* Called if our attempt to get a status from Orbot failed
|
||||
* after a defined period of time. See statusTimeout() on
|
||||
* OrbotInitializer.
|
||||
*/
|
||||
void onTimeout();
|
||||
|
||||
/**
|
||||
* Called if you requested validation that we are connecting
|
||||
* through Tor, and while we were able to connect to Orbot, that
|
||||
* validation failed.
|
||||
*/
|
||||
void onInvalid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to configure the Tor proxy from the results
|
||||
* returned by Orbot, using the best available proxy
|
||||
* (SOCKS if possible, else HTTP)
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
T withBestProxy();
|
||||
|
||||
/**
|
||||
* @return true if this builder supports HTTP proxies, false
|
||||
* otherwise
|
||||
*/
|
||||
boolean supportsHttpProxy();
|
||||
|
||||
/**
|
||||
* Call this to configure the Tor proxy from the results
|
||||
* returned by Orbot, using the HTTP proxy.
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
T withHttpProxy();
|
||||
|
||||
/**
|
||||
* @return true if this builder supports SOCKS proxies, false
|
||||
* otherwise
|
||||
*/
|
||||
boolean supportsSocksProxy();
|
||||
|
||||
/**
|
||||
* Call this to configure the Tor proxy from the results
|
||||
* returned by Orbot, using the SOCKS proxy.
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
T withSocksProxy();
|
||||
|
||||
/**
|
||||
* Applies your own custom TrustManagers, such as for
|
||||
* replacing the stock keystore support with a custom
|
||||
* keystore.
|
||||
*
|
||||
* @param trustManagers the TrustManagers to use
|
||||
* @return the builder
|
||||
*/
|
||||
T withTrustManagers(TrustManager[] trustManagers)
|
||||
throws NoSuchAlgorithmException, KeyManagementException;
|
||||
|
||||
/**
|
||||
* Call this if you want a weaker set of supported ciphers,
|
||||
* because you are running into compatibility problems with
|
||||
* some server due to a cipher mismatch. The better solution
|
||||
* is to fix the server.
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
T withWeakCiphers();
|
||||
|
||||
/**
|
||||
* Call this if you want the builder to confirm that we are
|
||||
* communicating over Tor, by reaching out to a Tor test
|
||||
* server and confirming our connection status. By default,
|
||||
* this is skipped. Adding this check adds security, but it
|
||||
* has the chance of false negatives (e.g., we cannot reach
|
||||
* that Tor server for some reason).
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
T withTorValidation();
|
||||
|
||||
/**
|
||||
* Builds a connection, applying the configuration already
|
||||
* specified in the builder.
|
||||
*
|
||||
* @param status status Intent from OrbotInitializer
|
||||
* @return the connection
|
||||
* @throws IOException
|
||||
*/
|
||||
C build(Intent status) throws Exception;
|
||||
|
||||
/**
|
||||
* Asynchronous version of build(), one that uses OrbotInitializer
|
||||
* internally to get the status and checks the validity of the Tor
|
||||
* connection (if requested). Note that your callback methods may
|
||||
* be invoked on any thread; do not assume that they will be called
|
||||
* on any particular thread.
|
||||
*
|
||||
* @param callback Callback to get a connection handed to you
|
||||
* for use, already set up for NetCipher
|
||||
*/
|
||||
void build(Callback<C> callback);
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 CommonsWare, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package info.guardianproject.netcipher.client;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
/**
|
||||
* Builds an HttpUrlConnection that connects via Tor through
|
||||
* Orbot.
|
||||
*/
|
||||
public class StrongConnectionBuilder
|
||||
extends StrongBuilderBase<StrongConnectionBuilder, HttpURLConnection> {
|
||||
private URL url;
|
||||
|
||||
/**
|
||||
* Creates a StrongConnectionBuilder using the strongest set
|
||||
* of options for security. Use this if the strongest set of
|
||||
* options is what you want; otherwise, create a
|
||||
* builder via the constructor and configure it as you see fit.
|
||||
*
|
||||
* @param context any Context will do
|
||||
* @return a configured StrongConnectionBuilder
|
||||
* @throws Exception
|
||||
*/
|
||||
static public StrongConnectionBuilder forMaxSecurity(Context context)
|
||||
throws Exception {
|
||||
return (new StrongConnectionBuilder(context)
|
||||
.withBestProxy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder instance.
|
||||
*
|
||||
* @param context any Context will do; builder will hold onto
|
||||
* Application context
|
||||
*/
|
||||
public StrongConnectionBuilder(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*
|
||||
* @param original builder to clone
|
||||
*/
|
||||
public StrongConnectionBuilder(StrongConnectionBuilder original) {
|
||||
super(original);
|
||||
this.url = original.url;
|
||||
}
|
||||
/*
|
||||
public boolean supportsSocksProxy() {
|
||||
return(false);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets the URL to build a connection for.
|
||||
*
|
||||
* @param url the URL
|
||||
* @return the builder
|
||||
* @throws MalformedURLException
|
||||
*/
|
||||
public StrongConnectionBuilder connectTo(String url)
|
||||
throws MalformedURLException {
|
||||
connectTo(new URL(url));
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URL to build a connection for.
|
||||
*
|
||||
* @param url the URL
|
||||
* @return the builder
|
||||
*/
|
||||
public StrongConnectionBuilder connectTo(URL url) {
|
||||
this.url = url;
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public HttpURLConnection build(Intent status) throws IOException {
|
||||
return (buildForUrl(status, url));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String get(Intent status, HttpURLConnection connection,
|
||||
String url) throws Exception {
|
||||
HttpURLConnection realConnection = buildForUrl(status, new URL(url));
|
||||
|
||||
return (slurp(realConnection.getInputStream()));
|
||||
}
|
||||
|
||||
private HttpURLConnection buildForUrl(Intent status, URL urlToUse)
|
||||
throws IOException {
|
||||
URLConnection result;
|
||||
Proxy proxy = buildProxy(status);
|
||||
|
||||
if (proxy == null) {
|
||||
result = urlToUse.openConnection();
|
||||
} else {
|
||||
result = urlToUse.openConnection(proxy);
|
||||
}
|
||||
|
||||
if (result instanceof HttpsURLConnection && sslContext != null) {
|
||||
SSLSocketFactory tlsOnly = buildSocketFactory();
|
||||
HttpsURLConnection https = (HttpsURLConnection) result;
|
||||
|
||||
https.setSSLSocketFactory(tlsOnly);
|
||||
}
|
||||
|
||||
return ((HttpURLConnection) result);
|
||||
}
|
||||
|
||||
// based on http://stackoverflow.com/a/309718/115145
|
||||
|
||||
public static String slurp(final InputStream is)
|
||||
throws IOException {
|
||||
final char[] buffer = new char[128];
|
||||
final StringBuilder out = new StringBuilder();
|
||||
final Reader in = new InputStreamReader(is, "UTF-8");
|
||||
|
||||
for (; ; ) {
|
||||
int rsz = in.read(buffer, 0, buffer.length);
|
||||
if (rsz < 0)
|
||||
break;
|
||||
out.append(buffer, 0, rsz);
|
||||
}
|
||||
|
||||
in.close();
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2016 Nathan Freitas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package info.guardianproject.netcipher.client;
|
||||
|
||||
public class StrongConstants {
|
||||
|
||||
/**
|
||||
* Ordered to prefer the stronger cipher suites as noted
|
||||
* http://op-co.de/blog/posts/android_ssl_downgrade/
|
||||
*/
|
||||
public static final String ENABLED_CIPHERS[] = {
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"SSL_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_RC4_128_MD5"};
|
||||
|
||||
/**
|
||||
* Ordered to prefer the stronger/newer TLS versions as noted
|
||||
* http://op-co.de/blog/posts/android_ssl_downgrade/
|
||||
*/
|
||||
public static final String ENABLED_PROTOCOLS[] = {"TLSv1.2", "TLSv1.1",
|
||||
"TLSv1"};
|
||||
|
||||
}
|
|
@ -1,546 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 Bhavit Singh Sengar
|
||||
* Copyright 2015-2016 Hans-Christoph Steiner
|
||||
* Copyright 2015-2016 Nathan Freitas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* From https://stackoverflow.com/a/29946540
|
||||
*/
|
||||
|
||||
package info.guardianproject.netcipher.client;
|
||||
|
||||
import android.net.SSLCertificateSocketFactory;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.net.ssl.HandshakeCompletedListener;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
/**
|
||||
* While making a secure connection, Android's {@link HttpsURLConnection} falls
|
||||
* back to SSLv3 from TLSv1. This is a bug in android versions < 4.4. It can be
|
||||
* fixed by removing the SSLv3 protocol from Enabled Protocols list. Use this as
|
||||
* the {@link SSLSocketFactory} for
|
||||
* {@link HttpsURLConnection#setDefaultSSLSocketFactory(SSLSocketFactory)}
|
||||
*
|
||||
* @author Bhavit S. Sengar
|
||||
* @author Hans-Christoph Steiner
|
||||
*/
|
||||
public class TlsOnlySocketFactory extends SSLSocketFactory {
|
||||
private static final int HANDSHAKE_TIMEOUT = 0;
|
||||
private static final String TAG = "TlsOnlySocketFactory";
|
||||
private final SSLSocketFactory delegate;
|
||||
private final boolean compatible;
|
||||
|
||||
public TlsOnlySocketFactory() {
|
||||
this.delegate = SSLCertificateSocketFactory.getDefault(HANDSHAKE_TIMEOUT, null);
|
||||
this.compatible = false;
|
||||
}
|
||||
|
||||
public TlsOnlySocketFactory(SSLSocketFactory delegate) {
|
||||
this.delegate = delegate;
|
||||
this.compatible = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make {@link SSLSocket}s that are compatible with outdated servers.
|
||||
*
|
||||
* @param delegate
|
||||
* @param compatible
|
||||
*/
|
||||
public TlsOnlySocketFactory(SSLSocketFactory delegate, boolean compatible) {
|
||||
this.delegate = delegate;
|
||||
this.compatible = compatible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return delegate.getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return delegate.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://timtaubert.de/blog/2014/11/the-sad-state-of-server-side-tls-session-resumption-implementations/">The sad state of server-side TLS Session Resumption implementations</a>
|
||||
*/
|
||||
private Socket makeSocketSafe(Socket socket, String host) {
|
||||
if (socket instanceof SSLSocket) {
|
||||
TlsOnlySSLSocket tempSocket =
|
||||
new TlsOnlySSLSocket((SSLSocket) socket, compatible);
|
||||
|
||||
if (delegate instanceof SSLCertificateSocketFactory &&
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
SSLCertificateSocketFactory factory = (SSLCertificateSocketFactory) delegate;
|
||||
factory.setHostname(socket, host);
|
||||
factory.setUseSessionTickets(socket, false);
|
||||
} else {
|
||||
tempSocket.setHostname(host);
|
||||
}
|
||||
|
||||
socket = tempSocket;
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
|
||||
throws IOException {
|
||||
return makeSocketSafe(delegate.createSocket(s, host, port, autoClose), host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
return makeSocketSafe(delegate.createSocket(host, port), host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
|
||||
throws IOException {
|
||||
return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort), host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
return makeSocketSafe(delegate.createSocket(host, port), host.getHostName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
|
||||
int localPort) throws IOException {
|
||||
return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort),
|
||||
address.getHostName());
|
||||
}
|
||||
|
||||
private class TlsOnlySSLSocket extends DelegateSSLSocket {
|
||||
|
||||
final boolean compatible;
|
||||
|
||||
private TlsOnlySSLSocket(SSLSocket delegate, boolean compatible) {
|
||||
super(delegate);
|
||||
this.compatible = compatible;
|
||||
|
||||
// badly configured servers can't handle a good config
|
||||
if (compatible) {
|
||||
ArrayList<String> protocols = new ArrayList<String>(Arrays.asList(delegate
|
||||
.getEnabledProtocols()));
|
||||
protocols.remove("SSLv2");
|
||||
protocols.remove("SSLv3");
|
||||
super.setEnabledProtocols(protocols.toArray(new String[protocols.size()]));
|
||||
|
||||
/*
|
||||
* Exclude extremely weak EXPORT ciphers. NULL ciphers should
|
||||
* never even have been an option in TLS.
|
||||
*/
|
||||
ArrayList<String> enabled = new ArrayList<String>(10);
|
||||
Pattern exclude = Pattern.compile(".*(EXPORT|NULL).*");
|
||||
for (String cipher : delegate.getEnabledCipherSuites()) {
|
||||
if (!exclude.matcher(cipher).matches()) {
|
||||
enabled.add(cipher);
|
||||
}
|
||||
}
|
||||
super.setEnabledCipherSuites(enabled.toArray(new String[enabled.size()]));
|
||||
return;
|
||||
} // else
|
||||
|
||||
// 16-19 support v1.1 and v1.2 but only by default starting in 20+
|
||||
// https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
|
||||
ArrayList<String> protocols = new ArrayList<String>(Arrays.asList(delegate
|
||||
.getSupportedProtocols()));
|
||||
protocols.remove("SSLv2");
|
||||
protocols.remove("SSLv3");
|
||||
super.setEnabledProtocols(protocols.toArray(new String[protocols.size()]));
|
||||
|
||||
/*
|
||||
* Exclude weak ciphers, like EXPORT, MD5, DES, and DH. NULL ciphers
|
||||
* should never even have been an option in TLS.
|
||||
*/
|
||||
ArrayList<String> enabledCiphers = new ArrayList<String>(10);
|
||||
Pattern exclude = Pattern.compile(".*(_DES|DH_|DSS|EXPORT|MD5|NULL|RC4|TLS_FALLBACK_SCSV).*");
|
||||
for (String cipher : delegate.getSupportedCipherSuites()) {
|
||||
if (!exclude.matcher(cipher).matches()) {
|
||||
enabledCiphers.add(cipher);
|
||||
}
|
||||
}
|
||||
super.setEnabledCipherSuites(enabledCiphers.toArray(new String[enabledCiphers.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* This works around a bug in Android < 19 where SSLv3 is forced
|
||||
*/
|
||||
@Override
|
||||
public void setEnabledProtocols(String[] protocols) {
|
||||
if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {
|
||||
List<String> systemProtocols;
|
||||
if (this.compatible) {
|
||||
systemProtocols = Arrays.asList(delegate.getEnabledProtocols());
|
||||
} else {
|
||||
systemProtocols = Arrays.asList(delegate.getSupportedProtocols());
|
||||
}
|
||||
List<String> enabledProtocols = new ArrayList<String>(systemProtocols);
|
||||
if (enabledProtocols.size() > 1) {
|
||||
enabledProtocols.remove("SSLv2");
|
||||
enabledProtocols.remove("SSLv3");
|
||||
} else {
|
||||
Log.w(TAG, "SSL stuck with protocol available for "
|
||||
+ String.valueOf(enabledProtocols));
|
||||
}
|
||||
protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
|
||||
}
|
||||
super.setEnabledProtocols(protocols);
|
||||
}
|
||||
}
|
||||
|
||||
public class DelegateSSLSocket extends SSLSocket {
|
||||
|
||||
protected final SSLSocket delegate;
|
||||
|
||||
DelegateSSLSocket(SSLSocket delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return delegate.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEnabledCipherSuites() {
|
||||
return delegate.getEnabledCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabledCipherSuites(String[] suites) {
|
||||
delegate.setEnabledCipherSuites(suites);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedProtocols() {
|
||||
return delegate.getSupportedProtocols();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEnabledProtocols() {
|
||||
return delegate.getEnabledProtocols();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabledProtocols(String[] protocols) {
|
||||
delegate.setEnabledProtocols(protocols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLSession getSession() {
|
||||
return delegate.getSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
|
||||
delegate.addHandshakeCompletedListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
|
||||
delegate.removeHandshakeCompletedListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startHandshake() throws IOException {
|
||||
delegate.startHandshake();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseClientMode(boolean mode) {
|
||||
delegate.setUseClientMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseClientMode() {
|
||||
return delegate.getUseClientMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNeedClientAuth(boolean need) {
|
||||
delegate.setNeedClientAuth(need);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWantClientAuth(boolean want) {
|
||||
delegate.setWantClientAuth(want);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getNeedClientAuth() {
|
||||
return delegate.getNeedClientAuth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getWantClientAuth() {
|
||||
return delegate.getWantClientAuth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnableSessionCreation(boolean flag) {
|
||||
delegate.setEnableSessionCreation(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getEnableSessionCreation() {
|
||||
return delegate.getEnableSessionCreation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress localAddr) throws IOException {
|
||||
delegate.bind(localAddr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress remoteAddr) throws IOException {
|
||||
delegate.connect(remoteAddr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
|
||||
delegate.connect(remoteAddr, timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketChannel getChannel() {
|
||||
return delegate.getChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getInetAddress() {
|
||||
return delegate.getInetAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return delegate.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getKeepAlive() throws SocketException {
|
||||
return delegate.getKeepAlive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getLocalAddress() {
|
||||
return delegate.getLocalAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalPort() {
|
||||
return delegate.getLocalPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
return delegate.getLocalSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getOOBInline() throws SocketException {
|
||||
return delegate.getOOBInline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
return delegate.getOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return delegate.getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getReceiveBufferSize() throws SocketException {
|
||||
return delegate.getReceiveBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteSocketAddress() {
|
||||
return delegate.getRemoteSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getReuseAddress() throws SocketException {
|
||||
return delegate.getReuseAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getSendBufferSize() throws SocketException {
|
||||
return delegate.getSendBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSoLinger() throws SocketException {
|
||||
return delegate.getSoLinger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getSoTimeout() throws SocketException {
|
||||
return delegate.getSoTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getTcpNoDelay() throws SocketException {
|
||||
return delegate.getTcpNoDelay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrafficClass() throws SocketException {
|
||||
return delegate.getTrafficClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBound() {
|
||||
return delegate.isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return delegate.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return delegate.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputShutdown() {
|
||||
return delegate.isInputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputShutdown() {
|
||||
return delegate.isOutputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendUrgentData(int value) throws IOException {
|
||||
delegate.sendUrgentData(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeepAlive(boolean keepAlive) throws SocketException {
|
||||
delegate.setKeepAlive(keepAlive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOOBInline(boolean oobinline) throws SocketException {
|
||||
delegate.setOOBInline(oobinline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
|
||||
delegate.setPerformancePreferences(connectionTime,
|
||||
latency, bandwidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setReceiveBufferSize(int size) throws SocketException {
|
||||
delegate.setReceiveBufferSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReuseAddress(boolean reuse) throws SocketException {
|
||||
delegate.setReuseAddress(reuse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSendBufferSize(int size) throws SocketException {
|
||||
delegate.setSendBufferSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoLinger(boolean on, int timeout) throws SocketException {
|
||||
delegate.setSoLinger(on, timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSoTimeout(int timeout) throws SocketException {
|
||||
delegate.setSoTimeout(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTcpNoDelay(boolean on) throws SocketException {
|
||||
delegate.setTcpNoDelay(on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrafficClass(int value) throws SocketException {
|
||||
delegate.setTrafficClass(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownInput() throws IOException {
|
||||
delegate.shutdownInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownOutput() throws IOException {
|
||||
delegate.shutdownOutput();
|
||||
}
|
||||
|
||||
// inspired by https://github.com/k9mail/k-9/commit/54f9fd36a77423a55f63fbf9b1bcea055a239768
|
||||
|
||||
public DelegateSSLSocket setHostname(String host) {
|
||||
try {
|
||||
delegate
|
||||
.getClass()
|
||||
.getMethod("setHostname", String.class)
|
||||
.invoke(delegate, host);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Could not enable SNI", e);
|
||||
}
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return delegate.equals(o);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,701 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014-2016 Hans-Christoph Steiner
|
||||
* Copyright 2012-2016 Nathan Freitas
|
||||
* Portions Copyright (c) 2016 CommonsWare, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package info.guardianproject.netcipher.proxy;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* Utility class to simplify setting up a proxy connection
|
||||
* to Orbot.
|
||||
* <p>
|
||||
* If you are using classes in the info.guardianproject.netcipher.client
|
||||
* package, call OrbotHelper.get(this).init(); from onCreate()
|
||||
* of a custom Application subclass, or from some other guaranteed
|
||||
* entry point to your app. At that point, the
|
||||
* info.guardianproject.netcipher.client classes will be ready
|
||||
* for use.
|
||||
*/
|
||||
public class OrbotHelper implements ProxyHelper {
|
||||
|
||||
private final static int REQUEST_CODE_STATUS = 100;
|
||||
|
||||
public final static String ORBOT_PACKAGE_NAME = "org.torproject.android";
|
||||
public final static String ORBOT_MARKET_URI = "market://details?id=" + ORBOT_PACKAGE_NAME;
|
||||
public final static String ORBOT_FDROID_URI = "https://f-droid.org/repository/browse/?fdid="
|
||||
+ ORBOT_PACKAGE_NAME;
|
||||
public final static String ORBOT_PLAY_URI = "https://play.google.com/store/apps/details?id="
|
||||
+ ORBOT_PACKAGE_NAME;
|
||||
|
||||
/**
|
||||
* A request to Orbot to transparently start Tor services
|
||||
*/
|
||||
public final static String ACTION_START = "org.torproject.android.intent.action.START";
|
||||
|
||||
/**
|
||||
* {@link Intent} send by Orbot with {@code ON/OFF/STARTING/STOPPING} status
|
||||
* included as an {@link #EXTRA_STATUS} {@code String}. Your app should
|
||||
* always receive {@code ACTION_STATUS Intent}s since any other app could
|
||||
* start Orbot. Also, user-triggered starts and stops will also cause
|
||||
* {@code ACTION_STATUS Intent}s to be broadcast.
|
||||
*/
|
||||
public final static String ACTION_STATUS = "org.torproject.android.intent.action.STATUS";
|
||||
|
||||
/**
|
||||
* {@code String} that contains a status constant: {@link #STATUS_ON},
|
||||
* {@link #STATUS_OFF}, {@link #STATUS_STARTING}, or
|
||||
* {@link #STATUS_STOPPING}
|
||||
*/
|
||||
public final static String EXTRA_STATUS = "org.torproject.android.intent.extra.STATUS";
|
||||
/**
|
||||
* A {@link String} {@code packageName} for Orbot to direct its status reply
|
||||
* to, used in {@link #ACTION_START} {@link Intent}s sent to Orbot
|
||||
*/
|
||||
public final static String EXTRA_PACKAGE_NAME = "org.torproject.android.intent.extra.PACKAGE_NAME";
|
||||
|
||||
public final static String EXTRA_PROXY_PORT_HTTP = "org.torproject.android.intent.extra.HTTP_PROXY_PORT";
|
||||
public final static String EXTRA_PROXY_PORT_SOCKS = "org.torproject.android.intent.extra.SOCKS_PROXY_PORT";
|
||||
|
||||
|
||||
/**
|
||||
* All tor-related services and daemons are stopped
|
||||
*/
|
||||
public final static String STATUS_OFF = "OFF";
|
||||
/**
|
||||
* All tor-related services and daemons have completed starting
|
||||
*/
|
||||
public final static String STATUS_ON = "ON";
|
||||
public final static String STATUS_STARTING = "STARTING";
|
||||
public final static String STATUS_STOPPING = "STOPPING";
|
||||
/**
|
||||
* The user has disabled the ability for background starts triggered by
|
||||
* apps. Fallback to the old Intent that brings up Orbot.
|
||||
*/
|
||||
public final static String STATUS_STARTS_DISABLED = "STARTS_DISABLED";
|
||||
|
||||
public final static String ACTION_START_TOR = "org.torproject.android.START_TOR";
|
||||
public final static String ACTION_REQUEST_HS = "org.torproject.android.REQUEST_HS_PORT";
|
||||
public final static int START_TOR_RESULT = 0x9234;
|
||||
public final static int HS_REQUEST_CODE = 9999;
|
||||
|
||||
|
||||
/*
|
||||
private OrbotHelper() {
|
||||
// only static utility methods, do not instantiate
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test whether a {@link URL} is a Tor Hidden Service host name, also known
|
||||
* as an ".onion address".
|
||||
*
|
||||
* @return whether the host name is a Tor .onion address
|
||||
*/
|
||||
public static boolean isOnionAddress(URL url) {
|
||||
return url.getHost().endsWith(".onion");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a URL {@link String} is a Tor Hidden Service host name, also known
|
||||
* as an ".onion address".
|
||||
*
|
||||
* @return whether the host name is a Tor .onion address
|
||||
*/
|
||||
public static boolean isOnionAddress(String urlString) {
|
||||
try {
|
||||
return isOnionAddress(new URL(urlString));
|
||||
} catch (MalformedURLException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a {@link Uri} is a Tor Hidden Service host name, also known
|
||||
* as an ".onion address".
|
||||
*
|
||||
* @return whether the host name is a Tor .onion address
|
||||
*/
|
||||
public static boolean isOnionAddress(Uri uri) {
|
||||
return uri.getHost().endsWith(".onion");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the tor process is running. This method is very
|
||||
* brittle, and is therefore deprecated in favor of using the
|
||||
* {@link #ACTION_STATUS} {@code Intent} along with the
|
||||
* {@link #requestStartTor(Context)} method.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isOrbotRunning(Context context) {
|
||||
int procId = TorServiceUtils.findProcessId(context);
|
||||
|
||||
return (procId != -1);
|
||||
}
|
||||
|
||||
public static boolean isOrbotInstalled(Context context) {
|
||||
return isAppInstalled(context, ORBOT_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
private static boolean isAppInstalled(Context context, String uri) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void requestHiddenServiceOnPort(Activity activity, int port) {
|
||||
Intent intent = new Intent(ACTION_REQUEST_HS);
|
||||
intent.setPackage(ORBOT_PACKAGE_NAME);
|
||||
intent.putExtra("hs_port", port);
|
||||
|
||||
activity.startActivityForResult(intent, HS_REQUEST_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* First, checks whether Orbot is installed. If Orbot is installed, then a
|
||||
* broadcast {@link Intent} is sent to request Orbot to start
|
||||
* transparently in the background. When Orbot receives this {@code
|
||||
* Intent}, it will immediately reply to the app that called this method
|
||||
* with an {@link #ACTION_STATUS} {@code Intent} that is broadcast to the
|
||||
* {@code packageName} of the provided {@link Context} (i.e. {@link
|
||||
* Context#getPackageName()}.
|
||||
* <p>
|
||||
* That reply {@link #ACTION_STATUS} {@code Intent} could say that the user
|
||||
* has disabled background starts with the status
|
||||
* {@link #STATUS_STARTS_DISABLED}. That means that Orbot ignored this
|
||||
* request. To directly prompt the user to start Tor, use
|
||||
* {@link #requestShowOrbotStart(Activity)}, which will bring up
|
||||
* Orbot itself for the user to manually start Tor. Orbot always broadcasts
|
||||
* it's status, so your app will receive those no matter how Tor gets
|
||||
* started.
|
||||
*
|
||||
* @param context the app {@link Context} will receive the reply
|
||||
* @return whether the start request was sent to Orbot
|
||||
* @see #requestShowOrbotStart(Activity activity)
|
||||
*/
|
||||
public static boolean requestStartTor(Context context) {
|
||||
if (OrbotHelper.isOrbotInstalled(context)) {
|
||||
Log.i("OrbotHelper", "requestStartTor " + context.getPackageName());
|
||||
Intent intent = getOrbotStartIntent(context);
|
||||
context.sendBroadcast(intent);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link Intent} for starting Orbot. Orbot will reply with the
|
||||
* current status to the {@code packageName} of the app in the provided
|
||||
* {@link Context} (i.e. {@link Context#getPackageName()}.
|
||||
*/
|
||||
public static Intent getOrbotStartIntent(Context context) {
|
||||
Intent intent = new Intent(ACTION_START);
|
||||
intent.setPackage(ORBOT_PACKAGE_NAME);
|
||||
intent.putExtra(EXTRA_PACKAGE_NAME, context.getPackageName());
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a barebones {@link Intent} for starting Orbot. This is deprecated
|
||||
* in favor of {@link #getOrbotStartIntent(Context)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Intent getOrbotStartIntent() {
|
||||
Intent intent = new Intent(ACTION_START);
|
||||
intent.setPackage(ORBOT_PACKAGE_NAME);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* First, checks whether Orbot is installed, then checks whether Orbot is
|
||||
* running. If Orbot is installed and not running, then an {@link Intent} is
|
||||
* sent to request the user to start Orbot, which will show the main Orbot screen.
|
||||
* The result will be returned in
|
||||
* {@link Activity#onActivityResult(int requestCode, int resultCode, Intent data)}
|
||||
* with a {@code requestCode} of {@code START_TOR_RESULT}
|
||||
* <p>
|
||||
* Orbot will also always broadcast the status of starting Tor via the
|
||||
* {@link #ACTION_STATUS} Intent, no matter how it is started.
|
||||
*
|
||||
* @param activity the {@code Activity} that gets the result of the
|
||||
* {@link #START_TOR_RESULT} request
|
||||
* @return whether the start request was sent to Orbot
|
||||
* @see #requestStartTor(Context context)
|
||||
*/
|
||||
public static boolean requestShowOrbotStart(Activity activity) {
|
||||
if (OrbotHelper.isOrbotInstalled(activity)) {
|
||||
if (!OrbotHelper.isOrbotRunning(activity)) {
|
||||
Intent intent = getShowOrbotStartIntent();
|
||||
activity.startActivityForResult(intent, START_TOR_RESULT);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Intent getShowOrbotStartIntent() {
|
||||
Intent intent = new Intent(ACTION_START_TOR);
|
||||
intent.setPackage(ORBOT_PACKAGE_NAME);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent getOrbotInstallIntent(Context context) {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(ORBOT_MARKET_URI));
|
||||
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> resInfos = pm.queryIntentActivities(intent, 0);
|
||||
|
||||
String foundPackageName = null;
|
||||
for (ResolveInfo r : resInfos) {
|
||||
Log.i("OrbotHelper", "market: " + r.activityInfo.packageName);
|
||||
if (TextUtils.equals(r.activityInfo.packageName, FDROID_PACKAGE_NAME)
|
||||
|| TextUtils.equals(r.activityInfo.packageName, PLAY_PACKAGE_NAME)) {
|
||||
foundPackageName = r.activityInfo.packageName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundPackageName == null) {
|
||||
intent.setData(Uri.parse(ORBOT_FDROID_URI));
|
||||
} else {
|
||||
intent.setPackage(foundPackageName);
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstalled(Context context) {
|
||||
return isOrbotInstalled(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestStatus(Context context) {
|
||||
isOrbotRunning(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestStart(Context context) {
|
||||
return requestStartTor(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getInstallIntent(Context context) {
|
||||
return getOrbotInstallIntent(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getStartIntent(Context context) {
|
||||
return getOrbotStartIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Orbot";
|
||||
}
|
||||
|
||||
/* MLM additions */
|
||||
|
||||
private final Context context;
|
||||
private final Handler handler;
|
||||
private boolean isInstalled = false;
|
||||
private Intent lastStatusIntent = null;
|
||||
private Set<StatusCallback> statusCallbacks =
|
||||
newSetFromMap(new WeakHashMap<StatusCallback, Boolean>());
|
||||
private Set<InstallCallback> installCallbacks =
|
||||
newSetFromMap(new WeakHashMap<InstallCallback, Boolean>());
|
||||
private long statusTimeoutMs = 30000L;
|
||||
private long installTimeoutMs = 60000L;
|
||||
private boolean validateOrbot = true;
|
||||
|
||||
abstract public static class SimpleStatusCallback
|
||||
implements StatusCallback {
|
||||
@Override
|
||||
public void onEnabled(Intent statusIntent) {
|
||||
// no-op; extend and override if needed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStarting() {
|
||||
// no-op; extend and override if needed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopping() {
|
||||
// no-op; extend and override if needed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled() {
|
||||
// no-op; extend and override if needed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotYetInstalled() {
|
||||
// no-op; extend and override if needed
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface used for reporting the results of an
|
||||
* attempt to install Orbot
|
||||
*/
|
||||
public interface InstallCallback {
|
||||
void onInstalled();
|
||||
|
||||
void onInstallTimeout();
|
||||
}
|
||||
|
||||
private static volatile OrbotHelper INSTANCE;
|
||||
|
||||
/**
|
||||
* Retrieves the singleton, initializing if if needed
|
||||
*
|
||||
* @param context any Context will do, as we will hold onto
|
||||
* the Application
|
||||
* @return the singleton
|
||||
*/
|
||||
synchronized public static OrbotHelper get(Context context) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new OrbotHelper(context);
|
||||
}
|
||||
|
||||
return (INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard constructor
|
||||
*
|
||||
* @param context any Context will do; OrbotInitializer will hold
|
||||
* onto the Application context
|
||||
*/
|
||||
private OrbotHelper(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.handler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a StatusCallback to be called when we find out that
|
||||
* Orbot is ready. If Orbot is ready for use, your callback
|
||||
* will be called with onEnabled() immediately, before this
|
||||
* method returns.
|
||||
*
|
||||
* @param cb a callback
|
||||
* @return the singleton, for chaining
|
||||
*/
|
||||
public OrbotHelper addStatusCallback(StatusCallback cb) {
|
||||
statusCallbacks.add(cb);
|
||||
|
||||
if (lastStatusIntent != null) {
|
||||
String status =
|
||||
lastStatusIntent.getStringExtra(OrbotHelper.EXTRA_STATUS);
|
||||
|
||||
if (status.equals(OrbotHelper.STATUS_ON)) {
|
||||
cb.onEnabled(lastStatusIntent);
|
||||
}
|
||||
}
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an existing registered StatusCallback.
|
||||
*
|
||||
* @param cb the callback to remove
|
||||
* @return the singleton, for chaining
|
||||
*/
|
||||
public OrbotHelper removeStatusCallback(StatusCallback cb) {
|
||||
statusCallbacks.remove(cb);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds an InstallCallback to be called when we find out that
|
||||
* Orbot is installed
|
||||
*
|
||||
* @param cb a callback
|
||||
* @return the singleton, for chaining
|
||||
*/
|
||||
public OrbotHelper addInstallCallback(InstallCallback cb) {
|
||||
installCallbacks.add(cb);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an existing registered InstallCallback.
|
||||
*
|
||||
* @param cb the callback to remove
|
||||
* @return the singleton, for chaining
|
||||
*/
|
||||
public OrbotHelper removeInstallCallback(InstallCallback cb) {
|
||||
installCallbacks.remove(cb);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how long of a delay, in milliseconds, after trying
|
||||
* to get a status from Orbot before we give up.
|
||||
* Defaults to 30000ms = 30 seconds = 0.000347222 days
|
||||
*
|
||||
* @param timeoutMs delay period in milliseconds
|
||||
* @return the singleton, for chaining
|
||||
*/
|
||||
public OrbotHelper statusTimeout(long timeoutMs) {
|
||||
statusTimeoutMs = timeoutMs;
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how long of a delay, in milliseconds, after trying
|
||||
* to install Orbot do we assume that it's not happening.
|
||||
* Defaults to 60000ms = 60 seconds = 1 minute = 1.90259e-6 years
|
||||
*
|
||||
* @param timeoutMs delay period in milliseconds
|
||||
* @return the singleton, for chaining
|
||||
*/
|
||||
public OrbotHelper installTimeout(long timeoutMs) {
|
||||
installTimeoutMs = timeoutMs;
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, NetCipher ensures that the Orbot on the
|
||||
* device is one of the official builds. Call this method
|
||||
* to skip that validation. Mostly, this is for developers
|
||||
* who have their own custom Orbot builds (e.g., for
|
||||
* dedicated hardware).
|
||||
*
|
||||
* @return the singleton, for chaining
|
||||
*/
|
||||
public OrbotHelper skipOrbotValidation() {
|
||||
validateOrbot = false;
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if Orbot is installed (the last time we checked),
|
||||
* false otherwise
|
||||
*/
|
||||
public boolean isInstalled() {
|
||||
return (isInstalled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the connection to Orbot, revalidating that it is installed
|
||||
* and requesting fresh status broadcasts. This is best run in your app's
|
||||
* {@link android.app.Application} subclass, in its
|
||||
* {@link android.app.Application#onCreate()} method.
|
||||
*
|
||||
* @return true if initialization is proceeding, false if Orbot is not installed,
|
||||
* or version of Orbot with a unofficial signing key is present.
|
||||
*/
|
||||
public boolean init() {
|
||||
Intent orbot = OrbotHelper.getOrbotStartIntent(context);
|
||||
|
||||
if (validateOrbot) {
|
||||
ArrayList<String> hashes = new ArrayList<String>();
|
||||
|
||||
// Tor Project signing key
|
||||
hashes.add("A4:54:B8:7A:18:47:A8:9E:D7:F5:E7:0F:BA:6B:BA:96:F3:EF:29:C2:6E:09:81:20:4F:E3:47:BF:23:1D:FD:5B");
|
||||
// f-droid.org signing key
|
||||
hashes.add("A7:02:07:92:4F:61:FF:09:37:1D:54:84:14:5C:4B:EE:77:2C:55:C1:9E:EE:23:2F:57:70:E1:82:71:F7:CB:AE");
|
||||
|
||||
orbot =
|
||||
SignatureUtils.validateBroadcastIntent(context, orbot,
|
||||
hashes, false);
|
||||
}
|
||||
|
||||
if (orbot != null) {
|
||||
isInstalled = true;
|
||||
handler.postDelayed(onStatusTimeout, statusTimeoutMs);
|
||||
context.registerReceiver(orbotStatusReceiver,
|
||||
new IntentFilter(OrbotHelper.ACTION_STATUS));
|
||||
context.sendBroadcast(orbot);
|
||||
} else {
|
||||
isInstalled = false;
|
||||
|
||||
for (StatusCallback cb : statusCallbacks) {
|
||||
cb.onNotYetInstalled();
|
||||
}
|
||||
}
|
||||
|
||||
return (isInstalled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that init() returned false, calling installOrbot()
|
||||
* will trigger an attempt to install Orbot from an available
|
||||
* distribution channel (e.g., the Play Store). Only call this
|
||||
* if the user is expecting it, such as in response to tapping
|
||||
* a dialog button or an action bar item.
|
||||
* <p>
|
||||
* Note that installation may take a long time, even if
|
||||
* the user is proceeding with the installation, due to network
|
||||
* speeds, waiting for user input, and so on. Either specify
|
||||
* a long timeout, or consider the timeout to be merely advisory
|
||||
* and use some other user input to cause you to try
|
||||
* init() again after, presumably, Orbot has been installed
|
||||
* and configured by the user.
|
||||
* <p>
|
||||
* If the user does install Orbot, we will attempt init()
|
||||
* again automatically. Hence, you will probably need user input
|
||||
* to tell you when the user has gotten Orbot up and going.
|
||||
*
|
||||
* @param host the Activity that is triggering this work
|
||||
*/
|
||||
public void installOrbot(Activity host) {
|
||||
handler.postDelayed(onInstallTimeout, installTimeoutMs);
|
||||
|
||||
IntentFilter filter =
|
||||
new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
||||
|
||||
filter.addDataScheme("package");
|
||||
|
||||
context.registerReceiver(orbotInstallReceiver, filter);
|
||||
host.startActivity(OrbotHelper.getOrbotInstallIntent(context));
|
||||
}
|
||||
|
||||
private BroadcastReceiver orbotStatusReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (TextUtils.equals(intent.getAction(),
|
||||
OrbotHelper.ACTION_STATUS)) {
|
||||
String status = intent.getStringExtra(OrbotHelper.EXTRA_STATUS);
|
||||
|
||||
if (status.equals(OrbotHelper.STATUS_ON)) {
|
||||
lastStatusIntent = intent;
|
||||
handler.removeCallbacks(onStatusTimeout);
|
||||
|
||||
for (StatusCallback cb : statusCallbacks) {
|
||||
cb.onEnabled(intent);
|
||||
}
|
||||
} else if (status.equals(OrbotHelper.STATUS_OFF)) {
|
||||
for (StatusCallback cb : statusCallbacks) {
|
||||
cb.onDisabled();
|
||||
}
|
||||
} else if (status.equals(OrbotHelper.STATUS_STARTING)) {
|
||||
for (StatusCallback cb : statusCallbacks) {
|
||||
cb.onStarting();
|
||||
}
|
||||
} else if (status.equals(OrbotHelper.STATUS_STOPPING)) {
|
||||
for (StatusCallback cb : statusCallbacks) {
|
||||
cb.onStopping();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private Runnable onStatusTimeout = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
context.unregisterReceiver(orbotStatusReceiver);
|
||||
|
||||
for (StatusCallback cb : statusCallbacks) {
|
||||
cb.onStatusTimeout();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private BroadcastReceiver orbotInstallReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (TextUtils.equals(intent.getAction(),
|
||||
Intent.ACTION_PACKAGE_ADDED)) {
|
||||
String pkgName = intent.getData().getEncodedSchemeSpecificPart();
|
||||
|
||||
if (OrbotHelper.ORBOT_PACKAGE_NAME.equals(pkgName)) {
|
||||
isInstalled = true;
|
||||
handler.removeCallbacks(onInstallTimeout);
|
||||
context.unregisterReceiver(orbotInstallReceiver);
|
||||
|
||||
for (InstallCallback cb : installCallbacks) {
|
||||
cb.onInstalled();
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private Runnable onInstallTimeout = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
context.unregisterReceiver(orbotInstallReceiver);
|
||||
|
||||
for (InstallCallback cb : installCallbacks) {
|
||||
cb.onInstallTimeout();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
|
||||
if (map.isEmpty()) {
|
||||
return new SetFromMap<E>(map);
|
||||
}
|
||||
throw new IllegalArgumentException("map not empty");
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2016 Nathan Freitas
|
||||
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package info.guardianproject.netcipher.proxy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public interface ProxyHelper {
|
||||
|
||||
public boolean isInstalled(Context context);
|
||||
|
||||
public void requestStatus(Context context);
|
||||
|
||||
public boolean requestStart(Context context);
|
||||
|
||||
public Intent getInstallIntent(Context context);
|
||||
|
||||
public Intent getStartIntent(Context context);
|
||||
|
||||
public String getName();
|
||||
|
||||
public final static String FDROID_PACKAGE_NAME = "org.fdroid.fdroid";
|
||||
public final static String PLAY_PACKAGE_NAME = "com.android.vending";
|
||||
|
||||
/**
|
||||
* A request to Orbot to transparently start Tor services
|
||||
*/
|
||||
public final static String ACTION_START = "android.intent.action.PROXY_START";
|
||||
/**
|
||||
* {@link Intent} send by Orbot with {@code ON/OFF/STARTING/STOPPING} status
|
||||
*/
|
||||
public final static String ACTION_STATUS = "android.intent.action.PROXY_STATUS";
|
||||
/**
|
||||
* {@code String} that contains a status constant: {@link #STATUS_ON},
|
||||
* {@link #STATUS_OFF}, {@link #STATUS_STARTING}, or
|
||||
* {@link #STATUS_STOPPING}
|
||||
*/
|
||||
public final static String EXTRA_STATUS = "android.intent.extra.PROXY_STATUS";
|
||||
|
||||
public final static String EXTRA_PROXY_PORT_HTTP = "android.intent.extra.PROXY_PORT_HTTP";
|
||||
public final static String EXTRA_PROXY_PORT_SOCKS = "android.intent.extra.PROXY_PORT_SOCKS";
|
||||
|
||||
/**
|
||||
* A {@link String} {@code packageName} for Orbot to direct its status reply
|
||||
* to, used in {@link #ACTION_START} {@link Intent}s sent to Orbot
|
||||
*/
|
||||
public final static String EXTRA_PACKAGE_NAME = "android.intent.extra.PROXY_PACKAGE_NAME";
|
||||
|
||||
/**
|
||||
* All tor-related services and daemons are stopped
|
||||
*/
|
||||
public final static String STATUS_OFF = "OFF";
|
||||
/**
|
||||
* All tor-related services and daemons have completed starting
|
||||
*/
|
||||
public final static String STATUS_ON = "ON";
|
||||
public final static String STATUS_STARTING = "STARTING";
|
||||
public final static String STATUS_STOPPING = "STOPPING";
|
||||
/**
|
||||
* The user has disabled the ability for background starts triggered by
|
||||
* apps. Fallback to the old Intent that brings up Orbot.
|
||||
*/
|
||||
public final static String STATUS_STARTS_DISABLED = "STARTS_DISABLED";
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2016 Nathan Freitas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package info.guardianproject.netcipher.proxy;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ProxySelector extends java.net.ProxySelector {
|
||||
|
||||
private ArrayList<Proxy> listProxies;
|
||||
|
||||
public ProxySelector() {
|
||||
super();
|
||||
|
||||
listProxies = new ArrayList<Proxy>();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void addProxy(Proxy.Type type, String host, int port) {
|
||||
Proxy proxy = new Proxy(type, new InetSocketAddress(host, port));
|
||||
listProxies.add(proxy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectFailed(URI uri, SocketAddress address,
|
||||
IOException failure) {
|
||||
Log.w("ProxySelector", "could not connect to " + address.toString() + ": " + failure.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Proxy> select(URI uri) {
|
||||
|
||||
return listProxies;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2016 Nathan Freitas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package info.guardianproject.netcipher.proxy;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.List;
|
||||
|
||||
public class PsiphonHelper implements ProxyHelper {
|
||||
|
||||
public final static String PACKAGE_NAME = "com.psiphon3";
|
||||
public final static String COMPONENT_NAME = "com.psiphon3.StatusActivity";
|
||||
|
||||
|
||||
public final static String MARKET_URI = "market://details?id=" + PACKAGE_NAME;
|
||||
public final static String FDROID_URI = "https://f-droid.org/repository/browse/?fdid="
|
||||
+ PACKAGE_NAME;
|
||||
public final static String ORBOT_PLAY_URI = "https://play.google.com/store/apps/details?id="
|
||||
+ PACKAGE_NAME;
|
||||
|
||||
public final static int DEFAULT_SOCKS_PORT = 1080;
|
||||
public final static int DEFAULT_HTTP_PORT = 8080;
|
||||
|
||||
@Override
|
||||
public boolean isInstalled(Context context) {
|
||||
return isAppInstalled(context, PACKAGE_NAME);
|
||||
}
|
||||
|
||||
|
||||
private static boolean isAppInstalled(Context context, String uri) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestStatus(final Context context) {
|
||||
|
||||
Thread thread = new Thread() {
|
||||
public void run() {
|
||||
//can connect to default HTTP proxy port?
|
||||
boolean isSocksOpen = false;
|
||||
boolean isHttpOpen = false;
|
||||
|
||||
int socksPort = DEFAULT_SOCKS_PORT;
|
||||
int httpPort = DEFAULT_HTTP_PORT;
|
||||
|
||||
for (int i = 0; i < 10 && (!isSocksOpen); i++)
|
||||
isSocksOpen = isPortOpen("127.0.0.1", socksPort++, 100);
|
||||
|
||||
for (int i = 0; i < 10 && (!isHttpOpen); i++)
|
||||
isHttpOpen = isPortOpen("127.0.0.1", httpPort++, 100);
|
||||
|
||||
//any other check?
|
||||
|
||||
Intent intent = new Intent(ProxyHelper.ACTION_STATUS);
|
||||
intent.putExtra(EXTRA_PACKAGE_NAME, PACKAGE_NAME);
|
||||
|
||||
if (isSocksOpen && isHttpOpen) {
|
||||
intent.putExtra(EXTRA_STATUS, STATUS_ON);
|
||||
|
||||
intent.putExtra(EXTRA_PROXY_PORT_HTTP, httpPort - 1);
|
||||
intent.putExtra(EXTRA_PROXY_PORT_SOCKS, socksPort - 1);
|
||||
|
||||
|
||||
} else {
|
||||
intent.putExtra(EXTRA_STATUS, STATUS_OFF);
|
||||
}
|
||||
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
};
|
||||
|
||||
thread.start();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestStart(Context context) {
|
||||
|
||||
Intent intent = getStartIntent(context);
|
||||
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getInstallIntent(Context context) {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(MARKET_URI));
|
||||
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> resInfos = pm.queryIntentActivities(intent, 0);
|
||||
|
||||
String foundPackageName = null;
|
||||
for (ResolveInfo r : resInfos) {
|
||||
if (TextUtils.equals(r.activityInfo.packageName, FDROID_PACKAGE_NAME)
|
||||
|| TextUtils.equals(r.activityInfo.packageName, PLAY_PACKAGE_NAME)) {
|
||||
foundPackageName = r.activityInfo.packageName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundPackageName == null) {
|
||||
intent.setData(Uri.parse(FDROID_URI));
|
||||
} else {
|
||||
intent.setPackage(foundPackageName);
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getStartIntent(Context context) {
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(new ComponentName(PACKAGE_NAME, COMPONENT_NAME));
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static boolean isPortOpen(final String ip, final int port, final int timeout) {
|
||||
try {
|
||||
Socket socket = new Socket();
|
||||
socket.connect(new InetSocketAddress(ip, port), timeout);
|
||||
socket.close();
|
||||
return true;
|
||||
} catch (ConnectException ce) {
|
||||
ce.printStackTrace();
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return PACKAGE_NAME;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package info.guardianproject.netcipher.proxy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
class SetFromMap<E> extends AbstractSet<E>
|
||||
implements Serializable {
|
||||
private static final long serialVersionUID = 2454657854757543876L;
|
||||
// Must be named as is, to pass serialization compatibility test.
|
||||
private final Map<E, Boolean> m;
|
||||
private transient Set<E> backingSet;
|
||||
|
||||
SetFromMap(final Map<E, Boolean> map) {
|
||||
m = map;
|
||||
backingSet = map.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
return backingSet.equals(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return backingSet.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E object) {
|
||||
return m.put(object, Boolean.TRUE) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
m.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return backingSet.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object object) {
|
||||
return backingSet.contains(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> collection) {
|
||||
return backingSet.containsAll(collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return m.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object object) {
|
||||
return m.remove(object) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> collection) {
|
||||
return backingSet.retainAll(collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return backingSet.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] contents) {
|
||||
return backingSet.toArray(contents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return backingSet.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return m.size();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
stream.defaultReadObject();
|
||||
backingSet = m.keySet();
|
||||
}
|
||||
}
|
|
@ -1,468 +0,0 @@
|
|||
/***
|
||||
* Copyright (c) 2014 CommonsWare, LLC
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package info.guardianproject.netcipher.proxy;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.Signature;
|
||||
import android.util.Log;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SignatureUtils {
|
||||
public static String getOwnSignatureHash(Context context)
|
||||
throws
|
||||
NameNotFoundException,
|
||||
NoSuchAlgorithmException {
|
||||
return (getSignatureHash(context, context.getPackageName()));
|
||||
}
|
||||
|
||||
public static String getSignatureHash(Context context, String packageName)
|
||||
throws
|
||||
NameNotFoundException,
|
||||
NoSuchAlgorithmException {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
Signature sig =
|
||||
context.getPackageManager()
|
||||
.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures[0];
|
||||
|
||||
return (toHexStringWithColons(md.digest(sig.toByteArray())));
|
||||
}
|
||||
|
||||
// based on https://stackoverflow.com/a/2197650/115145
|
||||
|
||||
public static String toHexStringWithColons(byte[] bytes) {
|
||||
char[] hexArray =
|
||||
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
||||
'C', 'D', 'E', 'F'};
|
||||
char[] hexChars = new char[(bytes.length * 3) - 1];
|
||||
int v;
|
||||
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
v = bytes[j] & 0xFF;
|
||||
hexChars[j * 3] = hexArray[v / 16];
|
||||
hexChars[j * 3 + 1] = hexArray[v % 16];
|
||||
|
||||
if (j < bytes.length - 1) {
|
||||
hexChars[j * 3 + 2] = ':';
|
||||
}
|
||||
}
|
||||
|
||||
return new String(hexChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the broadcast receiver for a given Intent
|
||||
* has the desired signature hash.
|
||||
* <p>
|
||||
* If you know the package name of the receiver, call
|
||||
* setPackage() on the Intent before passing into this method.
|
||||
* That will validate whether the package is installed and whether
|
||||
* it has the proper signature hash. You can distinguish between
|
||||
* these cases by passing true for the failIfHack parameter.
|
||||
* <p>
|
||||
* In general, there are three possible outcomes of calling
|
||||
* this method:
|
||||
* <p>
|
||||
* 1. You get a SecurityException, because failIfHack is true,
|
||||
* and we found some receiver whose app does not match the
|
||||
* desired hash. The user may have installed a repackaged
|
||||
* version of this app that is signed by the wrong key.
|
||||
* <p>
|
||||
* 2. You get null. If failIfHack is true, this means that no
|
||||
* receiver was found that matches the Intent. If failIfHack
|
||||
* is false, this means that no receiver was found that matches
|
||||
* the Intent and has a valid matching signature.
|
||||
* <p>
|
||||
* 3. You get an Intent. This means we found a matching receiver
|
||||
* that has a matching signature. The Intent will be a copy of
|
||||
* the passed-in Intent, with the component name set to the
|
||||
* matching receiver, so the "broadcast" will only go to this
|
||||
* one component.
|
||||
*
|
||||
* @param context any Context will do; the value is not retained
|
||||
* @param toValidate the Intent that you intend to broadcast
|
||||
* @param sigHash the signature hash of the app that you expect
|
||||
* to handle this broadcast
|
||||
* @param failIfHack true if you want a SecurityException if
|
||||
* a matching receiver is found but it has
|
||||
* the wrong signature hash, false otherwise
|
||||
* @return null if there is no matching receiver with the correct
|
||||
* hash, or a copy of the toValidate parameter with the full component
|
||||
* name of the target receiver added to the Intent
|
||||
*/
|
||||
public static Intent validateBroadcastIntent(Context context,
|
||||
Intent toValidate,
|
||||
String sigHash,
|
||||
boolean failIfHack) {
|
||||
ArrayList<String> sigHashes = new ArrayList<String>();
|
||||
|
||||
sigHashes.add(sigHash);
|
||||
|
||||
return (validateBroadcastIntent(context, toValidate, sigHashes,
|
||||
failIfHack));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the broadcast receiver for a given Intent
|
||||
* has a desired signature hash.
|
||||
* <p>
|
||||
* If you know the package name of the receiver, call
|
||||
* setPackage() on the Intent before passing into this method.
|
||||
* That will validate whether the package is installed and whether
|
||||
* it has a proper signature hash. You can distinguish between
|
||||
* these cases by passing true for the failIfHack parameter.
|
||||
* <p>
|
||||
* In general, there are three possible outcomes of calling
|
||||
* this method:
|
||||
* <p>
|
||||
* 1. You get a SecurityException, because failIfHack is true,
|
||||
* and we found some receiver whose app does not match the
|
||||
* desired hash. The user may have installed a repackaged
|
||||
* version of this app that is signed by the wrong key.
|
||||
* <p>
|
||||
* 2. You get null. If failIfHack is true, this means that no
|
||||
* receiver was found that matches the Intent. If failIfHack
|
||||
* is false, this means that no receiver was found that matches
|
||||
* the Intent and has a valid matching signature.
|
||||
* <p>
|
||||
* 3. You get an Intent. This means we found a matching receiver
|
||||
* that has a matching signature. The Intent will be a copy of
|
||||
* the passed-in Intent, with the component name set to the
|
||||
* matching receiver, so the "broadcast" will only go to this
|
||||
* one component.
|
||||
*
|
||||
* @param context any Context will do; the value is not retained
|
||||
* @param toValidate the Intent that you intend to broadcast
|
||||
* @param sigHashes the possible signature hashes of the app
|
||||
* that you expect to handle this broadcast
|
||||
* @param failIfHack true if you want a SecurityException if
|
||||
* a matching receiver is found but it has
|
||||
* the wrong signature hash, false otherwise
|
||||
* @return null if there is no matching receiver with the correct
|
||||
* hash, or a copy of the toValidate parameter with the full component
|
||||
* name of the target receiver added to the Intent
|
||||
*/
|
||||
public static Intent validateBroadcastIntent(Context context,
|
||||
Intent toValidate,
|
||||
List<String> sigHashes,
|
||||
boolean failIfHack) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
Intent result = null;
|
||||
List<ResolveInfo> receivers =
|
||||
pm.queryBroadcastReceivers(toValidate, 0);
|
||||
|
||||
if (receivers != null) {
|
||||
for (ResolveInfo info : receivers) {
|
||||
try {
|
||||
if (sigHashes.contains(getSignatureHash(context,
|
||||
info.activityInfo.packageName))) {
|
||||
ComponentName cn =
|
||||
new ComponentName(info.activityInfo.packageName,
|
||||
info.activityInfo.name);
|
||||
|
||||
result = new Intent(toValidate).setComponent(cn);
|
||||
break;
|
||||
} else if (failIfHack) {
|
||||
throw new SecurityException(
|
||||
"Package has signature hash mismatch: " +
|
||||
info.activityInfo.packageName);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.w("SignatureUtils",
|
||||
"Exception when computing signature hash", e);
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w("SignatureUtils",
|
||||
"Exception when computing signature hash", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the activity for a given Intent has the
|
||||
* desired signature hash.
|
||||
* <p>
|
||||
* If you know the package name of the activity, call
|
||||
* setPackage() on the Intent before passing into this method.
|
||||
* That will validate whether the package is installed and whether
|
||||
* it has the proper signature hash. You can distinguish between
|
||||
* these cases by passing true for the failIfHack parameter.
|
||||
* <p>
|
||||
* In general, there are three possible outcomes of calling
|
||||
* this method:
|
||||
* <p>
|
||||
* 1. You get a SecurityException, because failIfHack is true,
|
||||
* and we found some activity whose app does not match the
|
||||
* desired hash. The user may have installed a repackaged
|
||||
* version of this app that is signed by the wrong key.
|
||||
* <p>
|
||||
* 2. You get null. If failIfHack is true, this means that no
|
||||
* activity was found that matches the Intent. If failIfHack
|
||||
* is false, this means that no activity was found that matches
|
||||
* the Intent and has a valid matching signature.
|
||||
* <p>
|
||||
* 3. You get an Intent. This means we found a matching activity
|
||||
* that has a matching signature. The Intent will be a copy of
|
||||
* the passed-in Intent, with the component name set to the
|
||||
* matching activity, so a call to startActivity() for this
|
||||
* Intent is guaranteed to go to this specific activity.
|
||||
*
|
||||
* @param context any Context will do; the value is not retained
|
||||
* @param toValidate the Intent that you intend to use with
|
||||
* startActivity()
|
||||
* @param sigHash the signature hash of the app that you expect
|
||||
* to handle this activity
|
||||
* @param failIfHack true if you want a SecurityException if
|
||||
* a matching activity is found but it has
|
||||
* the wrong signature hash, false otherwise
|
||||
* @return null if there is no matching activity with the correct
|
||||
* hash, or a copy of the toValidate parameter with the full component
|
||||
* name of the target activity added to the Intent
|
||||
*/
|
||||
public static Intent validateActivityIntent(Context context,
|
||||
Intent toValidate,
|
||||
String sigHash,
|
||||
boolean failIfHack) {
|
||||
ArrayList<String> sigHashes = new ArrayList<String>();
|
||||
|
||||
sigHashes.add(sigHash);
|
||||
|
||||
return (validateActivityIntent(context, toValidate, sigHashes,
|
||||
failIfHack));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the activity for a given Intent has the
|
||||
* desired signature hash.
|
||||
* <p>
|
||||
* If you know the package name of the activity, call
|
||||
* setPackage() on the Intent before passing into this method.
|
||||
* That will validate whether the package is installed and whether
|
||||
* it has the proper signature hash. You can distinguish between
|
||||
* these cases by passing true for the failIfHack parameter.
|
||||
* <p>
|
||||
* In general, there are three possible outcomes of calling
|
||||
* this method:
|
||||
* <p>
|
||||
* 1. You get a SecurityException, because failIfHack is true,
|
||||
* and we found some activity whose app does not match the
|
||||
* desired hash. The user may have installed a repackaged
|
||||
* version of this app that is signed by the wrong key.
|
||||
* <p>
|
||||
* 2. You get null. If failIfHack is true, this means that no
|
||||
* activity was found that matches the Intent. If failIfHack
|
||||
* is false, this means that no activity was found that matches
|
||||
* the Intent and has a valid matching signature.
|
||||
* <p>
|
||||
* 3. You get an Intent. This means we found a matching activity
|
||||
* that has a matching signature. The Intent will be a copy of
|
||||
* the passed-in Intent, with the component name set to the
|
||||
* matching activity, so a call to startActivity() for this
|
||||
* Intent is guaranteed to go to this specific activity.
|
||||
*
|
||||
* @param context any Context will do; the value is not retained
|
||||
* @param toValidate the Intent that you intend to use with
|
||||
* startActivity()
|
||||
* @param sigHashes the signature hashes of the app that you expect
|
||||
* to handle this activity
|
||||
* @param failIfHack true if you want a SecurityException if
|
||||
* a matching activity is found but it has
|
||||
* the wrong signature hash, false otherwise
|
||||
* @return null if there is no matching activity with the correct
|
||||
* hash, or a copy of the toValidate parameter with the full component
|
||||
* name of the target activity added to the Intent
|
||||
*/
|
||||
public static Intent validateActivityIntent(Context context,
|
||||
Intent toValidate,
|
||||
List<String> sigHashes,
|
||||
boolean failIfHack) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
Intent result = null;
|
||||
List<ResolveInfo> activities =
|
||||
pm.queryIntentActivities(toValidate, 0);
|
||||
|
||||
if (activities != null) {
|
||||
for (ResolveInfo info : activities) {
|
||||
try {
|
||||
if (sigHashes.contains(getSignatureHash(context,
|
||||
info.activityInfo.packageName))) {
|
||||
ComponentName cn =
|
||||
new ComponentName(info.activityInfo.packageName,
|
||||
info.activityInfo.name);
|
||||
|
||||
result = new Intent(toValidate).setComponent(cn);
|
||||
break;
|
||||
} else if (failIfHack) {
|
||||
throw new SecurityException(
|
||||
"Package has signature hash mismatch: " +
|
||||
info.activityInfo.packageName);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.w("SignatureUtils",
|
||||
"Exception when computing signature hash", e);
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w("SignatureUtils",
|
||||
"Exception when computing signature hash", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the service for a given Intent has the
|
||||
* desired signature hash.
|
||||
* <p>
|
||||
* If you know the package name of the service, call
|
||||
* setPackage() on the Intent before passing into this method.
|
||||
* That will validate whether the package is installed and whether
|
||||
* it has the proper signature hash. You can distinguish between
|
||||
* these cases by passing true for the failIfHack parameter.
|
||||
* <p>
|
||||
* In general, there are three possible outcomes of calling
|
||||
* this method:
|
||||
* <p>
|
||||
* 1. You get a SecurityException, because failIfHack is true,
|
||||
* and we found some service whose app does not match the
|
||||
* desired hash. The user may have installed a repackaged
|
||||
* version of this app that is signed by the wrong key.
|
||||
* <p>
|
||||
* 2. You get null. If failIfHack is true, this means that no
|
||||
* service was found that matches the Intent. If failIfHack
|
||||
* is false, this means that no service was found that matches
|
||||
* the Intent and has a valid matching signature.
|
||||
* <p>
|
||||
* 3. You get an Intent. This means we found a matching service
|
||||
* that has a matching signature. The Intent will be a copy of
|
||||
* the passed-in Intent, with the component name set to the
|
||||
* matching service, so a call to startService() or
|
||||
* bindService() for this Intent is guaranteed to go to this
|
||||
* specific service.
|
||||
*
|
||||
* @param context any Context will do; the value is not retained
|
||||
* @param toValidate the Intent that you intend to use with
|
||||
* startService() or bindService()
|
||||
* @param sigHash the signature hash of the app that you expect
|
||||
* to handle this service
|
||||
* @param failIfHack true if you want a SecurityException if
|
||||
* a matching service is found but it has
|
||||
* the wrong signature hash, false otherwise
|
||||
* @return null if there is no matching service with the correct
|
||||
* hash, or a copy of the toValidate parameter with the full component
|
||||
* name of the target service added to the Intent
|
||||
*/
|
||||
public static Intent validateServiceIntent(Context context,
|
||||
Intent toValidate,
|
||||
String sigHash,
|
||||
boolean failIfHack) {
|
||||
ArrayList<String> sigHashes = new ArrayList<String>();
|
||||
|
||||
sigHashes.add(sigHash);
|
||||
|
||||
return (validateServiceIntent(context, toValidate, sigHashes,
|
||||
failIfHack));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the service for a given Intent has the
|
||||
* desired signature hash.
|
||||
* <p>
|
||||
* If you know the package name of the service, call
|
||||
* setPackage() on the Intent before passing into this method.
|
||||
* That will validate whether the package is installed and whether
|
||||
* it has the proper signature hash. You can distinguish between
|
||||
* these cases by passing true for the failIfHack parameter.
|
||||
* <p>
|
||||
* In general, there are three possible outcomes of calling
|
||||
* this method:
|
||||
* <p>
|
||||
* 1. You get a SecurityException, because failIfHack is true,
|
||||
* and we found some service whose app does not match the
|
||||
* desired hash. The user may have installed a repackaged
|
||||
* version of this app that is signed by the wrong key.
|
||||
* <p>
|
||||
* 2. You get null. If failIfHack is true, this means that no
|
||||
* service was found that matches the Intent. If failIfHack
|
||||
* is false, this means that no service was found that matches
|
||||
* the Intent and has a valid matching signature.
|
||||
* <p>
|
||||
* 3. You get an Intent. This means we found a matching service
|
||||
* that has a matching signature. The Intent will be a copy of
|
||||
* the passed-in Intent, with the component name set to the
|
||||
* matching service, so a call to startService() or
|
||||
* bindService() for this Intent is guaranteed to go to this
|
||||
* specific service.
|
||||
*
|
||||
* @param context any Context will do; the value is not retained
|
||||
* @param toValidate the Intent that you intend to use with
|
||||
* startService() or bindService()
|
||||
* @param sigHashes the signature hash of the app that you expect
|
||||
* to handle this service
|
||||
* @param failIfHack true if you want a SecurityException if
|
||||
* a matching service is found but it has
|
||||
* the wrong signature hash, false otherwise
|
||||
* @return null if there is no matching service with the correct
|
||||
* hash, or a copy of the toValidate parameter with the full component
|
||||
* name of the target service added to the Intent
|
||||
*/
|
||||
public static Intent validateServiceIntent(Context context,
|
||||
Intent toValidate,
|
||||
List<String> sigHashes,
|
||||
boolean failIfHack) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
Intent result = null;
|
||||
List<ResolveInfo> services =
|
||||
pm.queryIntentServices(toValidate, 0);
|
||||
|
||||
if (services != null) {
|
||||
for (ResolveInfo info : services) {
|
||||
try {
|
||||
if (sigHashes.contains(getSignatureHash(context,
|
||||
info.serviceInfo.packageName))) {
|
||||
ComponentName cn =
|
||||
new ComponentName(info.serviceInfo.packageName,
|
||||
info.serviceInfo.name);
|
||||
|
||||
result = new Intent(toValidate).setComponent(cn);
|
||||
break;
|
||||
} else if (failIfHack) {
|
||||
throw new SecurityException(
|
||||
"Package has signature hash mismatch: " +
|
||||
info.activityInfo.packageName);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.w("SignatureUtils",
|
||||
"Exception when computing signature hash", e);
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w("SignatureUtils",
|
||||
"Exception when computing signature hash", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 CommonsWare, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package info.guardianproject.netcipher.proxy;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Callback interface used for reporting Orbot status
|
||||
*/
|
||||
public interface StatusCallback {
|
||||
/**
|
||||
* Called when Orbot is operational
|
||||
*
|
||||
* @param statusIntent an Intent containing information about
|
||||
* Orbot, including proxy ports
|
||||
*/
|
||||
void onEnabled(Intent statusIntent);
|
||||
|
||||
/**
|
||||
* Called when Orbot reports that it is starting up
|
||||
*/
|
||||
void onStarting();
|
||||
|
||||
/**
|
||||
* Called when Orbot reports that it is shutting down
|
||||
*/
|
||||
void onStopping();
|
||||
|
||||
/**
|
||||
* Called when Orbot reports that it is no longer running
|
||||
*/
|
||||
void onDisabled();
|
||||
|
||||
/**
|
||||
* Called if our attempt to get a status from Orbot failed
|
||||
* after a defined period of time. See statusTimeout() on
|
||||
* OrbotInitializer.
|
||||
*/
|
||||
void onStatusTimeout();
|
||||
|
||||
/**
|
||||
* Called if Orbot is not yet installed. Usually, you handle
|
||||
* this by checking the return value from init() on OrbotInitializer
|
||||
* or calling isInstalled() on OrbotInitializer. However, if
|
||||
* you have need for it, if a callback is registered before
|
||||
* an init() call determines that Orbot is not installed, your
|
||||
* callback will be called with onNotYetInstalled().
|
||||
*/
|
||||
void onNotYetInstalled();
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
/*
|
||||
* Copyright 2009-2016 Nathan Freitas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package info.guardianproject.netcipher.proxy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class TorServiceUtils {
|
||||
|
||||
private final static String TAG = "TorUtils";
|
||||
// various console cmds
|
||||
public final static String SHELL_CMD_CHMOD = "chmod";
|
||||
public final static String SHELL_CMD_KILL = "kill -9";
|
||||
public final static String SHELL_CMD_RM = "rm";
|
||||
public final static String SHELL_CMD_PS = "ps";
|
||||
public final static String SHELL_CMD_PIDOF = "pidof";
|
||||
|
||||
public final static String CHMOD_EXE_VALUE = "700";
|
||||
|
||||
public static boolean isRootPossible() {
|
||||
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
try {
|
||||
|
||||
// Check if Superuser.apk exists
|
||||
File fileSU = new File("/system/app/Superuser.apk");
|
||||
if (fileSU.exists())
|
||||
return true;
|
||||
|
||||
fileSU = new File("/system/app/superuser.apk");
|
||||
if (fileSU.exists())
|
||||
return true;
|
||||
|
||||
fileSU = new File("/system/bin/su");
|
||||
if (fileSU.exists()) {
|
||||
String[] cmd = {
|
||||
"su"
|
||||
};
|
||||
int exitCode = TorServiceUtils.doShellCommand(cmd, log, false, true);
|
||||
if (exitCode != 0)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for 'su' binary
|
||||
String[] cmd = {
|
||||
"which su"
|
||||
};
|
||||
int exitCode = TorServiceUtils.doShellCommand(cmd, log, false, true);
|
||||
|
||||
if (exitCode == 0) {
|
||||
Log.d(TAG, "root exists, but not sure about permissions");
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
// this means that there is no root to be had (normally) so we won't
|
||||
// log anything
|
||||
Log.e(TAG, "Error checking for root access", e);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error checking for root access", e);
|
||||
// this means that there is no root to be had (normally)
|
||||
}
|
||||
|
||||
Log.e(TAG, "Could not acquire root permissions");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int findProcessId(Context context) {
|
||||
String dataPath = context.getFilesDir().getParentFile().getParentFile().getAbsolutePath();
|
||||
String command = dataPath + "/" + OrbotHelper.ORBOT_PACKAGE_NAME + "/app_bin/tor";
|
||||
int procId = -1;
|
||||
|
||||
try {
|
||||
procId = findProcessIdWithPidOf(command);
|
||||
|
||||
if (procId == -1)
|
||||
procId = findProcessIdWithPS(command);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
procId = findProcessIdWithPS(command);
|
||||
} catch (Exception e2) {
|
||||
Log.e(TAG, "Unable to get proc id for command: " + URLEncoder.encode(command), e2);
|
||||
}
|
||||
}
|
||||
|
||||
return procId;
|
||||
}
|
||||
|
||||
// use 'pidof' command
|
||||
public static int findProcessIdWithPidOf(String command) throws Exception {
|
||||
|
||||
int procId = -1;
|
||||
|
||||
Runtime r = Runtime.getRuntime();
|
||||
|
||||
Process procPs = null;
|
||||
|
||||
String baseName = new File(command).getName();
|
||||
// fix contributed my mikos on 2010.12.10
|
||||
procPs = r.exec(new String[]{
|
||||
SHELL_CMD_PIDOF, baseName
|
||||
});
|
||||
// procPs = r.exec(SHELL_CMD_PIDOF);
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
|
||||
String line = null;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
|
||||
try {
|
||||
// this line should just be the process id
|
||||
procId = Integer.parseInt(line.trim());
|
||||
break;
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e("TorServiceUtils", "unable to parse process pid: " + line, e);
|
||||
}
|
||||
}
|
||||
|
||||
return procId;
|
||||
|
||||
}
|
||||
|
||||
// use 'ps' command
|
||||
public static int findProcessIdWithPS(String command) throws Exception {
|
||||
|
||||
int procId = -1;
|
||||
|
||||
Runtime r = Runtime.getRuntime();
|
||||
|
||||
Process procPs = null;
|
||||
|
||||
procPs = r.exec(SHELL_CMD_PS);
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
|
||||
String line = null;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.indexOf(' ' + command) != -1) {
|
||||
|
||||
StringTokenizer st = new StringTokenizer(line, " ");
|
||||
st.nextToken(); // proc owner
|
||||
|
||||
procId = Integer.parseInt(st.nextToken().trim());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return procId;
|
||||
|
||||
}
|
||||
|
||||
public static int doShellCommand(String[] cmds, StringBuilder log, boolean runAsRoot,
|
||||
boolean waitFor) throws Exception {
|
||||
|
||||
Process proc = null;
|
||||
int exitCode = -1;
|
||||
|
||||
if (runAsRoot)
|
||||
proc = Runtime.getRuntime().exec("su");
|
||||
else
|
||||
proc = Runtime.getRuntime().exec("sh");
|
||||
|
||||
OutputStreamWriter out = new OutputStreamWriter(proc.getOutputStream());
|
||||
|
||||
for (int i = 0; i < cmds.length; i++) {
|
||||
// TorService.logMessage("executing shell cmd: " + cmds[i] +
|
||||
// "; runAsRoot=" + runAsRoot + ";waitFor=" + waitFor);
|
||||
|
||||
out.write(cmds[i]);
|
||||
out.write("\n");
|
||||
}
|
||||
|
||||
out.flush();
|
||||
out.write("exit\n");
|
||||
out.flush();
|
||||
|
||||
if (waitFor) {
|
||||
|
||||
final char buf[] = new char[10];
|
||||
|
||||
// Consume the "stdout"
|
||||
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
|
||||
int read = 0;
|
||||
while ((read = reader.read(buf)) != -1) {
|
||||
if (log != null)
|
||||
log.append(buf, 0, read);
|
||||
}
|
||||
|
||||
// Consume the "stderr"
|
||||
reader = new InputStreamReader(proc.getErrorStream());
|
||||
read = 0;
|
||||
while ((read = reader.read(buf)) != -1) {
|
||||
if (log != null)
|
||||
log.append(buf, 0, read);
|
||||
}
|
||||
|
||||
exitCode = proc.waitFor();
|
||||
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
public class status
|
||||
{
|
||||
public static boolean hasApplicationLoaded = false;
|
||||
public static String currentURL = "http://boogle.store/";
|
||||
}
|
|
@ -1,55 +1,50 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Message;
|
||||
import android.support.constraint.ConstraintLayout;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import android.os.Handler;
|
||||
import cz.msebera.android.httpclient.HttpHost;
|
||||
import cz.msebera.android.httpclient.HttpResponse;
|
||||
import cz.msebera.android.httpclient.client.HttpClient;
|
||||
import cz.msebera.android.httpclient.client.methods.HttpGet;
|
||||
import cz.msebera.android.httpclient.conn.params.ConnRoutePNames;
|
||||
import cz.msebera.android.httpclient.impl.client.DefaultHttpClient;
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
import info.guardianproject.netcipher.client.StrongBuilder;
|
||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
||||
{
|
||||
private static final webRequestHandler ourInstance = new webRequestHandler();
|
||||
private WebView[] view = new WebView[2];;
|
||||
private WebView[] view = new WebView[2];
|
||||
private ProgressBar progressBar;
|
||||
private EditText searchbar;
|
||||
private ConstraintLayout requestFailure;
|
||||
|
||||
public boolean isReloadedUrl = false;
|
||||
private int viewIndex = 1;
|
||||
private int currentViewIndex = 0;
|
||||
private String html = "";
|
||||
private String baseURL = "";
|
||||
private Boolean isLoading = false;
|
||||
private ProgressBar progressBar;
|
||||
private EditText searchbar;
|
||||
private Thread clientThread = null;
|
||||
private ConstraintLayout requestFailure;
|
||||
HttpGet request = null;
|
||||
private Handler updateUIHandler = null;
|
||||
|
||||
// test the local device proxy provided by Orbot/Tor
|
||||
private final static String PROXY_HOST = "127.0.0.1";
|
||||
private final static int PROXY_HTTP_PORT = 8118; // default for Orbot/Tor
|
||||
private final static int PROXY_SOCKS_PORT = 9050; // default for Orbot/Tor
|
||||
private Proxy.Type mProxyType = null;
|
||||
private final static int MESSAGE_UPDATE_TEXT_CHILD_THREAD =1;
|
||||
private final static int INTERNET_ERROR =2;
|
||||
|
||||
public static webRequestHandler getInstance() {
|
||||
return ourInstance;
|
||||
|
@ -70,11 +65,40 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
createUpdateUiHandler();
|
||||
}
|
||||
|
||||
public boolean isReloadedUrl = false;
|
||||
public void loadURL(final String url)
|
||||
{
|
||||
Log.d("ME HERE 1 : ","SUPER WOW");
|
||||
|
||||
try
|
||||
{
|
||||
preInitialization(url);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
clientThread = new Thread(() -> {
|
||||
try
|
||||
{
|
||||
if(url.contains("boogle.store"))
|
||||
{
|
||||
nonProxyConnection(url);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxyConnection(url);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
onError();
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
clientThread.start();
|
||||
}
|
||||
|
||||
public void preInitialization(String url)
|
||||
{
|
||||
if(!datamodel.getInstance().getIsLoadingURL())
|
||||
{
|
||||
|
@ -84,7 +108,9 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
{
|
||||
request.abort();
|
||||
isReloadedUrl = true;
|
||||
if(clientThread!=null)
|
||||
clientThread.stop();
|
||||
clientThread = null;
|
||||
searchbar.setText(url.replace("http://boogle.store","http://genesis.onion"));
|
||||
}
|
||||
progressBar.animate().alpha(0f);
|
||||
|
@ -92,18 +118,8 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
progressBar.animate().setDuration(300).alpha(1f);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.d("ERROR : ","SUPER WOW");
|
||||
}
|
||||
clientThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try
|
||||
{
|
||||
if(url.contains("boogle.store"))
|
||||
{
|
||||
public void nonProxyConnection(String url) throws IOException {
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
request = new HttpGet(url);
|
||||
baseURL = url;
|
||||
|
@ -124,27 +140,6 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
message.what = MESSAGE_UPDATE_TEXT_CHILD_THREAD;
|
||||
updateUIHandler.sendMessage(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxyConnection(url);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if(!isReloadedUrl)
|
||||
{
|
||||
Message message = new Message();
|
||||
message.what = INTERNET_ERROR;
|
||||
updateUIHandler.sendMessage(message);
|
||||
Log.d("ERROR : ","SUPER WOW");
|
||||
e.printStackTrace();
|
||||
}
|
||||
isReloadedUrl = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
clientThread.start();
|
||||
}
|
||||
|
||||
public void proxyConnection(String url) throws Exception {
|
||||
NetCipher.useTor();
|
||||
|
@ -152,16 +147,6 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
connection.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
|
||||
connection.setRequestProperty("Accept","*/*");
|
||||
connection.connect();
|
||||
int status = connection.getResponseCode();
|
||||
|
||||
int responseCode = connection.getResponseCode(); //can call this instead of con.connect()
|
||||
InputStream in;
|
||||
if (responseCode >= 400 && responseCode <= 499) {
|
||||
throw new Exception("Bad authentication status: " + responseCode); //provide a more meaningful exception message
|
||||
}
|
||||
else {
|
||||
in = connection.getInputStream();
|
||||
}
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader((connection.getInputStream())));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
@ -176,15 +161,18 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
updateUIHandler.sendMessage(message);
|
||||
}
|
||||
|
||||
private Handler updateUIHandler = null;
|
||||
private final static int MESSAGE_UPDATE_TEXT_CHILD_THREAD =1;
|
||||
private final static int INTERNET_ERROR =2;
|
||||
|
||||
public WebView getView()
|
||||
public void onError()
|
||||
{
|
||||
return view[currentViewIndex];
|
||||
if(!isReloadedUrl)
|
||||
{
|
||||
Message message = new Message();
|
||||
message.what = INTERNET_ERROR;
|
||||
updateUIHandler.sendMessage(message);
|
||||
}
|
||||
isReloadedUrl = false;
|
||||
}
|
||||
|
||||
@SuppressLint("HandlerLeak")
|
||||
private void createUpdateUiHandler()
|
||||
{
|
||||
|
||||
|
@ -194,7 +182,6 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
{
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
Log.i("APPLYING : ","SUCCESS : APPLYING");
|
||||
if(msg.what == MESSAGE_UPDATE_TEXT_CHILD_THREAD)
|
||||
{
|
||||
view[viewIndex].animate().setDuration(0).alpha(0f);
|
||||
|
@ -211,11 +198,7 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
viewIndex = 1;
|
||||
currentViewIndex=0;
|
||||
}
|
||||
view[currentViewIndex].animate().setDuration(0).alpha(0f).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
}
|
||||
view[currentViewIndex].animate().setDuration(0).alpha(0f).withEndAction((() -> {
|
||||
}));
|
||||
}
|
||||
else if (msg.what == INTERNET_ERROR)
|
||||
|
@ -223,12 +206,8 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
datamodel.getInstance().setIsLoadingURL(false);
|
||||
progressBar.animate().alpha(0f);
|
||||
requestFailure.setVisibility(View.VISIBLE);
|
||||
requestFailure.animate().alpha(1f).setDuration(300).withEndAction((new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
}
|
||||
}));;
|
||||
requestFailure.animate().alpha(1f).setDuration(300).withEndAction((() -> {
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -253,4 +232,37 @@ public class webRequestHandler implements StrongBuilder.Callback<HttpClient>
|
|||
public void onInvalid() {
|
||||
|
||||
}
|
||||
|
||||
public void getVersion(Context applicationContext)
|
||||
{
|
||||
new Thread()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
String webPage = "http://boogle.store/version";
|
||||
URL url = new URL(webPage);
|
||||
URLConnection urlConnection = null;
|
||||
urlConnection = url.openConnection();
|
||||
InputStream is = urlConnection.getInputStream();
|
||||
InputStreamReader isr = new InputStreamReader(is);
|
||||
|
||||
int numCharsRead;
|
||||
char[] charArray = new char[1024];
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while ((numCharsRead = isr.read(charArray)) > 0) {
|
||||
sb.append(charArray, 0, numCharsRead);
|
||||
}
|
||||
String result = sb.toString();
|
||||
preference_manager.getInstance().saveString("version",result,applicationContext);
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
<WebView
|
||||
android:id="@+id/pageLoader1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:layout_editor_absoluteY="8dp" tools:layout_editor_absoluteX="8dp">
|
||||
</WebView>
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/requestFailure"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:background="#ffffff"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_width="match_parent"
|
||||
>
|
||||
|
||||
<ImageButton
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#f8f8f8"
|
||||
android:id="@+id/errorBack" app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:textAlignment="viewStart"
|
||||
android:textStyle="bold"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPersonName"
|
||||
android:text=" Opps! Some Thing Went Wrong"
|
||||
android:textColor="#4d4d4d"
|
||||
android:ems="10"
|
||||
android:id="@+id/editText"
|
||||
app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="104dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
<Button
|
||||
android:text="Reload"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/reloadButton"
|
||||
android:background="@drawable/shape"
|
||||
android:textColor="#000000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:radius="1dp"
|
||||
android:bottomRightRadius="10dp"
|
||||
android:bottomLeftRadius="10dp"
|
||||
android:topLeftRadius="10dp"
|
||||
android:topRightRadius="10dp"
|
||||
android:layout_marginBottom="36dp" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.498" app:layout_constraintEnd_toEndOf="parent"/>
|
||||
<TextView
|
||||
android:text="These might be the problems you are facing\n\n\u2022 Webpage or Website might be down\n\u2022 Your Internet connection might be poor\n\u2022 You might be using a proxy\n\u2022 Website might be blocked by firewall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/textView"
|
||||
android:textColor="#4d4d4d"
|
||||
app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="170dp"
|
||||
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp" app:layout_constraintEnd_toEndOf="parent"/>
|
||||
<ImageView
|
||||
android:layout_width="53dp"
|
||||
android:layout_height="53dp" app:srcCompat="@mipmap/ic_launcher_round_v1"
|
||||
android:id="@+id/imageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="4dp"
|
||||
android:layout_marginStart="4dp"/>
|
||||
<TextView
|
||||
android:text="Genesis Search Engine"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:textSize="20sp"
|
||||
android:textColor="#4d4d4d"
|
||||
android:id="@+id/textView2"
|
||||
app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="68dp"/>
|
||||
<ImageView
|
||||
android:layout_width="205dp"
|
||||
android:layout_height="0dp" app:srcCompat="@drawable/interneticon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||
app:layout_constraintBottom_toTopOf="@+id/reloadButton" android:layout_marginBottom="35dp"
|
||||
android:layout_marginTop="35dp"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="4dp"
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:max="100"
|
||||
android:progress="0" app:layout_constraintTop_toTopOf="parent"
|
||||
/>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" android:id="@+id/splashScreen"
|
||||
android:background="@drawable/backgradient"
|
||||
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintVertical_bias="1.0">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="95dp"
|
||||
android:layout_height="95dp"
|
||||
android:indeterminateTint="#ffffff"
|
||||
app:layout_constraintStart_toStartOf="parent" app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="80dp"/>
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="70dp" app:srcCompat="@drawable/logolarge"
|
||||
android:id="@+id/imageView_loading" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="80dp"/>
|
||||
<TextView
|
||||
android:text="Genesis Search Engine"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ffffff"
|
||||
android:textStyle="bold"
|
||||
android:id="@+id/textView3"
|
||||
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="35dp"
|
||||
android:layout_marginEnd="231dp" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"/>
|
||||
<TextView
|
||||
android:text="Loading Please Wait"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ffffff"
|
||||
android:textStyle="bold"
|
||||
android:id="@+id/notification" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginTop="50dp" android:layout_marginStart="76dp" android:layout_marginEnd="76dp"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
|
@ -5,7 +5,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".applicationController">
|
||||
tools:context=".application_controller">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
@ -63,6 +63,7 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
<Button
|
||||
android:text="Reload"
|
||||
android:onClick="onReloadButtonPressed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/reloadButton"
|
||||
|
@ -119,6 +120,7 @@
|
|||
android:topLeftRadius="3dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/homeicon"
|
||||
android:onClick="onHomeButtonPressed"
|
||||
android:background="@drawable/pressedcolor"
|
||||
android:topRightRadius="3dp"/>
|
||||
<EditText
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" android:id="@+id/splashScreen"
|
||||
android:background="@drawable/backgradient"
|
||||
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintVertical_bias="1.0">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="70dp" app:srcCompat="@drawable/logolarge"
|
||||
android:id="@+id/imageView_loading" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="80dp"/>
|
||||
<TextView
|
||||
android:text="Genesis Search Engine"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ffffff"
|
||||
android:textStyle="bold"
|
||||
android:id="@+id/textView3"
|
||||
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="35dp"
|
||||
android:layout_marginEnd="231dp" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"/>
|
||||
<TextView
|
||||
android:text="Loading Please Wait"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ffffff"
|
||||
android:textStyle="bold"
|
||||
android:id="@+id/notification" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginTop="50dp" android:layout_marginStart="76dp" android:layout_marginEnd="76dp"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 25 KiB |
|
@ -1,7 +1,6 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.20'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
@ -9,7 +8,6 @@ buildscript {
|
|||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
|
|