Merge commit 'refs/merge-requests/45' of git://gitorious.org/f-droid/fdroidclient into merge-requests/45

This commit is contained in:
Ciaran Gultnieks 2013-12-04 17:44:31 +00:00
commit 831a1b40fa
13 changed files with 131 additions and 68 deletions

View File

@ -60,10 +60,11 @@
android:name="android.app.default_searchable" android:name="android.app.default_searchable"
android:value=".SearchResults" /> android:value=".SearchResults" />
</activity> </activity>
<activity <activity
android:name=".ManageRepo" android:name=".ManageRepo"
android:allowTaskReparenting="true"
android:label="@string/menu_manage" android:label="@string/menu_manage"
android:launchMode="singleTop"
android:parentActivityName=".FDroid" > android:parentActivityName=".FDroid" >
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"

View File

@ -4,17 +4,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" > android:orientation="vertical" >
<TextView
android:id="@+id/repo_alert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@android:drawable/ic_dialog_alert"
android:gravity="center"
android:text="@string/repo_exists"
android:textAppearance="@android:style/TextAppearance.Large"
android:textColor="@color/red"
android:visibility="gone" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -25,7 +14,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textUri" android:inputType="textUri"
android:maxLines="1" android:maxLines="2"
android:text="https://" /> android:text="https://" />
<TextView <TextView
@ -39,15 +28,17 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:digits="0123456789ABCDEFabcedf: " android:digits="0123456789ABCDEFabcedf: "
android:inputType="textNoSuggestions" android:inputType="textNoSuggestions"
android:maxLines="1" android:maxLines="3"
android:typeface="monospace" /> android:typeface="monospace" />
<CheckBox <TextView
android:id="@+id/overwrite_repo" android:id="@+id/overwrite_message"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:drawableLeft="@android:drawable/ic_dialog_alert"
android:drawablePadding="20dp"
android:padding="20dp" android:padding="20dp"
android:text="@string/repo_overwrite" android:text="@string/repo_delete_to_overwrite"
android:textAppearance="@android:style/TextAppearance.Medium" android:textAppearance="@android:style/TextAppearance.Medium"
android:visibility="gone" /> android:visibility="gone" />

View File

@ -56,6 +56,9 @@
<string name="repo_add_add">Add</string> <string name="repo_add_add">Add</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="enable">Enable</string>
<string name="add_key">Add Key</string>
<string name="overwrite">Overwrite</string>
<string name="repo_delete_title">Choose repository to remove</string> <string name="repo_delete_title">Choose repository to remove</string>
<string name="repo_update_title">Update repositories</string> <string name="repo_update_title">Update repositories</string>
@ -72,7 +75,10 @@
<string name="repo_add_url">Repository address</string> <string name="repo_add_url">Repository address</string>
<string name="repo_add_fingerprint">fingerprint (optional)</string> <string name="repo_add_fingerprint">fingerprint (optional)</string>
<string name="repo_exists">This repo already exists!</string> <string name="repo_exists">This repo already exists!</string>
<string name="repo_overwrite">Overwrite the existing repo?</string> <string name="repo_exists_add_fingerprint">This repo is already setup, this will add new key information.</string>
<string name="repo_exists_enable">This repo is already setup, confirm that you want to re-enable it.</string>
<string name="repo_exists_and_enabled">The incoming repo is already setup and enabled!</string>
<string name="repo_delete_to_overwrite">You must first delete this repo before you can add one with a different key!</string>
<string name="repo_alrt">The list of used repositories has <string name="repo_alrt">The list of used repositories has
changed.\nDo you changed.\nDo you

View File

@ -465,6 +465,8 @@ public class DB {
public static String calcFingerprint(String pubkey) { public static String calcFingerprint(String pubkey) {
String ret = null; String ret = null;
if (pubkey == null)
return null;
try { try {
// keytool -list -v gives you the SHA-256 fingerprint // keytool -list -v gives you the SHA-256 fingerprint
MessageDigest digest = MessageDigest.getInstance("SHA-256"); MessageDigest digest = MessageDigest.getInstance("SHA-256");
@ -1410,7 +1412,7 @@ public class DB {
String calcedFingerprint = DB.calcFingerprint(pubkey); String calcedFingerprint = DB.calcFingerprint(pubkey);
if (fingerprint == null) { if (fingerprint == null) {
fingerprint = calcedFingerprint; fingerprint = calcedFingerprint;
} else { } else if (calcedFingerprint != null) {
fingerprint = fingerprint.toUpperCase(); fingerprint = fingerprint.toUpperCase();
if (!fingerprint.equals(calcedFingerprint)) { if (!fingerprint.equals(calcedFingerprint)) {
throw new SecurityException("Given fingerprint does not match calculated one! (" throw new SecurityException("Given fingerprint does not match calculated one! ("

View File

@ -26,6 +26,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.AlertDialog.Builder; import android.app.AlertDialog.Builder;
import android.app.ListActivity; import android.app.ListActivity;
@ -35,24 +36,22 @@ import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.NavUtils;
import android.support.v4.view.MenuItemCompat;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button; import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ListView; import android.widget.ListView;
import android.widget.SimpleAdapter; import android.widget.SimpleAdapter;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import org.fdroid.fdroid.DB.Repo; import org.fdroid.fdroid.DB.Repo;
import android.support.v4.app.NavUtils;
import android.support.v4.view.MenuItemCompat;
import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.compat.ActionBarCompat;
public class ManageRepo extends ListActivity { public class ManageRepo extends ListActivity {
@ -62,6 +61,11 @@ public class ManageRepo extends ListActivity {
private boolean changed = false; private boolean changed = false;
private enum PositiveAction {
ADD_NEW, ENABLE, IGNORE
}
private PositiveAction positiveAction;
private List<DB.Repo> repos; private List<DB.Repo> repos;
private static List<String> reposToDisable; private static List<String> reposToDisable;
@ -116,13 +120,25 @@ public class ManageRepo extends ListActivity {
/* an URL from a click or a QRCode scan */ /* an URL from a click or a QRCode scan */
Uri uri = intent.getData(); Uri uri = intent.getData();
if (uri != null) { if (uri != null) {
// scheme should only ever be pure ASCII: // scheme should only ever be pure ASCII aka Locale.ENGLISH
String scheme = intent.getScheme().toLowerCase(Locale.ENGLISH); String scheme = intent.getScheme().toLowerCase(Locale.ENGLISH);
String fingerprint = uri.getUserInfo(); String fingerprint = uri.getUserInfo();
String host = uri.getHost().toLowerCase(Locale.ENGLISH);
if (scheme.equals("fdroidrepos") || scheme.equals("fdroidrepo") if (scheme.equals("fdroidrepos") || scheme.equals("fdroidrepo")
|| scheme.equals("https") || scheme.equals("http")) { || scheme.equals("https") || scheme.equals("http")) {
String uriString = uri.toString().replace("fdroidrepo", "http"). // QRCode are more efficient in all upper case, so some incoming
replace(fingerprint + "@", ""); // URLs might be encoded in all upper case. Therefore, we allow
// the standard paths to be encoded all upper case, then they'll
// be forced to lower case. The scheme and host are downcased
// just to make them more readable in the dialog.
String uriString = uri.toString()
.replace(fingerprint + "@", "") // remove fingerprint
.replaceAll("/*$", "") // remove all trailing slashes
.replaceAll("/FDROID/REPO$", "/fdroid/repo")
.replaceAll("/FDROID/ARCHIVE$", "/fdroid/archive")
.replace(uri.getHost(), host) // downcase host name
.replace(intent.getScheme(), scheme) // downcase scheme
.replace("fdroidrepo", "http"); // make proper URL
showAddRepo(uriString, fingerprint); showAddRepo(uriString, fingerprint);
Log.i("ManageRepo", uriString + " fingerprint: " + fingerprint); Log.i("ManageRepo", uriString + " fingerprint: " + fingerprint);
} }
@ -219,10 +235,10 @@ public class ManageRepo extends ListActivity {
return repos; return repos;
} }
protected Repo getRepo(String repoUri, List<Repo> repos) { protected Repo getRepoByAddress(String address, List<Repo> repos) {
if (repoUri != null) if (address != null)
for (Repo repo : repos) for (Repo repo : repos)
if (repoUri.equals(repo.address)) if (address.equals(repo.address))
return repo; return repo;
return null; return null;
} }
@ -237,7 +253,7 @@ public class ManageRepo extends ListActivity {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
private void showAddRepo(String uriString, String fingerprint) { private void showAddRepo(String newAddress, String newFingerprint) {
LayoutInflater li = LayoutInflater.from(this); LayoutInflater li = LayoutInflater.from(this);
View view = li.inflate(R.layout.addrepo, null); View view = li.inflate(R.layout.addrepo, null);
Builder p = new AlertDialog.Builder(this).setView(view); Builder p = new AlertDialog.Builder(this).setView(view);
@ -245,6 +261,9 @@ public class ManageRepo extends ListActivity {
final EditText uriEditText = (EditText) view.findViewById(R.id.edit_uri); final EditText uriEditText = (EditText) view.findViewById(R.id.edit_uri);
final EditText fingerprintEditText = (EditText) view.findViewById(R.id.edit_fingerprint); final EditText fingerprintEditText = (EditText) view.findViewById(R.id.edit_fingerprint);
List<Repo> repos = getRepos();
final Repo repo = getRepoByAddress(newAddress, repos);
alrt.setIcon(android.R.drawable.ic_menu_add); alrt.setIcon(android.R.drawable.ic_menu_add);
alrt.setTitle(getString(R.string.repo_add_title)); alrt.setTitle(getString(R.string.repo_add_title));
alrt.setButton(DialogInterface.BUTTON_POSITIVE, alrt.setButton(DialogInterface.BUTTON_POSITIVE,
@ -252,10 +271,15 @@ public class ManageRepo extends ListActivity {
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
addRepo(uriEditText.getText().toString(), String fp = fingerprintEditText.getText().toString();
fingerprintEditText.getText().toString()); // the DB uses null for no fingerprint but the above
changed = true; // code returns "" rather than null if its blank
redraw(); if (fp.equals(""))
fp = null;
if (positiveAction == PositiveAction.ADD_NEW)
addRepoPositiveAction(uriEditText.getText().toString(), fp, null);
else if (positiveAction == PositiveAction.ENABLE)
addRepoPositiveAction(null, null, repo);
} }
}); });
@ -264,35 +288,74 @@ public class ManageRepo extends ListActivity {
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
setResult(Activity.RESULT_CANCELED);
finish();
return; return;
} }
}); });
alrt.show(); alrt.show();
List<Repo> repos = getRepos(); final TextView overwriteMessage = (TextView) view.findViewById(R.id.overwrite_message);
Repo repo = getRepo(uriString, repos); overwriteMessage.setVisibility(View.GONE);
if (repo != null) { if (repo == null) {
TextView tv = (TextView) view.findViewById(R.id.repo_alert); // no existing repo, add based on what we have
tv.setVisibility(0); positiveAction = PositiveAction.ADD_NEW;
tv.setText(R.string.repo_exists); } else {
// found the address in the DB of existing repos
final Button addButton = alrt.getButton(DialogInterface.BUTTON_POSITIVE); final Button addButton = alrt.getButton(DialogInterface.BUTTON_POSITIVE);
addButton.setEnabled(false); alrt.setTitle(R.string.repo_exists);
final CheckBox overwriteCheckBox = (CheckBox) view.findViewById(R.id.overwrite_repo); overwriteMessage.setVisibility(View.VISIBLE);
overwriteCheckBox.setVisibility(0); if (repo.fingerprint == null && newFingerprint != null) {
overwriteCheckBox.setOnClickListener(new OnClickListener() { // we're upgrading from unsigned to signed repo
@Override overwriteMessage.setText(R.string.repo_exists_add_fingerprint);
public void onClick(View v) { addButton.setText(R.string.add_key);
addButton.setEnabled(overwriteCheckBox.isChecked()); positiveAction = PositiveAction.ADD_NEW;
} else if (newFingerprint == null || newFingerprint.equals(repo.fingerprint)) {
// this entry already exists and is not enabled, offer to enable it
if (repo.inuse) {
alrt.dismiss();
Toast.makeText(this, R.string.repo_exists_and_enabled, Toast.LENGTH_LONG).show();
return;
} else {
overwriteMessage.setText(R.string.repo_exists_enable);
addButton.setText(R.string.enable);
positiveAction = PositiveAction.ENABLE;
}
} else {
// same address with different fingerprint, this could be
// malicious, so force the user to manually delete the repo
// before adding this one
overwriteMessage.setTextColor(getResources().getColor(R.color.red));
overwriteMessage.setText(R.string.repo_delete_to_overwrite);
addButton.setText(R.string.overwrite);
addButton.setEnabled(false);
positiveAction = PositiveAction.IGNORE;
} }
});
// TODO if address and fingerprint match, then enable existing repo
// TODO if address matches but fingerprint doesn't, handle this with extra widgets
} }
if (uriString != null) if (newAddress != null)
uriEditText.setText(uriString); uriEditText.setText(newAddress);
if (fingerprint != null) if (newFingerprint != null)
fingerprintEditText.setText(fingerprint); fingerprintEditText.setText(newFingerprint);
}
private void addRepoPositiveAction(String address, String fingerprint, Repo repo) {
if (address != null) {
addRepo(address, fingerprint);
} else if (repo != null) {
// force-enable an existing repo
repo.inuse = true;
try {
DB db = DB.getDB();
db.updateRepoByAddress(repo);
} finally {
DB.releaseDB();
}
}
changed = true;
redraw();
setResult(Activity.RESULT_OK);
finish();
} }
@Override @Override