diff --git a/F-Droid/src/org/fdroid/fdroid/AppDetails.java b/F-Droid/src/org/fdroid/fdroid/AppDetails.java index 1220ee88f..89c6d0548 100644 --- a/F-Droid/src/org/fdroid/fdroid/AppDetails.java +++ b/F-Droid/src/org/fdroid/fdroid/AppDetails.java @@ -97,6 +97,7 @@ import org.fdroid.fdroid.net.Downloader; import java.io.File; import java.util.Iterator; import java.util.List; +import org.fdroid.fdroid.data.Credentials; interface AppDetailsData { App getApp(); @@ -855,8 +856,20 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A return repo.address; } + @Nullable + private Credentials getRepoCredentials(Apk apk) { + final String[] projection = {RepoProvider.DataColumns.USERNAME, RepoProvider.DataColumns.PASSWORD}; + Repo repo = RepoProvider.Helper.findById(this, apk.repo, projection); + if (repo == null || repo.username == null || repo.password == null) { + return null; + } + return repo.getCredentials(); + } + private void startDownload(Apk apk, String repoAddress) { downloadHandler = new ApkDownloader(getBaseContext(), app, apk, repoAddress); + downloadHandler.setCredentials(getRepoCredentials(apk)); + localBroadcastManager.registerReceiver(downloaderProgressReceiver, new IntentFilter(Downloader.LOCAL_ACTION_PROGRESS)); downloadHandler.setProgressListener(this); diff --git a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java index aedbd07c8..f8b26fa23 100644 --- a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java +++ b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java @@ -96,8 +96,7 @@ public class RepoUpdater { try { downloader = DownloaderFactory.create(context, getIndexAddress(), File.createTempFile("index-", "-downloaded", context.getCacheDir()), - repo.username, - repo.password + repo.getCredentials() ); downloader.setCacheTag(repo.lastetag); downloader.downloadUninterrupted(); diff --git a/F-Droid/src/org/fdroid/fdroid/data/Credentials.java b/F-Droid/src/org/fdroid/fdroid/data/Credentials.java new file mode 100644 index 000000000..0a5ebdd1e --- /dev/null +++ b/F-Droid/src/org/fdroid/fdroid/data/Credentials.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 Christian Morgner (christian.morgner@structr.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.fdroid.fdroid.data; + +import java.net.HttpURLConnection; + +/** + * Credentials to authenticate HTTP requests. Implementations if this interface + * should encapsulate the authentication of an HTTP request in the authenticate + * method. + */ +public interface Credentials { + + /** + * Implement this method to provide authentication for the given connection. + * @param connection the HTTP connection to authenticate + */ + public void authenticate(final HttpURLConnection connection); +} diff --git a/F-Droid/src/org/fdroid/fdroid/data/Repo.java b/F-Droid/src/org/fdroid/fdroid/data/Repo.java index ff4fa6711..abe4509b0 100644 --- a/F-Droid/src/org/fdroid/fdroid/data/Repo.java +++ b/F-Droid/src/org/fdroid/fdroid/data/Repo.java @@ -9,6 +9,7 @@ import org.fdroid.fdroid.Utils; import java.net.MalformedURLException; import java.net.URL; import java.util.Date; +import org.fdroid.fdroid.net.auth.HttpBasicCredentials; public class Repo extends ValueObject { @@ -148,6 +149,21 @@ public class Repo extends ValueObject { return value; } + /** + * Returns the credentials for this repo, or null of no authentication + * method is configured. + * @return the credentials or null + */ + public Credentials getCredentials() { + + // return the only credentials implementation we have right now + if (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) { + return new HttpBasicCredentials(username, password); + } + + return null; + } + public void setValues(ContentValues values) { if (values.containsKey(RepoProvider.DataColumns._ID)) { diff --git a/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java index f2b379321..6a2bddec8 100644 --- a/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java +++ b/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java @@ -39,6 +39,7 @@ import org.fdroid.fdroid.data.SanitizedFile; import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; +import org.fdroid.fdroid.data.Credentials; /** * Downloads and verifies (against the Apk.hash) the apk file. @@ -77,6 +78,7 @@ public class ApkDownloader implements AsyncDownloader.Listener { private ProgressListener listener; private AsyncDownloader dlWrapper; + private Credentials credentials; private boolean isComplete; private final long id = ++downloadIdCounter; @@ -195,7 +197,7 @@ public class ApkDownloader implements AsyncDownloader.Listener { Utils.debugLog(TAG, "Downloading apk from " + remoteAddress + " to " + localFile); try { - dlWrapper = DownloaderFactory.createAsync(context, remoteAddress, localFile, app.name + " " + curApk.version, curApk.id, this); + dlWrapper = DownloaderFactory.createAsync(context, remoteAddress, localFile, app.name + " " + curApk.version, curApk.id, credentials, this); dlWrapper.download(); return true; } catch (IOException e) { @@ -292,4 +294,8 @@ public class ApkDownloader implements AsyncDownloader.Listener { public int getTotalBytes() { return dlWrapper != null ? dlWrapper.getTotalBytes() : 0; } + + public void setCredentials(final Credentials credentials) { + this.credentials = credentials; + } } diff --git a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java index eb7fba3b3..3fbd81ff1 100644 --- a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java +++ b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java @@ -11,6 +11,7 @@ import org.fdroid.fdroid.Utils; import java.io.File; import java.io.IOException; import java.net.URL; +import org.fdroid.fdroid.data.Credentials; public class DownloaderFactory { @@ -45,10 +46,10 @@ public class DownloaderFactory { public static Downloader create(Context context, URL url, File destFile) throws IOException { - return create(context, url, destFile, null, null); + return create(context, url, destFile, null); } - public static Downloader create(Context context, URL url, File destFile, final String username, final String password) + public static Downloader create(Context context, URL url, File destFile, Credentials credentials) throws IOException { if (isBluetoothAddress(url)) { String macAddress = url.getHost().replace("-", ":"); @@ -60,7 +61,7 @@ public class DownloaderFactory { if (isLocalFile(url)) { return new LocalFileDownloader(context, url, destFile); } - return new HttpDownloader(context, url, destFile, username, password); + return new HttpDownloader(context, url, destFile, credentials); } private static boolean isBluetoothAddress(URL url) { @@ -71,11 +72,11 @@ public class DownloaderFactory { return "file".equalsIgnoreCase(url.getProtocol()); } - public static AsyncDownloader createAsync(Context context, String urlString, File destFile, String title, String id, AsyncDownloader.Listener listener) throws IOException { - return createAsync(context, new URL(urlString), destFile, title, id, listener); + public static AsyncDownloader createAsync(Context context, String urlString, File destFile, String title, String id, Credentials credentials, AsyncDownloader.Listener listener) throws IOException { + return createAsync(context, new URL(urlString), destFile, title, id, credentials, listener); } - public static AsyncDownloader createAsync(Context context, URL url, File destFile, String title, String id, AsyncDownloader.Listener listener) + public static AsyncDownloader createAsync(Context context, URL url, File destFile, String title, String id, Credentials credentials, AsyncDownloader.Listener listener) throws IOException { // To re-enable, fix the following: // * https://gitlab.com/fdroid/fdroidclient/issues/445 @@ -85,7 +86,7 @@ public class DownloaderFactory { return new AsyncDownloaderFromAndroid(context, listener, title, id, url.toString(), destFile); } Utils.debugLog(TAG, "Using AsyncDownloadWrapper"); - return new AsyncDownloadWrapper(create(context, url, destFile), listener); + return new AsyncDownloadWrapper(create(context, url, destFile, credentials), listener); } static boolean isOnionAddress(URL url) { diff --git a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java index 78415e69b..6c4ce511f 100644 --- a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java +++ b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java @@ -1,10 +1,9 @@ package org.fdroid.fdroid.net; import android.content.Context; -import android.text.TextUtils; import android.util.Log; +import com.nostra13.universalimageloader.core.download.BaseImageDownloader; -import org.apache.commons.net.util.Base64; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Utils; @@ -22,6 +21,7 @@ import java.net.SocketAddress; import java.net.URL; import javax.net.ssl.SSLHandshakeException; +import org.fdroid.fdroid.data.Credentials; public class HttpDownloader extends Downloader { private static final String TAG = "HttpDownloader"; @@ -30,21 +30,19 @@ public class HttpDownloader extends Downloader { protected static final String HEADER_FIELD_ETAG = "ETag"; protected HttpURLConnection connection; - private final String username; - private final String password; + private Credentials credentials; private int statusCode = -1; HttpDownloader(Context context, URL url, File destFile) throws FileNotFoundException, MalformedURLException { - this(context, url, destFile, null, null); + this(context, url, destFile, null); } - HttpDownloader(Context context, URL url, File destFile, final String username, final String password) + HttpDownloader(Context context, URL url, File destFile, final Credentials credentials) throws FileNotFoundException, MalformedURLException { super(context, url, destFile); - this.username = username; - this.password = password; + this.credentials = credentials; } /** @@ -100,9 +98,8 @@ public class HttpDownloader extends Downloader { } else { connection = (HttpURLConnection) sourceUrl.openConnection(); - if (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) { - // add authorization header from username / password if set - connection.setRequestProperty("Authorization", "Basic " + Base64.encodeBase64String((username + ":" + password).getBytes())); + if (credentials != null) { + credentials.authenticate((HttpURLConnection)connection); } } } diff --git a/F-Droid/src/org/fdroid/fdroid/net/auth/HttpBasicCredentials.java b/F-Droid/src/org/fdroid/fdroid/net/auth/HttpBasicCredentials.java new file mode 100644 index 000000000..84117616e --- /dev/null +++ b/F-Droid/src/org/fdroid/fdroid/net/auth/HttpBasicCredentials.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 Christian Morgner (christian.morgner@structr.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.fdroid.fdroid.net.auth; + +import android.text.TextUtils; +import java.net.HttpURLConnection; +import org.apache.commons.net.util.Base64; +import org.fdroid.fdroid.data.Credentials; + +/** + * Credentials implementation for HTTP Basic Authentication. + */ +public class HttpBasicCredentials implements Credentials { + + private final String username; + private final String password; + + public HttpBasicCredentials(final String username, final String password) { + this.username = username; + this.password = password; + } + + @Override + public void authenticate(final HttpURLConnection connection) { + + if (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) { + + // add authorization header from username / password if set + connection.setRequestProperty("Authorization", "Basic " + Base64.encodeBase64String((username + ":" + password).getBytes())); + } + } +}