diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ea7c7e0d..3943d2260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * find local repos on the same network using Bonjour/mDNS +* use FDroid repos on Tor Hidden Services (.onion addresses) + * directly send installed apps to other devices via Bluetooth and Android Beam (NFC+Bluetooth), also compatible with Samsung/HTC S-Beam diff --git a/src/org/fdroid/fdroid/net/Downloader.java b/src/org/fdroid/fdroid/net/Downloader.java index 4f6ee766d..4a9b96899 100644 --- a/src/org/fdroid/fdroid/net/Downloader.java +++ b/src/org/fdroid/fdroid/net/Downloader.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; +import java.net.URL; public abstract class Downloader { @@ -23,6 +24,8 @@ public abstract class Downloader { private ProgressListener progressListener = null; private Bundle eventData = null; private File outputFile; + + protected URL sourceUrl; protected String cacheTag = null; public static final String EVENT_PROGRESS = "downloadProgress"; diff --git a/src/org/fdroid/fdroid/net/DownloaderFactory.java b/src/org/fdroid/fdroid/net/DownloaderFactory.java index 8206db94b..df0ab415b 100644 --- a/src/org/fdroid/fdroid/net/DownloaderFactory.java +++ b/src/org/fdroid/fdroid/net/DownloaderFactory.java @@ -10,11 +10,23 @@ public class DownloaderFactory { public static Downloader create(String url, Context context) throws IOException { - return new HttpDownloader(url, context); + if (isOnionAddress(url)) { + return new TorHttpDownloader(url, context); + } else { + return new HttpDownloader(url, context); + } } public static Downloader create(String url, File destFile) throws IOException { - return new HttpDownloader(url, destFile); + if (isOnionAddress(url)) { + return new TorHttpDownloader(url, destFile); + } else { + return new HttpDownloader(url, destFile); + } + } + + private static boolean isOnionAddress(String url) { + return url.matches("^[a-zA-Z0-9]+://[^/]+\\.onion/.*"); } } diff --git a/src/org/fdroid/fdroid/net/HttpDownloader.java b/src/org/fdroid/fdroid/net/HttpDownloader.java index 7f5cc8e96..50fc41a65 100644 --- a/src/org/fdroid/fdroid/net/HttpDownloader.java +++ b/src/org/fdroid/fdroid/net/HttpDownloader.java @@ -16,11 +16,10 @@ import javax.net.ssl.SSLHandshakeException; public class HttpDownloader extends Downloader { private static final String TAG = "org.fdroid.fdroid.net.HttpDownloader"; - private static final String HEADER_IF_NONE_MATCH = "If-None-Match"; - private static final String HEADER_FIELD_ETAG = "ETag"; + protected static final String HEADER_IF_NONE_MATCH = "If-None-Match"; + protected static final String HEADER_FIELD_ETAG = "ETag"; - private URL sourceUrl; - private HttpURLConnection connection; + protected HttpURLConnection connection; private int statusCode = -1; // The context is required for opening the file to write to. @@ -54,28 +53,31 @@ public class HttpDownloader extends Downloader { @Override public void download() throws IOException, InterruptedException { try { - connection = (HttpURLConnection)sourceUrl.openConnection(); - - if (wantToCheckCache()) { - setupCacheCheck(); - Log.i(TAG, "Checking cached status of " + sourceUrl); - statusCode = connection.getResponseCode(); - } - - if (isCached()) { - Log.i(TAG, sourceUrl + " is cached, so not downloading (HTTP " + statusCode + ")"); - } else { - Log.i(TAG, "Downloading from " + sourceUrl); - downloadFromStream(); - updateCacheCheck(); - } + connection = (HttpURLConnection) sourceUrl.openConnection(); + doDownload(); } catch (SSLHandshakeException e) { - // TODO this should be handled better, it is not internationalised here. + // TODO this should be handled better, it is not internationalised here throw new IOException( "A problem occurred while establishing an SSL " + "connection. If this problem persists, AND you have a " + "very old device, you could try using http instead of " + - "https for the repo URL." + Log.getStackTraceString(e) ); + "https for the repo URL." + Log.getStackTraceString(e)); + } + } + + protected void doDownload() throws IOException, InterruptedException { + if (wantToCheckCache()) { + setupCacheCheck(); + Log.i(TAG, "Checking cached status of " + sourceUrl); + statusCode = connection.getResponseCode(); + } + + if (isCached()) { + Log.i(TAG, sourceUrl + " is cached, so not downloading (HTTP " + statusCode + ")"); + } else { + Log.i(TAG, "Downloading from " + sourceUrl); + downloadFromStream(); + updateCacheCheck(); } } diff --git a/src/org/fdroid/fdroid/net/TorHttpDownloader.java b/src/org/fdroid/fdroid/net/TorHttpDownloader.java new file mode 100644 index 000000000..13e6c09bb --- /dev/null +++ b/src/org/fdroid/fdroid/net/TorHttpDownloader.java @@ -0,0 +1,45 @@ + +package org.fdroid.fdroid.net; + +import android.content.Context; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.SocketAddress; + +import javax.net.ssl.SSLHandshakeException; + +public class TorHttpDownloader extends HttpDownloader { + + TorHttpDownloader(String url, Context ctx) throws IOException { + super(url, ctx); + } + + TorHttpDownloader(String url, File destFile) + throws FileNotFoundException, MalformedURLException { + super(url, destFile); + } + + @Override + public void download() throws IOException, InterruptedException { + try { + SocketAddress sa = new InetSocketAddress("127.0.0.1", 8118); + Proxy tor = new Proxy(Proxy.Type.HTTP, sa); + connection = (HttpURLConnection) sourceUrl.openConnection(tor); + doDownload(); + } catch (SSLHandshakeException e) { + throw new IOException( + "A problem occurred while establishing an SSL " + + "connection. If this problem persists, AND you have a " + + "very old device, you could try using http instead of " + + "https for the repo URL." + Log.getStackTraceString(e)); + } + } + +}