From 7865b92285893ddb87fa8351d724d09d0a1eb781 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Mon, 26 May 2014 20:24:13 +0200 Subject: ContactHelper can read email addresses from contact list --- OpenKeychain/src/main/AndroidManifest.xml | 1 + .../keychain/helper/ContactHelper.java | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index f4007c098..fd26d6acf 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -53,6 +53,7 @@ + (emailSet); } + + public static List getContactMails(Context context) { + ContentResolver resolver = context.getContentResolver(); + Cursor mailCursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, + new String[]{ContactsContract.CommonDataKinds.Email.DATA}, + null, null, null); + if (mailCursor == null) return null; + + Set mails = new HashSet(); + while (mailCursor.moveToNext()) { + String email = mailCursor.getString(0); + if (email != null) { + mails.add(email); + } + } + mailCursor.close(); + return new ArrayList(mails); + } } -- cgit v1.2.3 From 3110122a85c5659a758a8f234381a7de783bdbca Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Tue, 27 May 2014 19:45:58 +0200 Subject: Add ability to resolve HkpKeyserver from _hkp._tcp SRV record --- .../keychain/keyimport/HkpKeyserver.java | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 5969455bd..2041548f3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -33,6 +33,10 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.util.Log; +import org.xbill.DNS.Lookup; +import org.xbill.DNS.Record; +import org.xbill.DNS.SRVRecord; +import org.xbill.DNS.Type; import java.io.IOException; import java.io.InputStream; @@ -45,6 +49,8 @@ import java.net.URLDecoder; import java.net.URLEncoder; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; @@ -336,4 +342,36 @@ public class HkpKeyserver extends Keyserver { client.getConnectionManager().shutdown(); } } + + @Override + public String toString() { + return mHost + ":" + mPort; + } + + /** + * Tries to find a server responsible for a given domain + * + * @return A responsible Keyserver or null if not found. + */ + public static HkpKeyserver resolve(String domain) { + try { + Record[] records = new Lookup("_hkp._tcp." + domain, Type.SRV).run(); + if (records.length > 0) { + Arrays.sort(records, new Comparator() { + @Override + public int compare(Record lhs, Record rhs) { + if (!(lhs instanceof SRVRecord)) return 1; + if (!(rhs instanceof SRVRecord)) return -1; + return ((SRVRecord) lhs).getPriority() - ((SRVRecord) rhs).getPriority(); + } + }); + Record record = records[0]; // This is our best choice + if (record instanceof SRVRecord) { + return new HkpKeyserver(((SRVRecord) record).getTarget().toString(), (short) ((SRVRecord) record).getPort()); + } + } + } catch (Exception ignored) { + } + return null; + } } -- cgit v1.2.3 From 8e5767f967646412a03563c966c3bb5b7c26e0fa Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Tue, 27 May 2014 20:17:49 +0200 Subject: Store origin with ImportKeysListEntry --- .../sufficientlysecure/keychain/keyimport/HkpKeyserver.java | 1 + .../keychain/keyimport/ImportKeysListEntry.java | 11 +++++++++++ .../keychain/keyimport/KeybaseKeyserver.java | 1 + 3 files changed, 13 insertions(+) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 2041548f3..fbe38c30a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -244,6 +244,7 @@ public class HkpKeyserver extends Keyserver { while (matcher.find()) { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(query); + entry.setOrigin("hkp:"+mHost+":"+mPort); entry.setBitStrength(Integer.parseInt(matcher.group(3))); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index 04b86e295..8af41a8a3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -52,6 +52,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { public String mPrimaryUserId; private String mExtraData; private String mQuery; + private String mOrigin; private boolean mSelected; @@ -77,6 +78,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { dest.writeInt(mBytes.length); dest.writeByteArray(mBytes); dest.writeString(mExtraData); + dest.writeString(mOrigin); } public static final Creator CREATOR = new Creator() { @@ -97,6 +99,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { vr.mBytes = new byte[source.readInt()]; source.readByteArray(vr.mBytes); vr.mExtraData = source.readString(); + vr.mOrigin = source.readString(); return vr; } @@ -218,6 +221,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable { mQuery = query; } + public String getOrigin() { + return mOrigin; + } + + public void setOrigin(String origin) { + mOrigin = origin; + } + /** * Constructor for later querying from keyserver */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index f9b6abf18..43c0b12fd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -87,6 +87,7 @@ public class KeybaseKeyserver extends Keyserver { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(mQuery); + entry.setOrigin("keybase.io"); String keybaseId = JWalk.getString(match, "components", "username", "val"); String fullName = JWalk.getString(match, "components", "full_name", "val"); -- cgit v1.2.3 From cb92c9ccc811a72b4e216f819be32b19748113c7 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Tue, 27 May 2014 21:16:52 +0200 Subject: Add hkps support --- .../keychain/keyimport/HkpKeyserver.java | 46 +++++++++++++++++----- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index fbe38c30a..71c251ddc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -81,6 +81,7 @@ public class HkpKeyserver extends Keyserver { private String mHost; private short mPort; + private boolean mSecure; /** * pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags% @@ -147,6 +148,7 @@ public class HkpKeyserver extends Keyserver { Pattern.CASE_INSENSITIVE); private static final short PORT_DEFAULT = 11371; + private static final short PORT_DEFAULT_HKPS = 443; /** * @param hostAndPort may be just @@ -157,19 +159,45 @@ public class HkpKeyserver extends Keyserver { public HkpKeyserver(String hostAndPort) { String host = hostAndPort; short port = PORT_DEFAULT; - final int colonPosition = hostAndPort.lastIndexOf(':'); - if (colonPosition > 0) { - host = hostAndPort.substring(0, colonPosition); - final String portStr = hostAndPort.substring(colonPosition + 1); - port = Short.decode(portStr); + boolean secure = false; + String[] parts = hostAndPort.split(":"); + if (parts.length > 1) { + if (!parts[0].contains(".")) { // This is not a domain or ip, so it must be a protocol name + if (parts[0].equalsIgnoreCase("hkps") || parts[0].equalsIgnoreCase("https")) { + secure = true; + port = PORT_DEFAULT_HKPS; + } else if (!parts[0].equalsIgnoreCase("hkp") && !parts[0].equalsIgnoreCase("http")) { + throw new IllegalArgumentException("Protocol " + parts[0] + " is unknown"); + } + host = parts[1]; + if (host.startsWith("//")) { // People tend to type https:// and hkps://, so we'll support that as well + host = host.substring(2); + } + if (parts.length > 2) { + port = Short.decode(parts[2]); + } + } else { + host = parts[0]; + port = Short.decode(parts[1]); + } } mHost = host; mPort = port; + mSecure = secure; } public HkpKeyserver(String host, short port) { + this(host, port, false); + } + + public HkpKeyserver(String host, short port, boolean secure) { mHost = host; mPort = port; + mSecure = secure; + } + + private String getUrlPrefix() { + return mSecure ? "https://" : "http://"; } private String query(String request) throws QueryFailedException, HttpError { @@ -181,7 +209,7 @@ public class HkpKeyserver extends Keyserver { } for (int i = 0; i < ips.length; ++i) { try { - String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request; + String url = getUrlPrefix() + ips[i].getHostAddress() + ":" + mPort + request; Log.d(Constants.TAG, "hkp keyserver query: " + url); URL realUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); @@ -244,7 +272,7 @@ public class HkpKeyserver extends Keyserver { while (matcher.find()) { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(query); - entry.setOrigin("hkp:"+mHost+":"+mPort); + entry.setOrigin("hkp:" + mHost + ":" + mPort); entry.setBitStrength(Integer.parseInt(matcher.group(3))); @@ -297,7 +325,7 @@ public class HkpKeyserver extends Keyserver { public String get(String keyIdHex) throws QueryFailedException { HttpClient client = new DefaultHttpClient(); try { - String query = "http://" + mHost + ":" + mPort + + String query = getUrlPrefix() + mHost + ":" + mPort + "/pks/lookup?op=get&options=mr&search=" + keyIdHex; Log.d(Constants.TAG, "hkp keyserver get: " + query); HttpGet get = new HttpGet(query); @@ -326,7 +354,7 @@ public class HkpKeyserver extends Keyserver { public void add(String armoredKey) throws AddKeyException { HttpClient client = new DefaultHttpClient(); try { - String query = "http://" + mHost + ":" + mPort + "/pks/add"; + String query = getUrlPrefix() + mHost + ":" + mPort + "/pks/add"; HttpPost post = new HttpPost(query); Log.d(Constants.TAG, "hkp keyserver add: " + query); List nameValuePairs = new ArrayList(2); -- cgit v1.2.3 From c676e534799545f6aa95071463c10aa0b2f92b9d Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 28 May 2014 20:44:01 +0200 Subject: Fix url building to support certificate check on hkps servers Note: the CA used by sks-keyservers.net is not valid for android, thus using hkps fails for them. pgp.mit.edu uses a perfectly valid cert. --- .../keychain/keyimport/HkpKeyserver.java | 27 +++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 71c251ddc..b064fc5b1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -201,15 +201,26 @@ public class HkpKeyserver extends Keyserver { } private String query(String request) throws QueryFailedException, HttpError { - InetAddress ips[]; - try { - ips = InetAddress.getAllByName(mHost); - } catch (UnknownHostException e) { - throw new QueryFailedException(e.toString()); + List urls = new ArrayList(); + if (mSecure) { + urls.add(getUrlPrefix() + mHost + ":" + mPort + request); + } else { + InetAddress ips[]; + try { + ips = InetAddress.getAllByName(mHost); + } catch (UnknownHostException e) { + throw new QueryFailedException(e.toString()); + } + for (InetAddress ip : ips) { + // Note: This is actually not HTTP 1.1 compliant, as we hide the real "Host" value, + // but Android's HTTPUrlConnection does not support any other way to set + // Socket's remote IP address... + urls.add(getUrlPrefix() + ip.getHostAddress() + ":" + mPort + request); + } } - for (int i = 0; i < ips.length; ++i) { + + for (String url : urls) { try { - String url = getUrlPrefix() + ips[i].getHostAddress() + ":" + mPort + request; Log.d(Constants.TAG, "hkp keyserver query: " + url); URL realUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); @@ -272,7 +283,7 @@ public class HkpKeyserver extends Keyserver { while (matcher.find()) { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(query); - entry.setOrigin("hkp:" + mHost + ":" + mPort); + entry.setOrigin(getUrlPrefix() + mHost + ":" + mPort); entry.setBitStrength(Integer.parseInt(matcher.group(3))); -- cgit v1.2.3 From be490307f97b5e73bad5c4882afab6dff1f10b48 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 29 May 2014 10:24:00 +0200 Subject: Download from origin during ACTION_DOWNLOAD_AND_IMPORT_KEYS --- .../sufficientlysecure/keychain/service/KeychainIntentService.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 6f38418ff..b6ee234f4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -796,10 +796,11 @@ public class KeychainIntentService extends IntentService ArrayList entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST); String keyServer = data.getString(DOWNLOAD_KEY_SERVER); - // this downloads the keys and places them into the ImportKeysListEntry entries - HkpKeyserver server = new HkpKeyserver(keyServer); - for (ImportKeysListEntry entry : entries) { + + // this downloads the keys and places them into the ImportKeysListEntry entries + HkpKeyserver server = new HkpKeyserver(entry.getOrigin() != null ? entry.getOrigin() : keyServer); + // if available use complete fingerprint for get request byte[] downloadedKeyBytes; if (entry.getFingerprintHex() != null) { -- cgit v1.2.3 From 518f3e1763ae1fad54a20f3cba6578e79adcdb63 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 29 May 2014 10:33:15 +0200 Subject: Make abstract methods in Keyserver public (implementations make them public anyway) --- .../java/org/sufficientlysecure/keychain/keyimport/Keyserver.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java index 868f543f0..842e7d922 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java @@ -48,12 +48,12 @@ public abstract class Keyserver { private static final long serialVersionUID = -507574859137295530L; } - abstract List search(String query) throws QueryFailedException, + public abstract List search(String query) throws QueryFailedException, QueryNeedsRepairException; - abstract String get(String keyIdHex) throws QueryFailedException; + public abstract String get(String keyIdHex) throws QueryFailedException; - abstract void add(String armoredKey) throws AddKeyException; + public abstract void add(String armoredKey) throws AddKeyException; public static String readAll(InputStream in, String encoding) throws IOException { ByteArrayOutputStream raw = new ByteArrayOutputStream(); -- cgit v1.2.3 From 3417a7a2de190efe5c5ea19581dd4f099453a99e Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 29 May 2014 10:34:50 +0200 Subject: Store nice origin with keybase keys (that can't be interpreted as HKP server) --- .../org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index 43c0b12fd..8054edd3e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -31,6 +31,7 @@ import java.net.URLEncoder; import java.util.ArrayList; public class KeybaseKeyserver extends Keyserver { + public static final String ORIGIN = "keybase:keybase.io"; private String mQuery; @Override @@ -87,7 +88,7 @@ public class KeybaseKeyserver extends Keyserver { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(mQuery); - entry.setOrigin("keybase.io"); + entry.setOrigin(ORIGIN); String keybaseId = JWalk.getString(match, "components", "username", "val"); String fullName = JWalk.getString(match, "components", "full_name", "val"); -- cgit v1.2.3 From 34b97cb136376d14d4a1a48ccd89afa7d8abbb48 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 29 May 2014 11:43:41 +0200 Subject: Merge ACTION_DOWNLOAD_AND_IMPORT_KEYS and ACTION_IMPORT_KEYBASE_KEYS --- .../keychain/service/KeychainIntentService.java | 69 +++++----------------- 1 file changed, 15 insertions(+), 54 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index b6ee234f4..2b8745f0a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; +import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; @@ -742,68 +743,28 @@ public class KeychainIntentService extends IntentService } catch (Exception e) { sendErrorToHandler(e); } - } else if (ACTION_IMPORT_KEYBASE_KEYS.equals(action)) { - ArrayList entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST); - - try { - KeybaseKeyserver server = new KeybaseKeyserver(); - for (ImportKeysListEntry entry : entries) { - // the keybase handle is in userId(1) - String keybaseId = entry.getExtraData(); - byte[] downloadedKeyBytes = server.get(keybaseId).getBytes(); - - // create PGPKeyRing object based on downloaded armored key - PGPKeyRing downloadedKey = null; - BufferedInputStream bufferedInput = - new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes)); - if (bufferedInput.available() > 0) { - InputStream in = PGPUtil.getDecoderStream(bufferedInput); - PGPObjectFactory objectFactory = new PGPObjectFactory(in); - - // get first object in block - Object obj; - if ((obj = objectFactory.nextObject()) != null) { - - if (obj instanceof PGPKeyRing) { - downloadedKey = (PGPKeyRing) obj; - } else { - throw new PgpGeneralException("Object not recognized as PGPKeyRing!"); - } - } - } - - // save key bytes in entry object for doing the - // actual import afterwards - entry.setBytes(downloadedKey.getEncoded()); - } - - Intent importIntent = new Intent(this, KeychainIntentService.class); - importIntent.setAction(ACTION_IMPORT_KEYRING); - Bundle importData = new Bundle(); - importData.putParcelableArrayList(IMPORT_KEY_LIST, entries); - importIntent.putExtra(EXTRA_DATA, importData); - importIntent.putExtra(EXTRA_MESSENGER, mMessenger); - - // now import it with this service - onHandleIntent(importIntent); - - // result is handled in ACTION_IMPORT_KEYRING - } catch (Exception e) { - sendErrorToHandler(e); - } - } else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action)) { + } else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action) || ACTION_IMPORT_KEYBASE_KEYS.equals(action)) { try { ArrayList entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST); String keyServer = data.getString(DOWNLOAD_KEY_SERVER); + // this downloads the keys and places them into the ImportKeysListEntry entries for (ImportKeysListEntry entry : entries) { - // this downloads the keys and places them into the ImportKeysListEntry entries - HkpKeyserver server = new HkpKeyserver(entry.getOrigin() != null ? entry.getOrigin() : keyServer); + Keyserver server; + if (entry.getOrigin() == null) { + server = new HkpKeyserver(keyServer); + } else if (KeybaseKeyserver.ORIGIN.equals(entry.getOrigin())) { + server = new KeybaseKeyserver(); + } else { + server = new HkpKeyserver(entry.getOrigin()); + } // if available use complete fingerprint for get request byte[] downloadedKeyBytes; - if (entry.getFingerprintHex() != null) { + if (KeybaseKeyserver.ORIGIN.equals(entry.getOrigin())) { + downloadedKeyBytes = server.get(entry.getExtraData()).getBytes(); + } else if (entry.getFingerprintHex() != null) { downloadedKeyBytes = server.get("0x" + entry.getFingerprintHex()).getBytes(); } else { downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes(); @@ -833,7 +794,7 @@ public class KeychainIntentService extends IntentService if (entry.getFingerprintHex() != null) { String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex( downloadedKey.getPublicKey().getFingerprint()); - if (downloadedKeyFp.equals(entry.getFingerprintHex())) { + if (downloadedKeyFp.equalsIgnoreCase(entry.getFingerprintHex())) { Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " + "the requested fingerprint!"); } else { -- cgit v1.2.3 From 54b7b0e522b00cf6dcacc18bce1efaebb662119b Mon Sep 17 00:00:00 2001 From: Tim Bray Date: Tue, 3 Jun 2014 11:30:22 -0700 Subject: fixed error message --- .../org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java | 3 ++- .../org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index f9b6abf18..145738e66 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -144,7 +144,8 @@ public class KeybaseKeyserver extends Keyserver { try { JSONObject json = new JSONObject(text); if (JWalk.getInt(json, "status", "code") != 0) { - throw new QueryFailedException("Keybase autocomplete search failed"); + throw new QueryFailedException("Keybase.io query failed: " + path + "?" + + query); } return json; } catch (JSONException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java index a996079c9..a639fe0e0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java @@ -18,8 +18,8 @@ package org.sufficientlysecure.keychain.ui; import android.content.Context; -import android.support.v4.app.Fragment; import android.os.Bundle; +import android.support.v4.app.Fragment; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -31,9 +31,7 @@ import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Log; /** * Import public keys from the Keybase.io directory. First cut: just raw search. -- cgit v1.2.3 From cc2ef0c17ca1d032477eb21308c5ea677b1cc548 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 4 Jun 2014 17:05:57 +0200 Subject: Store expired state within ImportKeysListEntry --- .../sufficientlysecure/keychain/keyimport/HkpKeyserver.java | 1 + .../keychain/keyimport/ImportKeysListEntry.java | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index b064fc5b1..d4f1af84f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -308,6 +308,7 @@ public class HkpKeyserver extends Keyserver { entry.setDate(tmpGreg.getTime()); entry.setRevoked(matcher.group(6).contains("r")); + entry.setExpired(matcher.group(6).contains("e")); ArrayList userIds = new ArrayList(); final String uidLines = matcher.group(7); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index 8af41a8a3..ea4f5948e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -44,6 +44,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { public long keyId; public String keyIdHex; public boolean revoked; + public boolean expired; public Date date; // TODO: not displayed public String fingerprintHex; public int bitStrength; @@ -68,6 +69,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { dest.writeStringList(userIds); dest.writeLong(keyId); dest.writeByte((byte) (revoked ? 1 : 0)); + dest.writeByte((byte) (expired ? 1 : 0)); dest.writeSerializable(date); dest.writeString(fingerprintHex); dest.writeString(keyIdHex); @@ -89,6 +91,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { source.readStringList(vr.userIds); vr.keyId = source.readLong(); vr.revoked = source.readByte() == 1; + vr.expired = source.readByte() == 1; vr.date = (Date) source.readSerializable(); vr.fingerprintHex = source.readString(); vr.keyIdHex = source.readString(); @@ -129,6 +132,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.mSelected = selected; } + public boolean isExpired() { + return expired; + } + + public void setExpired(boolean expired) { + this.expired = expired; + } + public long getKeyId() { return keyId; } -- cgit v1.2.3 From dd959876f4a1a7f26a3f7524e238416c1a30c7e5 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 4 Jun 2014 17:55:24 +0200 Subject: First version of automatic contact discovery. TODO: - Configuration (much of it) - Enabled by default? - Which keys to import? Current state: All non-revoked and non-expired with matching userid - Search for keys if already known? Current state: yes, may cause traffic (configuration: only when wifi?) - Update interval: Currently Android handles it, might be good (causes automatic refresh on new contact and stuff like that) or bad (too many of refreshes) --- OpenKeychain/src/main/AndroidManifest.xml | 26 ++++ .../keychain/KeychainApplication.java | 13 ++ .../keychain/helper/EmailKeyHelper.java | 97 +++++++++++++++ .../service/ContactSyncAdapterService.java | 70 +++++++++++ .../keychain/service/DummyAccountService.java | 131 +++++++++++++++++++++ OpenKeychain/src/main/res/values/strings.xml | 2 + OpenKeychain/src/main/res/xml/account_desc.xml | 6 + .../main/res/xml/custom_pgp_contacts_structure.xml | 7 ++ .../src/main/res/xml/sync_adapter_desc.xml | 6 + 9 files changed, 358 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java create mode 100644 OpenKeychain/src/main/res/xml/account_desc.xml create mode 100644 OpenKeychain/src/main/res/xml/custom_pgp_contacts_structure.xml create mode 100644 OpenKeychain/src/main/res/xml/sync_adapter_desc.xml (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index fd26d6acf..9a2011205 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -53,6 +53,10 @@ + + + + @@ -435,6 +439,28 @@ + + + + + + + + + + + + + + + + diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index f911318a0..3ac3a9dee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -17,6 +17,8 @@ package org.sufficientlysecure.keychain; +import android.accounts.Account; +import android.accounts.AccountManager; import android.app.Application; import android.content.Context; import android.graphics.PorterDuff; @@ -76,6 +78,17 @@ public class KeychainApplication extends Application { brandGlowEffect(getApplicationContext(), getApplicationContext().getResources().getColor(R.color.emphasis)); + + setupAccountAsNeeded(); + } + + private void setupAccountAsNeeded() { + AccountManager manager = AccountManager.get(this); + Account[] accounts = manager.getAccountsByType(getPackageName()); + if (accounts == null || accounts.length == 0) { + Account dummy = new Account(getString(R.string.app_name), getPackageName()); + manager.addAccountExplicitly(dummy, null, null); + } } static void brandGlowEffect(Context context, int brandColor) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java new file mode 100644 index 000000000..80f52f914 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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, see . + */ + +package org.sufficientlysecure.keychain.helper; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Messenger; +import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; +import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; +import org.sufficientlysecure.keychain.keyimport.Keyserver; +import org.sufficientlysecure.keychain.service.KeychainIntentService; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class EmailKeyHelper { + + public static void importContacts(Context context, Messenger messenger) { + importAll(context, messenger, ContactHelper.getContactMails(context)); + } + + public static void importAll(Context context, Messenger messenger, List mails) { + Set keys = new HashSet(); + for (String mail : mails) { + keys.addAll(getEmailKeys(context, mail)); + } + importKeys(context, messenger, new ArrayList(keys)); + } + + public static List getEmailKeys(Context context, String mail) { + Set keys = new HashSet(); + + // Try _hkp._tcp SRV record first + String[] mailparts = mail.split("@"); + if (mailparts.length == 2) { + HkpKeyserver hkp = HkpKeyserver.resolve(mailparts[1]); + if (hkp != null) { + keys.addAll(getEmailKeys(mail, hkp)); + } + } + + // Most users don't have the SRV record, so ask a default server as well + String[] servers = Preferences.getPreferences(context).getKeyServers(); + if (servers != null && servers.length != 0) { + HkpKeyserver hkp = new HkpKeyserver(servers[0]); + keys.addAll(getEmailKeys(mail, hkp)); + } + return new ArrayList(keys); + } + + private static void importKeys(Context context, Messenger messenger, List keys) { + Intent importIntent = new Intent(context, KeychainIntentService.class); + importIntent.setAction(KeychainIntentService.ACTION_DOWNLOAD_AND_IMPORT_KEYS); + Bundle importData = new Bundle(); + importData.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, + new ArrayList(keys)); + importIntent.putExtra(KeychainIntentService.EXTRA_DATA, importData); + importIntent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + context.startService(importIntent); + } + + public static List getEmailKeys(String mail, Keyserver keyServer) { + Set keys = new HashSet(); + try { + for (ImportKeysListEntry key : keyServer.search(mail)) { + if (key.isRevoked() || key.isExpired()) continue; + for (String userId : key.getUserIds()) { + if (userId.toLowerCase().contains(mail.toLowerCase())) { + keys.add(key); + } + } + } + } catch (Keyserver.QueryFailedException ignored) { + } catch (Keyserver.QueryNeedsRepairException ignored) { + } + return new ArrayList(keys); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java new file mode 100644 index 000000000..4d0397196 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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, see . + */ + +package org.sufficientlysecure.keychain.service; + +import android.accounts.Account; +import android.app.Service; +import android.content.AbstractThreadedSyncAdapter; +import android.content.ContentProviderClient; +import android.content.Intent; +import android.content.SyncResult; +import android.os.*; +import org.sufficientlysecure.keychain.helper.EmailKeyHelper; +import org.sufficientlysecure.keychain.util.Log; + +public class ContactSyncAdapterService extends Service { + + private class ContactSyncAdapter extends AbstractThreadedSyncAdapter { + + public ContactSyncAdapter() { + super(ContactSyncAdapterService.this, true); + } + + @Override + public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, + final SyncResult syncResult) { + EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(), + new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + Bundle data = msg.getData(); + switch (msg.arg1) { + case KeychainIntentServiceHandler.MESSAGE_OKAY: + return true; + case KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS: + if (data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS) && + data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)) { + Log.d("Keychain/ContactSync/DownloadKeys", "Progress: " + + data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS) + "/" + + data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)); + return false; + } + default: + Log.d("Keychain/ContactSync/DownloadKeys", "Syncing... " + msg.toString()); + return false; + } + } + }))); + } + } + + @Override + public IBinder onBind(Intent intent) { + return new ContactSyncAdapter().getSyncAdapterBinder(); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java new file mode 100644 index 000000000..d3b29d5cf --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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, see . + */ + +package org.sufficientlysecure.keychain.service; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.NetworkErrorException; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.widget.Toast; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; + +/** + * This service actually does nothing, it's sole task is to show a Toast if the use tries to create an account. + */ +public class DummyAccountService extends Service { + + private class Toaster { + private static final String TOAST_MESSAGE = "toast_message"; + private Context context; + private Handler handler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + Toast.makeText(context, msg.getData().getString(TOAST_MESSAGE), Toast.LENGTH_LONG).show(); + return true; + } + }); + + private Toaster(Context context) { + this.context = context; + } + + public void toast(int resourceId) { + toast(context.getString(resourceId)); + } + + public void toast(String message) { + Message msg = new Message(); + Bundle bundle = new Bundle(); + bundle.putString(TOAST_MESSAGE, message); + msg.setData(bundle); + handler.sendMessage(msg); + } + } + + private class Authenticator extends AbstractAccountAuthenticator { + + public Authenticator() { + super(DummyAccountService.this); + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { + Log.d("DummyAccountService", "editProperties"); + return null; + } + + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, + String[] requiredFeatures, Bundle options) throws NetworkErrorException { + response.onResult(new Bundle()); + toaster.toast(R.string.info_no_manual_account_creation); + Log.d("DummyAccountService", "addAccount"); + return null; + } + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) + throws NetworkErrorException { + Log.d("DummyAccountService", "confirmCredentials"); + return null; + } + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, + Bundle options) throws NetworkErrorException { + Log.d("DummyAccountService", "getAuthToken"); + return null; + } + + @Override + public String getAuthTokenLabel(String authTokenType) { + Log.d("DummyAccountService", "getAuthTokenLabel"); + return null; + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, + Bundle options) throws NetworkErrorException { + Log.d("DummyAccountService", "updateCredentials"); + return null; + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) + throws NetworkErrorException { + Log.d("DummyAccountService", "hasFeatures"); + return null; + } + } + + private Toaster toaster; + + @Override + public IBinder onBind(Intent intent) { + toaster = new Toaster(this); + return new Authenticator().getIBinder(); + } +} diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 1ba8a6d2d..70b8616d4 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -518,5 +518,7 @@ unknown cannot sign No encryption subkey available! + Do not create OpenKeychain-Accounts manually. + For more information, see Help. diff --git a/OpenKeychain/src/main/res/xml/account_desc.xml b/OpenKeychain/src/main/res/xml/account_desc.xml new file mode 100644 index 000000000..94ffdf40b --- /dev/null +++ b/OpenKeychain/src/main/res/xml/account_desc.xml @@ -0,0 +1,6 @@ + + + diff --git a/OpenKeychain/src/main/res/xml/custom_pgp_contacts_structure.xml b/OpenKeychain/src/main/res/xml/custom_pgp_contacts_structure.xml new file mode 100644 index 000000000..3318f3b45 --- /dev/null +++ b/OpenKeychain/src/main/res/xml/custom_pgp_contacts_structure.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml b/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml new file mode 100644 index 000000000..d8fe60e91 --- /dev/null +++ b/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file -- cgit v1.2.3 From 6a637462782b4ce57ecf154edf0974114181b8ad Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 4 Jun 2014 18:07:28 +0200 Subject: Fix regex for hkp parsing to support multiple uids --- .../java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index d4f1af84f..2ec9e1c07 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -116,7 +116,7 @@ public class HkpKeyserver extends Keyserver { */ public static final Pattern PUB_KEY_LINE = Pattern .compile("pub:([0-9a-fA-F]+):([0-9]+):([0-9]+):([0-9]+):([0-9]*):([rde]*)[ \n\r]*" // pub line - + "(uid:(.*):([0-9]+):([0-9]*):([rde]*))+", // one or more uid lines + + "((uid:([^:]*):([0-9]+):([0-9]*):([rde]*)[ \n\r]*)+)", // one or more uid lines Pattern.CASE_INSENSITIVE); /** @@ -144,7 +144,7 @@ public class HkpKeyserver extends Keyserver { * */ public static final Pattern UID_LINE = Pattern - .compile("uid:(.*):([0-9]+):([0-9]*):([rde]*)", + .compile("uid:([^:]*):([0-9]+):([0-9]*):([rde]*)", Pattern.CASE_INSENSITIVE); private static final short PORT_DEFAULT = 11371; -- cgit v1.2.3 From dc1e26f39c9c7fa88dd28d2920a2919f83e0575c Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 5 Jun 2014 00:59:39 +0200 Subject: Make keylist case insensitive You want "michael" to be next to "Michael", don't you? --- .../java/org/sufficientlysecure/keychain/ui/KeyListFragment.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 9c90b5eb7..d5a753133 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -253,7 +253,7 @@ public class KeyListFragment extends LoaderFragment static final int INDEX_HAS_ANY_SECRET = 6; static final String ORDER = - KeyRings.HAS_ANY_SECRET + " DESC, " + KeyRings.USER_ID + " ASC"; + KeyRings.HAS_ANY_SECRET + " DESC, UPPER(" + KeyRings.USER_ID + ") ASC"; @Override @@ -593,7 +593,7 @@ public class KeyListFragment extends LoaderFragment String userId = mCursor.getString(KeyListFragment.INDEX_USER_ID); String headerText = convertView.getResources().getString(R.string.user_id_no_name); if (userId != null && userId.length() > 0) { - headerText = "" + userId.subSequence(0, 1).charAt(0); + headerText = "" + userId.charAt(0); } holder.mText.setText(headerText); holder.mCount.setVisibility(View.GONE); @@ -622,7 +622,7 @@ public class KeyListFragment extends LoaderFragment // otherwise, return the first character of the name as ID String userId = mCursor.getString(KeyListFragment.INDEX_USER_ID); if (userId != null && userId.length() > 0) { - return userId.charAt(0); + return Character.toUpperCase(userId.charAt(0)); } else { return Long.MAX_VALUE; } -- cgit v1.2.3 From c84a1ecfff8d3f21ac83ef7b041f22f11e38936b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 5 Jun 2014 14:06:07 +0200 Subject: import-log: add parcelable prototype --- .../keychain/pgp/OperationResultParcel.java | 123 +++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java new file mode 100644 index 000000000..497c3dd71 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java @@ -0,0 +1,123 @@ +package org.sufficientlysecure.keychain.pgp; + +import android.R; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; + +/** Represent the result of an operation. + * + * This class holds a result and the log of an operation. It can be subclassed + * to include typed additional information specific to the operation. To keep + * the class structure (somewhat) simple, this class contains an exhaustive + * list (ie, enum) of all possible log types, which should in all cases be tied + * to string resource ids. + * + */ +public class OperationResultParcel implements Parcelable { + /** Holds the overall result. A value of 0 is considered a success, all + * other values may represent failure or varying degrees of success. */ + final int mResult; + + /// A list of log entries tied to the operation result. + final ArrayList mLog; + + public OperationResultParcel(int result, ArrayList log) { + mResult = result; + mLog = log; + } + + public OperationResultParcel(Parcel source) { + mResult = source.readInt(); + mLog = source.createTypedArrayList(LogEntryParcel.CREATOR); + } + + public boolean isSuccessful() { + return mResult == 0; + } + + /** One entry in the log. */ + public static class LogEntryParcel implements Parcelable { + final LogType mType; + final LogLevel mLevel; + final String[] mParameters; + + public LogEntryParcel(LogType type, LogLevel level, String[] parameters) { + mType = type; + mLevel = level; + mParameters = parameters; + } + + public LogEntryParcel(Parcel source) { + mType = LogType.values()[source.readInt()]; + mLevel = LogLevel.values()[source.readInt()]; + mParameters = source.createStringArray(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType.ordinal()); + dest.writeInt(mLevel.ordinal()); + dest.writeStringArray(mParameters); + } + + public static final Creator CREATOR = new Creator() { + public LogEntryParcel createFromParcel(final Parcel source) { + return new LogEntryParcel(source); + } + + public LogEntryParcel[] newArray(final int size) { + return new LogEntryParcel[size]; + } + }; + + } + + public static enum LogType { + // TODO add actual log entry types here + MSG_IMPORT_OK (R.string.copy), + MSG_IMPORT_FAILED (R.string.cancel); + + private int mMsgId; + LogType(int msgId) { + mMsgId = msgId; + } + } + + /** Enumeration of possible log levels. */ + public static enum LogLevel { + DEBUG, + INFO, + WARN, + /** If any ERROR log entry is included in the result, the overall operation should have failed. */ + ERROR, + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mResult); + dest.writeTypedList(mLog); + } + + public static final Creator CREATOR = new Creator() { + public OperationResultParcel createFromParcel(final Parcel source) { + return new OperationResultParcel(source); + } + + public OperationResultParcel[] newArray(final int size) { + return new OperationResultParcel[size]; + } + }; + +} -- cgit v1.2.3 From 781197021875d06e22ff28fb7983391b36910c5b Mon Sep 17 00:00:00 2001 From: Tim Bray Date: Thu, 5 Jun 2014 08:51:55 -0700 Subject: Don't show full fingerprint in key search results --- .../sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 114c6afae..02ddbb45f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -149,12 +149,8 @@ public class ImportKeysAdapter extends ArrayAdapter { holder.keyId.setText(entry.keyIdHex); - if (entry.fingerprintHex != null) { - holder.fingerprint.setVisibility(View.VISIBLE); - holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerprintHex)); - } else { - holder.fingerprint.setVisibility(View.GONE); - } + // don't show full fingerprint on key import + holder.fingerprint.setVisibility(View.GONE); if (entry.bitStrength != 0 && entry.algorithm != null) { holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm); -- cgit v1.2.3 From 5466adee410280af390023b163ee512d98be0601 Mon Sep 17 00:00:00 2001 From: Tim Bray Date: Thu, 5 Jun 2014 11:56:38 -0700 Subject: Clean up keyimport.ImportKeysListEntry --- .../keychain/keyimport/ImportKeysListEntry.java | 116 ++++++++++----------- .../keychain/ui/adapter/ImportKeysAdapter.java | 17 ++- 2 files changed, 66 insertions(+), 67 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index c43f72235..c64794bb6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -32,16 +32,16 @@ import java.util.Date; public class ImportKeysListEntry implements Serializable, Parcelable { private static final long serialVersionUID = -7797972103284992662L; - public ArrayList userIds; - public long keyId; - public String keyIdHex; - public boolean revoked; - public Date date; // TODO: not displayed - public String fingerprintHex; - public int bitStrength; - public String algorithm; - public boolean secretKey; - public String mPrimaryUserId; + private ArrayList mUserIds; + private long mKeyId; + private String mKeyIdHex; + private boolean mRevoked; + private Date mDate; // TODO: not displayed + private String mFingerprintHex; + private int mBitStrength; + private String mAlgorithm; + private boolean mSecretKey; + private String mPrimaryUserId; private String mExtraData; private String mQuery; @@ -54,15 +54,15 @@ public class ImportKeysListEntry implements Serializable, Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPrimaryUserId); - dest.writeStringList(userIds); - dest.writeLong(keyId); - dest.writeByte((byte) (revoked ? 1 : 0)); - dest.writeSerializable(date); - dest.writeString(fingerprintHex); - dest.writeString(keyIdHex); - dest.writeInt(bitStrength); - dest.writeString(algorithm); - dest.writeByte((byte) (secretKey ? 1 : 0)); + dest.writeStringList(mUserIds); + dest.writeLong(mKeyId); + dest.writeByte((byte) (mRevoked ? 1 : 0)); + dest.writeSerializable(mDate); + dest.writeString(mFingerprintHex); + dest.writeString(mKeyIdHex); + dest.writeInt(mBitStrength); + dest.writeString(mAlgorithm); + dest.writeByte((byte) (mSecretKey ? 1 : 0)); dest.writeByte((byte) (mSelected ? 1 : 0)); dest.writeString(mExtraData); } @@ -71,16 +71,16 @@ public class ImportKeysListEntry implements Serializable, Parcelable { public ImportKeysListEntry createFromParcel(final Parcel source) { ImportKeysListEntry vr = new ImportKeysListEntry(); vr.mPrimaryUserId = source.readString(); - vr.userIds = new ArrayList(); - source.readStringList(vr.userIds); - vr.keyId = source.readLong(); - vr.revoked = source.readByte() == 1; - vr.date = (Date) source.readSerializable(); - vr.fingerprintHex = source.readString(); - vr.keyIdHex = source.readString(); - vr.bitStrength = source.readInt(); - vr.algorithm = source.readString(); - vr.secretKey = source.readByte() == 1; + vr.mUserIds = new ArrayList(); + source.readStringList(vr.mUserIds); + vr.mKeyId = source.readLong(); + vr.mRevoked = source.readByte() == 1; + vr.mDate = (Date) source.readSerializable(); + vr.mFingerprintHex = source.readString(); + vr.mKeyIdHex = source.readString(); + vr.mBitStrength = source.readInt(); + vr.mAlgorithm = source.readString(); + vr.mSecretKey = source.readByte() == 1; vr.mSelected = source.readByte() == 1; vr.mExtraData = source.readString(); @@ -93,7 +93,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { }; public String getKeyIdHex() { - return keyIdHex; + return mKeyIdHex; } public boolean isSelected() { @@ -105,71 +105,71 @@ public class ImportKeysListEntry implements Serializable, Parcelable { } public long getKeyId() { - return keyId; + return mKeyId; } public void setKeyId(long keyId) { - this.keyId = keyId; + this.mKeyId = keyId; } public void setKeyIdHex(String keyIdHex) { - this.keyIdHex = keyIdHex; + this.mKeyIdHex = keyIdHex; } public boolean isRevoked() { - return revoked; + return mRevoked; } public void setRevoked(boolean revoked) { - this.revoked = revoked; + this.mRevoked = revoked; } public Date getDate() { - return date; + return mDate; } public void setDate(Date date) { - this.date = date; + this.mDate = date; } public String getFingerprintHex() { - return fingerprintHex; + return mFingerprintHex; } public void setFingerprintHex(String fingerprintHex) { - this.fingerprintHex = fingerprintHex; + this.mFingerprintHex = fingerprintHex; } public int getBitStrength() { - return bitStrength; + return mBitStrength; } public void setBitStrength(int bitStrength) { - this.bitStrength = bitStrength; + this.mBitStrength = bitStrength; } public String getAlgorithm() { - return algorithm; + return mAlgorithm; } public void setAlgorithm(String algorithm) { - this.algorithm = algorithm; + this.mAlgorithm = algorithm; } public boolean isSecretKey() { - return secretKey; + return mSecretKey; } public void setSecretKey(boolean secretKey) { - this.secretKey = secretKey; + this.mSecretKey = secretKey; } public ArrayList getUserIds() { - return userIds; + return mUserIds; } public void setUserIds(ArrayList userIds) { - this.userIds = userIds; + this.mUserIds = userIds; } public String getPrimaryUserId() { @@ -201,10 +201,10 @@ public class ImportKeysListEntry implements Serializable, Parcelable { */ public ImportKeysListEntry() { // keys from keyserver are always public keys; from keybase too - secretKey = false; + mSecretKey = false; // do not select by default mSelected = false; - userIds = new ArrayList(); + mUserIds = new ArrayList(); } /** @@ -215,24 +215,24 @@ public class ImportKeysListEntry implements Serializable, Parcelable { // selected is default this.mSelected = true; - secretKey = ring.isSecret(); + mSecretKey = ring.isSecret(); UncachedPublicKey key = ring.getPublicKey(); mPrimaryUserId = key.getPrimaryUserId(); - userIds = key.getUnorderedUserIds(); + mUserIds = key.getUnorderedUserIds(); // if there was no user id flagged as primary, use the first one if (mPrimaryUserId == null) { - mPrimaryUserId = userIds.get(0); + mPrimaryUserId = mUserIds.get(0); } - this.keyId = key.getKeyId(); - this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId); + this.mKeyId = key.getKeyId(); + this.mKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mKeyId); - this.revoked = key.maybeRevoked(); - this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); - this.bitStrength = key.getBitStrength(); + this.mRevoked = key.maybeRevoked(); + this.mFingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); + this.mBitStrength = key.getBitStrength(); final int algorithm = key.getAlgorithm(); - this.algorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm); + this.mAlgorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 041fc8040..233b1fca8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -33,7 +33,6 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.util.Highlighter; import java.util.ArrayList; @@ -120,13 +119,13 @@ public class ImportKeysAdapter extends ArrayAdapter { } // main user id - String userId = entry.userIds.get(0); + String userId = entry.getUserIds().get(0); String[] userIdSplit = KeyRing.splitUserId(userId); // name if (userIdSplit[0] != null) { // show red user id if it is a secret key - if (entry.secretKey) { + if (entry.isSecretKey()) { holder.mainUserId.setText(mActivity.getString(R.string.secret_key) + " " + userIdSplit[0]); holder.mainUserId.setTextColor(Color.RED); @@ -147,26 +146,26 @@ public class ImportKeysAdapter extends ArrayAdapter { holder.mainUserIdRest.setVisibility(View.GONE); } - holder.keyId.setText(entry.keyIdHex); + holder.keyId.setText(entry.getKeyIdHex()); // don't show full fingerprint on key import holder.fingerprint.setVisibility(View.GONE); - if (entry.bitStrength != 0 && entry.algorithm != null) { - holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm); + if (entry.getBitStrength() != 0 && entry.getAlgorithm() != null) { + holder.algorithm.setText("" + entry.getBitStrength() + "/" + entry.getAlgorithm()); holder.algorithm.setVisibility(View.VISIBLE); } else { holder.algorithm.setVisibility(View.INVISIBLE); } - if (entry.revoked) { + if (entry.isRevoked()) { holder.status.setVisibility(View.VISIBLE); holder.status.setText(R.string.revoked); } else { holder.status.setVisibility(View.GONE); } - if (entry.userIds.size() == 1) { + if (entry.getUserIds().size() == 1) { holder.userIdsList.setVisibility(View.GONE); } else { holder.userIdsList.setVisibility(View.VISIBLE); @@ -174,7 +173,7 @@ public class ImportKeysAdapter extends ArrayAdapter { // clear view from holder holder.userIdsList.removeAllViews(); - Iterator it = entry.userIds.iterator(); + Iterator it = entry.getUserIds().iterator(); // skip primary user id it.next(); while (it.hasNext()) { -- cgit v1.2.3 From 80e99986401b635f4eeef5d13740911d10740aef Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 5 Jun 2014 23:22:21 +0200 Subject: Show keys with android contacts This means to sync userid + keyid into contact storage. Android will merge them to normal contacts based on primary userid. --- OpenKeychain/src/main/AndroidManifest.xml | 6 ++ .../org/sufficientlysecure/keychain/Constants.java | 2 + .../keychain/KeychainApplication.java | 11 +-- .../keychain/helper/ContactHelper.java | 89 +++++++++++++++++++++- .../service/ContactSyncAdapterService.java | 4 + .../keychain/ui/ViewKeyActivity.java | 13 +++- OpenKeychain/src/main/res/values/strings.xml | 1 + .../main/res/xml/custom_pgp_contacts_structure.xml | 4 +- 8 files changed, 119 insertions(+), 11 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 9a2011205..31c809334 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -58,6 +58,7 @@ + + + + + + getMailAccounts(Context context) { final Account[] accounts = AccountManager.get(context).getAccounts(); final Set emailSet = new HashSet(); @@ -60,4 +75,74 @@ public class ContactHelper { mailCursor.close(); return new ArrayList(mails); } + + public static Uri dataUriFromContactUri(Context context, Uri contactUri) { + Cursor contactMasterKey = context.getContentResolver().query(contactUri, new String[]{ContactsContract.Data.DATA2}, null, null, null, null); + if (contactMasterKey != null) { + if (contactMasterKey.moveToNext()) { + return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0)); + } + contactMasterKey.close(); + } + return null; + } + + public static void writeKeysToContacts(Context context) { + ContentResolver resolver = context.getContentResolver(); + Cursor cursor = resolver.query(KeychainContract.KeyRings.buildUnifiedKeyRingsUri(), KEYS_TO_CONTACT_PROJECTION, + null, null, null); + if (cursor != null) { + while (cursor.moveToNext()) { + String[] userId = PgpKeyHelper.splitUserId(cursor.getString(0)); + String fingerprint = PgpKeyHelper.convertFingerprintToHex(cursor.getBlob(1)); + String keyIdShort = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(2)); + long masterKeyId = cursor.getLong(3); + int rawContactId = -1; + Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, RAW_CONTACT_ID_PROJECTION, + FIND_RAW_CONTACT_SELECTION, new String[]{Constants.PACKAGE_NAME, fingerprint}, null, null); + if (raw != null) { + if (raw.moveToNext()) { + rawContactId = raw.getInt(0); + } + raw.close(); + } + ArrayList ops = new ArrayList(); + if (rawContactId == -1) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, context.getString(R.string.app_name)) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.PACKAGE_NAME) + .withValue(ContactsContract.RawContacts.SOURCE_ID, fingerprint) + .build()); + if (userId[0] != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, userId[0]) + .build()); + } + if (userId[1] != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Email.DATA, userId[1]) + .build()); + } + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, Constants.CUSTOM_CONTACT_DATA_MIME_TYPE) + .withValue(ContactsContract.Data.DATA1, String.format(context.getString(R.string.contact_show_key), keyIdShort)) + .withValue(ContactsContract.Data.DATA2, masterKeyId) + .build()); + } + try { + resolver.applyBatch(ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + e.printStackTrace(); + } catch (OperationApplicationException e) { + e.printStackTrace(); + } + } + cursor.close(); + } + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java index 4d0397196..a52dbfa27 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java @@ -24,6 +24,8 @@ import android.content.ContentProviderClient; import android.content.Intent; import android.content.SyncResult; import android.os.*; +import org.sufficientlysecure.keychain.KeychainApplication; +import org.sufficientlysecure.keychain.helper.ContactHelper; import org.sufficientlysecure.keychain.helper.EmailKeyHelper; import org.sufficientlysecure.keychain.util.Log; @@ -60,6 +62,8 @@ public class ContactSyncAdapterService extends Service { } } }))); + KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this); + ContactHelper.writeKeysToContacts(ContactSyncAdapterService.this); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index bed116f5f..010144851 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui; import android.annotation.TargetApi; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; @@ -32,6 +33,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.provider.ContactsContract; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; @@ -47,6 +49,7 @@ import com.devspark.appmsg.AppMsg; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ContactHelper; import org.sufficientlysecure.keychain.helper.ExportHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; @@ -125,7 +128,7 @@ public class ViewKeyActivity extends ActionBarActivity implements switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB); } - Uri dataUri = getIntent().getData(); + Uri dataUri = getDataUri(); if (dataUri == null) { Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); finish(); @@ -163,6 +166,14 @@ public class ViewKeyActivity extends ActionBarActivity implements mViewPager.setCurrentItem(switchToTab); } + private Uri getDataUri() { + Uri dataUri = getIntent().getData(); + if (dataUri != null && dataUri.getHost().equals(ContactsContract.AUTHORITY)) { + dataUri = ContactHelper.dataUriFromContactUri(this, dataUri); + } + return dataUri; + } + private void loadData(Uri dataUri) { mDataUri = dataUri; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 09f76c675..ec6b389ed 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -521,5 +521,6 @@ No encryption subkey available! Do not create OpenKeychain-Accounts manually. For more information, see Help. + Show key (%s) diff --git a/OpenKeychain/src/main/res/xml/custom_pgp_contacts_structure.xml b/OpenKeychain/src/main/res/xml/custom_pgp_contacts_structure.xml index 3318f3b45..5f5f2be80 100644 --- a/OpenKeychain/src/main/res/xml/custom_pgp_contacts_structure.xml +++ b/OpenKeychain/src/main/res/xml/custom_pgp_contacts_structure.xml @@ -1,7 +1,5 @@ + android:detailColumn="data1"/> \ No newline at end of file -- cgit v1.2.3 From 9d02bc85e2c3fffbb18c4bbd8889db827ac2e8f0 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 6 Jun 2014 00:51:24 +0200 Subject: Fix compile error introduced during merge --- .../java/org/sufficientlysecure/keychain/helper/ContactHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java index 4b85d7e80..f50ccf6f8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java @@ -27,6 +27,7 @@ import android.provider.ContactsContract; import android.util.Patterns; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.util.Log; @@ -93,7 +94,7 @@ public class ContactHelper { null, null, null); if (cursor != null) { while (cursor.moveToNext()) { - String[] userId = PgpKeyHelper.splitUserId(cursor.getString(0)); + String[] userId = KeyRing.splitUserId(cursor.getString(0)); String fingerprint = PgpKeyHelper.convertFingerprintToHex(cursor.getBlob(1)); String keyIdShort = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(2)); long masterKeyId = cursor.getLong(3); -- cgit v1.2.3 From b995b836a38ef8a87aa189f408f8542bf5a2c94c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Jun 2014 16:14:15 +0200 Subject: import-log: improve operationresultparcel, add indentation --- .../keychain/pgp/OperationResultParcel.java | 20 +++++++++++----- .../keychain/provider/ProviderHelper.java | 27 ++++++++++++++++++---- 2 files changed, 37 insertions(+), 10 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java index 497c3dd71..f2da4389d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java @@ -1,9 +1,10 @@ package org.sufficientlysecure.keychain.pgp; -import android.R; import android.os.Parcel; import android.os.Parcelable; +import org.sufficientlysecure.keychain.R; + import java.util.ArrayList; /** Represent the result of an operation. @@ -39,20 +40,26 @@ public class OperationResultParcel implements Parcelable { /** One entry in the log. */ public static class LogEntryParcel implements Parcelable { - final LogType mType; final LogLevel mLevel; + final LogType mType; final String[] mParameters; + final int mIndent; - public LogEntryParcel(LogType type, LogLevel level, String[] parameters) { - mType = type; + public LogEntryParcel(LogLevel level, LogType type, String[] parameters, int indent) { mLevel = level; + mType = type; mParameters = parameters; + mIndent = indent; + } + public LogEntryParcel(LogLevel level, LogType type, String[] parameters) { + this(level, type, parameters, 0); } public LogEntryParcel(Parcel source) { - mType = LogType.values()[source.readInt()]; mLevel = LogLevel.values()[source.readInt()]; + mType = LogType.values()[source.readInt()]; mParameters = source.createStringArray(); + mIndent = source.readInt(); } @Override @@ -62,9 +69,10 @@ public class OperationResultParcel implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mType.ordinal()); dest.writeInt(mLevel.ordinal()); + dest.writeInt(mType.ordinal()); dest.writeStringArray(mParameters); + dest.writeInt(mIndent); } public static final Creator CREATOR = new Creator() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index ca7e622bb..83b55cc14 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,6 +29,9 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.OperationResultParcel; +import org.sufficientlysecure.keychain.pgp.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.pgp.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; @@ -59,12 +62,21 @@ import java.util.List; import java.util.Set; public class ProviderHelper { - private Context mContext; - private ContentResolver mContentResolver; + private final Context mContext; + private final ContentResolver mContentResolver; + private final ArrayList mLog; + private int mIndent; public ProviderHelper(Context context) { - this.mContext = context; - this.mContentResolver = context.getContentResolver(); + this(context, null, 0); + } + + public ProviderHelper(Context context, ArrayList log, + int indent) { + mContext = context; + mContentResolver = context.getContentResolver(); + mLog = log; + mIndent = indent; } public static class NotFoundException extends Exception { @@ -76,6 +88,13 @@ public class ProviderHelper { } } + public void log(LogLevel level, LogType type) { + mLog.add(new OperationResultParcel.LogEntryParcel(level, type, null, mIndent)); + } + public void log(LogLevel level, LogType type, String[] parameters) { + mLog.add(new OperationResultParcel.LogEntryParcel(level, type, parameters, mIndent)); + } + // If we ever switch to api level 11, we can ditch this whole mess! public static final int FIELD_TYPE_NULL = 1; // this is called integer to stay coherent with the constants in Cursor (api level 11) -- cgit v1.2.3 From 787f6edf3260056030025388b921e9d6ce996d92 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Jun 2014 16:15:27 +0200 Subject: import-log: add log statements in import routine --- .../keychain/pgp/OperationResultParcel.java | 40 ++- .../keychain/provider/ProviderHelper.java | 282 ++++++++++++++------- 2 files changed, 225 insertions(+), 97 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java index f2da4389d..8110590b1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java @@ -88,9 +88,43 @@ public class OperationResultParcel implements Parcelable { } public static enum LogType { - // TODO add actual log entry types here - MSG_IMPORT_OK (R.string.copy), - MSG_IMPORT_FAILED (R.string.cancel); + MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch), + MSG_IP_BAD_TYPE_SECRET (R.string.msg_ip_bad_type_secret), + MSG_IP_DELETE_OLD_FAIL (R.string.msg_ip_delete_old_fail), + MSG_IP_DELETE_OLD_OK (R.string.msg_ip_delete_old_ok), + MSG_IP_ENCODE_FAIL (R.string.msg_ip_encode_fail), + MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), + MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), + MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), + MSG_IP_IMPORTING (R.string.msg_ip_importing), + MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), + MSG_IP_INSERT_SUBKEY (R.string.msg_ip_insert_subkey), + MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), + MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), + MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), + MSG_IP_SUCCESS (R.string.msg_ip_success), + MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), + MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), + MSG_IP_TRUST_USING_SEC (R.string.msg_ip_trust_using_sec), + MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), + MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), + MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), + MSG_IP_UID_CERTS_UNKNOWN (R.string.msg_ip_uid_certs_unknown), + MSG_IP_UID_CLASSIFYING (R.string.msg_ip_uid_classifying), + MSG_IP_UID_INSERT (R.string.msg_ip_uid_insert), + MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), + MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), + MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), + MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), + MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), + MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), + MSG_IS_IMPORTING (R.string.msg_is_importing), + MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), + MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), + MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), + MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), + MSG_IS_SUCCESS (R.string.msg_is_success), + ; private int mMsgId; LogType(int msgId) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 83b55cc14..170fc4df2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -241,136 +241,208 @@ public class ProviderHelper { * Saves PGPPublicKeyRing with its keys and userIds in DB */ @SuppressWarnings("unchecked") - public void savePublicKeyRing(UncachedKeyRing keyRing) throws IOException { + public OperationResultParcel savePublicKeyRing(UncachedKeyRing keyRing) { if (keyRing.isSecret()) { - throw new RuntimeException("Tried to save secret keyring as public! " + - "This is a bug, please file a bug report."); + log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); + return new OperationResultParcel(1, mLog); } UncachedPublicKey masterKey = keyRing.getPublicKey(); long masterKeyId = masterKey.getKeyId(); + log(LogLevel.INFO, LogType.MSG_IP_IMPORTING, + new String[]{Long.toString(masterKeyId)}); // IF there is a secret key, preserve it! - UncachedKeyRing secretRing = null; + UncachedKeyRing secretRing; try { secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); + log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); } catch (NotFoundException e) { - Log.e(Constants.TAG, "key not found!"); + secretRing = null; } // delete old version of this keyRing, which also deletes all keys and userIds on cascade try { mContentResolver.delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); } catch (UnsupportedOperationException e) { Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); + log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); } // insert new version of this keyRing ContentValues values = new ContentValues(); values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); - values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); - Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); - mContentResolver.insert(uri, values); + try { + values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); + } catch (IOException e) { + log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); + return new OperationResultParcel(1, mLog); + } // save all keys and userIds included in keyRing object in database ArrayList operations = new ArrayList(); - int rank = 0; - for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - operations.add(buildPublicKeyOperations(masterKeyId, key, rank)); - ++rank; - } + try { - // get a list of owned secret keys, for verification filtering - LongSparseArray allKeyRings = - getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); - // special case: available secret keys verify themselves! - if (secretRing != null) { - allKeyRings.put(secretRing.getMasterKeyId(), secretRing.getPublicKey()); - } + log(LogLevel.INFO, LogType.MSG_IP_INSERT_KEYRING); + Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); + operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); - // classify and order user ids. primary are moved to the front, revoked to the back, - // otherwise the order in the keyfile is preserved. - List uids = new ArrayList(); - - for (String userId : new IterableIterator( - masterKey.getUnorderedUserIds().iterator())) { - UserIdItem item = new UserIdItem(); - uids.add(item); - item.userId = userId; - - // look through signatures for this specific key - for (WrappedSignature cert : new IterableIterator( - masterKey.getSignaturesForId(userId))) { - long certId = cert.getKeyId(); - try { - // self signature - if (certId == masterKeyId) { - cert.init(masterKey); - if (!cert.verifySignature(masterKey, userId)) { - // not verified?! dang! TODO notify user? this is kinda serious... - Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!"); - continue; - } - // is this the first, or a more recent certificate? - if (item.selfCert == null || - item.selfCert.getCreationTime().before(cert.getCreationTime())) { + log(LogLevel.INFO, LogType.MSG_IP_INSERT_SUBKEYS); + mIndent += 1; + int rank = 0; + for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + log(LogLevel.DEBUG, LogType.MSG_IP_INSERT_SUBKEY, new String[] { + PgpKeyHelper.convertKeyIdToHex(masterKeyId) + }); + operations.add(buildPublicKeyOperations(masterKeyId, key, rank)); + ++rank; + } + mIndent -= 1; + + log(LogLevel.DEBUG, LogType.MSG_IP_TRUST_RETRIEVE); + // get a list of owned secret keys, for verification filtering + LongSparseArray trustedKeys = + getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); + // special case: available secret keys verify themselves! + if (secretRing != null) { + trustedKeys.put(secretRing.getMasterKeyId(), secretRing.getPublicKey()); + log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING_SEC, new String[]{ + Integer.toString(trustedKeys.size()) + }); + } else { + log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { + Integer.toString(trustedKeys.size()) + }); + } + + // classify and order user ids. primary are moved to the front, revoked to the back, + // otherwise the order in the keyfile is preserved. + log(LogLevel.DEBUG, LogType.MSG_IP_UID_CLASSIFYING); + mIndent += 1; + List uids = new ArrayList(); + for (String userId : new IterableIterator( + masterKey.getUnorderedUserIds().iterator())) { + UserIdItem item = new UserIdItem(); + uids.add(item); + item.userId = userId; + + int unknownCerts = 0; + + log(LogLevel.INFO, LogType.MSG_IP_UID_PROCESSING, new String[] { userId }); + mIndent += 1; + // look through signatures for this specific key + for (WrappedSignature cert : new IterableIterator( + masterKey.getSignaturesForId(userId))) { + long certId = cert.getKeyId(); + try { + // self signature + if (certId == masterKeyId) { + cert.init(masterKey); + if (!cert.verifySignature(masterKey, userId)) { + // Bad self certification? That's kinda bad... + log(LogLevel.ERROR, LogType.MSG_IP_UID_SELF_BAD); + return new OperationResultParcel(1, mLog); + } + + // if we already have a cert.. + if (item.selfCert != null) { + // ..is this perchance a more recent one? + if (item.selfCert.getCreationTime().before(cert.getCreationTime())) { + log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_NEWER); + } else { + log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_IGNORING_OLD); + continue; + } + } else { + log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_GOOD); + } + + // save certificate as primary self-cert item.selfCert = cert; item.isPrimary = cert.isPrimaryUserId(); item.isRevoked = cert.isRevocation(); + } - } - // verify signatures from known private keys - if (allKeyRings.indexOfKey(certId) >= 0) { - cert.init(allKeyRings.get(certId)); - if (cert.verifySignature(masterKey, userId)) { - item.trustedCerts.add(cert); + + // verify signatures from known private keys + if (trustedKeys.indexOfKey(certId) >= 0) { + UncachedPublicKey trustedKey = trustedKeys.get(certId); + cert.init(trustedKey); + if (cert.verifySignature(masterKey, userId)) { + item.trustedCerts.add(cert); + log(LogLevel.INFO, LogType.MSG_IP_UID_CERT_GOOD, new String[] { + PgpKeyHelper.convertKeyIdToHex(trustedKey.getKeyId()) + }); + } else { + log(LogLevel.WARN, LogType.MSG_IP_UID_CERT_BAD); + } } + + unknownCerts += 1; + + } catch (PgpGeneralException e) { + log(LogLevel.WARN, LogType.MSG_IP_UID_CERT_ERROR, new String[]{ + PgpKeyHelper.convertKeyIdToHex(cert.getKeyId()) + }); } - } catch (PgpGeneralException e) { - Log.e(Constants.TAG, "Signature verification failed! " - + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyId()) - + " from " - + PgpKeyHelper.convertKeyIdToHex(cert.getKeyId()), e); } - } - } + mIndent -= 1; + + if (unknownCerts > 0) { + log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[] { + Integer.toString(unknownCerts) + }); + } - // primary before regular before revoked (see UserIdItem.compareTo) - // this is a stable sort, so the order of keys is otherwise preserved. - Collections.sort(uids); - // iterate and put into db - for (int userIdRank = 0; userIdRank < uids.size(); userIdRank++) { - UserIdItem item = uids.get(userIdRank); - operations.add(buildUserIdOperations(masterKeyId, item, userIdRank)); - // no self cert is bad, but allowed by the rfc... - if (item.selfCert != null) { - operations.add(buildCertOperations( - masterKeyId, userIdRank, item.selfCert, Certs.VERIFIED_SELF)); - } - // don't bother with trusted certs if the uid is revoked, anyways - if (item.isRevoked) { - continue; } - for (int i = 0; i < item.trustedCerts.size(); i++) { - operations.add(buildCertOperations( - masterKeyId, userIdRank, item.trustedCerts.get(i), Certs.VERIFIED_SECRET)); + mIndent -= 1; + + log(LogLevel.INFO, LogType.MSG_IP_UID_INSERT); + // primary before regular before revoked (see UserIdItem.compareTo) + // this is a stable sort, so the order of keys is otherwise preserved. + Collections.sort(uids); + // iterate and put into db + for (int userIdRank = 0; userIdRank < uids.size(); userIdRank++) { + UserIdItem item = uids.get(userIdRank); + operations.add(buildUserIdOperations(masterKeyId, item, userIdRank)); + // no self cert is bad, but allowed by the rfc... + if (item.selfCert != null) { + operations.add(buildCertOperations( + masterKeyId, userIdRank, item.selfCert, Certs.VERIFIED_SELF)); + } + // don't bother with trusted certs if the uid is revoked, anyways + if (item.isRevoked) { + continue; + } + for (int i = 0; i < item.trustedCerts.size(); i++) { + operations.add(buildCertOperations( + masterKeyId, userIdRank, item.trustedCerts.get(i), Certs.VERIFIED_SECRET)); + } } - } - try { + log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); + } catch (IOException e) { + log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); } catch (RemoteException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); + log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); } catch (OperationApplicationException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); + log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EX); } // Save the saved keyring (if any) if (secretRing != null) { + log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); + mIndent += 1; saveSecretKeyRing(secretRing); + mIndent -= 1; } + log(LogLevel.INFO, LogType.MSG_IP_SUCCESS); + return new OperationResultParcel(0, mLog); + } private static class UserIdItem implements Comparable { @@ -398,13 +470,29 @@ public class ProviderHelper { * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring * is already in the database! */ - public void saveSecretKeyRing(UncachedKeyRing keyRing) throws IOException { + public OperationResultParcel saveSecretKeyRing(UncachedKeyRing keyRing) { if (!keyRing.isSecret()) { - throw new RuntimeException("Tried to save publkc keyring as secret! " + - "This is a bug, please file a bug report."); + log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); + return new OperationResultParcel(1, mLog); } long masterKeyId = keyRing.getMasterKeyId(); + log(LogLevel.INFO, LogType.MSG_IS_IMPORTING, + new String[]{ Long.toString(masterKeyId) }); + + // save secret keyring + try { + ContentValues values = new ContentValues(); + values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); + values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); + // insert new version of this keyRing + Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); + mContentResolver.insert(uri, values); + } catch (IOException e) { + Log.e(Constants.TAG, "Failed to encode key!", e); + log(LogLevel.ERROR, LogType.MSG_IS_IO_EXCPTION); + return new OperationResultParcel(1, mLog); + } { Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); @@ -416,24 +504,30 @@ public class ProviderHelper { values.put(Keys.HAS_SECRET, 1); // then, mark exactly the keys we have available + log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS); + mIndent += 1; for (Long sub : new IterableIterator(keyRing.getAvailableSubkeys().iterator())) { - mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] { + int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] { Long.toString(sub) }); + if(upd == 0) { + log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, new String[] { + PgpKeyHelper.convertKeyIdToHex(sub) + }); + } else { + log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, new String[] { + PgpKeyHelper.convertKeyIdToHex(sub) + }); + } } + mIndent -= 1; + // this implicitly leaves all keys which were not in the secret key ring // with has_secret = 0 } - // save secret keyring - { - ContentValues values = new ContentValues(); - values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); - values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); - // insert new version of this keyRing - Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); - mContentResolver.insert(uri, values); - } + log(LogLevel.INFO, LogType.MSG_IS_SUCCESS); + return new OperationResultParcel(0, mLog); } -- cgit v1.2.3 From e083ccc37029bea5c130582756dc119920f577be Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Jun 2014 16:15:45 +0200 Subject: import-log: add import log string resources --- OpenKeychain/src/main/res/values/strings.xml | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index eeb6b3742..0755d9fb2 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -520,4 +520,42 @@ Encoding error No encryption subkey available! + + Applying insert batch operation. + Tried to import secret keyring as public. This is a bug, please file a report! + No old key deleted (creating a new one?) + Deleted old key from database + Operation failed due to encoding error + Operation failed due to i/o error + Operation failed due to database error + Operation failed due to internal error + Importing public keyring + Inserting keyring data + Inserting subkey %s + Inserting subkeys + Preserving available secret key + Re-inserting secret key + Successfully inserted secret keyring + Retrieving trusted keys + Using %s trusted keys + Secret key available, self certificates are trusted + Encountered bad certificate! + Error processing certificate! + Found good certificate from %s + Ignored %s certificates from unknown pubkeys + Classifying user ids + Inserting user ids + Processing user id %s + Bad self certificate encountered! + Found good self certificate + Ignoring older self certificate + Using more recent good self certificate + Tried to import public keyring as secret. This is a bug, please file a report! + Importing secret key %s + Processing secret subkeys + Error encoding keyring + Subkey %s unavailable in public key + Marked %s as available + Successfully inserted secret keyring + -- cgit v1.2.3 From 118225d7d2b6401006352897386a40131c6cd854 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Jun 2014 17:28:36 +0200 Subject: import-log: add output to logcat (for debugging) --- .../keychain/pgp/OperationResultParcel.java | 5 ++++- .../sufficientlysecure/keychain/pgp/PgpImportExport.java | 15 ++++++++------- .../keychain/provider/ProviderHelper.java | 7 +++++++ 3 files changed, 19 insertions(+), 8 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java index 8110590b1..ccb4b935c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java @@ -126,10 +126,13 @@ public class OperationResultParcel implements Parcelable { MSG_IS_SUCCESS (R.string.msg_is_success), ; - private int mMsgId; + private final int mMsgId; LogType(int msgId) { mMsgId = msgId; } + public int getMsgId() { + return mMsgId; + } } /** Enumeration of possible log levels. */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 14ec67e64..5ce0b11dd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -152,13 +152,14 @@ public class PgpImportExport { } } - mProviderHelper.savePublicKeyRing(key); - /*switch(status) { - case RETURN_UPDATED: oldKeys++; break; - case RETURN_OK: newKeys++; break; - case RETURN_BAD: badKeys++; break; - }*/ - // TODO proper import feedback + mProviderHelper.resetLog(); + OperationResultParcel result = mProviderHelper.savePublicKeyRing(key); + for(OperationResultParcel.LogEntryParcel loge : result.mLog) { + Log.d(Constants.TAG, + loge.mIndent + + new String(new char[loge.mIndent]).replace("\0", " ") + + mContext.getString(loge.mType.getMsgId(), (Object[]) loge.mParameters)); + } newKeys += 1; } catch (PgpGeneralException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 170fc4df2..b3a08a063 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -79,6 +79,13 @@ public class ProviderHelper { mIndent = indent; } + public void resetLog() { + if(mLog != null) { + mLog.clear(); + mIndent = 0; + } + } + public static class NotFoundException extends Exception { public NotFoundException() { } -- cgit v1.2.3 From c36b311d5f5c2b43ec3f1549d9a059fb0de3bde2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Jun 2014 17:29:39 +0200 Subject: import-log: better stripped key logging --- .../keychain/pgp/OperationResultParcel.java | 1 + .../keychain/pgp/UncachedKeyRing.java | 5 ++- .../keychain/provider/ProviderHelper.java | 51 ++++++++++++++++------ OpenKeychain/src/main/res/values/strings.xml | 3 +- 4 files changed, 43 insertions(+), 17 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java index ccb4b935c..216e4b497 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java @@ -123,6 +123,7 @@ public class OperationResultParcel implements Parcelable { MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), + MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), MSG_IS_SUCCESS (R.string.msg_is_success), ; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 02e5411ca..1264c8c36 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Vector; @@ -149,13 +150,13 @@ public class UncachedKeyRing { aos.close(); } - public ArrayList getAvailableSubkeys() { + public HashSet getAvailableSubkeys() { if(!isSecret()) { throw new RuntimeException("Tried to find available subkeys from non-secret keys. " + "This is a programming error and should never happen!"); } - ArrayList result = new ArrayList(); + HashSet result = new HashSet(); // then, mark exactly the keys we have available for (PGPSecretKey sub : new IterableIterator( ((PGPSecretKeyRing) mRing).getSecretKeys())) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index b3a08a063..5c8bf6752 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -68,7 +68,7 @@ public class ProviderHelper { private int mIndent; public ProviderHelper(Context context) { - this(context, null, 0); + this(context, new ArrayList(), 0); } public ProviderHelper(Context context, ArrayList log, @@ -96,10 +96,14 @@ public class ProviderHelper { } public void log(LogLevel level, LogType type) { - mLog.add(new OperationResultParcel.LogEntryParcel(level, type, null, mIndent)); + if(mLog != null) { + mLog.add(new OperationResultParcel.LogEntryParcel(level, type, null, mIndent)); + } } public void log(LogLevel level, LogType type, String[] parameters) { - mLog.add(new OperationResultParcel.LogEntryParcel(level, type, parameters, mIndent)); + if(mLog != null) { + mLog.add(new OperationResultParcel.LogEntryParcel(level, type, parameters, mIndent)); + } } // If we ever switch to api level 11, we can ditch this whole mess! @@ -258,6 +262,7 @@ public class ProviderHelper { long masterKeyId = masterKey.getKeyId(); log(LogLevel.INFO, LogType.MSG_IP_IMPORTING, new String[]{Long.toString(masterKeyId)}); + mIndent += 1; // IF there is a secret key, preserve it! UncachedKeyRing secretRing; @@ -301,7 +306,7 @@ public class ProviderHelper { int rank = 0; for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { log(LogLevel.DEBUG, LogType.MSG_IP_INSERT_SUBKEY, new String[] { - PgpKeyHelper.convertKeyIdToHex(masterKeyId) + PgpKeyHelper.convertKeyIdToHex(key.getKeyId()) }); operations.add(buildPublicKeyOperations(masterKeyId, key, rank)); ++rank; @@ -433,10 +438,19 @@ public class ProviderHelper { mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); + Log.e(Constants.TAG, "IOException during import", e); + mIndent -= 1; + return new OperationResultParcel(1, mLog); } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); + Log.e(Constants.TAG, "RemoteException during import", e); + mIndent -= 1; + return new OperationResultParcel(1, mLog); } catch (OperationApplicationException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EX); + Log.e(Constants.TAG, "OperationApplicationException during import", e); + mIndent -= 1; + return new OperationResultParcel(1, mLog); } // Save the saved keyring (if any) @@ -448,6 +462,7 @@ public class ProviderHelper { } log(LogLevel.INFO, LogType.MSG_IP_SUCCESS); + mIndent -= 1; return new OperationResultParcel(0, mLog); } @@ -513,17 +528,25 @@ public class ProviderHelper { // then, mark exactly the keys we have available log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS); mIndent += 1; - for (Long sub : new IterableIterator(keyRing.getAvailableSubkeys().iterator())) { - int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] { - Long.toString(sub) - }); - if(upd == 0) { - log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, new String[] { - PgpKeyHelper.convertKeyIdToHex(sub) - }); + Set available = keyRing.getAvailableSubkeys(); + for (UncachedPublicKey sub : + new IterableIterator(keyRing.getPublicKeys())) { + long id = sub.getKeyId(); + if(available.contains(id)) { + int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", + new String[] { Long.toString(id) }); + if (upd == 1) { + log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, new String[]{ + PgpKeyHelper.convertKeyIdToHex(id) + }); + } else { + log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, new String[]{ + PgpKeyHelper.convertKeyIdToHex(id) + }); + } } else { - log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, new String[] { - PgpKeyHelper.convertKeyIdToHex(sub) + log(LogLevel.INFO, LogType.MSG_IS_SUBKEY_STRIPPED, new String[]{ + PgpKeyHelper.convertKeyIdToHex(id) }); } } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 0755d9fb2..6f21900fe 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -535,7 +535,7 @@ Inserting subkeys Preserving available secret key Re-inserting secret key - Successfully inserted secret keyring + Successfully inserted public keyring Retrieving trusted keys Using %s trusted keys Secret key available, self certificates are trusted @@ -556,6 +556,7 @@ Error encoding keyring Subkey %s unavailable in public key Marked %s as available + Marked %s as stripped Successfully inserted secret keyring -- cgit v1.2.3 From 55ca0841f6ac9be91fa185442a5c11cebc016441 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 6 Jun 2014 17:39:11 +0200 Subject: Fixing TAG and string resource --- .../keychain/service/ContactSyncAdapterService.java | 5 +++-- OpenKeychain/src/main/res/values/strings.xml | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java index a52dbfa27..8db9294df 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java @@ -24,6 +24,7 @@ import android.content.ContentProviderClient; import android.content.Intent; import android.content.SyncResult; import android.os.*; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.helper.ContactHelper; import org.sufficientlysecure.keychain.helper.EmailKeyHelper; @@ -51,13 +52,13 @@ public class ContactSyncAdapterService extends Service { case KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS: if (data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS) && data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)) { - Log.d("Keychain/ContactSync/DownloadKeys", "Progress: " + + Log.d(Constants.TAG, "Progress: " + data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS) + "/" + data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)); return false; } default: - Log.d("Keychain/ContactSync/DownloadKeys", "Syncing... " + msg.toString()); + Log.d(Constants.TAG, "Syncing... " + msg.toString()); return false; } } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index ec6b389ed..9545c1ed4 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -519,8 +519,7 @@ cannot sign Encoding error No encryption subkey available! - Do not create OpenKeychain-Accounts manually. - For more information, see Help. + Do not create OpenKeychain-Accounts manually.\nFor more information, see Help. Show key (%s) -- cgit v1.2.3 From 5601f1b76f42276950487d635c9ea87006f15621 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 6 Jun 2014 17:42:28 +0200 Subject: Fix TAG in account service as well --- .../keychain/service/DummyAccountService.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java index d3b29d5cf..008502ce7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java @@ -29,6 +29,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.widget.Toast; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.Log; @@ -73,7 +74,7 @@ public class DummyAccountService extends Service { @Override public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { - Log.d("DummyAccountService", "editProperties"); + Log.d(Constants.TAG, "DummyAccountService.editProperties"); return null; } @@ -82,41 +83,41 @@ public class DummyAccountService extends Service { String[] requiredFeatures, Bundle options) throws NetworkErrorException { response.onResult(new Bundle()); toaster.toast(R.string.info_no_manual_account_creation); - Log.d("DummyAccountService", "addAccount"); + Log.d(Constants.TAG, "DummyAccountService.addAccount"); return null; } @Override public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException { - Log.d("DummyAccountService", "confirmCredentials"); + Log.d(Constants.TAG, "DummyAccountService.confirmCredentials"); return null; } @Override public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { - Log.d("DummyAccountService", "getAuthToken"); + Log.d(Constants.TAG, "DummyAccountService.getAuthToken"); return null; } @Override public String getAuthTokenLabel(String authTokenType) { - Log.d("DummyAccountService", "getAuthTokenLabel"); + Log.d(Constants.TAG, "DummyAccountService.getAuthTokenLabel"); return null; } @Override public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { - Log.d("DummyAccountService", "updateCredentials"); + Log.d(Constants.TAG, "DummyAccountService.updateCredentials"); return null; } @Override public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException { - Log.d("DummyAccountService", "hasFeatures"); + Log.d(Constants.TAG, "DummyAccountService.hasFeatures"); return null; } } -- cgit v1.2.3 From 341247d4467997aa22850f5b82ac906bb28296cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 6 Jun 2014 22:46:39 +0200 Subject: Hide subkeys, certs tabs in key view, checkable menu item to show/hide them --- .../keychain/ui/ViewKeyActivity.java | 87 +++++++++++++++++++--- .../keychain/ui/adapter/PagerTabStripAdapter.java | 10 +++ OpenKeychain/src/main/res/menu/key_view.xml | 6 ++ OpenKeychain/src/main/res/values/strings.xml | 1 + 4 files changed, 94 insertions(+), 10 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index f27a4ad27..b396cd849 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -92,6 +92,8 @@ public class ViewKeyActivity extends ActionBarActivity implements private static final int LOADER_ID_UNIFIED = 0; + private boolean mShowAdvancedTabs; + @Override protected void onCreate(Bundle savedInstanceState) { @@ -116,9 +118,6 @@ public class ViewKeyActivity extends ActionBarActivity implements mViewPager = (ViewPager) findViewById(R.id.view_key_pager); mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.view_key_sliding_tab_layout); - mTabsAdapter = new PagerTabStripAdapter(this); - mViewPager.setAdapter(mTabsAdapter); - int switchToTab = TAB_MAIN; Intent intent = getIntent(); if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) { @@ -136,6 +135,18 @@ public class ViewKeyActivity extends ActionBarActivity implements initNfc(dataUri); + mShowAdvancedTabs = false; + + initTabs(dataUri); + + // switch to tab selected by extra + mViewPager.setCurrentItem(switchToTab); + } + + private void initTabs(Uri dataUri) { + mTabsAdapter = new PagerTabStripAdapter(this); + mViewPager.setAdapter(mTabsAdapter); + Bundle mainBundle = new Bundle(); mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); mTabsAdapter.addTab(ViewKeyMainFragment.class, @@ -146,6 +157,11 @@ public class ViewKeyActivity extends ActionBarActivity implements mTabsAdapter.addTab(ViewKeyShareFragment.class, mainBundle, getString(R.string.key_view_tab_share)); + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); + } + + private void addAdvancedTabs(Uri dataUri) { Bundle keyDetailsBundle = new Bundle(); keyDetailsBundle.putParcelable(ViewKeyKeysFragment.ARG_DATA_URI, dataUri); mTabsAdapter.addTab(ViewKeyKeysFragment.class, @@ -156,11 +172,46 @@ public class ViewKeyActivity extends ActionBarActivity implements mTabsAdapter.addTab(ViewKeyCertsFragment.class, certBundle, getString(R.string.key_view_tab_certs)); - // NOTE: must be after adding the tabs! + // update layout after operations mSlidingTabLayout.setViewPager(mViewPager); + } - // switch to tab selected by extra - mViewPager.setCurrentItem(switchToTab); + private void removeAdvancedTabs() { + // before removing, switch to the first tab if necessary + if (mViewPager.getCurrentItem() >= TAB_KEYS) { + // remove _after_ switching to the main tab + mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + if (ViewPager.SCROLL_STATE_SETTLING == state) { + mTabsAdapter.removeTab(TAB_CERTS); + mTabsAdapter.removeTab(TAB_KEYS); + + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); + + // remove this listener again +// mViewPager.setOnPageChangeListener(null); + } + } + }); + + mViewPager.setCurrentItem(TAB_MAIN); + } else { + mTabsAdapter.removeTab(TAB_CERTS); + mTabsAdapter.removeTab(TAB_KEYS); + } + + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); } private void loadData(Uri dataUri) { @@ -177,6 +228,9 @@ public class ViewKeyActivity extends ActionBarActivity implements public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.key_view, menu); + + MenuItem showAdvancedInfoItem = menu.findItem(R.id.menu_key_view_advanced); + showAdvancedInfoItem.setChecked(mShowAdvancedTabs); return true; } @@ -184,24 +238,37 @@ public class ViewKeyActivity extends ActionBarActivity implements public boolean onOptionsItemSelected(MenuItem item) { try { switch (item.getItemId()) { - case android.R.id.home: + case android.R.id.home: { Intent homeIntent = new Intent(this, KeyListActivity.class); homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(homeIntent); return true; - case R.id.menu_key_view_update: + } + case R.id.menu_key_view_update: { updateFromKeyserver(mDataUri, mProviderHelper); return true; - case R.id.menu_key_view_export_keyserver: + } + case R.id.menu_key_view_export_keyserver: { uploadToKeyserver(mDataUri); return true; - case R.id.menu_key_view_export_file: + } + case R.id.menu_key_view_export_file: { exportToFile(mDataUri, mExportHelper, mProviderHelper); return true; + } case R.id.menu_key_view_delete: { deleteKey(mDataUri, mExportHelper); return true; } + case R.id.menu_key_view_advanced: { + mShowAdvancedTabs = !mShowAdvancedTabs; + item.setChecked(mShowAdvancedTabs); + if (mShowAdvancedTabs) { + addAdvancedTabs(mDataUri); + } else { + removeAdvancedTabs(); + } + } } } catch (ProviderHelper.NotFoundException e) { AppMsg.makeText(this, R.string.error_key_not_found, AppMsg.STYLE_ALERT).show(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java index 977740567..3e3098b10 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java @@ -20,8 +20,13 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentTransaction; import android.support.v7.app.ActionBarActivity; +import android.view.ViewGroup; + +import org.sufficientlysecure.keychain.Constants; import java.util.ArrayList; @@ -52,6 +57,11 @@ public class PagerTabStripAdapter extends FragmentPagerAdapter { notifyDataSetChanged(); } + public void removeTab(int index) { + mTabs.remove(index); + notifyDataSetChanged(); + } + @Override public int getCount() { return mTabs.size(); diff --git a/OpenKeychain/src/main/res/menu/key_view.xml b/OpenKeychain/src/main/res/menu/key_view.xml index 864016801..64877d725 100644 --- a/OpenKeychain/src/main/res/menu/key_view.xml +++ b/OpenKeychain/src/main/res/menu/key_view.xml @@ -31,4 +31,10 @@ app:showAsAction="never" android:title="@string/menu_delete_key" /> + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index eeb6b3742..82095a611 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -102,6 +102,7 @@ Select all Add keys Export all keys + Show advanced info Sign -- cgit v1.2.3 From d2430fe0e37f5996e3dbe758ea8c00ebde61445e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 6 Jun 2014 22:47:28 +0200 Subject: Move SlidingTabLayout and SlidingTabStrip into appropriate subpackage --- .../keychain/ui/HelpActivity.java | 2 +- .../keychain/ui/ViewKeyActivity.java | 2 +- .../keychain/ui/widget/SlidingTabLayout.java | 318 +++++++++++++++++++++ .../keychain/ui/widget/SlidingTabStrip.java | 211 ++++++++++++++ .../keychain/util/SlidingTabLayout.java | 318 --------------------- .../keychain/util/SlidingTabStrip.java | 211 -------------- OpenKeychain/src/main/res/layout/help_activity.xml | 2 +- .../src/main/res/layout/view_key_activity.xml | 2 +- 8 files changed, 533 insertions(+), 533 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabLayout.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabStrip.java (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java index 9edaff19a..bbc1e4b1f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java @@ -25,7 +25,7 @@ import android.support.v7.app.ActionBarActivity; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; -import org.sufficientlysecure.keychain.util.SlidingTabLayout; +import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; public class HelpActivity extends ActionBarActivity { public static final String EXTRA_SELECTED_TAB = "selected_tab"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index b396cd849..7a0e83d9b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -54,7 +54,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.SlidingTabLayout; +import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; import java.util.Date; import java.util.HashMap; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java new file mode 100644 index 000000000..17471c86c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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 org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.HorizontalScrollView; +import android.widget.TextView; + +/** + * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html + */ + +/** + * To be used with ViewPager to provide a tab indicator component which give constant feedback as to + * the user's scroll progress. + *

+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. + *

+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors + * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The + * alternative is via the {@link TabColorizer} interface which provides you complete control over + * which color is used for any individual position. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + */ +public class SlidingTabLayout extends HorizontalScrollView { + + /** + * Allows complete control over the colors drawn in the tab layout. Set with + * {@link #setCustomTabColorizer(TabColorizer)}. + */ + public interface TabColorizer { + + /** + * @return return the color of the indicator used when {@code position} is selected. + */ + int getIndicatorColor(int position); + + /** + * @return return the color of the divider drawn to the right of {@code position}. + */ + int getDividerColor(int position); + + } + + private static final int TITLE_OFFSET_DIPS = 24; + private static final int TAB_VIEW_PADDING_DIPS = 16; + private static final int TAB_VIEW_TEXT_SIZE_SP = 12; + + private int mTitleOffset; + + private int mTabViewLayoutId; + private int mTabViewTextViewId; + + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; + + private final SlidingTabStrip mTabStrip; + + public SlidingTabLayout(Context context) { + this(context, null); + } + + public SlidingTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // Disable the Scroll Bar + setHorizontalScrollBarEnabled(false); + // Make sure that the Tab Strips fills this View + setFillViewport(true); + + mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); + + mTabStrip = new SlidingTabStrip(context); + addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } + + /** + * Set the custom {@link TabColorizer} to be used. + *

+ * If you only require simple custmisation then you can use + * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve + * similar effects. + */ + public void setCustomTabColorizer(TabColorizer tabColorizer) { + mTabStrip.setCustomTabColorizer(tabColorizer); + } + + /** + * Sets the colors to be used for indicating the selected tab. These colors are treated as a + * circular array. Providing one color will mean that all tabs are indicated with the same color. + */ + public void setSelectedIndicatorColors(int... colors) { + mTabStrip.setSelectedIndicatorColors(colors); + } + + /** + * Sets the colors to be used for tab dividers. These colors are treated as a circular array. + * Providing one color will mean that all tabs are indicated with the same color. + */ + public void setDividerColors(int... colors) { + mTabStrip.setDividerColors(colors); + } + + /** + * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are + * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so + * that the layout can update it's scroll position correctly. + * + * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mViewPagerPageChangeListener = listener; + } + + /** + * Set the custom layout to be inflated for the tab views. + * + * @param layoutResId Layout id to be inflated + * @param textViewId id of the {@link TextView} in the inflated view + */ + public void setCustomTabView(int layoutResId, int textViewId) { + mTabViewLayoutId = layoutResId; + mTabViewTextViewId = textViewId; + } + + /** + * Sets the associated view pager. Note that the assumption here is that the pager content + * (number of tabs and tab titles) does not change after this call has been made. + */ + public void setViewPager(ViewPager viewPager) { + mTabStrip.removeAllViews(); + + mViewPager = viewPager; + if (viewPager != null) { + viewPager.setOnPageChangeListener(new InternalViewPagerListener()); + populateTabStrip(); + } + } + + /** + * Create a default view to be used for tabs. This is called if a custom tab view is not set via + * {@link #setCustomTabView(int, int)}. + */ + protected TextView createDefaultTabView(Context context) { + TextView textView = new TextView(context); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); + textView.setTypeface(Typeface.DEFAULT_BOLD); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // If we're running on Honeycomb or newer, then we can use the Theme's + // selectableItemBackground to ensure that the View has a pressed state + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true); + textView.setBackgroundResource(outValue.resourceId); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style + textView.setAllCaps(true); + } + + int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); + textView.setPadding(padding, padding, padding, padding); + + return textView; + } + + private void populateTabStrip() { + final PagerAdapter adapter = mViewPager.getAdapter(); + final View.OnClickListener tabClickListener = new TabClickListener(); + + for (int i = 0; i < adapter.getCount(); i++) { + View tabView = null; + TextView tabTitleView = null; + + if (mTabViewLayoutId != 0) { + // If there is a custom tab view layout id set, try and inflate it + tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, + false); + tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); + } + + if (tabView == null) { + tabView = createDefaultTabView(getContext()); + } + + if (tabTitleView == null && TextView.class.isInstance(tabView)) { + tabTitleView = (TextView) tabView; + } + + tabTitleView.setText(adapter.getPageTitle(i)); + tabView.setOnClickListener(tabClickListener); + + mTabStrip.addView(tabView); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mViewPager != null) { + scrollToTab(mViewPager.getCurrentItem(), 0); + } + } + + private void scrollToTab(int tabIndex, int positionOffset) { + final int tabStripChildCount = mTabStrip.getChildCount(); + if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { + return; + } + + View selectedChild = mTabStrip.getChildAt(tabIndex); + if (selectedChild != null) { + int targetScrollX = selectedChild.getLeft() + positionOffset; + + if (tabIndex > 0 || positionOffset > 0) { + // If we're not at the first child and are mid-scroll, make sure we obey the offset + targetScrollX -= mTitleOffset; + } + + scrollTo(targetScrollX, 0); + } + } + + private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { + private int mScrollState; + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + mTabStrip.onViewPagerPageChanged(position, positionOffset); + + View selectedTitle = mTabStrip.getChildAt(position); + int extraOffset = (selectedTitle != null) + ? (int) (positionOffset * selectedTitle.getWidth()) + : 0; + scrollToTab(position, extraOffset); + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mTabStrip.onViewPagerPageChanged(position, 0f); + scrollToTab(position, 0); + } + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageSelected(position); + } + } + + } + + private class TabClickListener implements View.OnClickListener { + @Override + public void onClick(View v) { + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + if (v == mTabStrip.getChildAt(i)) { + mViewPager.setCurrentItem(i); + return; + } + } + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java new file mode 100644 index 000000000..4c41e12c5 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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 org.sufficientlysecure.keychain.ui.widget; + +import android.R; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.LinearLayout; + +/** + * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html + */ +class SlidingTabStrip extends LinearLayout { + + private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; + private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; + private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8; + private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFFAA66CC; + + private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; + private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; + private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; + + private final int mBottomBorderThickness; + private final Paint mBottomBorderPaint; + + private final int mSelectedIndicatorThickness; + private final Paint mSelectedIndicatorPaint; + + private final int mDefaultBottomBorderColor; + + private final Paint mDividerPaint; + private final float mDividerHeight; + + private int mSelectedPosition; + private float mSelectionOffset; + + private SlidingTabLayout.TabColorizer mCustomTabColorizer; + private final SimpleTabColorizer mDefaultTabColorizer; + + SlidingTabStrip(Context context) { + this(context, null); + } + + SlidingTabStrip(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + + final float density = getResources().getDisplayMetrics().density; + + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); + final int themeForegroundColor = outValue.data; + + mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, + DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); + + mDefaultTabColorizer = new SimpleTabColorizer(); + mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); + mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, + DEFAULT_DIVIDER_COLOR_ALPHA)); + + mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); + mBottomBorderPaint = new Paint(); + mBottomBorderPaint.setColor(mDefaultBottomBorderColor); + + mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); + mSelectedIndicatorPaint = new Paint(); + + mDividerHeight = DEFAULT_DIVIDER_HEIGHT; + mDividerPaint = new Paint(); + mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density)); + } + + void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { + mCustomTabColorizer = customTabColorizer; + invalidate(); + } + + void setSelectedIndicatorColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setIndicatorColors(colors); + invalidate(); + } + + void setDividerColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setDividerColors(colors); + invalidate(); + } + + void onViewPagerPageChanged(int position, float positionOffset) { + mSelectedPosition = position; + mSelectionOffset = positionOffset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + final int height = getHeight(); + final int childCount = getChildCount(); + final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); + final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null + ? mCustomTabColorizer + : mDefaultTabColorizer; + + // Thick colored underline below the current selection + if (childCount > 0) { + View selectedTitle = getChildAt(mSelectedPosition); + int left = selectedTitle.getLeft(); + int right = selectedTitle.getRight(); + int color = tabColorizer.getIndicatorColor(mSelectedPosition); + + if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { + int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); + if (color != nextColor) { + color = blendColors(nextColor, color, mSelectionOffset); + } + + // Draw the selection partway between the tabs + View nextTitle = getChildAt(mSelectedPosition + 1); + left = (int) (mSelectionOffset * nextTitle.getLeft() + + (1.0f - mSelectionOffset) * left); + right = (int) (mSelectionOffset * nextTitle.getRight() + + (1.0f - mSelectionOffset) * right); + } + + mSelectedIndicatorPaint.setColor(color); + + canvas.drawRect(left, height - mSelectedIndicatorThickness, right, + height, mSelectedIndicatorPaint); + } + + // Thin underline along the entire bottom edge + canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); + + // Vertical separators between the titles + int separatorTop = (height - dividerHeightPx) / 2; + for (int i = 0; i < childCount - 1; i++) { + View child = getChildAt(i); + mDividerPaint.setColor(tabColorizer.getDividerColor(i)); + canvas.drawLine(child.getRight(), separatorTop, child.getRight(), + separatorTop + dividerHeightPx, mDividerPaint); + } + } + + /** + * Set the alpha value of the {@code color} to be the given {@code alpha} value. + */ + private static int setColorAlpha(int color, byte alpha) { + return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + /** + * Blend {@code color1} and {@code color2} using the given ratio. + * + * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, + * 0.0 will return {@code color2}. + */ + private static int blendColors(int color1, int color2, float ratio) { + final float inverseRation = 1f - ratio; + float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); + float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); + float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); + return Color.rgb((int) r, (int) g, (int) b); + } + + private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { + private int[] mIndicatorColors; + private int[] mDividerColors; + + @Override + public final int getIndicatorColor(int position) { + return mIndicatorColors[position % mIndicatorColors.length]; + } + + @Override + public final int getDividerColor(int position) { + return mDividerColors[position % mDividerColors.length]; + } + + void setIndicatorColors(int... colors) { + mIndicatorColors = colors; + } + + void setDividerColors(int... colors) { + mDividerColors = colors; + } + } +} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabLayout.java deleted file mode 100644 index 065034be1..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabLayout.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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 org.sufficientlysecure.keychain.util; - -import android.content.Context; -import android.graphics.Typeface; -import android.os.Build; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.HorizontalScrollView; -import android.widget.TextView; - -/** - * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html - */ - -/** - * To be used with ViewPager to provide a tab indicator component which give constant feedback as to - * the user's scroll progress. - *

- * To use the component, simply add it to your view hierarchy. Then in your - * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call - * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. - *

- * The colors can be customized in two ways. The first and simplest is to provide an array of colors - * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The - * alternative is via the {@link TabColorizer} interface which provides you complete control over - * which color is used for any individual position. - *

- * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, - * providing the layout ID of your custom layout. - */ -public class SlidingTabLayout extends HorizontalScrollView { - - /** - * Allows complete control over the colors drawn in the tab layout. Set with - * {@link #setCustomTabColorizer(TabColorizer)}. - */ - public interface TabColorizer { - - /** - * @return return the color of the indicator used when {@code position} is selected. - */ - int getIndicatorColor(int position); - - /** - * @return return the color of the divider drawn to the right of {@code position}. - */ - int getDividerColor(int position); - - } - - private static final int TITLE_OFFSET_DIPS = 24; - private static final int TAB_VIEW_PADDING_DIPS = 16; - private static final int TAB_VIEW_TEXT_SIZE_SP = 12; - - private int mTitleOffset; - - private int mTabViewLayoutId; - private int mTabViewTextViewId; - - private ViewPager mViewPager; - private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; - - private final SlidingTabStrip mTabStrip; - - public SlidingTabLayout(Context context) { - this(context, null); - } - - public SlidingTabLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - // Disable the Scroll Bar - setHorizontalScrollBarEnabled(false); - // Make sure that the Tab Strips fills this View - setFillViewport(true); - - mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); - - mTabStrip = new SlidingTabStrip(context); - addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - } - - /** - * Set the custom {@link TabColorizer} to be used. - *

- * If you only require simple custmisation then you can use - * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve - * similar effects. - */ - public void setCustomTabColorizer(TabColorizer tabColorizer) { - mTabStrip.setCustomTabColorizer(tabColorizer); - } - - /** - * Sets the colors to be used for indicating the selected tab. These colors are treated as a - * circular array. Providing one color will mean that all tabs are indicated with the same color. - */ - public void setSelectedIndicatorColors(int... colors) { - mTabStrip.setSelectedIndicatorColors(colors); - } - - /** - * Sets the colors to be used for tab dividers. These colors are treated as a circular array. - * Providing one color will mean that all tabs are indicated with the same color. - */ - public void setDividerColors(int... colors) { - mTabStrip.setDividerColors(colors); - } - - /** - * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are - * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so - * that the layout can update it's scroll position correctly. - * - * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) - */ - public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { - mViewPagerPageChangeListener = listener; - } - - /** - * Set the custom layout to be inflated for the tab views. - * - * @param layoutResId Layout id to be inflated - * @param textViewId id of the {@link TextView} in the inflated view - */ - public void setCustomTabView(int layoutResId, int textViewId) { - mTabViewLayoutId = layoutResId; - mTabViewTextViewId = textViewId; - } - - /** - * Sets the associated view pager. Note that the assumption here is that the pager content - * (number of tabs and tab titles) does not change after this call has been made. - */ - public void setViewPager(ViewPager viewPager) { - mTabStrip.removeAllViews(); - - mViewPager = viewPager; - if (viewPager != null) { - viewPager.setOnPageChangeListener(new InternalViewPagerListener()); - populateTabStrip(); - } - } - - /** - * Create a default view to be used for tabs. This is called if a custom tab view is not set via - * {@link #setCustomTabView(int, int)}. - */ - protected TextView createDefaultTabView(Context context) { - TextView textView = new TextView(context); - textView.setGravity(Gravity.CENTER); - textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); - textView.setTypeface(Typeface.DEFAULT_BOLD); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - // If we're running on Honeycomb or newer, then we can use the Theme's - // selectableItemBackground to ensure that the View has a pressed state - TypedValue outValue = new TypedValue(); - getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, - outValue, true); - textView.setBackgroundResource(outValue.resourceId); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style - textView.setAllCaps(true); - } - - int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); - textView.setPadding(padding, padding, padding, padding); - - return textView; - } - - private void populateTabStrip() { - final PagerAdapter adapter = mViewPager.getAdapter(); - final View.OnClickListener tabClickListener = new TabClickListener(); - - for (int i = 0; i < adapter.getCount(); i++) { - View tabView = null; - TextView tabTitleView = null; - - if (mTabViewLayoutId != 0) { - // If there is a custom tab view layout id set, try and inflate it - tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, - false); - tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); - } - - if (tabView == null) { - tabView = createDefaultTabView(getContext()); - } - - if (tabTitleView == null && TextView.class.isInstance(tabView)) { - tabTitleView = (TextView) tabView; - } - - tabTitleView.setText(adapter.getPageTitle(i)); - tabView.setOnClickListener(tabClickListener); - - mTabStrip.addView(tabView); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (mViewPager != null) { - scrollToTab(mViewPager.getCurrentItem(), 0); - } - } - - private void scrollToTab(int tabIndex, int positionOffset) { - final int tabStripChildCount = mTabStrip.getChildCount(); - if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { - return; - } - - View selectedChild = mTabStrip.getChildAt(tabIndex); - if (selectedChild != null) { - int targetScrollX = selectedChild.getLeft() + positionOffset; - - if (tabIndex > 0 || positionOffset > 0) { - // If we're not at the first child and are mid-scroll, make sure we obey the offset - targetScrollX -= mTitleOffset; - } - - scrollTo(targetScrollX, 0); - } - } - - private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { - private int mScrollState; - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - int tabStripChildCount = mTabStrip.getChildCount(); - if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { - return; - } - - mTabStrip.onViewPagerPageChanged(position, positionOffset); - - View selectedTitle = mTabStrip.getChildAt(position); - int extraOffset = (selectedTitle != null) - ? (int) (positionOffset * selectedTitle.getWidth()) - : 0; - scrollToTab(position, extraOffset); - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, - positionOffsetPixels); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - mScrollState = state; - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageScrollStateChanged(state); - } - } - - @Override - public void onPageSelected(int position) { - if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { - mTabStrip.onViewPagerPageChanged(position, 0f); - scrollToTab(position, 0); - } - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageSelected(position); - } - } - - } - - private class TabClickListener implements View.OnClickListener { - @Override - public void onClick(View v) { - for (int i = 0; i < mTabStrip.getChildCount(); i++) { - if (v == mTabStrip.getChildAt(i)) { - mViewPager.setCurrentItem(i); - return; - } - } - } - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabStrip.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabStrip.java deleted file mode 100644 index 9ce6f943e..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabStrip.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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 org.sufficientlysecure.keychain.util; - -import android.R; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.View; -import android.widget.LinearLayout; - -/** - * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html - */ -class SlidingTabStrip extends LinearLayout { - - private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; - private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; - private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8; - private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFFAA66CC; - - private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; - private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; - private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; - - private final int mBottomBorderThickness; - private final Paint mBottomBorderPaint; - - private final int mSelectedIndicatorThickness; - private final Paint mSelectedIndicatorPaint; - - private final int mDefaultBottomBorderColor; - - private final Paint mDividerPaint; - private final float mDividerHeight; - - private int mSelectedPosition; - private float mSelectionOffset; - - private SlidingTabLayout.TabColorizer mCustomTabColorizer; - private final SimpleTabColorizer mDefaultTabColorizer; - - SlidingTabStrip(Context context) { - this(context, null); - } - - SlidingTabStrip(Context context, AttributeSet attrs) { - super(context, attrs); - setWillNotDraw(false); - - final float density = getResources().getDisplayMetrics().density; - - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); - final int themeForegroundColor = outValue.data; - - mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, - DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); - - mDefaultTabColorizer = new SimpleTabColorizer(); - mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); - mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, - DEFAULT_DIVIDER_COLOR_ALPHA)); - - mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); - mBottomBorderPaint = new Paint(); - mBottomBorderPaint.setColor(mDefaultBottomBorderColor); - - mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); - mSelectedIndicatorPaint = new Paint(); - - mDividerHeight = DEFAULT_DIVIDER_HEIGHT; - mDividerPaint = new Paint(); - mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density)); - } - - void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { - mCustomTabColorizer = customTabColorizer; - invalidate(); - } - - void setSelectedIndicatorColors(int... colors) { - // Make sure that the custom colorizer is removed - mCustomTabColorizer = null; - mDefaultTabColorizer.setIndicatorColors(colors); - invalidate(); - } - - void setDividerColors(int... colors) { - // Make sure that the custom colorizer is removed - mCustomTabColorizer = null; - mDefaultTabColorizer.setDividerColors(colors); - invalidate(); - } - - void onViewPagerPageChanged(int position, float positionOffset) { - mSelectedPosition = position; - mSelectionOffset = positionOffset; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - final int height = getHeight(); - final int childCount = getChildCount(); - final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); - final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null - ? mCustomTabColorizer - : mDefaultTabColorizer; - - // Thick colored underline below the current selection - if (childCount > 0) { - View selectedTitle = getChildAt(mSelectedPosition); - int left = selectedTitle.getLeft(); - int right = selectedTitle.getRight(); - int color = tabColorizer.getIndicatorColor(mSelectedPosition); - - if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { - int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); - if (color != nextColor) { - color = blendColors(nextColor, color, mSelectionOffset); - } - - // Draw the selection partway between the tabs - View nextTitle = getChildAt(mSelectedPosition + 1); - left = (int) (mSelectionOffset * nextTitle.getLeft() + - (1.0f - mSelectionOffset) * left); - right = (int) (mSelectionOffset * nextTitle.getRight() + - (1.0f - mSelectionOffset) * right); - } - - mSelectedIndicatorPaint.setColor(color); - - canvas.drawRect(left, height - mSelectedIndicatorThickness, right, - height, mSelectedIndicatorPaint); - } - - // Thin underline along the entire bottom edge - canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); - - // Vertical separators between the titles - int separatorTop = (height - dividerHeightPx) / 2; - for (int i = 0; i < childCount - 1; i++) { - View child = getChildAt(i); - mDividerPaint.setColor(tabColorizer.getDividerColor(i)); - canvas.drawLine(child.getRight(), separatorTop, child.getRight(), - separatorTop + dividerHeightPx, mDividerPaint); - } - } - - /** - * Set the alpha value of the {@code color} to be the given {@code alpha} value. - */ - private static int setColorAlpha(int color, byte alpha) { - return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); - } - - /** - * Blend {@code color1} and {@code color2} using the given ratio. - * - * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, - * 0.0 will return {@code color2}. - */ - private static int blendColors(int color1, int color2, float ratio) { - final float inverseRation = 1f - ratio; - float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); - float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); - float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); - return Color.rgb((int) r, (int) g, (int) b); - } - - private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { - private int[] mIndicatorColors; - private int[] mDividerColors; - - @Override - public final int getIndicatorColor(int position) { - return mIndicatorColors[position % mIndicatorColors.length]; - } - - @Override - public final int getDividerColor(int position) { - return mDividerColors[position % mDividerColors.length]; - } - - void setIndicatorColors(int... colors) { - mIndicatorColors = colors; - } - - void setDividerColors(int... colors) { - mDividerColors = colors; - } - } -} \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/help_activity.xml b/OpenKeychain/src/main/res/layout/help_activity.xml index 76ba183b7..3ad087da3 100644 --- a/OpenKeychain/src/main/res/layout/help_activity.xml +++ b/OpenKeychain/src/main/res/layout/help_activity.xml @@ -4,7 +4,7 @@ android:layout_height="match_parent" android:orientation="vertical" > - diff --git a/OpenKeychain/src/main/res/layout/view_key_activity.xml b/OpenKeychain/src/main/res/layout/view_key_activity.xml index f43aade25..5aa1cd167 100644 --- a/OpenKeychain/src/main/res/layout/view_key_activity.xml +++ b/OpenKeychain/src/main/res/layout/view_key_activity.xml @@ -35,7 +35,7 @@ android:visibility="gone" android:id="@+id/status_divider" /> - -- cgit v1.2.3 From 067ffa876d4ec4e1f2ee63c851cb3c48f7eb1aa2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:30:20 +0200 Subject: import-log: add OperationResults, use it in ImportKeys operation --- .../keychain/pgp/OperationResultParcel.java | 169 ----------------- .../keychain/pgp/PgpImportExport.java | 45 +++-- .../keychain/provider/ProviderHelper.java | 167 ++++++++++------- .../keychain/service/KeychainIntentService.java | 12 +- .../keychain/service/OperationResultParcel.java | 207 +++++++++++++++++++++ .../keychain/service/OperationResults.java | 63 +++++++ OpenKeychain/src/main/res/values/strings.xml | 12 +- 7 files changed, 417 insertions(+), 258 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java deleted file mode 100644 index 216e4b497..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.sufficientlysecure.keychain.pgp; - -import android.os.Parcel; -import android.os.Parcelable; - -import org.sufficientlysecure.keychain.R; - -import java.util.ArrayList; - -/** Represent the result of an operation. - * - * This class holds a result and the log of an operation. It can be subclassed - * to include typed additional information specific to the operation. To keep - * the class structure (somewhat) simple, this class contains an exhaustive - * list (ie, enum) of all possible log types, which should in all cases be tied - * to string resource ids. - * - */ -public class OperationResultParcel implements Parcelable { - /** Holds the overall result. A value of 0 is considered a success, all - * other values may represent failure or varying degrees of success. */ - final int mResult; - - /// A list of log entries tied to the operation result. - final ArrayList mLog; - - public OperationResultParcel(int result, ArrayList log) { - mResult = result; - mLog = log; - } - - public OperationResultParcel(Parcel source) { - mResult = source.readInt(); - mLog = source.createTypedArrayList(LogEntryParcel.CREATOR); - } - - public boolean isSuccessful() { - return mResult == 0; - } - - /** One entry in the log. */ - public static class LogEntryParcel implements Parcelable { - final LogLevel mLevel; - final LogType mType; - final String[] mParameters; - final int mIndent; - - public LogEntryParcel(LogLevel level, LogType type, String[] parameters, int indent) { - mLevel = level; - mType = type; - mParameters = parameters; - mIndent = indent; - } - public LogEntryParcel(LogLevel level, LogType type, String[] parameters) { - this(level, type, parameters, 0); - } - - public LogEntryParcel(Parcel source) { - mLevel = LogLevel.values()[source.readInt()]; - mType = LogType.values()[source.readInt()]; - mParameters = source.createStringArray(); - mIndent = source.readInt(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mLevel.ordinal()); - dest.writeInt(mType.ordinal()); - dest.writeStringArray(mParameters); - dest.writeInt(mIndent); - } - - public static final Creator CREATOR = new Creator() { - public LogEntryParcel createFromParcel(final Parcel source) { - return new LogEntryParcel(source); - } - - public LogEntryParcel[] newArray(final int size) { - return new LogEntryParcel[size]; - } - }; - - } - - public static enum LogType { - MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch), - MSG_IP_BAD_TYPE_SECRET (R.string.msg_ip_bad_type_secret), - MSG_IP_DELETE_OLD_FAIL (R.string.msg_ip_delete_old_fail), - MSG_IP_DELETE_OLD_OK (R.string.msg_ip_delete_old_ok), - MSG_IP_ENCODE_FAIL (R.string.msg_ip_encode_fail), - MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), - MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), - MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), - MSG_IP_IMPORTING (R.string.msg_ip_importing), - MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), - MSG_IP_INSERT_SUBKEY (R.string.msg_ip_insert_subkey), - MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), - MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), - MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), - MSG_IP_SUCCESS (R.string.msg_ip_success), - MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), - MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), - MSG_IP_TRUST_USING_SEC (R.string.msg_ip_trust_using_sec), - MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), - MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), - MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), - MSG_IP_UID_CERTS_UNKNOWN (R.string.msg_ip_uid_certs_unknown), - MSG_IP_UID_CLASSIFYING (R.string.msg_ip_uid_classifying), - MSG_IP_UID_INSERT (R.string.msg_ip_uid_insert), - MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), - MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), - MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), - MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), - MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), - MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), - MSG_IS_IMPORTING (R.string.msg_is_importing), - MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), - MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), - MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), - MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), - MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), - MSG_IS_SUCCESS (R.string.msg_is_success), - ; - - private final int mMsgId; - LogType(int msgId) { - mMsgId = msgId; - } - public int getMsgId() { - return mMsgId; - } - } - - /** Enumeration of possible log levels. */ - public static enum LogLevel { - DEBUG, - INFO, - WARN, - /** If any ERROR log entry is included in the result, the overall operation should have failed. */ - ERROR, - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mResult); - dest.writeTypedList(mLog); - } - - public static final Creator CREATOR = new Creator() { - public OperationResultParcel createFromParcel(final Parcel source) { - return new OperationResultParcel(source); - } - - public OperationResultParcel[] newArray(final int size) { - return new OperationResultParcel[size]; - } - }; - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 5ce0b11dd..ebc5a7868 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -33,6 +33,9 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; +import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; @@ -123,12 +126,9 @@ public class PgpImportExport { } } - /** - * Imports keys from given data. If keyIds is given only those are imported - */ - public Bundle importKeyRings(List entries) + /** Imports keys from given data. If keyIds is given only those are imported */ + public ImportResult importKeyRings(List entries) throws PgpGeneralException, PGPException, IOException { - Bundle returnData = new Bundle(); updateProgress(R.string.progress_importing, 0, 100); @@ -152,14 +152,8 @@ public class PgpImportExport { } } - mProviderHelper.resetLog(); OperationResultParcel result = mProviderHelper.savePublicKeyRing(key); - for(OperationResultParcel.LogEntryParcel loge : result.mLog) { - Log.d(Constants.TAG, - loge.mIndent - + new String(new char[loge.mIndent]).replace("\0", " ") - + mContext.getString(loge.mType.getMsgId(), (Object[]) loge.mParameters)); - } + newKeys += 1; } catch (PgpGeneralException e) { @@ -171,11 +165,30 @@ public class PgpImportExport { updateProgress(position / entries.size() * 100, 100); } - returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys); - returnData.putInt(KeychainIntentService.RESULT_IMPORT_UPDATED, oldKeys); - returnData.putInt(KeychainIntentService.RESULT_IMPORT_BAD, badKeys); + OperationLog log = mProviderHelper.getLog(); + int resultType; + // Any key imported - overall success + if (newKeys > 0 || oldKeys > 0) { + if (badKeys > 0) { + resultType = ImportResult.RESULT_PARTIAL_WITH_ERRORS; + } else if (log.containsWarnings()) { + resultType = ImportResult.RESULT_OK_WITH_WARNINGS; + } else if (newKeys > 0 && oldKeys > 0) { + resultType = ImportResult.RESULT_OK_BOTHKEYS; + } else if (newKeys > 0) { + resultType = ImportResult.RESULT_OK_NEWKEYS; + } else { + resultType = ImportResult.RESULT_OK_UPDATED; + } + // No keys imported, overall failure + } else if (badKeys > 0) { + resultType = ImportResult.RESULT_FAIL_ERROR; + } else { + resultType = ImportResult.RESULT_FAIL_NOTHING; + } + + return new ImportResult(resultType, log, newKeys, oldKeys, badKeys); - return returnData; } public Bundle exportKeyRings(ArrayList publicKeyRingMasterIds, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 5c8bf6752..ab4672a98 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,9 +29,10 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.OperationResultParcel; -import org.sufficientlysecure.keychain.pgp.OperationResultParcel.LogType; -import org.sufficientlysecure.keychain.pgp.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; @@ -48,6 +49,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.OperationResults; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -61,18 +63,27 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +/** This class contains high level methods for database access. Despite its + * name, it is not only a helper but actually the main interface for all + * synchronous database operations. + * + * Operations in this class write logs (TODO). These can be obtained from the + * OperationResultParcel return values directly, but are also accumulated over + * the lifetime of the executing ProviderHelper object unless the resetLog() + * method is called to start a new one specifically. + * + */ public class ProviderHelper { private final Context mContext; private final ContentResolver mContentResolver; - private final ArrayList mLog; + private OperationLog mLog; private int mIndent; public ProviderHelper(Context context) { - this(context, new ArrayList(), 0); + this(context, new OperationLog(), 0); } - public ProviderHelper(Context context, ArrayList log, - int indent) { + public ProviderHelper(Context context, OperationLog log, int indent) { mContext = context; mContentResolver = context.getContentResolver(); mLog = log; @@ -81,11 +92,16 @@ public class ProviderHelper { public void resetLog() { if(mLog != null) { - mLog.clear(); + // Start a new log (leaving the old one intact) + mLog = new OperationLog(); mIndent = 0; } } + public OperationLog getLog() { + return mLog; + } + public static class NotFoundException extends Exception { public NotFoundException() { } @@ -97,12 +113,12 @@ public class ProviderHelper { public void log(LogLevel level, LogType type) { if(mLog != null) { - mLog.add(new OperationResultParcel.LogEntryParcel(level, type, null, mIndent)); + mLog.add(level, type, null, mIndent); } } public void log(LogLevel level, LogType type, String[] parameters) { if(mLog != null) { - mLog.add(new OperationResultParcel.LogEntryParcel(level, type, parameters, mIndent)); + mLog.add(level, type, parameters, mIndent); } } @@ -258,6 +274,9 @@ public class ProviderHelper { return new OperationResultParcel(1, mLog); } + // Canonicalize this key, to assert a number of assumptions made about the key. + keyRing = keyRing.canonicalize(mLog); + UncachedPublicKey masterKey = keyRing.getPublicKey(); long masterKeyId = masterKey.getKeyId(); log(LogLevel.INFO, LogType.MSG_IP_IMPORTING, @@ -282,34 +301,80 @@ public class ProviderHelper { log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); } - // insert new version of this keyRing - ContentValues values = new ContentValues(); - values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); try { - values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); - } catch (IOException e) { - log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); - return new OperationResultParcel(1, mLog); - } - - // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); - try { + // save all keys and userIds included in keyRing object in database + ArrayList operations = new ArrayList(); log(LogLevel.INFO, LogType.MSG_IP_INSERT_KEYRING); - Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); - operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); + { // insert keyring + // insert new version of this keyRing + ContentValues values = new ContentValues(); + values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); + try { + values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); + } catch (IOException e) { + log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); + return new OperationResultParcel(1, mLog); + } + + Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); + operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); + } log(LogLevel.INFO, LogType.MSG_IP_INSERT_SUBKEYS); mIndent += 1; - int rank = 0; - for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - log(LogLevel.DEBUG, LogType.MSG_IP_INSERT_SUBKEY, new String[] { - PgpKeyHelper.convertKeyIdToHex(key.getKeyId()) - }); - operations.add(buildPublicKeyOperations(masterKeyId, key, rank)); - ++rank; + { // insert subkeys + Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); + int rank = 0; + for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY, new String[]{ + PgpKeyHelper.convertKeyIdToHex(key.getKeyId()) + }); + mIndent += 1; + + ContentValues values = new ContentValues(); + values.put(Keys.MASTER_KEY_ID, masterKeyId); + values.put(Keys.RANK, rank); + + values.put(Keys.KEY_ID, key.getKeyId()); + values.put(Keys.KEY_SIZE, key.getBitStrength()); + values.put(Keys.ALGORITHM, key.getAlgorithm()); + values.put(Keys.FINGERPRINT, key.getFingerprint()); + + boolean c = key.canCertify(), s = key.canSign(), e = key.canEncrypt(); + values.put(Keys.CAN_CERTIFY, c); + values.put(Keys.CAN_SIGN, s); + values.put(Keys.CAN_ENCRYPT, e); + values.put(Keys.IS_REVOKED, key.isRevoked()); + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_FLAGS, new String[] { "X" }); + + Date creation = key.getCreationTime(); + values.put(Keys.CREATION, creation.getTime() / 1000); + if (creation.after(new Date())) { + log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[] { + creation.toString() + }); + return new OperationResultParcel(1, mLog); + } + Date expiryDate = key.getExpiryTime(); + if (expiryDate != null) { + values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); + if (key.isExpired()) { + log(LogLevel.INFO, LogType.MSG_IP_SUBKEY_EXPIRED, new String[] { + expiryDate.toString() + }); + } else { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[] { + expiryDate.toString() + }); + } + } + + operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); + ++rank; + mIndent -= 1; + } } mIndent -= 1; @@ -374,7 +439,12 @@ public class ProviderHelper { // save certificate as primary self-cert item.selfCert = cert; item.isPrimary = cert.isPrimaryUserId(); - item.isRevoked = cert.isRevocation(); + if (cert.isRevocation()) { + item.isRevoked = true; + log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); + } else { + item.isRevoked = false; + } } @@ -500,7 +570,7 @@ public class ProviderHelper { long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.INFO, LogType.MSG_IS_IMPORTING, - new String[]{ Long.toString(masterKeyId) }); + new String[]{Long.toString(masterKeyId)}); // save secret keyring try { @@ -575,37 +645,6 @@ public class ProviderHelper { saveSecretKeyRing(secRing); } - /** - * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing - */ - private ContentProviderOperation - buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException { - - ContentValues values = new ContentValues(); - values.put(Keys.MASTER_KEY_ID, masterKeyId); - values.put(Keys.RANK, rank); - - values.put(Keys.KEY_ID, key.getKeyId()); - values.put(Keys.KEY_SIZE, key.getBitStrength()); - values.put(Keys.ALGORITHM, key.getAlgorithm()); - values.put(Keys.FINGERPRINT, key.getFingerprint()); - - values.put(Keys.CAN_CERTIFY, key.canCertify()); - values.put(Keys.CAN_SIGN, key.canSign()); - values.put(Keys.CAN_ENCRYPT, key.canEncrypt()); - values.put(Keys.IS_REVOKED, key.maybeRevoked()); - - values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000); - Date expiryDate = key.getExpiryTime(); - if (expiryDate != null) { - values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); - } - - Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - /** * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 498b963f2..f2231fbb5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -174,14 +174,11 @@ public class KeychainIntentService extends IntentService public static final String RESULT_DECRYPTED_BYTES = "decrypted_data"; public static final String RESULT_DECRYPT_VERIFY_RESULT = "signature"; - // import - public static final String RESULT_IMPORT_ADDED = "added"; - public static final String RESULT_IMPORT_UPDATED = "updated"; - public static final String RESULT_IMPORT_BAD = "bad"; - // export public static final String RESULT_EXPORT = "exported"; + public static final String RESULT = "result"; + Messenger mMessenger; private boolean mIsCanceled; @@ -648,7 +645,10 @@ public class KeychainIntentService extends IntentService List entries = data.getParcelableArrayList(IMPORT_KEY_LIST); PgpImportExport pgpImportExport = new PgpImportExport(this, this); - Bundle resultData = pgpImportExport.importKeyRings(entries); + OperationResults.ImportResult result = pgpImportExport.importKeyRings(entries); + + Bundle resultData = new Bundle(); + resultData.putParcelable(RESULT, result); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); } catch (Exception e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java new file mode 100644 index 000000000..a1a1d0067 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -0,0 +1,207 @@ +package org.sufficientlysecure.keychain.service; + +import android.os.Parcel; +import android.os.Parcelable; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.IterableIterator; + +import java.util.ArrayList; + +/** Represent the result of an operation. + * + * This class holds a result and the log of an operation. It can be subclassed + * to include typed additional information specific to the operation. To keep + * the class structure (somewhat) simple, this class contains an exhaustive + * list (ie, enum) of all possible log types, which should in all cases be tied + * to string resource ids. + * + */ +public class OperationResultParcel implements Parcelable { + /** Holds the overall result, the number specifying varying degrees of success. + * Values smaller than 100 are considered an overall success. */ + final int mResult; + + public static final int RESULT_OK = 0; + public static final int RESULT_ERROR = 100; + + /// A list of log entries tied to the operation result. + final OperationLog mLog; + + public OperationResultParcel(int result, OperationLog log) { + mResult = result; + mLog = log; + } + + public OperationResultParcel(Parcel source) { + mResult = source.readInt(); + mLog = new OperationLog(); + mLog.addAll(source.createTypedArrayList(LogEntryParcel.CREATOR)); + } + + public int getResult() { + return mResult; + } + + public boolean isSuccessful() { + return mResult < 100; + } + + public OperationLog getLog() { + return mLog; + } + + /** One entry in the log. */ + public static class LogEntryParcel implements Parcelable { + public final LogLevel mLevel; + public final LogType mType; + public final String[] mParameters; + public final int mIndent; + + public LogEntryParcel(LogLevel level, LogType type, String[] parameters, int indent) { + mLevel = level; + mType = type; + mParameters = parameters; + mIndent = indent; + } + public LogEntryParcel(LogLevel level, LogType type, String[] parameters) { + this(level, type, parameters, 0); + } + + public LogEntryParcel(Parcel source) { + mLevel = LogLevel.values()[source.readInt()]; + mType = LogType.values()[source.readInt()]; + mParameters = source.createStringArray(); + mIndent = source.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mLevel.ordinal()); + dest.writeInt(mType.ordinal()); + dest.writeStringArray(mParameters); + dest.writeInt(mIndent); + } + + public static final Creator CREATOR = new Creator() { + public LogEntryParcel createFromParcel(final Parcel source) { + return new LogEntryParcel(source); + } + + public LogEntryParcel[] newArray(final int size) { + return new LogEntryParcel[size]; + } + }; + + } + + public static enum LogType { + MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch), + MSG_IP_BAD_TYPE_SECRET (R.string.msg_ip_bad_type_secret), + MSG_IP_DELETE_OLD_FAIL (R.string.msg_ip_delete_old_fail), + MSG_IP_DELETE_OLD_OK (R.string.msg_ip_delete_old_ok), + MSG_IP_ENCODE_FAIL (R.string.msg_ip_encode_fail), + MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), + MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), + MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), + MSG_IP_IMPORTING (R.string.msg_ip_importing), + MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), + MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), + MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), + MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), + MSG_IP_SUBKEY (R.string.msg_ip_subkey), + MSG_IP_SUBKEY_EXPIRED (R.string.msg_ip_subkey_expired), + MSG_IP_SUBKEY_EXPIRES (R.string.msg_ip_subkey_expires), + MSG_IP_SUBKEY_FLAGS (R.string.msg_ip_subkey_flags), + MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), + MSG_IP_SUCCESS (R.string.msg_ip_success), + MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), + MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), + MSG_IP_TRUST_USING_SEC (R.string.msg_ip_trust_using_sec), + MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), + MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), + MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), + MSG_IP_UID_CERTS_UNKNOWN (R.string.msg_ip_uid_certs_unknown), + MSG_IP_UID_CLASSIFYING (R.string.msg_ip_uid_classifying), + MSG_IP_UID_INSERT (R.string.msg_ip_uid_insert), + MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), + MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), + MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), + MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), + MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), + MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), + MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), + MSG_IS_IMPORTING (R.string.msg_is_importing), + MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), + MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), + MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), + MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), + MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), + MSG_IS_SUCCESS (R.string.msg_is_success), + ; + + private final int mMsgId; + LogType(int msgId) { + mMsgId = msgId; + } + public int getMsgId() { + return mMsgId; + } + } + + /** Enumeration of possible log levels. */ + public static enum LogLevel { + OK, + DEBUG, + INFO, + WARN, + /** If any ERROR log entry is included in the result, the overall operation should have failed. */ + ERROR, + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mResult); + dest.writeTypedList(mLog); + } + + public static final Creator CREATOR = new Creator() { + public OperationResultParcel createFromParcel(final Parcel source) { + return new OperationResultParcel(source); + } + + public OperationResultParcel[] newArray(final int size) { + return new OperationResultParcel[size]; + } + }; + + public static class OperationLog extends ArrayList { + + /// Simple convenience method + public void add(LogLevel level, LogType type, String[] parameters, int indent) { + add(new OperationResultParcel.LogEntryParcel(level, type, parameters, indent)); + } + + public boolean containsWarnings() { + int warn = LogLevel.WARN.ordinal(); + for(LogEntryParcel entry : new IterableIterator(iterator())) { + if (entry.mLevel.ordinal() >= warn) { + return true; + } + } + return false; + } + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java new file mode 100644 index 000000000..e08b50500 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -0,0 +1,63 @@ +package org.sufficientlysecure.keychain.service; + +import android.os.Parcel; + +public abstract class OperationResults { + + public static class ImportResult extends OperationResultParcel { + + public final int mNewKeys, mUpdatedKeys, mBadKeys; + + // Operation ok, at least one new key (no warnings) + public static final int RESULT_OK_NEWKEYS = 1; + // Operation ok, at least one new and one updated key (no warnings) + public static final int RESULT_OK_BOTHKEYS = 2; + // Operation ok, no new keys but upated ones (no warnings) + public static final int RESULT_OK_UPDATED = 3; + // Operation ok, but with warnings + public static final int RESULT_OK_WITH_WARNINGS = 4; + + // Operation partially ok, but at least one key failed! + public static final int RESULT_PARTIAL_WITH_ERRORS = 50; + + // Operation failed, errors thrown and no new keys imported + public static final int RESULT_FAIL_ERROR = 100; + // Operation failed, no keys to import... + public static final int RESULT_FAIL_NOTHING = 101; + + public ImportResult(Parcel source) { + super(source); + mNewKeys = source.readInt(); + mUpdatedKeys = source.readInt(); + mBadKeys = source.readInt(); + } + + public ImportResult(int result, OperationLog log, + int newKeys, int updatedKeys, int badKeys) { + super(result, log); + mNewKeys = newKeys; + mUpdatedKeys = updatedKeys; + mBadKeys = badKeys; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mNewKeys); + dest.writeInt(mUpdatedKeys); + dest.writeInt(mBadKeys); + } + + public static Creator CREATOR = new Creator() { + public ImportResult createFromParcel(final Parcel source) { + return new ImportResult(source); + } + + public ImportResult[] newArray(final int size) { + return new ImportResult[size]; + } + }; + + } + +} diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 6f21900fe..86219da70 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -529,13 +529,17 @@ Operation failed due to i/o error Operation failed due to database error Operation failed due to internal error - Importing public keyring + Importing public keyring %s Inserting keyring data - Inserting subkey %s Inserting subkeys Preserving available secret key - Re-inserting secret key + Processing subkey %s + Subkey expired on %s + Subkey expires on %s + Subkey flags: %s + Subkey creation date lies in the future! (%s) Successfully inserted public keyring + Re-inserting secret key Retrieving trusted keys Using %s trusted keys Secret key available, self certificates are trusted @@ -546,6 +550,7 @@ Classifying user ids Inserting user ids Processing user id %s + Found uid revocation certificate Bad self certificate encountered! Found good self certificate Ignoring older self certificate @@ -558,5 +563,6 @@ Marked %s as available Marked %s as stripped Successfully inserted secret keyring + Log -- cgit v1.2.3 From cdc61c43927f11835fffa090ccb045d206728692 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:51:16 +0200 Subject: canonicalize: first step(s) --- .../keychain/keyimport/ImportKeysListEntry.java | 2 +- .../keychain/pgp/UncachedKeyRing.java | 55 ++++++++++++++++++++++ .../keychain/pgp/UncachedPublicKey.java | 12 ++++- 3 files changed, 66 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index c43f72235..7a7475603 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -229,7 +229,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.keyId = key.getKeyId(); this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId); - this.revoked = key.maybeRevoked(); + this.revoked = key.isRevoked(); this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); this.bitStrength = key.getBitStrength(); final int algorithm = key.getAlgorithm(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 1264c8c36..624b7d068 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -7,9 +7,14 @@ import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -169,4 +174,54 @@ public class UncachedKeyRing { return result; } + /** "Canonicalizes" a key, removing inconsistencies in the process. This operation can be + * applied to public keyrings only. + * + * More specifically: + * - Remove all non-verifying self-certificates + * - Remove all expired self-certificates + * - Remove all certificates flagged as "local" + * - Remove all certificates which are superseded by a newer one on the same target + * + * After this cleaning, a number of checks are done: + * - See if each subkey retains a valid self certificate + * - See if each user id retains a valid self certificate + * + * This operation writes an OperationLog which can be used as part of a OperationResultParcel. + * + * If any of these checks fail, the operation as a whole fails and the keyring is declared + * unusable. (TODO: allow forcing of import?) + * + * TODO implement + * + * @return A canonicalized key + * + */ + public UncachedKeyRing canonicalize(OperationLog log) { + if(isSecret()) { + throw new RuntimeException("Tried to canonicalize non-secret keyring. " + + "This is a programming error and should never happen!"); + } + + // dummy + log.add(LogLevel.INFO, LogType.MSG_IP_BAD_TYPE_SECRET, null, 0); + + /* + // Remove all non-verifying self certificates + for (PGPPublicKey key : new IterableIterator(mRing.getPublicKeys())) { + + for (PGPSignature sig : new IterableIterator( + key.getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION + : PGPSignature.SUBKEY_REVOCATION))) { + return true; + } + + }*/ + + return this; + + + } + + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java index 108c8c8c3..33db7771b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java @@ -2,6 +2,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureSubpacketVector; @@ -9,6 +10,7 @@ import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProv import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.IterableIterator; +import java.security.SignatureException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -28,8 +30,13 @@ public class UncachedPublicKey { } /** The revocation signature is NOT checked here, so this may be false! */ - public boolean maybeRevoked() { - return mPublicKey.isRevoked(); + public boolean isRevoked() { + for (PGPSignature sig : new IterableIterator( + mPublicKey.getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION + : PGPSignature.SUBKEY_REVOCATION))) { + return true; + } + return false; } public Date getCreationTime() { @@ -193,4 +200,5 @@ public class UncachedPublicKey { } }; } + } -- cgit v1.2.3 From d73a3e2fa89663e133ce4750e4aba86354c8b252 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:52:38 +0200 Subject: import-log: use supertoast in ImportKeyActivity --- .../keychain/ui/ImportKeysActivity.java | 113 ++++++++++++++++----- 1 file changed, 87 insertions(+), 26 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 48602aaa1..d1869454d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -38,7 +38,10 @@ import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; -import com.devspark.appmsg.AppMsg; +import com.github.johnpersano.supertoasts.SuperCardToast; +import com.github.johnpersano.supertoasts.SuperToast; +import com.github.johnpersano.supertoasts.util.OnClickWrapper; +import com.github.johnpersano.supertoasts.util.Style; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -47,7 +50,7 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.ui.dialog.BadImportKeyDialogFragment; +import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -135,6 +138,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O } handleActions(savedInstanceState, getIntent()); + } protected void handleActions(Bundle savedInstanceState, Intent intent) { @@ -331,8 +335,11 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O public void loadFromFingerprint(Bundle savedInstanceState, String fingerprint) { if (fingerprint == null || fingerprint.length() < 40) { - AppMsg.makeText(this, R.string.import_qr_code_too_short_fingerprint, - AppMsg.STYLE_ALERT).show(); + SuperCardToast toast = SuperCardToast.create(this, + getString(R.string.import_qr_code_too_short_fingerprint), + SuperToast.Duration.LONG); + toast.setBackground(SuperToast.Background.RED); + toast.show(); return; } @@ -368,34 +375,84 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { // get returned data bundle Bundle returnData = message.getData(); + final ImportResult result = + returnData.getParcelable(KeychainIntentService.RESULT); + + // , make pessimistic assumptions + String str = Integer.toString(result.getResult()); + int duration = 0, color = Style.RED; + + switch(result.getResult()) { + case ImportResult.RESULT_OK_NEWKEYS: + color = Style.GREEN; + duration = SuperToast.Duration.LONG; + str = getResources().getQuantityString( + R.plurals.keys_added, result.mNewKeys, result.mNewKeys); + break; + + case ImportResult.RESULT_OK_UPDATED: + color = Style.GREEN; + duration = SuperToast.Duration.LONG; + str = getResources().getQuantityString( + R.plurals.keys_updated, result.mNewKeys, result.mNewKeys); + break; + + case ImportResult.RESULT_OK_BOTHKEYS: + color = Style.GREEN; + duration = SuperToast.Duration.LONG; + str = getResources().getQuantityString( + R.plurals.keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); + str += getResources().getQuantityString( + R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys); + break; + + case ImportResult.RESULT_OK_WITH_WARNINGS: + str = "ok with warnings"; + color = Style.ORANGE; + break; + + case ImportResult.RESULT_PARTIAL_WITH_ERRORS: + str = "partial with errors"; + color = Style.ORANGE; + break; + + case ImportResult.RESULT_FAIL_ERROR: + str = "fail error"; + color = Style.RED; + break; + + case ImportResult.RESULT_FAIL_NOTHING: + str = getString(R.string.no_keys_added_or_updated); + color = Style.RED; + break; - int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED); - int updated = returnData - .getInt(KeychainIntentService.RESULT_IMPORT_UPDATED); - int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD); - String toastMessage; - if (added > 0 && updated > 0) { - String addedStr = getResources().getQuantityString( - R.plurals.keys_added_and_updated_1, added, added); - String updatedStr = getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, updated, updated); - toastMessage = addedStr + updatedStr; - } else if (added > 0) { - toastMessage = getResources().getQuantityString(R.plurals.keys_added, - added, added); - } else if (updated > 0) { - toastMessage = getResources().getQuantityString(R.plurals.keys_updated, - updated, updated); - } else { - toastMessage = getString(R.string.no_keys_added_or_updated); } - AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO) - .show(); + SuperCardToast toast = new SuperCardToast(ImportKeysActivity.this, + SuperToast.Type.BUTTON, Style.getStyle(color, SuperToast.Animations.POPUP)); + toast.setText(str); + toast.setDuration(duration); + toast.setIndeterminate(duration == 0); + toast.setButtonText("View log"); + toast.setSwipeToDismiss(true); + toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", + new SuperToast.OnClickListener() { + @Override + public void onClick(View view, Parcelable token) { + // Intent intent = new Intent( + // ImportKeysActivity.this, LogDisplayActivity.class); + // intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + // startActivity(intent); + } + })); + toast.show(); + + /* if (bad > 0) { BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad); badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog"); } + */ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { ImportKeysActivity.this.setResult(Activity.RESULT_OK, mPendingIntentData); @@ -483,7 +540,11 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O startService(intent); } else { - AppMsg.makeText(this, R.string.error_nothing_import, AppMsg.STYLE_ALERT).show(); + SuperCardToast toast = SuperCardToast.create(this, + getString(R.string.error_nothing_import), + SuperToast.Duration.LONG); + toast.setBackground(SuperToast.Background.RED); + toast.show(); } } -- cgit v1.2.3 From 7324bfcb536b4975365ab9affa0a71ef29e29b93 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:53:25 +0200 Subject: import-log: add LogDisplay activity --- OpenKeychain/src/main/AndroidManifest.xml | 6 ++ .../keychain/ui/ImportKeysActivity.java | 8 +- .../keychain/ui/LogDisplayActivity.java | 17 +++++ .../keychain/ui/LogDisplayFragment.java | 86 ++++++++++++++++++++++ .../src/main/res/layout/import_keys_activity.xml | 37 +++++----- .../src/main/res/layout/log_display_activity.xml | 46 ++++++++++++ .../src/main/res/layout/log_display_fragment.xml | 11 +++ .../src/main/res/layout/log_display_item.xml | 14 ++++ 8 files changed, 204 insertions(+), 21 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java create mode 100644 OpenKeychain/src/main/res/layout/log_display_activity.xml create mode 100644 OpenKeychain/src/main/res/layout/log_display_fragment.xml create mode 100644 OpenKeychain/src/main/res/layout/log_display_item.xml (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index f4007c098..f33886d44 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -434,6 +434,12 @@ + + diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index d1869454d..90d772bd1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -438,10 +438,10 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O new SuperToast.OnClickListener() { @Override public void onClick(View view, Parcelable token) { - // Intent intent = new Intent( - // ImportKeysActivity.this, LogDisplayActivity.class); - // intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); - // startActivity(intent); + Intent intent = new Intent( + ImportKeysActivity.this, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + startActivity(intent); } })); toast.show(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java new file mode 100644 index 000000000..28c120d01 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -0,0 +1,17 @@ +package org.sufficientlysecure.keychain.ui; + +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; + +import org.sufficientlysecure.keychain.R; + +public class LogDisplayActivity extends ActionBarActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.log_display_activity); + } + +} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java new file mode 100644 index 000000000..36d3e932e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -0,0 +1,86 @@ +package org.sufficientlysecure.keychain.ui; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogEntryParcel; + +import java.util.ArrayList; + +public class LogDisplayFragment extends ListFragment { + + LogAdapter mAdapter; + + public static final String EXTRA_RESULT = "log"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + Intent intent = getActivity().getIntent(); + if (intent.getExtras() == null || !intent.getExtras().containsKey(EXTRA_RESULT)) { + getActivity().finish(); + return; + } + + OperationResultParcel result = intent.getParcelableExtra(EXTRA_RESULT); + if (result == null) { + getActivity().finish(); + return; + } + + mAdapter = new LogAdapter(getActivity(), result.getLog()); + setListAdapter(mAdapter); + + } + + private class LogAdapter extends ArrayAdapter { + + private LayoutInflater mInflater; + private int dipFactor; + + public LogAdapter(Context context, ArrayList log) { + super(context, R.layout.log_display_item, log); + mInflater = LayoutInflater.from(getContext()); + dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + (float) 6, getResources().getDisplayMetrics()); + + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LogEntryParcel entry = getItem(position); + TextView text; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.log_display_item, parent, false); + text = (TextView) convertView.findViewById(R.id.log_text); + convertView.setTag(text); + } else { + text = (TextView) convertView.getTag(); + } + + text.setPadding(entry.mIndent*dipFactor, 0, 0, 0); + text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); + switch (entry.mLevel) { + case DEBUG: text.setTextColor(Color.GRAY); break; + case INFO: text.setTextColor(Color.BLACK); break; + case WARN: text.setTextColor(Color.YELLOW); break; + case ERROR: text.setTextColor(Color.RED); break; + } + + return convertView; + } + + } +} diff --git a/OpenKeychain/src/main/res/layout/import_keys_activity.xml b/OpenKeychain/src/main/res/layout/import_keys_activity.xml index 2a332823e..0486b6bd6 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_activity.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_activity.xml @@ -1,22 +1,37 @@ - + android:layout_height="fill_parent" + android:orientation="vertical"> + + + + @@ -43,16 +58,4 @@ style="@style/SelectableItem" /> - - - \ No newline at end of file + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/log_display_activity.xml b/OpenKeychain/src/main/res/layout/log_display_activity.xml new file mode 100644 index 000000000..591e2650c --- /dev/null +++ b/OpenKeychain/src/main/res/layout/log_display_activity.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/log_display_fragment.xml b/OpenKeychain/src/main/res/layout/log_display_fragment.xml new file mode 100644 index 000000000..442e72d09 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/log_display_fragment.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/log_display_item.xml b/OpenKeychain/src/main/res/layout/log_display_item.xml new file mode 100644 index 000000000..f0e0e4ecb --- /dev/null +++ b/OpenKeychain/src/main/res/layout/log_display_item.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file -- cgit v1.2.3 From eac582a313c779e77b0fd67358417d512680facd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 13:52:05 +0200 Subject: import-log: some cosmetics --- .../keychain/pgp/UncachedKeyRing.java | 5 ++- .../keychain/ui/ImportKeysActivity.java | 3 +- .../keychain/ui/LogDisplayFragment.java | 2 +- .../res/drawable-hdpi/ic_action_view_as_list.png | Bin 0 -> 308 bytes .../res/drawable-mdpi/ic_action_view_as_list.png | Bin 0 -> 246 bytes .../res/drawable-xhdpi/ic_action_view_as_list.png | Bin 0 -> 337 bytes .../res/drawable-xxhdpi/ic_action_view_as_list.png | Bin 0 -> 431 bytes OpenKeychain/src/main/res/values/strings.xml | 49 +++++++++++---------- 8 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 OpenKeychain/src/main/res/drawable-hdpi/ic_action_view_as_list.png create mode 100644 OpenKeychain/src/main/res/drawable-mdpi/ic_action_view_as_list.png create mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/ic_action_view_as_list.png create mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_view_as_list.png (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 624b7d068..7853d0b00 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -204,7 +204,8 @@ public class UncachedKeyRing { } // dummy - log.add(LogLevel.INFO, LogType.MSG_IP_BAD_TYPE_SECRET, null, 0); + log.add(LogLevel.START, LogType.MSG_KC, + new String[] { PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()) }, 0); /* // Remove all non-verifying self certificates @@ -218,6 +219,8 @@ public class UncachedKeyRing { }*/ + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, 0); + return this; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 90d772bd1..9932e3e18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -432,8 +432,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setText(str); toast.setDuration(duration); toast.setIndeterminate(duration == 0); - toast.setButtonText("View log"); toast.setSwipeToDismiss(true); + toast.setButtonIcon(R.drawable.ic_action_view_as_list, + getResources().getString(R.string.view_log)); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", new SuperToast.OnClickListener() { @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 36d3e932e..5e038d3f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -54,7 +54,7 @@ public class LogDisplayFragment extends ListFragment { super(context, R.layout.log_display_item, log); mInflater = LayoutInflater.from(getContext()); dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - (float) 6, getResources().getDisplayMetrics()); + (float) 10, getResources().getDisplayMetrics()); } diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_action_view_as_list.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_view_as_list.png new file mode 100644 index 000000000..86da228e9 Binary files /dev/null and b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_view_as_list.png differ diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_action_view_as_list.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_view_as_list.png new file mode 100644 index 000000000..ccb4c7d7b Binary files /dev/null and b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_view_as_list.png differ diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_view_as_list.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_view_as_list.png new file mode 100644 index 000000000..b9c93c8c2 Binary files /dev/null and b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_view_as_list.png differ diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_view_as_list.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_view_as_list.png new file mode 100644 index 000000000..460041640 Binary files /dev/null and b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_view_as_list.png differ diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 86219da70..e1ff77c63 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -28,6 +28,7 @@ Certify Identities Key Details Help + Log Identities @@ -499,28 +500,7 @@ error! key unavailable - - Certifier - Certificate Details - Identity - <unknown> - No certificates for this key - Identities to certify - Revocation Reason - Verification Status - Type - Key not found! - Error processing key! - subkey unavailable - stripped - Secret keys can only be deleted individually! - View Certificate Details - unknown - cannot sign - Encoding error - No encryption subkey available! - - + Applying insert batch operation. Tried to import secret keyring as public. This is a bug, please file a report! No old key deleted (creating a new one?) @@ -556,6 +536,8 @@ Ignoring older self certificate Using more recent good self certificate Tried to import public keyring as secret. This is a bug, please file a report! + + Importing secret key %s Processing secret subkeys Error encoding keyring @@ -563,6 +545,27 @@ Marked %s as available Marked %s as stripped Successfully inserted secret keyring - Log + + + Certifier + Certificate Details + Identity + <unknown> + No certificates for this key + Identities to certify + Revocation Reason + Verification Status + Type + Key not found! + Error processing key! + subkey unavailable + stripped + Secret keys can only be deleted individually! + View Certificate Details + unknown + cannot sign + Encoding error + No encryption subkey available! + View Log -- cgit v1.2.3 From e41e6ea0deec06703c5b9c80e429aff8ab110534 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 15:27:26 +0200 Subject: import-log: more interface work --- .../keychain/pgp/PgpImportExport.java | 2 - .../keychain/provider/ProviderHelper.java | 44 +++++++++++++++------- .../keychain/service/OperationResultParcel.java | 26 ++++++++++--- .../keychain/service/OperationResults.java | 2 - .../keychain/ui/ImportKeysActivity.java | 36 +++++++++--------- .../keychain/ui/LogDisplayFragment.java | 1 + OpenKeychain/src/main/res/values/strings.xml | 33 +++++++++++----- 7 files changed, 94 insertions(+), 50 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index ebc5a7868..4cb41708d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -171,8 +171,6 @@ public class PgpImportExport { if (newKeys > 0 || oldKeys > 0) { if (badKeys > 0) { resultType = ImportResult.RESULT_PARTIAL_WITH_ERRORS; - } else if (log.containsWarnings()) { - resultType = ImportResult.RESULT_OK_WITH_WARNINGS; } else if (newKeys > 0 && oldKeys > 0) { resultType = ImportResult.RESULT_OK_BOTHKEYS; } else if (newKeys > 0) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index ab4672a98..cbaf72270 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -49,7 +49,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; -import org.sufficientlysecure.keychain.service.OperationResults; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -252,7 +251,7 @@ public class ProviderHelper { throw new NotFoundException("Secret key not available!"); } return secret - ? new WrappedSecretKeyRing(blob, hasAnySecret, verified) + ? new WrappedSecretKeyRing(blob, true, verified) : new WrappedPublicKeyRing(blob, hasAnySecret, verified); } else { throw new NotFoundException("Key not found!"); @@ -274,14 +273,15 @@ public class ProviderHelper { return new OperationResultParcel(1, mLog); } - // Canonicalize this key, to assert a number of assumptions made about the key. + long masterKeyId = keyRing.getMasterKeyId(); + log(LogLevel.START, LogType.MSG_IP, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + mIndent += 1; + + // Canonicalize this key, to assert a number of assumptions made about it. keyRing = keyRing.canonicalize(mLog); UncachedPublicKey masterKey = keyRing.getPublicKey(); - long masterKeyId = masterKey.getKeyId(); - log(LogLevel.INFO, LogType.MSG_IP_IMPORTING, - new String[]{Long.toString(masterKeyId)}); - mIndent += 1; // IF there is a secret key, preserve it! UncachedKeyRing secretRing; @@ -342,12 +342,28 @@ public class ProviderHelper { values.put(Keys.ALGORITHM, key.getAlgorithm()); values.put(Keys.FINGERPRINT, key.getFingerprint()); - boolean c = key.canCertify(), s = key.canSign(), e = key.canEncrypt(); + boolean c = key.canCertify(), e = key.canEncrypt(), s = key.canSign(); values.put(Keys.CAN_CERTIFY, c); - values.put(Keys.CAN_SIGN, s); values.put(Keys.CAN_ENCRYPT, e); + values.put(Keys.CAN_SIGN, s); values.put(Keys.IS_REVOKED, key.isRevoked()); - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_FLAGS, new String[] { "X" }); + if (c) { + if (e) { + log(LogLevel.DEBUG,s ? LogType.MSG_IP_SUBKEY_FLAGS_CES + : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS + : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + } + } else { + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES + : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS + : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + } + } Date creation = key.getCreationTime(); values.put(Keys.CREATION, creation.getTime() / 1000); @@ -531,7 +547,7 @@ public class ProviderHelper { mIndent -= 1; } - log(LogLevel.INFO, LogType.MSG_IP_SUCCESS); + log(LogLevel.OK, LogType.MSG_IP_SUCCESS); mIndent -= 1; return new OperationResultParcel(0, mLog); @@ -569,8 +585,8 @@ public class ProviderHelper { } long masterKeyId = keyRing.getMasterKeyId(); - log(LogLevel.INFO, LogType.MSG_IS_IMPORTING, - new String[]{Long.toString(masterKeyId)}); + log(LogLevel.START, LogType.MSG_IS, + new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}); // save secret keyring try { @@ -626,7 +642,7 @@ public class ProviderHelper { // with has_secret = 0 } - log(LogLevel.INFO, LogType.MSG_IS_SUCCESS); + log(LogLevel.OK, LogType.MSG_IS_SUCCESS); return new OperationResultParcel(0, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index a1a1d0067..85b7c456f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -101,6 +101,9 @@ public class OperationResultParcel implements Parcelable { } public static enum LogType { + + // import public + MSG_IP(R.string.msg_ip), MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch), MSG_IP_BAD_TYPE_SECRET (R.string.msg_ip_bad_type_secret), MSG_IP_DELETE_OLD_FAIL (R.string.msg_ip_delete_old_fail), @@ -109,7 +112,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), - MSG_IP_IMPORTING (R.string.msg_ip_importing), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), @@ -118,6 +120,14 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_EXPIRED (R.string.msg_ip_subkey_expired), MSG_IP_SUBKEY_EXPIRES (R.string.msg_ip_subkey_expires), MSG_IP_SUBKEY_FLAGS (R.string.msg_ip_subkey_flags), + MSG_IP_SUBKEY_FLAGS_CES (R.string.msg_ip_subkey_flags_ces), + MSG_IP_SUBKEY_FLAGS_CEX (R.string.msg_ip_subkey_flags_cex), + MSG_IP_SUBKEY_FLAGS_CXS (R.string.msg_ip_subkey_flags_cxs), + MSG_IP_SUBKEY_FLAGS_XES (R.string.msg_ip_subkey_flags_xes), + MSG_IP_SUBKEY_FLAGS_CXX (R.string.msg_ip_subkey_flags_cxx), + MSG_IP_SUBKEY_FLAGS_XEX (R.string.msg_ip_subkey_flags_xex), + MSG_IP_SUBKEY_FLAGS_XXS (R.string.msg_ip_subkey_flags_xxs), + MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), @@ -135,14 +145,20 @@ public class OperationResultParcel implements Parcelable { MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), + + // import secret + MSG_IS(R.string.msg_is), MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), - MSG_IS_IMPORTING (R.string.msg_is_importing), MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), MSG_IS_SUCCESS (R.string.msg_is_success), + + // keyring canonicalization + MSG_KC(R.string.msg_kc), + MSG_KC_SUCCESS(R.string.msg_kc_success), ; private final int mMsgId; @@ -156,12 +172,12 @@ public class OperationResultParcel implements Parcelable { /** Enumeration of possible log levels. */ public static enum LogLevel { - OK, + START, // should occur once at the start of each independent operation + OK, // should occur once at the end of a successful operation + ERROR, // should occur once at the end of a failed operation DEBUG, INFO, WARN, - /** If any ERROR log entry is included in the result, the overall operation should have failed. */ - ERROR, } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index e08b50500..bee6917be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -14,8 +14,6 @@ public abstract class OperationResults { public static final int RESULT_OK_BOTHKEYS = 2; // Operation ok, no new keys but upated ones (no warnings) public static final int RESULT_OK_UPDATED = 3; - // Operation ok, but with warnings - public static final int RESULT_OK_WITH_WARNINGS = 4; // Operation partially ok, but at least one key failed! public static final int RESULT_PARTIAL_WITH_ERRORS = 50; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 9932e3e18..61d7e7949 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -378,37 +378,37 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O final ImportResult result = returnData.getParcelable(KeychainIntentService.RESULT); - // , make pessimistic assumptions - String str = Integer.toString(result.getResult()); - int duration = 0, color = Style.RED; + String str = ""; + boolean hasWarnings = result.getLog().containsWarnings(); + int duration = 0, color = hasWarnings ? Style.ORANGE : Style.GREEN; + String withWarnings = hasWarnings + ? getResources().getString(R.string.with_warnings) : ""; switch(result.getResult()) { case ImportResult.RESULT_OK_NEWKEYS: - color = Style.GREEN; - duration = SuperToast.Duration.LONG; + if (!hasWarnings) { + duration = SuperToast.Duration.LONG; + } str = getResources().getQuantityString( - R.plurals.keys_added, result.mNewKeys, result.mNewKeys); + R.plurals.keys_added, result.mNewKeys, result.mNewKeys, withWarnings); break; case ImportResult.RESULT_OK_UPDATED: - color = Style.GREEN; - duration = SuperToast.Duration.LONG; + if (!hasWarnings) { + duration = SuperToast.Duration.LONG; + } str = getResources().getQuantityString( - R.plurals.keys_updated, result.mNewKeys, result.mNewKeys); + R.plurals.keys_updated, result.mNewKeys, result.mNewKeys, withWarnings); break; case ImportResult.RESULT_OK_BOTHKEYS: - color = Style.GREEN; - duration = SuperToast.Duration.LONG; + if (!hasWarnings) { + duration = SuperToast.Duration.LONG; + } str = getResources().getQuantityString( R.plurals.keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); str += getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys); - break; - - case ImportResult.RESULT_OK_WITH_WARNINGS: - str = "ok with warnings"; - color = Style.ORANGE; + R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); break; case ImportResult.RESULT_PARTIAL_WITH_ERRORS: @@ -435,6 +435,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setSwipeToDismiss(true); toast.setButtonIcon(R.drawable.ic_action_view_as_list, getResources().getString(R.string.view_log)); + toast.setButtonTextColor(R.color.emphasis_dark); + toast.setTextColor(R.color.emphasis_dark); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", new SuperToast.OnClickListener() { @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 5e038d3f6..275c9ac18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -73,6 +73,7 @@ public class LogDisplayFragment extends ListFragment { text.setPadding(entry.mIndent*dipFactor, 0, 0, 0); text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); switch (entry.mLevel) { + case OK: text.setTextColor(Color.GREEN); break; case DEBUG: text.setTextColor(Color.GRAY); break; case INFO: text.setTextColor(Color.BLACK); break; case WARN: text.setTextColor(Color.YELLOW); break; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index e1ff77c63..b60e281ee 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -222,23 +222,24 @@ Also export secret keys? - Successfully added %d key - Successfully added %d keys + Successfully added %1$d key + Successfully added %1$d keys - and updated %d key. - and updated %d keys. + and updated %1$d key%2$s. + and updated %1$d keys%2$s. - Successfully added %d key. - Successfully added %d keys. + Successfully added %1$d key%2$s. + Successfully added %1$d keys%2$s. - Successfully updated %d key. - Successfully updated %d keys. + Successfully updated %1$d key%2$s. + Successfully updated %1$d keys%2$s. No keys added or updated. + , with warnings Successfully exported 1 key. Successfully exported %d keys. No keys exported. @@ -509,7 +510,7 @@ Operation failed due to i/o error Operation failed due to database error Operation failed due to internal error - Importing public keyring %s + Importing public keyring %s Inserting keyring data Inserting subkeys Preserving available secret key @@ -517,6 +518,14 @@ Subkey expired on %s Subkey expires on %s Subkey flags: %s + Subkey flags: certify, encrypt, sign + Subkey flags: certify, encrypt + Subkey flags: certify, sign + Subkey flags: encrypt, sign + Subkey flags: certify + Subkey flags: encrypt + Subkey flags: sign + Subkey flags: none Subkey creation date lies in the future! (%s) Successfully inserted public keyring Re-inserting secret key @@ -538,7 +547,7 @@ Tried to import public keyring as secret. This is a bug, please file a report! - Importing secret key %s + Importing secret key %s Processing secret subkeys Error encoding keyring Subkey %s unavailable in public key @@ -546,6 +555,10 @@ Marked %s as stripped Successfully inserted secret keyring + + Canonicalizing keyring %s + Successfully canonicalized keyring + Certifier Certificate Details -- cgit v1.2.3 From f38556cab1215f1e49f2e6c2e90627a4c00b02d5 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 16:24:04 +0200 Subject: import-log: switch to flags instead of statuses for result int --- .../keychain/pgp/PgpImportExport.java | 44 ++++++----- .../keychain/provider/ProviderHelper.java | 24 +++--- .../keychain/service/OperationResultParcel.java | 9 ++- .../keychain/service/OperationResults.java | 55 ++++++++++--- .../keychain/ui/ImportKeysActivity.java | 91 +++++++++++----------- OpenKeychain/src/main/res/values-de/strings.xml | 10 +-- OpenKeychain/src/main/res/values-es/strings.xml | 10 +-- OpenKeychain/src/main/res/values-fr/strings.xml | 10 +-- OpenKeychain/src/main/res/values-it/strings.xml | 10 +-- OpenKeychain/src/main/res/values-ja/strings.xml | 10 +-- OpenKeychain/src/main/res/values-nl/strings.xml | 10 +-- OpenKeychain/src/main/res/values-pl/strings.xml | 10 +-- OpenKeychain/src/main/res/values-ru/strings.xml | 10 +-- OpenKeychain/src/main/res/values-sl/strings.xml | 10 +-- OpenKeychain/src/main/res/values-uk/strings.xml | 10 +-- OpenKeychain/src/main/res/values/strings.xml | 42 +++++----- 16 files changed, 205 insertions(+), 160 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 4cb41708d..bafb086d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -33,9 +33,9 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; @@ -152,9 +152,12 @@ public class PgpImportExport { } } - OperationResultParcel result = mProviderHelper.savePublicKeyRing(key); - - newKeys += 1; + SaveKeyringResult result = mProviderHelper.savePublicKeyRing(key); + if (result.updated()) { + newKeys += 1; + } else { + oldKeys += 1; + } } catch (PgpGeneralException e) { Log.e(Constants.TAG, "Encountered bad key on import!", e); @@ -166,23 +169,26 @@ public class PgpImportExport { } OperationLog log = mProviderHelper.getLog(); - int resultType; - // Any key imported - overall success - if (newKeys > 0 || oldKeys > 0) { + int resultType = 0; + // special return case: no new keys at all + if (badKeys == 0 && newKeys == 0 && oldKeys == 0) { + resultType = ImportResult.RESULT_FAIL_NOTHING; + } else { + if (newKeys > 0) { + resultType |= ImportResult.RESULT_OK_NEWKEYS; + } + if (oldKeys > 0) { + resultType |= ImportResult.RESULT_OK_UPDATED; + } if (badKeys > 0) { - resultType = ImportResult.RESULT_PARTIAL_WITH_ERRORS; - } else if (newKeys > 0 && oldKeys > 0) { - resultType = ImportResult.RESULT_OK_BOTHKEYS; - } else if (newKeys > 0) { - resultType = ImportResult.RESULT_OK_NEWKEYS; - } else { - resultType = ImportResult.RESULT_OK_UPDATED; + resultType |= ImportResult.RESULT_WITH_ERRORS; + if (newKeys == 0 && oldKeys == 0) { + resultType |= ImportResult.RESULT_ERROR; + } + } + if (log.containsWarnings()) { + resultType |= ImportResult.RESULT_WITH_WARNINGS; } - // No keys imported, overall failure - } else if (badKeys > 0) { - resultType = ImportResult.RESULT_FAIL_ERROR; - } else { - resultType = ImportResult.RESULT_FAIL_NOTHING; } return new ImportResult(resultType, log, newKeys, oldKeys, badKeys); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index cbaf72270..00db473b3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -267,12 +268,15 @@ public class ProviderHelper { * Saves PGPPublicKeyRing with its keys and userIds in DB */ @SuppressWarnings("unchecked") - public OperationResultParcel savePublicKeyRing(UncachedKeyRing keyRing) { + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { if (keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } + // start with ok result + int result = SaveKeyringResult.SAVED_PUBLIC; + long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IP, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); @@ -296,6 +300,7 @@ public class ProviderHelper { try { mContentResolver.delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); + result |= SaveKeyringResult.UPDATED; } catch (UnsupportedOperationException e) { Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); @@ -315,7 +320,7 @@ public class ProviderHelper { values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); @@ -371,7 +376,7 @@ public class ProviderHelper { log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[] { creation.toString() }); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } Date expiryDate = key.getExpiryTime(); if (expiryDate != null) { @@ -436,7 +441,7 @@ public class ProviderHelper { if (!cert.verifySignature(masterKey, userId)) { // Bad self certification? That's kinda bad... log(LogLevel.ERROR, LogType.MSG_IP_UID_SELF_BAD); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } // if we already have a cert.. @@ -526,17 +531,17 @@ public class ProviderHelper { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); Log.e(Constants.TAG, "IOException during import", e); mIndent -= 1; - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); mIndent -= 1; - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } catch (OperationApplicationException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EX); Log.e(Constants.TAG, "OperationApplicationException during import", e); mIndent -= 1; - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } // Save the saved keyring (if any) @@ -544,12 +549,13 @@ public class ProviderHelper { log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); mIndent += 1; saveSecretKeyRing(secretRing); + result |= SaveKeyringResult.SAVED_SECRET; mIndent -= 1; } log(LogLevel.OK, LogType.MSG_IP_SUCCESS); mIndent -= 1; - return new OperationResultParcel(0, mLog); + return new SaveKeyringResult(result, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 85b7c456f..8a3b06f67 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -18,12 +18,13 @@ import java.util.ArrayList; * */ public class OperationResultParcel implements Parcelable { - /** Holds the overall result, the number specifying varying degrees of success. - * Values smaller than 100 are considered an overall success. */ + /** Holds the overall result, the number specifying varying degrees of success. The first bit + * is 0 on overall success, 1 on overall failure. All other bits may be used for more specific + * conditions. */ final int mResult; public static final int RESULT_OK = 0; - public static final int RESULT_ERROR = 100; + public static final int RESULT_ERROR = 1; /// A list of log entries tied to the operation result. final OperationLog mLog; @@ -44,7 +45,7 @@ public class OperationResultParcel implements Parcelable { } public boolean isSuccessful() { - return mResult < 100; + return (mResult & 1) == 1; } public OperationLog getLog() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index bee6917be..342d07953 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -8,20 +8,31 @@ public abstract class OperationResults { public final int mNewKeys, mUpdatedKeys, mBadKeys; - // Operation ok, at least one new key (no warnings) - public static final int RESULT_OK_NEWKEYS = 1; - // Operation ok, at least one new and one updated key (no warnings) - public static final int RESULT_OK_BOTHKEYS = 2; - // Operation ok, no new keys but upated ones (no warnings) - public static final int RESULT_OK_UPDATED = 3; + // At least one new key + public static final int RESULT_OK_NEWKEYS = 2; + // At least one updated key + public static final int RESULT_OK_UPDATED = 4; + // At least one key failed (might still be an overall success) + public static final int RESULT_WITH_ERRORS = 8; + // There are warnings in the log + public static final int RESULT_WITH_WARNINGS = 16; - // Operation partially ok, but at least one key failed! - public static final int RESULT_PARTIAL_WITH_ERRORS = 50; + // No keys to import... + public static final int RESULT_FAIL_NOTHING = 32 +1; - // Operation failed, errors thrown and no new keys imported - public static final int RESULT_FAIL_ERROR = 100; - // Operation failed, no keys to import... - public static final int RESULT_FAIL_NOTHING = 101; + public boolean isOkBoth() { + return (mResult & (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED)) + == (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED); + } + public boolean isOkNew() { + return (mResult & RESULT_OK_NEWKEYS) > 0; + } + public boolean isOkUpdated() { + return (mResult & RESULT_OK_UPDATED) > 0; + } + public boolean isFailNothing() { + return (mResult & RESULT_FAIL_NOTHING) > 0; + } public ImportResult(Parcel source) { super(source); @@ -58,4 +69,24 @@ public abstract class OperationResults { } + public static class SaveKeyringResult extends OperationResultParcel { + + public SaveKeyringResult(int result, OperationLog log) { + super(result, log); + } + + // Some old key was updated + public static final int UPDATED = 2; + + // Public key was saved + public static final int SAVED_PUBLIC = 8; + // Secret key was saved (not exclusive with public!) + public static final int SAVED_SECRET = 16; + + public boolean updated() { + return (mResult & UPDATED) == UPDATED; + } + + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 61d7e7949..3ff4f9887 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -378,55 +378,54 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O final ImportResult result = returnData.getParcelable(KeychainIntentService.RESULT); - String str = ""; - boolean hasWarnings = result.getLog().containsWarnings(); - int duration = 0, color = hasWarnings ? Style.ORANGE : Style.GREEN; - String withWarnings = hasWarnings - ? getResources().getString(R.string.with_warnings) : ""; - - switch(result.getResult()) { - case ImportResult.RESULT_OK_NEWKEYS: - if (!hasWarnings) { - duration = SuperToast.Duration.LONG; - } - str = getResources().getQuantityString( - R.plurals.keys_added, result.mNewKeys, result.mNewKeys, withWarnings); - break; + int resultType = result.getResult(); - case ImportResult.RESULT_OK_UPDATED: - if (!hasWarnings) { - duration = SuperToast.Duration.LONG; - } - str = getResources().getQuantityString( - R.plurals.keys_updated, result.mNewKeys, result.mNewKeys, withWarnings); - break; + String str; + int duration, color; - case ImportResult.RESULT_OK_BOTHKEYS: - if (!hasWarnings) { - duration = SuperToast.Duration.LONG; - } - str = getResources().getQuantityString( - R.plurals.keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); - str += getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); - break; + // Not an overall failure + if ((resultType & ImportResult.RESULT_ERROR) == 0) { + String withWarnings; - case ImportResult.RESULT_PARTIAL_WITH_ERRORS: - str = "partial with errors"; + // Any warnings? + if ((resultType & ImportResult.RESULT_WITH_WARNINGS) > 0) { + duration = 0; color = Style.ORANGE; - break; - - case ImportResult.RESULT_FAIL_ERROR: - str = "fail error"; - color = Style.RED; - break; - - case ImportResult.RESULT_FAIL_NOTHING: - str = getString(R.string.no_keys_added_or_updated); + withWarnings = getResources().getString(R.string.import_with_warnings); + } else { + duration = SuperToast.Duration.LONG; + color = Style.GREEN; + withWarnings = ""; + } + + // New and updated keys + if (result.isOkBoth()) { + str = getResources().getQuantityString( + R.plurals.import_keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); + str += getResources().getQuantityString( + R.plurals.import_keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); + } else if (result.isOkNew()) { + str = getResources().getQuantityString( + R.plurals.import_keys_added, result.mNewKeys, result.mNewKeys, withWarnings); + } else if (result.isOkUpdated()) { + str = getResources().getQuantityString( + R.plurals.import_keys_updated, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); + } else { + duration = 0; color = Style.RED; - break; - + str = "internal error"; + } + + } else { + duration = 0; + color = Style.RED; + if (result.isFailNothing()) { + str = getString(R.string.import_error_nothing); + } else { + str = getString(R.string.import_error); + } } + SuperCardToast toast = new SuperCardToast(ImportKeysActivity.this, SuperToast.Type.BUTTON, Style.getStyle(color, SuperToast.Animations.POPUP)); toast.setText(str); @@ -434,9 +433,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setIndeterminate(duration == 0); toast.setSwipeToDismiss(true); toast.setButtonIcon(R.drawable.ic_action_view_as_list, - getResources().getString(R.string.view_log)); - toast.setButtonTextColor(R.color.emphasis_dark); - toast.setTextColor(R.color.emphasis_dark); + getResources().getString(R.string.import_view_log)); + toast.setButtonTextColor(getResources().getColor(R.color.black)); + toast.setTextColor(getResources().getColor(R.color.black)); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", new SuperToast.OnClickListener() { @Override diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml index e2dfa196b..3485846ad 100644 --- a/OpenKeychain/src/main/res/values-de/strings.xml +++ b/OpenKeychain/src/main/res/values-de/strings.xml @@ -206,23 +206,23 @@ Es wurde eine leere Identität hinzugefügt. Wirklich fortfahren? Soll der öffentliche Schlüssel \'%s\' wirklich gelöscht werden?\nDies kann nicht rückgängig gemacht werden! Private Schlüssel auch exportieren - + %d Schlüssel erfolgreich hinzugefügt %d Schlüssel erfolgreich hinzugefügt - + und %d Schlüssel erfolgreich aktualisiert. und %d Schlüssel erfolgreich aktualisiert. - + %d Schlüssel erfolgreich hinzugefügt. %d Schlüssel erfolgreich hinzugefügt. - + %d Schlüssel erfolgreich aktualisiert. %d Schlüssel erfolgreich aktualisiert. - Keine Schlüssel hinzugefügt oder aktualisiert. + Keine Schlüssel hinzugefügt oder aktualisiert. 1 Schlüssel erfolgreich exportiert. %d Schlüssel erfolgreich exportiert. Keine Schlüssel exportiert. diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml index 45d3d565b..f1e4e347d 100644 --- a/OpenKeychain/src/main/res/values-es/strings.xml +++ b/OpenKeychain/src/main/res/values-es/strings.xml @@ -206,23 +206,23 @@ Ha añadido una identidad vacía, ¿está seguro de que quiere continuar? ¿De veras quiere borrar la clave pública \'%s\'?\n¡No puede deshacer esto! ¿Exportar también las claves secretas? - + %d clave añadida satisfactoriamente %d claves añadidas satisfactoriamente - + y actualizada %d clave. y actualizadas %d claves. - + %d clave añadida satisfactoriamente. %d claves añadidas satisfactoriamente. - + %d clave actualizada satisfactoriamente. %d claves actualizadas satisfactoriamente. - No se han añadido o actualizado claves. + No se han añadido o actualizado claves. Se ha exportado 1 clave satisfactoriamente. %d claves exportadas satisfactoriamente. No se han exportado claves. diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml index f49127b6f..55a85fb9b 100644 --- a/OpenKeychain/src/main/res/values-fr/strings.xml +++ b/OpenKeychain/src/main/res/values-fr/strings.xml @@ -206,23 +206,23 @@ Vous avez ajouté une identité vide, êtes-vous certain de vouloir continuer ? Voulez-vous vraiment supprimer la clef publique %s ?\nCeci est irréversible ! Exporter aussi les clefs secrètes ? - + %d clef ajoutée avec succès %d clefs ajoutées avec succès - + et %d clef mise à jour. et %d clefs mises à jour. - + %d clef ajoutée avec succès. %d clefs ajoutées avec succès. - + %d clef mise à jour avec succès. %d clefs mises à jour avec succès. - Aucune clef ajoutée ou mise à jour. + Aucune clef ajoutée ou mise à jour. 1 clef exportée avec succès. %d clefs exportées avec succès. Aucune clef exportée. diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml index eae4dd4af..300627fa7 100644 --- a/OpenKeychain/src/main/res/values-it/strings.xml +++ b/OpenKeychain/src/main/res/values-it/strings.xml @@ -206,23 +206,23 @@ Hai aggiunto una identità vuota, sei sicuro di voler continuare? Vuoi veramente eliminare la chiave pubblica \'%s\'?\nNon potrai annullare! Esportare anche le chiavi segrete? - + %d chiave aggiunta correttamente %d chiavi aggiunte correttamente - + e %d chiave aggiornata. e %d chiavi aggiornate. - + %d chiave aggiunta correttamente. %d chiavi aggiunte correttamente. - + %d chiave aggiornata correttamente. %d chiavi aggiornate correttamente. - Nessuna chiave aggiunta o aggiornata. + Nessuna chiave aggiunta o aggiornata. 1 chiave esportata correttamente. %d chiavi esportate correttamente. Nessuna chiave esportata. diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml index c40e9dbdc..63fef2af2 100644 --- a/OpenKeychain/src/main/res/values-ja/strings.xml +++ b/OpenKeychain/src/main/res/values-ja/strings.xml @@ -203,19 +203,19 @@ あなたは空のユーザIDを追加しました、このまま続けますか? 公開鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません! 秘密鍵もエクスポートしますか? - + %d の鍵を追加しました - + そして %d の鍵をアップロードしました。 - + %d の鍵を追加しました。 - + %d の鍵をアップロードしました。 - 鍵の追加もしくは更新はありませんでした。 + 鍵の追加もしくは更新はありませんでした。 1つの鍵をエクスポートしました。 %d の鍵をエクスポートしました。 鍵をエクスポートしていません。 diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml index d35d83517..f75d7a166 100644 --- a/OpenKeychain/src/main/res/values-nl/strings.xml +++ b/OpenKeychain/src/main/res/values-nl/strings.xml @@ -206,23 +206,23 @@ U heeft een lege identiteit toegevoegd, weet u zeker dat u wilt doorgaan? Wilt u echt de publieke sleutel \'%s\' verwijderen?\nDit kunt u niet ongedaan maken! Ook geheime sleutels exporteren? - + Succesvol %d sleutel toegevoegd Succesvol %d sleutels toegevoegd - + en %d sleutel bijgewerkt. en %d sleutels bijgewerkt. - + Succesvol %d sleutel toegevoegd. Succesvol %d sleutels toegevoegd. - + Succesvol %d sleutel bijgewerkt. Succesvol %d sleutels bijgewerkt. - Geen sleutels toegevoegd of bijgewerkt. + Geen sleutels toegevoegd of bijgewerkt. 1 sleutel succesvol geëxporteerd. Succesvol %d sleutels geëxporteerd. Geen sleutels geëxporteerd. diff --git a/OpenKeychain/src/main/res/values-pl/strings.xml b/OpenKeychain/src/main/res/values-pl/strings.xml index d1b7de393..851e77c3a 100644 --- a/OpenKeychain/src/main/res/values-pl/strings.xml +++ b/OpenKeychain/src/main/res/values-pl/strings.xml @@ -191,27 +191,27 @@ Zostały dokonane zmiany w pęku kluczy, czy chcesz je zachować? Czy na pewno chcesz usunąć klucz publiczny \'%s\'?\nNie można cofnąć tej operacji! Czy wyeksportować również klucze prywatne? - + Pomyślnie dodano %d klucz Pomyślnie dodano %d kluczy Pomyślnie dodano %d kluczy - + i zaktualizowano %d klucz. i zaktualizowano %d kluczy. i zaktualizowano %d kluczy. - + Pomyślnie dodano %d klucz. Pomyślnie dodano %d kluczy. Pomyślnie dodano %d kluczy. - + Pomyślnie zaktualizowano %d klucz. Pomyślnie zaktualizowano %d kluczy. Pomyślnie zaktualizowano %d kluczy. - Nie dodano ani zaktualizowano żadnych kluczy. + Nie dodano ani zaktualizowano żadnych kluczy. Pomyślnie wyeksportowano 1 klucz. Pomyślnie wyeksportowano %d kluczy. Nie wyeksportowano żadnych kluczy. diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml index 0becea0bc..b108324d1 100644 --- a/OpenKeychain/src/main/res/values-ru/strings.xml +++ b/OpenKeychain/src/main/res/values-ru/strings.xml @@ -206,27 +206,27 @@ Вы добавили пустой идентификатор. Вы уверены, что хотите продолжить? Вы правда хотите удалить публичный ключ \'%s\'?\nЭто действие нельзя отменить! Экспортировать секретные ключи? - + Успешно добавлено %d ключ Успешно добавлено %d ключей Успешно добавлено %d ключей - + и обновлен %d ключ. и обновлено %d ключей. и обновлено %d ключей. - + Добавлен %d ключ Добавлено %d ключей Добавлено %d ключей - + Обновлен %d ключ. Обновлено %d ключей. Обновлено %d ключей. - Нет обновленных или добавленных ключей + Нет обновленных или добавленных ключей Успешный экспорт 1 ключа. Экспортировано %d ключей. Ключи не были экспортированы. diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml index 8b12cdebe..0fe44725b 100644 --- a/OpenKeychain/src/main/res/values-sl/strings.xml +++ b/OpenKeychain/src/main/res/values-sl/strings.xml @@ -212,31 +212,31 @@ Dodali ste prazno identiteto, ali res želite nadaljevati? Ali res želite izbrisati javni ključ \'%s\'?\nTega koraka ne boste mogli preklicati! Želite izvoziti tudi zasebne ključe? - + Uspešno dodan %d ključ Uspešno dodana %d ključa Uspešno dodani %d ključi Uspešno dodanih %d ključev - + in posodbljen %d. in posodobljena %d. in posodobljeni %d. in posodobljenih %d. - + Uspešno dodan %d ključ. Uspešno dodana %d ključa. Uspešno dodani %d ključi. Uspešno dodanih %d ključev. - + Uspešno posodobljen %d ključ. Uspešno posodobljena %d ključa. Uspešno posodobljeni %d ključi. Uspešno posodobljenih %d ključev. - Noben ključ ni bil dodan ali posodobljen. + Noben ključ ni bil dodan ali posodobljen. Uspešno izvožen 1 ključ. Uspešno izvoženih ključev: %d Noben ključ ni bil izvožen. diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml index b27b6ffd3..2951d13f8 100644 --- a/OpenKeychain/src/main/res/values-uk/strings.xml +++ b/OpenKeychain/src/main/res/values-uk/strings.xml @@ -209,27 +209,27 @@ Ви вже додали порожню сутність. Ви справді хочете продовжити? Ви справді хочете вилучити відкритий ключ \'%s\'?\nВи не зможете це відмінити! Також експортувати секретні ключі? - + Успішно додано %d ключ Успішно додано %d ключі Успішно додано %d ключів - + і оновлено %d ключ. і оновлено %d ключі. і оновлено %d ключів. - + Успішно додано %d ключ. Успішно додано %d ключі. Успішно додано %d ключів. - + Успішно оновлено %d ключ. Успішно оновлено %d ключі. Успішно оновлено %d ключів. - Жодного ключа не додано та не оновлено. + Жодного ключа не додано та не оновлено. Успішно експортовано 1 ключ. Успішно експортовано %d ключів. Жодного ключа не експортовано. diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index b60e281ee..8ef93551b 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -221,25 +221,6 @@ Do you really want to delete the public key \'%s\'?\nYou can\'t undo this! Also export secret keys? - - Successfully added %1$d key - Successfully added %1$d keys - - - and updated %1$d key%2$s. - and updated %1$d keys%2$s. - - - Successfully added %1$d key%2$s. - Successfully added %1$d keys%2$s. - - - Successfully updated %1$d key%2$s. - Successfully updated %1$d keys%2$s. - - - No keys added or updated. - , with warnings Successfully exported 1 key. Successfully exported %d keys. No keys exported. @@ -408,6 +389,28 @@ Get key from clipboard Get key from Keybase.io + + + Successfully added %1$d key + Successfully added %1$d keys + + + and updated %1$d key%2$s. + and updated %1$d keys%2$s. + + + Successfully added %1$d key%2$s. + Successfully added %1$d keys%2$s. + + + Successfully updated %1$d key%2$s. + Successfully updated %1$d keys%2$s. + + View Log + Nothing to import. + Error importing keys! + , with warnings + Decrypt File with OpenKeychain Import Key with OpenKeychain @@ -579,6 +582,5 @@ cannot sign Encoding error No encryption subkey available! - View Log -- cgit v1.2.3 From 3895c10a5821a3ce11946c4cd88298c0b7410d5f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 20:06:28 +0200 Subject: import-log: work on log fragment ui --- .../keychain/provider/ProviderHelper.java | 2 +- .../keychain/service/OperationResultParcel.java | 6 +- .../keychain/ui/LogDisplayActivity.java | 4 + .../keychain/ui/LogDisplayFragment.java | 108 ++++++++++++++++++--- .../src/main/res/layout/log_display_item.xml | 10 +- OpenKeychain/src/main/res/values/strings.xml | 4 +- 6 files changed, 113 insertions(+), 21 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 00db473b3..f83ea24df 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -553,8 +553,8 @@ public class ProviderHelper { mIndent -= 1; } - log(LogLevel.OK, LogType.MSG_IP_SUCCESS); mIndent -= 1; + log(LogLevel.OK, LogType.MSG_IP_SUCCESS); return new SaveKeyringResult(result, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 8a3b06f67..4c288502c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -173,12 +173,12 @@ public class OperationResultParcel implements Parcelable { /** Enumeration of possible log levels. */ public static enum LogLevel { - START, // should occur once at the start of each independent operation - OK, // should occur once at the end of a successful operation - ERROR, // should occur once at the end of a failed operation DEBUG, INFO, WARN, + ERROR, // should occur once at the end of a failed operation + START, // should occur once at the start of each independent operation + OK, // should occur once at the end of a successful operation } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java index 28c120d01..a0d449195 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -1,7 +1,11 @@ package org.sufficientlysecure.keychain.ui; import android.os.Bundle; +import android.support.v4.view.GestureDetectorCompat; import android.support.v7.app.ActionBarActivity; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.MotionEvent; import org.sufficientlysecure.keychain.R; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 275c9ac18..980ee5e4a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -5,22 +5,40 @@ import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.support.v4.view.GestureDetectorCompat; +import android.support.v4.view.MotionEventCompat; import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.Filterable; +import android.widget.ImageView; import android.widget.TextView; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogEntryParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; +import java.util.HashMap; -public class LogDisplayFragment extends ListFragment { +public class LogDisplayFragment extends ListFragment implements OnTouchListener { + HashMap mAdapters = new HashMap(); LogAdapter mAdapter; + LogLevel mLevel = LogLevel.DEBUG; + + OperationResultParcel mResult; + + GestureDetector mDetector; public static final String EXTRA_RESULT = "log"; @@ -34,15 +52,68 @@ public class LogDisplayFragment extends ListFragment { return; } - OperationResultParcel result = intent.getParcelableExtra(EXTRA_RESULT); - if (result == null) { + mResult = intent.getParcelableExtra(EXTRA_RESULT); + if (mResult == null) { getActivity().finish(); return; } - mAdapter = new LogAdapter(getActivity(), result.getLog()); + mAdapter = new LogAdapter(getActivity(), mResult.getLog(), LogLevel.DEBUG); + mAdapters.put(LogLevel.DEBUG, mAdapter); setListAdapter(mAdapter); + mDetector = new GestureDetector(getActivity(), new SimpleOnGestureListener() { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float vx, float vy) { + Log.d(Constants.TAG, "x: " + vx + ", y: " + vy); + if (vx < -2000) { + decreaseLogLevel(); + } else if (vx > 2000) { + increaseLogLevel(); + } + return true; + } + }); + + } + + public void decreaseLogLevel() { + switch (mLevel) { + case DEBUG: mLevel = LogLevel.INFO; break; + case INFO: mLevel = LogLevel.WARN; break; + } + refreshLevel(); + } + + public void increaseLogLevel() { + switch (mLevel) { + case INFO: mLevel = LogLevel.DEBUG; break; + case WARN: mLevel = LogLevel.INFO; break; + } + refreshLevel(); + } + + private void refreshLevel() { + /* TODO not sure if this is a good idea + if (!mAdapters.containsKey(mLevel)) { + mAdapters.put(mLevel, new LogAdapter(getActivity(), mResult.getLog(), mLevel)); + } + mAdapter = mAdapters.get(mLevel); + setListAdapter(mAdapter); + */ + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getListView().setDividerHeight(0); + getListView().setOnTouchListener(this); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + mDetector.onTouchEvent(event); + return false; } private class LogAdapter extends ArrayAdapter { @@ -50,12 +121,18 @@ public class LogDisplayFragment extends ListFragment { private LayoutInflater mInflater; private int dipFactor; - public LogAdapter(Context context, ArrayList log) { - super(context, R.layout.log_display_item, log); + public LogAdapter(Context context, ArrayList log, LogLevel level) { + super(context, R.layout.log_display_item); mInflater = LayoutInflater.from(getContext()); dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - (float) 10, getResources().getDisplayMetrics()); - + (float) 8, getResources().getDisplayMetrics()); + // we can't use addAll for a LogLevel.DEBUG shortcut here, unfortunately :( + for (LogEntryParcel e : log) { + if (e.mLevel.ordinal() >= level.ordinal()) { + add(e); + } + } + notifyDataSetChanged(); } @Override @@ -69,15 +146,18 @@ public class LogDisplayFragment extends ListFragment { } else { text = (TextView) convertView.getTag(); } + ImageView img = (ImageView) convertView.findViewById(R.id.log_img); - text.setPadding(entry.mIndent*dipFactor, 0, 0, 0); text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); + text.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK); + convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0); switch (entry.mLevel) { - case OK: text.setTextColor(Color.GREEN); break; - case DEBUG: text.setTextColor(Color.GRAY); break; - case INFO: text.setTextColor(Color.BLACK); break; - case WARN: text.setTextColor(Color.YELLOW); break; - case ERROR: text.setTextColor(Color.RED); break; + case DEBUG: img.setBackgroundColor(Color.GRAY); break; + case INFO: img.setBackgroundColor(Color.BLACK); break; + case WARN: img.setBackgroundColor(Color.YELLOW); break; + case ERROR: img.setBackgroundColor(Color.RED); break; + case START: img.setBackgroundColor(Color.GREEN); break; + case OK: img.setBackgroundColor(Color.GREEN); break; } return convertView; diff --git a/OpenKeychain/src/main/res/layout/log_display_item.xml b/OpenKeychain/src/main/res/layout/log_display_item.xml index f0e0e4ecb..35489afed 100644 --- a/OpenKeychain/src/main/res/layout/log_display_item.xml +++ b/OpenKeychain/src/main/res/layout/log_display_item.xml @@ -4,11 +4,19 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> + + + android:layout_marginBottom="4dp" + android:layout_marginLeft="8dp" /> \ No newline at end of file diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 8ef93551b..45aa88433 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -530,7 +530,7 @@ Subkey flags: sign Subkey flags: none Subkey creation date lies in the future! (%s) - Successfully inserted public keyring + Successfully imported public keyring Re-inserting secret key Retrieving trusted keys Using %s trusted keys @@ -556,7 +556,7 @@ Subkey %s unavailable in public key Marked %s as available Marked %s as stripped - Successfully inserted secret keyring + Successfully imported secret keyring Canonicalizing keyring %s -- cgit v1.2.3 From 8d75d3e00e02527c4bc717cbcd937b1457eb4bda Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 11 Jun 2014 00:37:23 +0200 Subject: import-log: use holder pattern in log fragment --- .../keychain/ui/LogDisplayFragment.java | 40 +++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 980ee5e4a..496e98c18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -5,8 +5,6 @@ import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.ListFragment; -import android.support.v4.view.GestureDetectorCompat; -import android.support.v4.view.MotionEventCompat; import android.util.TypedValue; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; @@ -16,7 +14,6 @@ import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; -import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; @@ -135,29 +132,40 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener notifyDataSetChanged(); } + private class ItemHolder { + final TextView mText; + final ImageView mImg; + public ItemHolder(TextView text, ImageView image) { + mText = text; + mImg = image; + } + } + @Override public View getView(int position, View convertView, ViewGroup parent) { LogEntryParcel entry = getItem(position); - TextView text; + ItemHolder ih; if (convertView == null) { convertView = mInflater.inflate(R.layout.log_display_item, parent, false); - text = (TextView) convertView.findViewById(R.id.log_text); - convertView.setTag(text); + ih = new ItemHolder( + (TextView) convertView.findViewById(R.id.log_text), + (ImageView) convertView.findViewById(R.id.log_img) + ); + convertView.setTag(ih); } else { - text = (TextView) convertView.getTag(); + ih = (ItemHolder) convertView.getTag(); } - ImageView img = (ImageView) convertView.findViewById(R.id.log_img); - text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); - text.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK); + ih.mText.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); + ih.mText.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK); convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0); switch (entry.mLevel) { - case DEBUG: img.setBackgroundColor(Color.GRAY); break; - case INFO: img.setBackgroundColor(Color.BLACK); break; - case WARN: img.setBackgroundColor(Color.YELLOW); break; - case ERROR: img.setBackgroundColor(Color.RED); break; - case START: img.setBackgroundColor(Color.GREEN); break; - case OK: img.setBackgroundColor(Color.GREEN); break; + case DEBUG: ih.mImg.setBackgroundColor(Color.GRAY); break; + case INFO: ih.mImg.setBackgroundColor(Color.BLACK); break; + case WARN: ih.mImg.setBackgroundColor(Color.YELLOW); break; + case ERROR: ih.mImg.setBackgroundColor(Color.RED); break; + case START: ih.mImg.setBackgroundColor(Color.GREEN); break; + case OK: ih.mImg.setBackgroundColor(Color.GREEN); break; } return convertView; -- cgit v1.2.3 From 47368f1d24cb3b4112133a6dddd2793e6787dfdd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 01:37:49 +0200 Subject: import-log: better way to check self trust, and fix log level filtering --- .../keychain/provider/ProviderHelper.java | 18 +++++------------- .../keychain/service/OperationResultParcel.java | 4 +--- OpenKeychain/src/main/res/values/strings.xml | 1 - 3 files changed, 6 insertions(+), 17 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index f83ea24df..8d95da8b0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -399,21 +399,12 @@ public class ProviderHelper { } mIndent -= 1; - log(LogLevel.DEBUG, LogType.MSG_IP_TRUST_RETRIEVE); // get a list of owned secret keys, for verification filtering LongSparseArray trustedKeys = getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); - // special case: available secret keys verify themselves! - if (secretRing != null) { - trustedKeys.put(secretRing.getMasterKeyId(), secretRing.getPublicKey()); - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING_SEC, new String[]{ - Integer.toString(trustedKeys.size()) - }); - } else { - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { - Integer.toString(trustedKeys.size()) - }); - } + log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { + Integer.toString(trustedKeys.size()) + }); // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. @@ -513,7 +504,8 @@ public class ProviderHelper { // no self cert is bad, but allowed by the rfc... if (item.selfCert != null) { operations.add(buildCertOperations( - masterKeyId, userIdRank, item.selfCert, Certs.VERIFIED_SELF)); + masterKeyId, userIdRank, item.selfCert, + secretRing != null ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); } // don't bother with trusted certs if the uid is revoked, anyways if (item.isRevoked) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 4c288502c..473cf8244 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -133,7 +133,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), - MSG_IP_TRUST_USING_SEC (R.string.msg_ip_trust_using_sec), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), @@ -210,9 +209,8 @@ public class OperationResultParcel implements Parcelable { } public boolean containsWarnings() { - int warn = LogLevel.WARN.ordinal(); for(LogEntryParcel entry : new IterableIterator(iterator())) { - if (entry.mLevel.ordinal() >= warn) { + if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) { return true; } } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 45aa88433..367fdb599 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -534,7 +534,6 @@ Re-inserting secret key Retrieving trusted keys Using %s trusted keys - Secret key available, self certificates are trusted Encountered bad certificate! Error processing certificate! Found good certificate from %s -- cgit v1.2.3 From dea98a4a7e3143acfc01ce1567a9d17c25025b4d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 01:52:41 +0200 Subject: import-log: properly distinguish return states --- .../keychain/pgp/PgpImportExport.java | 17 +++++++---------- .../keychain/provider/ProviderHelper.java | 18 ++++++++++-------- .../keychain/service/OperationResultParcel.java | 4 ++-- .../keychain/service/OperationResults.java | 6 +++--- .../keychain/ui/ImportKeysActivity.java | 6 +++--- 5 files changed, 25 insertions(+), 26 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index bafb086d0..bb45cc7db 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -58,10 +58,6 @@ public class PgpImportExport { private ProviderHelper mProviderHelper; - public static final int RETURN_OK = 0; - public static final int RETURN_BAD = -2; - public static final int RETURN_UPDATED = 1; - public PgpImportExport(Context context, Progressable progressable) { super(); this.mContext = context; @@ -118,10 +114,9 @@ public class PgpImportExport { if (aos != null) { aos.close(); } - if (bos != null) { - bos.close(); - } + bos.close(); } catch (IOException e) { + // this is just a finally thing, no matter if it doesn't work out. } } } @@ -153,10 +148,12 @@ public class PgpImportExport { } SaveKeyringResult result = mProviderHelper.savePublicKeyRing(key); - if (result.updated()) { - newKeys += 1; - } else { + if (!result.success()) { + badKeys += 1; + } else if (result.updated()) { oldKeys += 1; + } else { + newKeys += 1; } } catch (PgpGeneralException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 8d95da8b0..40af285e1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -297,12 +297,12 @@ public class ProviderHelper { } // delete old version of this keyRing, which also deletes all keys and userIds on cascade - try { - mContentResolver.delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + int deleted = mContentResolver.delete( + KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + if (deleted > 0) { log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); result |= SaveKeyringResult.UPDATED; - } catch (UnsupportedOperationException e) { - Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); + } else { log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); } @@ -577,14 +577,16 @@ public class ProviderHelper { * is already in the database! */ public OperationResultParcel saveSecretKeyRing(UncachedKeyRing keyRing) { - if (!keyRing.isSecret()) { - log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); - return new OperationResultParcel(1, mLog); - } long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IS, new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}); + mIndent += 1; + + if (!keyRing.isSecret()) { + log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); + return new OperationResultParcel(1, mLog); + } // save secret keyring try { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 473cf8244..67ff8318b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -44,8 +44,8 @@ public class OperationResultParcel implements Parcelable { return mResult; } - public boolean isSuccessful() { - return (mResult & 1) == 1; + public boolean success() { + return (mResult & 1) == 0; } public OperationLog getLog() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index 342d07953..6c44b01f1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -25,13 +25,13 @@ public abstract class OperationResults { == (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED); } public boolean isOkNew() { - return (mResult & RESULT_OK_NEWKEYS) > 0; + return (mResult & RESULT_OK_NEWKEYS) == RESULT_OK_NEWKEYS; } public boolean isOkUpdated() { - return (mResult & RESULT_OK_UPDATED) > 0; + return (mResult & RESULT_OK_UPDATED) == RESULT_OK_UPDATED; } public boolean isFailNothing() { - return (mResult & RESULT_FAIL_NOTHING) > 0; + return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING; } public ImportResult(Parcel source) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 3ff4f9887..7fa18406c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -404,12 +404,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O R.plurals.import_keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); str += getResources().getQuantityString( R.plurals.import_keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); - } else if (result.isOkNew()) { - str = getResources().getQuantityString( - R.plurals.import_keys_added, result.mNewKeys, result.mNewKeys, withWarnings); } else if (result.isOkUpdated()) { str = getResources().getQuantityString( R.plurals.import_keys_updated, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); + } else if (result.isOkNew()) { + str = getResources().getQuantityString( + R.plurals.import_keys_added, result.mNewKeys, result.mNewKeys, withWarnings); } else { duration = 0; color = Style.RED; -- cgit v1.2.3 From 466eddb0051d8b19576b60be7e3769f13b308a57 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 15:36:35 +0200 Subject: canonicalize: implementation, first draft --- .../keychain/pgp/UncachedKeyRing.java | 294 +++++++++++++++++++-- .../keychain/pgp/WrappedSignature.java | 26 +- .../keychain/provider/ProviderHelper.java | 2 +- .../keychain/service/OperationResultParcel.java | 23 +- OpenKeychain/src/main/res/values/strings.xml | 21 +- 5 files changed, 337 insertions(+), 29 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 7853d0b00..215c17590 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -5,16 +5,17 @@ import org.spongycastle.bcpg.S2K; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -179,51 +180,300 @@ public class UncachedKeyRing { * * More specifically: * - Remove all non-verifying self-certificates - * - Remove all expired self-certificates * - Remove all certificates flagged as "local" * - Remove all certificates which are superseded by a newer one on the same target * - * After this cleaning, a number of checks are done: + * After this cleaning, a number of checks are done: TODO implement * - See if each subkey retains a valid self certificate * - See if each user id retains a valid self certificate * * This operation writes an OperationLog which can be used as part of a OperationResultParcel. * - * If any of these checks fail, the operation as a whole fails and the keyring is declared - * unusable. (TODO: allow forcing of import?) - * - * TODO implement - * * @return A canonicalized key * */ - public UncachedKeyRing canonicalize(OperationLog log) { - if(isSecret()) { + public UncachedKeyRing canonicalize(OperationLog log, int indent) { + if (isSecret()) { throw new RuntimeException("Tried to canonicalize non-secret keyring. " + "This is a programming error and should never happen!"); } - // dummy log.add(LogLevel.START, LogType.MSG_KC, - new String[] { PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()) }, 0); + new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); + indent += 1; + + int removedCerts = 0; + + PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; + PGPPublicKey masterKey = mRing.getPublicKey(); + final long masterKeyId = masterKey.getKeyID(); + + { + log.add(LogLevel.DEBUG, LogType.MSG_KC_MASTER, + new String[]{PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())}, indent); + indent += 1; + + PGPPublicKey modified = masterKey; + PGPSignature revocation = null; + for (PGPSignature zert : new IterableIterator(masterKey.getSignatures())) { + int type = zert.getSignatureType(); + // Disregard certifications on user ids, we will deal with those later + if (type == PGPSignature.NO_CERTIFICATION + || type == PGPSignature.DEFAULT_CERTIFICATION + || type == PGPSignature.CASUAL_CERTIFICATION + || type == PGPSignature.POSITIVE_CERTIFICATION + || type == PGPSignature.CERTIFICATION_REVOCATION) { + continue; + } + WrappedSignature cert = new WrappedSignature(zert); + + if (type != PGPSignature.KEY_REVOCATION) { + // Unknown type, just remove + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + "0x" + Integer.toString(type, 16) + }, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey)) { + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_ERR, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + // first revocation? fine then. + if (revocation == null) { + revocation = zert; + // more revocations? at least one is superfluous, then. + } else if (revocation.getCreationTime().before(zert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, revocation); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); + revocation = zert; + } else { + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); + } + } + + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + PGPSignature selfCert = null; + revocation = null; + + // look through signatures for this specific key + for (PGPSignature zert : new IterableIterator( + masterKey.getSignaturesForID(userId))) { + WrappedSignature cert = new WrappedSignature(zert); + long certId = cert.getKeyId(); + + // If this is a foreign signature, never mind + if (certId != masterKeyId) { + continue; + } + + // Otherwise, first make sure it checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey, userId)) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD, + new String[] { userId }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_ERR, + new String[] { userId }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + continue; + } + + switch (zert.getSignatureType()) { + case PGPSignature.DEFAULT_CERTIFICATION: + case PGPSignature.NO_CERTIFICATION: + case PGPSignature.CASUAL_CERTIFICATION: + case PGPSignature.POSITIVE_CERTIFICATION: + if (selfCert == null) { + selfCert = zert; + } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, selfCert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + new String[] { userId }, indent); + selfCert = zert; + } else { + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + new String[] { userId }, indent); + } + // If there is a revocation certificate, and it's older than this, drop it + if (revocation != null + && revocation.getCreationTime().before(selfCert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, revocation); + revocation = null; + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + new String[] { userId }, indent); + } + break; + + case PGPSignature.CERTIFICATION_REVOCATION: + // If this is older than the (latest) self cert, drop it + if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + new String[] { userId }, indent); + continue; + } + // first revocation? remember it. + if (revocation == null) { + revocation = zert; + // more revocations? at least one is superfluous, then. + } else if (revocation.getCreationTime().before(cert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, revocation); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + new String[] { userId }, indent); + revocation = zert; + } else { + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + new String[] { userId }, indent); + } + break; + + default: + log.add(LogLevel.WARN, LogType.MSG_KC_UID_UNKNOWN_CERT, + new String[] { + "0x" + Integer.toString(zert.getSignatureType(), 16), + userId + }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + } + + } + } + + // Replace modified key in the keyring + ring = PGPPublicKeyRing.insertPublicKey(ring, modified); - /* - // Remove all non-verifying self certificates - for (PGPPublicKey key : new IterableIterator(mRing.getPublicKeys())) { + log.add(LogLevel.DEBUG, LogType.MSG_KC_MASTER_SUCCESS, null, indent); + indent -= 1; + + } - for (PGPSignature sig : new IterableIterator( - key.getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION - : PGPSignature.SUBKEY_REVOCATION))) { - return true; + // Process all keys + // NOTE we rely here on the special property that this iterator is independent from the keyring! + for (PGPPublicKey key : new IterableIterator(ring.getPublicKeys())) { + // Only care about the master key here! + if (key.isMasterKey()) { + continue; + } + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY, + new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); + indent += 1; + // A subkey needs exactly one subkey binding certificate, and optionally one revocation + // certificate. + PGPPublicKey modified = key; + PGPSignature cert = null, revocation = null; + for (PGPSignature zig : new IterableIterator(key.getSignatures())) { + // remove from keyring (for now) + modified = PGPPublicKey.removeCertification(modified, zig); + WrappedSignature sig = new WrappedSignature(zig); + int type = sig.getSignatureType(); + + // filter out bad key types... + if (sig.getKeyId() != masterKey.getKeyID()) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_KEYID, null, indent); + continue; + } + if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + "0x" + Integer.toString(type, 16) + }, indent); + continue; + } + + // make sure the certificate checks out + try { + sig.init(masterKey); + if (!sig.verifySignature(masterKey, key)) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD, null, indent); + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_ERR, null, indent); + continue; + } + + if (type == PGPSignature.SUBKEY_BINDING) { + // TODO verify primary key binding signature for signing keys! + // if we already have a cert, and this one is not newer: skip it + if (cert != null && cert.getCreationTime().before(sig.getCreationTime())) { + continue; + } + cert = zig; + // if this is newer than a possibly existing revocation, drop that one + if (revocation != null && cert.getCreationTime().after(revocation.getCreationTime())) { + revocation = null; + } + // it must be a revocation, then (we made sure above) + } else { + // if there is no binding (yet), or the revocation is newer than the binding: keep it + if (cert == null || cert.getCreationTime().before(sig.getCreationTime())) { + revocation = zig; + } + } } - }*/ + // it is not properly bound? error! + if (cert == null) { + ring = PGPPublicKeyRing.removePublicKey(ring, modified); - log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, 0); + log.add(LogLevel.ERROR, LogType.MSG_KC_SUBKEY_NO_CERT, + new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); + indent -= 1; + continue; + } - return this; + // re-add certification + modified = PGPPublicKey.addCertification(modified, cert); + // add revocation, if any + if (revocation != null) { + modified = PGPPublicKey.addCertification(modified, revocation); + } + // replace pubkey in keyring + ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY_SUCCESS, null, indent); + indent -= 1; + } + + if (removedCerts > 0) { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_REMOVED, + new String[] { Integer.toString(removedCerts) }, indent); + } else { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, indent); + } + return new UncachedKeyRing(ring); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index 1b7a5e8ba..093b6c96f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -35,7 +35,7 @@ public class WrappedSignature { final PGPSignature mSig; - protected WrappedSignature(PGPSignature sig) { + WrappedSignature(PGPSignature sig) { mSig = sig; } @@ -88,7 +88,7 @@ public class WrappedSignature { init(key.getPublicKey()); } - protected void init(PGPPublicKey key) throws PgpGeneralException { + void init(PGPPublicKey key) throws PgpGeneralException { try { JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() @@ -125,7 +125,27 @@ public class WrappedSignature { } } - protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException { + boolean verifySignature(PGPPublicKey key) throws PgpGeneralException { + try { + return mSig.verifyCertification(key); + } catch (SignatureException e) { + throw new PgpGeneralException("Sign!", e); + } catch (PGPException e) { + throw new PgpGeneralException("Error!", e); + } + } + + boolean verifySignature(PGPPublicKey masterKey, PGPPublicKey subKey) throws PgpGeneralException { + try { + return mSig.verifyCertification(masterKey, subKey); + } catch (SignatureException e) { + throw new PgpGeneralException("Sign!", e); + } catch (PGPException e) { + throw new PgpGeneralException("Error!", e); + } + } + + boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException { try { return mSig.verifyCertification(uid, key); } catch (SignatureException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 40af285e1..6c004f19a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -283,7 +283,7 @@ public class ProviderHelper { mIndent += 1; // Canonicalize this key, to assert a number of assumptions made about it. - keyRing = keyRing.canonicalize(mLog); + keyRing = keyRing.canonicalize(mLog, mIndent); UncachedPublicKey masterKey = keyRing.getPublicKey(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 67ff8318b..48f40026e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -157,8 +157,27 @@ public class OperationResultParcel implements Parcelable { MSG_IS_SUCCESS (R.string.msg_is_success), // keyring canonicalization - MSG_KC(R.string.msg_kc), - MSG_KC_SUCCESS(R.string.msg_kc_success), + MSG_KC (R.string.msg_kc), + MSG_KC_CERT_BAD_ERR (R.string.msg_kc_cert_bad_err), + MSG_KC_CERT_BAD_KEYID (R.string.msg_kc_cert_bad_keyid), + MSG_KC_CERT_BAD (R.string.msg_kc_cert_bad), + MSG_KC_CERT_BAD_TYPE (R.string.msg_kc_cert_bad_type), + MSG_KC_MASTER (R.string.msg_kc_master), + MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), + MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), + MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), + MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), + MSG_KC_SUBKEY_NO_CERT (R.string.msg_kc_subkey_no_cert), + MSG_KC_SUBKEY (R.string.msg_kc_subkey), + MSG_KC_SUBKEY_SUCCESS (R.string.msg_kc_subkey_success), + MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), + MSG_KC_SUCCESS (R.string.msg_kc_success), + MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), + MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), + MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), + MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), + MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), + MSG_KC_UID_UNKNOWN_CERT (R.string.msg_kc_uid_unknown_cert), ; private final int mMsgId; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 367fdb599..27568132b 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -559,7 +559,26 @@ Canonicalizing keyring %s - Successfully canonicalized keyring + Certificate verification failed! + Certificate verification failed with an error! + Certificate issuer id mismatch + Unknown certificate type: %s + Processing master key + OK + Removing bad keyring revocation key + Removing bad keyring revocation key + Removing redundant keyring revocation key + No valid certificate found for %s, removing from ring! + Processing subkey %s + OK + Keyring canonicalization successful + Keyring canonicalization successful, removed %s certificates + Removing bad self certificate for user id %s + Removing bad self certificate for user id "%s" + Removing outdated self certificate for user id "%s" + Removing redundant revocation certificate for user id "%s" + Removing outdated revocation certificate for user id "%s" + Removing unknown certificate of type 0x%1$s from user id "%2$s" Certifier -- cgit v1.2.3 From dae503284f47eb7e5eed71140f9fceaa2ff420c2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 17:38:48 +0200 Subject: canonicalize: more stuff --- .../keychain/keyimport/ParcelableKeyRing.java | 2 +- .../keychain/pgp/UncachedKeyRing.java | 79 +++++++++++++--------- .../keychain/service/OperationResultParcel.java | 17 +++-- .../keychain/ui/ImportKeysActivity.java | 2 + OpenKeychain/src/main/res/values/strings.xml | 17 +++-- 5 files changed, 70 insertions(+), 47 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java index 5da6c4cd3..fdf561aaf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java @@ -3,7 +3,7 @@ package org.sufficientlysecure.keychain.keyimport; import android.os.Parcel; import android.os.Parcelable; -/** This is a trivial wrapper around UncachedKeyRing which implements Parcelable. It exists +/** This is a trivial wrapper around keyring bytes which implements Parcelable. It exists * for the sole purpose of keeping spongycastle and android imports in separate packages. */ public class ParcelableKeyRing implements Parcelable { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 215c17590..a8e4820cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -15,7 +15,6 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; -import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -24,7 +23,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -229,7 +227,7 @@ public class UncachedKeyRing { if (type != PGPSignature.KEY_REVOCATION) { // Unknown type, just remove - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); modified = PGPPublicKey.removeCertification(modified, zert); @@ -380,81 +378,98 @@ public class UncachedKeyRing { } // Process all keys - // NOTE we rely here on the special property that this iterator is independent from the keyring! for (PGPPublicKey key : new IterableIterator(ring.getPublicKeys())) { - // Only care about the master key here! + // Don't care about the master key here, that one gets special treatment above if (key.isMasterKey()) { continue; } - log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY, + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB, new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); indent += 1; // A subkey needs exactly one subkey binding certificate, and optionally one revocation // certificate. PGPPublicKey modified = key; - PGPSignature cert = null, revocation = null; + PGPSignature selfCert = null, revocation = null; for (PGPSignature zig : new IterableIterator(key.getSignatures())) { // remove from keyring (for now) modified = PGPPublicKey.removeCertification(modified, zig); - WrappedSignature sig = new WrappedSignature(zig); - int type = sig.getSignatureType(); + WrappedSignature cert = new WrappedSignature(zig); + int type = cert.getSignatureType(); // filter out bad key types... - if (sig.getKeyId() != masterKey.getKeyID()) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_KEYID, null, indent); + if (cert.getKeyId() != masterKey.getKeyID()) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_KEYID, null, indent); continue; } if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); continue; } - // make sure the certificate checks out - try { - sig.init(masterKey); - if (!sig.verifySignature(masterKey, key)) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD, null, indent); + if (type == PGPSignature.SUBKEY_BINDING) { + // TODO verify primary key binding signature for signing keys! + + // make sure the certificate checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey, key)) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD, null, indent); + log.add(LogLevel.WARN, LogType.MSG_KC_SUB, new String[] { + cert.getCreationTime().toString() + }, indent); + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_ERR, null, indent); continue; } - } catch (PgpGeneralException e) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_ERR, null, indent); - continue; - } - if (type == PGPSignature.SUBKEY_BINDING) { - // TODO verify primary key binding signature for signing keys! // if we already have a cert, and this one is not newer: skip it - if (cert != null && cert.getCreationTime().before(sig.getCreationTime())) { + if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) { continue; } - cert = zig; + selfCert = zig; // if this is newer than a possibly existing revocation, drop that one - if (revocation != null && cert.getCreationTime().after(revocation.getCreationTime())) { + if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { revocation = null; } - // it must be a revocation, then (we made sure above) + + // it must be a revocation, then (we made sure above) } else { + + // make sure the certificate checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(key)) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, null, indent); + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD_ERR, null, indent); + continue; + } + // if there is no binding (yet), or the revocation is newer than the binding: keep it - if (cert == null || cert.getCreationTime().before(sig.getCreationTime())) { + if (selfCert == null || selfCert.getCreationTime().before(cert.getCreationTime())) { revocation = zig; } } } // it is not properly bound? error! - if (cert == null) { + if (selfCert == null) { ring = PGPPublicKeyRing.removePublicKey(ring, modified); - log.add(LogLevel.ERROR, LogType.MSG_KC_SUBKEY_NO_CERT, + log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT, new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); indent -= 1; continue; } // re-add certification - modified = PGPPublicKey.addCertification(modified, cert); + modified = PGPPublicKey.addCertification(modified, selfCert); // add revocation, if any if (revocation != null) { modified = PGPPublicKey.addCertification(modified, revocation); @@ -462,7 +477,7 @@ public class UncachedKeyRing { // replace pubkey in keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); - log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY_SUCCESS, null, indent); + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_SUCCESS, null, indent); indent -= 1; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 48f40026e..5c223e870 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -158,18 +158,21 @@ public class OperationResultParcel implements Parcelable { // keyring canonicalization MSG_KC (R.string.msg_kc), - MSG_KC_CERT_BAD_ERR (R.string.msg_kc_cert_bad_err), - MSG_KC_CERT_BAD_KEYID (R.string.msg_kc_cert_bad_keyid), - MSG_KC_CERT_BAD (R.string.msg_kc_cert_bad), - MSG_KC_CERT_BAD_TYPE (R.string.msg_kc_cert_bad_type), MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), - MSG_KC_SUBKEY_NO_CERT (R.string.msg_kc_subkey_no_cert), - MSG_KC_SUBKEY (R.string.msg_kc_subkey), - MSG_KC_SUBKEY_SUCCESS (R.string.msg_kc_subkey_success), + MSG_KC_SUB (R.string.msg_kc_sub), + MSG_KC_SUB_BAD_ERR(R.string.msg_kc_sub_bad_err), + MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), + MSG_KC_SUB_BAD(R.string.msg_kc_sub_bad), + MSG_KC_SUB_BAD_TYPE(R.string.msg_kc_sub_bad_type), + MSG_KC_SUB_NO_CERT(R.string.msg_kc_sub_no_cert), + MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), + MSG_KC_SUB_REVOKE_BAD (R.string.msg_kc_sub_revoke_bad), + MSG_KC_SUB_REVOKE_DUP (R.string.msg_kc_sub_revoke_dup), + MSG_KC_SUB_SUCCESS (R.string.msg_kc_sub_success), MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), MSG_KC_SUCCESS (R.string.msg_kc_success), MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 7fa18406c..f389726ff 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -456,10 +456,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O } */ + /* if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { ImportKeysActivity.this.setResult(Activity.RESULT_OK, mPendingIntentData); finish(); } + */ } } }; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 27568132b..bcf66d58f 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -559,18 +559,21 @@ Canonicalizing keyring %s - Certificate verification failed! - Certificate verification failed with an error! - Certificate issuer id mismatch - Unknown certificate type: %s Processing master key OK Removing bad keyring revocation key Removing bad keyring revocation key Removing redundant keyring revocation key - No valid certificate found for %s, removing from ring! - Processing subkey %s - OK + Processing subkey %s + Subkey binding certificate verification failed! + Subkey binding certificate verification failed with an error! + Subkey binding issuer id mismatch + Unknown subkey certificate type: %s + No valid certificate found for %s, removing from ring! + Removing bad subkey revocation key + Removing bad subkey revocation key + Removing redundant keyring revocation key + OK Keyring canonicalization successful Keyring canonicalization successful, removed %s certificates Removing bad self certificate for user id %s -- cgit v1.2.3 From e4a7d4f6e5dc6eb0acac2aa4945852ae2f1d8bb8 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 18:10:48 +0200 Subject: import-log: minor improvements --- .../keychain/pgp/PgpImportExport.java | 11 +- .../keychain/pgp/UncachedKeyRing.java | 3 - .../keychain/provider/ProviderHelper.java | 113 ++++++++++++--------- .../keychain/service/OperationResultParcel.java | 2 + OpenKeychain/src/main/res/values/strings.xml | 4 +- 5 files changed, 77 insertions(+), 56 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index bb45cc7db..e1967429a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -127,9 +127,7 @@ public class PgpImportExport { updateProgress(R.string.progress_importing, 0, 100); - int newKeys = 0; - int oldKeys = 0; - int badKeys = 0; + int newKeys = 0, oldKeys = 0, badKeys = 0; int position = 0; for (ParcelableKeyRing entry : entries) { @@ -147,7 +145,12 @@ public class PgpImportExport { } } - SaveKeyringResult result = mProviderHelper.savePublicKeyRing(key); + SaveKeyringResult result; + if (key.isSecret()) { + result = mProviderHelper.saveSecretKeyRing(key); + } else { + result = mProviderHelper.savePublicKeyRing(key); + } if (!result.success()) { badKeys += 1; } else if (result.updated()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index a8e4820cf..1edc529c6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -416,9 +416,6 @@ public class UncachedKeyRing { cert.init(masterKey); if (!cert.verifySignature(masterKey, key)) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD, null, indent); - log.add(LogLevel.WARN, LogType.MSG_KC_SUB, new String[] { - cert.getCreationTime().toString() - }, indent); continue; } } catch (PgpGeneralException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 6c004f19a..519d5ee0f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -296,20 +296,14 @@ public class ProviderHelper { secretRing = null; } - // delete old version of this keyRing, which also deletes all keys and userIds on cascade - int deleted = mContentResolver.delete( - KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); - if (deleted > 0) { - log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); - result |= SaveKeyringResult.UPDATED; - } else { - log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); - } - + ArrayList operations; try { + log(LogLevel.DEBUG, LogType.MSG_IP_PREPARE); + mIndent += 1; + // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); + operations = new ArrayList(); log(LogLevel.INFO, LogType.MSG_IP_INSERT_KEYRING); { // insert keyring @@ -354,26 +348,26 @@ public class ProviderHelper { values.put(Keys.IS_REVOKED, key.isRevoked()); if (c) { if (e) { - log(LogLevel.DEBUG,s ? LogType.MSG_IP_SUBKEY_FLAGS_CES - : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CES + : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); } else { log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS - : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); } } else { if (e) { log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES - : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); } else { log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS - : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); } } Date creation = key.getCreationTime(); values.put(Keys.CREATION, creation.getTime() / 1000); if (creation.after(new Date())) { - log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[] { + log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[]{ creation.toString() }); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); @@ -382,11 +376,11 @@ public class ProviderHelper { if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); if (key.isExpired()) { - log(LogLevel.INFO, LogType.MSG_IP_SUBKEY_EXPIRED, new String[] { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRED, new String[]{ expiryDate.toString() }); } else { - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[] { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[]{ expiryDate.toString() }); } @@ -402,8 +396,8 @@ public class ProviderHelper { // get a list of owned secret keys, for verification filtering LongSparseArray trustedKeys = getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { - Integer.toString(trustedKeys.size()) + log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[]{ + Integer.toString(trustedKeys.size()) }); // classify and order user ids. primary are moved to the front, revoked to the back, @@ -419,7 +413,7 @@ public class ProviderHelper { int unknownCerts = 0; - log(LogLevel.INFO, LogType.MSG_IP_UID_PROCESSING, new String[] { userId }); + log(LogLevel.INFO, LogType.MSG_IP_UID_PROCESSING, new String[]{ userId }); mIndent += 1; // look through signatures for this specific key for (WrappedSignature cert : new IterableIterator( @@ -453,7 +447,7 @@ public class ProviderHelper { item.isPrimary = cert.isPrimaryUserId(); if (cert.isRevocation()) { item.isRevoked = true; - log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); + log(LogLevel.DEBUG, LogType.MSG_IP_UID_REVOKED); } else { item.isRevoked = false; } @@ -467,7 +461,8 @@ public class ProviderHelper { if (cert.verifySignature(masterKey, userId)) { item.trustedCerts.add(cert); log(LogLevel.INFO, LogType.MSG_IP_UID_CERT_GOOD, new String[] { - PgpKeyHelper.convertKeyIdToHex(trustedKey.getKeyId()) + PgpKeyHelper.convertKeyIdToHexShort(trustedKey.getKeyId()), + trustedKey.getPrimaryUserId() }); } else { log(LogLevel.WARN, LogType.MSG_IP_UID_CERT_BAD); @@ -485,7 +480,7 @@ public class ProviderHelper { mIndent -= 1; if (unknownCerts > 0) { - log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[] { + log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[]{ Integer.toString(unknownCerts) }); } @@ -517,13 +512,43 @@ public class ProviderHelper { } } - log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); - mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); + log(LogLevel.DEBUG, LogType.MSG_IP_PREPARE_SUCCESS); + mIndent -= 1; + } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); Log.e(Constants.TAG, "IOException during import", e); mIndent -= 1; return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + try { + // delete old version of this keyRing, which also deletes all keys and userIds on cascade + int deleted = mContentResolver.delete( + KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + if (deleted > 0) { + log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); + result |= SaveKeyringResult.UPDATED; + } else { + log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); + } + + log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); + mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); + + // Save the saved keyring (if any) + if (secretRing != null) { + log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); + mIndent += 1; + saveSecretKeyRing(secretRing); + result |= SaveKeyringResult.SAVED_SECRET; + mIndent -= 1; + } + + mIndent -= 1; + log(LogLevel.OK, LogType.MSG_IP_SUCCESS); + return new SaveKeyringResult(result, mLog); + } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); @@ -536,19 +561,6 @@ public class ProviderHelper { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - // Save the saved keyring (if any) - if (secretRing != null) { - log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); - mIndent += 1; - saveSecretKeyRing(secretRing); - result |= SaveKeyringResult.SAVED_SECRET; - mIndent -= 1; - } - - mIndent -= 1; - log(LogLevel.OK, LogType.MSG_IP_SUCCESS); - return new SaveKeyringResult(result, mLog); - } private static class UserIdItem implements Comparable { @@ -575,18 +587,23 @@ public class ProviderHelper { /** * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring * is already in the database! + * + * TODO allow adding secret keys where no public key exists (ie, consolidate keys) */ - public OperationResultParcel saveSecretKeyRing(UncachedKeyRing keyRing) { + public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing keyRing) { + + if (!keyRing.isSecret()) { + log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IS, - new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}); + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); mIndent += 1; - if (!keyRing.isSecret()) { - log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); - return new OperationResultParcel(1, mLog); - } + // IF this is successful, it's a secret key + int result = SaveKeyringResult.SAVED_SECRET; // save secret keyring try { @@ -599,7 +616,7 @@ public class ProviderHelper { } catch (IOException e) { Log.e(Constants.TAG, "Failed to encode key!", e); log(LogLevel.ERROR, LogType.MSG_IS_IO_EXCPTION); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } { @@ -643,7 +660,7 @@ public class ProviderHelper { } log(LogLevel.OK, LogType.MSG_IS_SUCCESS); - return new OperationResultParcel(0, mLog); + return new SaveKeyringResult(result, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 5c223e870..9790d216d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -115,6 +115,8 @@ public class OperationResultParcel implements Parcelable { MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), + MSG_IP_PREPARE (R.string.msg_ip_prepare), + MSG_IP_PREPARE_SUCCESS(R.string.msg_ip_prepare_success), MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), MSG_IP_SUBKEY (R.string.msg_ip_subkey), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index bcf66d58f..642106c1b 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -516,6 +516,8 @@ Importing public keyring %s Inserting keyring data Inserting subkeys + Preparing database operations + OK Preserving available secret key Processing subkey %s Subkey expired on %s @@ -536,7 +538,7 @@ Using %s trusted keys Encountered bad certificate! Error processing certificate! - Found good certificate from %s + Found good certificate from %2$s (%2$s) Ignored %s certificates from unknown pubkeys Classifying user ids Inserting user ids -- cgit v1.2.3 From 59701250ba442b12d98f7b328a4cc61db1fe9158 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 18:11:31 +0200 Subject: import: use wrapped keyring instead of uncached keyring for trust --- .../keychain/provider/ProviderHelper.java | 44 ++++++++++------------ OpenKeychain/src/main/res/values/strings.xml | 2 +- 2 files changed, 20 insertions(+), 26 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 519d5ee0f..1c68a7b0b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,7 +29,7 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.pgp.WrappedPublicKey; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; @@ -172,36 +172,31 @@ public class ProviderHelper { } } - public Object getUnifiedData(long masterKeyId, String column, int type) - throws NotFoundException { - return getUnifiedData(masterKeyId, new String[]{column}, new int[]{type}).get(column); - } - public HashMap getUnifiedData(long masterKeyId, String[] proj, int[] types) throws NotFoundException { return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types); } - private LongSparseArray getUncachedMasterKeys(Uri queryUri) { - Cursor cursor = mContentResolver.query(queryUri, - new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA}, - null, null, null); + private LongSparseArray getAllWrappedMasterKeys() { + Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { + KeyRings.MASTER_KEY_ID, + // we pick from cache only information that is not easily available from keyrings + KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED, + // and of course, ring data + KeyRings.PUBKEY_DATA + }, KeyRings.HAS_ANY_SECRET + " = 1", null, null); - LongSparseArray result = - new LongSparseArray(cursor.getCount()); + LongSparseArray result = + new LongSparseArray(cursor.getCount()); try { if (cursor != null && cursor.moveToFirst()) do { long masterKeyId = cursor.getLong(0); - byte[] data = cursor.getBlob(1); - if (data != null) { - try { - result.put(masterKeyId, - UncachedKeyRing.decodeFromData(data).getPublicKey()); - } catch(PgpGeneralException e) { - Log.e(Constants.TAG, "Error parsing keyring, skipping " + masterKeyId, e); - } catch(IOException e) { - Log.e(Constants.TAG, "IO error, skipping keyring" + masterKeyId, e); - } + boolean hasAnySecret = cursor.getInt(1) > 0; + int verified = cursor.getInt(2); + byte[] blob = cursor.getBlob(3); + if (blob != null) { + result.put(masterKeyId, + new WrappedPublicKeyRing(blob, hasAnySecret, verified).getSubkey()); } } while (cursor.moveToNext()); } finally { @@ -394,8 +389,7 @@ public class ProviderHelper { mIndent -= 1; // get a list of owned secret keys, for verification filtering - LongSparseArray trustedKeys = - getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); + LongSparseArray trustedKeys = getAllWrappedMasterKeys(); log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[]{ Integer.toString(trustedKeys.size()) }); @@ -456,7 +450,7 @@ public class ProviderHelper { // verify signatures from known private keys if (trustedKeys.indexOfKey(certId) >= 0) { - UncachedPublicKey trustedKey = trustedKeys.get(certId); + WrappedPublicKey trustedKey = trustedKeys.get(certId); cert.init(trustedKey); if (cert.verifySignature(masterKey, userId)) { item.trustedCerts.add(cert); diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 642106c1b..41cf0d246 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -538,7 +538,7 @@ Using %s trusted keys Encountered bad certificate! Error processing certificate! - Found good certificate from %2$s (%2$s) + Found good certificate from %1$s (%2$s) Ignored %s certificates from unknown pubkeys Classifying user ids Inserting user ids -- cgit v1.2.3 From 9dd40b7238826ec05dc3162c58cfa90909b1c5ad Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 18:17:35 +0200 Subject: import-log: more minor improvements --- .../keychain/provider/ProviderHelper.java | 11 +++++------ .../keychain/service/OperationResultParcel.java | 4 +--- OpenKeychain/src/main/res/values/strings.xml | 14 ++++++-------- 3 files changed, 12 insertions(+), 17 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 1c68a7b0b..96bf3f207 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -390,13 +390,12 @@ public class ProviderHelper { // get a list of owned secret keys, for verification filtering LongSparseArray trustedKeys = getAllWrappedMasterKeys(); - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[]{ - Integer.toString(trustedKeys.size()) - }); // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. - log(LogLevel.DEBUG, LogType.MSG_IP_UID_CLASSIFYING); + log(LogLevel.INFO, LogType.MSG_IP_UID_CLASSIFYING, new String[]{ + Integer.toString(trustedKeys.size()) + }); mIndent += 1; List uids = new ArrayList(); for (String userId : new IterableIterator( @@ -471,18 +470,18 @@ public class ProviderHelper { }); } } - mIndent -= 1; if (unknownCerts > 0) { log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[]{ Integer.toString(unknownCerts) }); } + mIndent -= 1; } mIndent -= 1; - log(LogLevel.INFO, LogType.MSG_IP_UID_INSERT); + log(LogLevel.DEBUG, LogType.MSG_IP_UID_REORDER); // primary before regular before revoked (see UserIdItem.compareTo) // this is a stable sort, so the order of keys is otherwise preserved. Collections.sort(uids); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 9790d216d..f83dc796f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -133,14 +133,12 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), - MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), - MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), MSG_IP_UID_CERTS_UNKNOWN (R.string.msg_ip_uid_certs_unknown), MSG_IP_UID_CLASSIFYING (R.string.msg_ip_uid_classifying), - MSG_IP_UID_INSERT (R.string.msg_ip_uid_insert), + MSG_IP_UID_REORDER(R.string.msg_ip_uid_reorder), MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 41cf0d246..c57347055 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -534,14 +534,12 @@ Subkey creation date lies in the future! (%s) Successfully imported public keyring Re-inserting secret key - Retrieving trusted keys - Using %s trusted keys Encountered bad certificate! Error processing certificate! Found good certificate from %1$s (%2$s) - Ignored %s certificates from unknown pubkeys - Classifying user ids - Inserting user ids + Ignoring %s certificates from unknown pubkeys + Classifying user ids, using %s trusted signatures + Re-ordering user ids Processing user id %s Found uid revocation certificate Bad self certificate encountered! @@ -567,15 +565,15 @@ Removing bad keyring revocation key Removing redundant keyring revocation key Processing subkey %s - Subkey binding certificate verification failed! - Subkey binding certificate verification failed with an error! + Removing invalid subkey binding certificate + Removing bad subkey binding certificate! Subkey binding issuer id mismatch Unknown subkey certificate type: %s No valid certificate found for %s, removing from ring! Removing bad subkey revocation key Removing bad subkey revocation key Removing redundant keyring revocation key - OK + Subkey binding OK Keyring canonicalization successful Keyring canonicalization successful, removed %s certificates Removing bad self certificate for user id %s -- cgit v1.2.3 From 0594d9156ef148e3dd4ad7301cb4c036893dd383 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 21:57:03 +0200 Subject: canonicalize: filter out future and local certificates --- .../keychain/pgp/UncachedKeyRing.java | 80 +++++++++++++++++++--- .../keychain/pgp/WrappedSignature.java | 7 ++ .../keychain/service/OperationResultParcel.java | 11 ++- OpenKeychain/src/main/res/values/strings.xml | 19 +++-- 4 files changed, 98 insertions(+), 19 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 1edc529c6..8019b2b52 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -2,6 +2,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.S2K; +import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; @@ -23,6 +24,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -178,6 +180,7 @@ public class UncachedKeyRing { * * More specifically: * - Remove all non-verifying self-certificates + * - Remove all "future" self-certificates * - Remove all certificates flagged as "local" * - Remove all certificates which are superseded by a newer one on the same target * @@ -200,6 +203,8 @@ public class UncachedKeyRing { new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); indent += 1; + final Date now = new Date(); + int removedCerts = 0; PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; @@ -215,6 +220,7 @@ public class UncachedKeyRing { PGPSignature revocation = null; for (PGPSignature zert : new IterableIterator(masterKey.getSignatures())) { int type = zert.getSignatureType(); + // Disregard certifications on user ids, we will deal with those later if (type == PGPSignature.NO_CERTIFICATION || type == PGPSignature.DEFAULT_CERTIFICATION @@ -227,7 +233,7 @@ public class UncachedKeyRing { if (type != PGPSignature.KEY_REVOCATION) { // Unknown type, just remove - log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); modified = PGPPublicKey.removeCertification(modified, zert); @@ -235,6 +241,22 @@ public class UncachedKeyRing { continue; } + if (cert.getCreationTime().after(now)) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + try { cert.init(masterKey); if (!cert.verifySignature(masterKey)) { @@ -276,7 +298,38 @@ public class UncachedKeyRing { WrappedSignature cert = new WrappedSignature(zert); long certId = cert.getKeyId(); - // If this is a foreign signature, never mind + + int type = zert.getSignatureType(); + if (type != PGPSignature.DEFAULT_CERTIFICATION + && type != PGPSignature.NO_CERTIFICATION + && type != PGPSignature.CASUAL_CERTIFICATION + && type != PGPSignature.POSITIVE_CERTIFICATION + && type != PGPSignature.CERTIFICATION_REVOCATION) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TYPE, + new String[] { + "0x" + Integer.toString(zert.getSignatureType(), 16) + }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + } + + if (cert.getCreationTime().after(now)) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + // If this is a foreign signature, never mind any further if (certId != masterKeyId) { continue; } @@ -299,7 +352,7 @@ public class UncachedKeyRing { continue; } - switch (zert.getSignatureType()) { + switch (type) { case PGPSignature.DEFAULT_CERTIFICATION: case PGPSignature.NO_CERTIFICATION: case PGPSignature.CASUAL_CERTIFICATION: @@ -356,14 +409,6 @@ public class UncachedKeyRing { } break; - default: - log.add(LogLevel.WARN, LogType.MSG_KC_UID_UNKNOWN_CERT, - new String[] { - "0x" + Integer.toString(zert.getSignatureType(), 16), - userId - }, indent); - modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; } } @@ -401,6 +446,7 @@ public class UncachedKeyRing { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_KEYID, null, indent); continue; } + if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) @@ -408,6 +454,18 @@ public class UncachedKeyRing { continue; } + if (cert.getCreationTime().after(now)) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TIME, null, indent); + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_LOCAL, null, indent); + continue; + } + if (type == PGPSignature.SUBKEY_BINDING) { // TODO verify primary key binding signature for signing keys! diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index 093b6c96f..be7f960a9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -178,4 +178,11 @@ public class WrappedSignature { return new WrappedSignature(signatures.get(0)); } + public boolean isLocal() { + if (!mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) { + return false; + } + SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE); + return p.getData()[0] == 0; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index f83dc796f..e7fb951cd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -161,12 +161,17 @@ public class OperationResultParcel implements Parcelable { MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), + MSG_KC_REVOKE_BAD_LOCAL (R.string.msg_kc_revoke_bad_local), + MSG_KC_REVOKE_BAD_TIME (R.string.msg_kc_revoke_bad_time), + MSG_KC_REVOKE_BAD_TYPE (R.string.msg_kc_revoke_bad_type), MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), MSG_KC_SUB (R.string.msg_kc_sub), + MSG_KC_SUB_BAD(R.string.msg_kc_sub_bad), MSG_KC_SUB_BAD_ERR(R.string.msg_kc_sub_bad_err), + MSG_KC_SUB_BAD_LOCAL(R.string.msg_kc_sub_bad_local), MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), - MSG_KC_SUB_BAD(R.string.msg_kc_sub_bad), + MSG_KC_SUB_BAD_TIME(R.string.msg_kc_sub_bad_time), MSG_KC_SUB_BAD_TYPE(R.string.msg_kc_sub_bad_type), MSG_KC_SUB_NO_CERT(R.string.msg_kc_sub_no_cert), MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), @@ -176,11 +181,13 @@ public class OperationResultParcel implements Parcelable { MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), MSG_KC_SUCCESS (R.string.msg_kc_success), MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), + MSG_KC_UID_BAD_LOCAL (R.string.msg_kc_uid_bad_local), + MSG_KC_UID_BAD_TIME (R.string.msg_kc_uid_bad_time), + MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type), MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), - MSG_KC_UID_UNKNOWN_CERT (R.string.msg_kc_uid_unknown_cert), ; private final int mMsgId; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index c57347055..1f0cf0c4e 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -561,15 +561,20 @@ Canonicalizing keyring %s Processing master key OK - Removing bad keyring revocation key - Removing bad keyring revocation key - Removing redundant keyring revocation key + Removing bad keyring revocation certificate + Removing keyring revocation certificate with "local" flag + Removing keyring revocation certificate with future timestamp + Removing master key certificate of unknown type (%s) + Removing bad keyring revocation certificate + Removing redundant keyring revocation certificate Processing subkey %s Removing invalid subkey binding certificate - Removing bad subkey binding certificate! + Removing bad subkey binding certificate + Removing subkey binding certificate with "local" flag Subkey binding issuer id mismatch + Removing subkey binding certificate with future timestamp Unknown subkey certificate type: %s - No valid certificate found for %s, removing from ring! + No valid certificate found for %s, removing from ring Removing bad subkey revocation key Removing bad subkey revocation key Removing redundant keyring revocation key @@ -577,11 +582,13 @@ Keyring canonicalization successful Keyring canonicalization successful, removed %s certificates Removing bad self certificate for user id %s + Removing user id certificate with "local" flag + Removing user id with future timestamp + Removing user id certificate of unknown type (%s) Removing bad self certificate for user id "%s" Removing outdated self certificate for user id "%s" Removing redundant revocation certificate for user id "%s" Removing outdated revocation certificate for user id "%s" - Removing unknown certificate of type 0x%1$s from user id "%2$s" Certifier -- cgit v1.2.3 From 79131be5f09c9ebaaa9c76ef717b2bd68c9b49db Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 22:27:32 +0200 Subject: canonicalize: simplify import with info from canonicalization --- .../keychain/provider/ProviderHelper.java | 44 +++++----------------- .../keychain/service/OperationResultParcel.java | 4 -- OpenKeychain/src/main/res/values/strings.xml | 4 -- 3 files changed, 9 insertions(+), 43 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 96bf3f207..102c8e6d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -177,7 +177,7 @@ public class ProviderHelper { return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types); } - private LongSparseArray getAllWrappedMasterKeys() { + private LongSparseArray getTrustedMasterKeys() { Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { KeyRings.MASTER_KEY_ID, // we pick from cache only information that is not easily available from keyrings @@ -361,12 +361,6 @@ public class ProviderHelper { Date creation = key.getCreationTime(); values.put(Keys.CREATION, creation.getTime() / 1000); - if (creation.after(new Date())) { - log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[]{ - creation.toString() - }); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); - } Date expiryDate = key.getExpiryTime(); if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); @@ -389,7 +383,7 @@ public class ProviderHelper { mIndent -= 1; // get a list of owned secret keys, for verification filtering - LongSparseArray trustedKeys = getAllWrappedMasterKeys(); + LongSparseArray trustedKeys = getTrustedMasterKeys(); // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. @@ -415,34 +409,16 @@ public class ProviderHelper { try { // self signature if (certId == masterKeyId) { - cert.init(masterKey); - if (!cert.verifySignature(masterKey, userId)) { - // Bad self certification? That's kinda bad... - log(LogLevel.ERROR, LogType.MSG_IP_UID_SELF_BAD); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); - } - // if we already have a cert.. - if (item.selfCert != null) { - // ..is this perchance a more recent one? - if (item.selfCert.getCreationTime().before(cert.getCreationTime())) { - log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_NEWER); - } else { - log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_IGNORING_OLD); - continue; - } - } else { + // NOTE self-certificates are already verified during canonicalization, + // AND we know there is at most one cert plus at most one revocation + if (!cert.isRevocation()) { + item.selfCert = cert; + item.isPrimary = cert.isPrimaryUserId(); log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_GOOD); - } - - // save certificate as primary self-cert - item.selfCert = cert; - item.isPrimary = cert.isPrimaryUserId(); - if (cert.isRevocation()) { + } else { item.isRevoked = true; log(LogLevel.DEBUG, LogType.MSG_IP_UID_REVOKED); - } else { - item.isRevoked = false; } } @@ -489,10 +465,8 @@ public class ProviderHelper { for (int userIdRank = 0; userIdRank < uids.size(); userIdRank++) { UserIdItem item = uids.get(userIdRank); operations.add(buildUserIdOperations(masterKeyId, item, userIdRank)); - // no self cert is bad, but allowed by the rfc... if (item.selfCert != null) { - operations.add(buildCertOperations( - masterKeyId, userIdRank, item.selfCert, + operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfCert, secretRing != null ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); } // don't bother with trusted certs if the uid is revoked, anyways diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index e7fb951cd..701285fa8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -131,7 +131,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_FLAGS_XEX (R.string.msg_ip_subkey_flags_xex), MSG_IP_SUBKEY_FLAGS_XXS (R.string.msg_ip_subkey_flags_xxs), MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), - MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), @@ -141,10 +140,7 @@ public class OperationResultParcel implements Parcelable { MSG_IP_UID_REORDER(R.string.msg_ip_uid_reorder), MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), - MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), - MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), - MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), // import secret MSG_IS(R.string.msg_is), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 1f0cf0c4e..29bcdd679 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -531,7 +531,6 @@ Subkey flags: encrypt Subkey flags: sign Subkey flags: none - Subkey creation date lies in the future! (%s) Successfully imported public keyring Re-inserting secret key Encountered bad certificate! @@ -542,10 +541,7 @@ Re-ordering user ids Processing user id %s Found uid revocation certificate - Bad self certificate encountered! Found good self certificate - Ignoring older self certificate - Using more recent good self certificate Tried to import public keyring as secret. This is a bug, please file a report! -- cgit v1.2.3 From 073433fa747d698c6666081b2ee312062ecf2115 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 23:10:44 +0200 Subject: canonicalize: require primary key binding certificates for signing subkeys --- .../keychain/pgp/UncachedKeyRing.java | 44 ++++++++++++++++++++-- .../keychain/service/OperationResultParcel.java | 3 ++ OpenKeychain/src/main/res/values/strings.xml | 7 +++- 3 files changed, 49 insertions(+), 5 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 8019b2b52..e309ed632 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -3,6 +3,8 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.S2K; import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPKeyFlags; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; @@ -10,6 +12,7 @@ import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -298,7 +301,6 @@ public class UncachedKeyRing { WrappedSignature cert = new WrappedSignature(zert); long certId = cert.getKeyId(); - int type = zert.getSignatureType(); if (type != PGPSignature.DEFAULT_CERTIFICATION && type != PGPSignature.NO_CERTIFICATION @@ -435,9 +437,12 @@ public class UncachedKeyRing { // certificate. PGPPublicKey modified = key; PGPSignature selfCert = null, revocation = null; - for (PGPSignature zig : new IterableIterator(key.getSignatures())) { + uids: for (PGPSignature zig : new IterableIterator(key.getSignatures())) { // remove from keyring (for now) modified = PGPPublicKey.removeCertification(modified, zig); + // add this too, easier than adding it for every single "continue" case + removedCerts += 1; + WrappedSignature cert = new WrappedSignature(zig); int type = cert.getSignatureType(); @@ -467,7 +472,6 @@ public class UncachedKeyRing { } if (type == PGPSignature.SUBKEY_BINDING) { - // TODO verify primary key binding signature for signing keys! // make sure the certificate checks out try { @@ -481,10 +485,42 @@ public class UncachedKeyRing { continue; } + if (zig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { + int flags = ((KeyFlags) zig.getHashedSubPackets() + .getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags(); + // If this subkey is allowed to sign data, + if ((flags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN) { + try { + PGPSignatureList list = zig.getUnhashedSubPackets().getEmbeddedSignatures(); + boolean ok = false; + for (int i = 0; i < list.size(); i++) { + WrappedSignature subsig = new WrappedSignature(list.get(i)); + if (subsig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) { + subsig.init(key); + if (subsig.verifySignature(masterKey, key)) { + ok = true; + } else { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD, null, indent); + continue uids; + } + } + } + if (!ok) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, null, indent); + continue; + } + } catch (Exception e) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, null, indent); + continue; + } + } + } + // if we already have a cert, and this one is not newer: skip it if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) { continue; } + selfCert = zig; // if this is newer than a possibly existing revocation, drop that one if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { @@ -525,9 +561,11 @@ public class UncachedKeyRing { // re-add certification modified = PGPPublicKey.addCertification(modified, selfCert); + removedCerts -= 1; // add revocation, if any if (revocation != null) { modified = PGPPublicKey.addCertification(modified, revocation); + removedCerts -= 1; } // replace pubkey in keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 701285fa8..b5f01ce4d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -169,6 +169,9 @@ public class OperationResultParcel implements Parcelable { MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), MSG_KC_SUB_BAD_TIME(R.string.msg_kc_sub_bad_time), MSG_KC_SUB_BAD_TYPE(R.string.msg_kc_sub_bad_type), + MSG_KC_SUB_PRIMARY_BAD(R.string.msg_kc_sub_primary_bad), + MSG_KC_SUB_PRIMARY_BAD_ERR(R.string.msg_kc_sub_primary_bad_err), + MSG_KC_SUB_PRIMARY_NONE(R.string.msg_kc_sub_primary_none), MSG_KC_SUB_NO_CERT(R.string.msg_kc_sub_no_cert), MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), MSG_KC_SUB_REVOKE_BAD (R.string.msg_kc_sub_revoke_bad), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 29bcdd679..b415c6d42 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -514,8 +514,8 @@ Operation failed due to database error Operation failed due to internal error Importing public keyring %s - Inserting keyring data - Inserting subkeys + Encoding keyring data + Evaluating subkeys Preparing database operations OK Preserving available secret key @@ -570,6 +570,9 @@ Subkey binding issuer id mismatch Removing subkey binding certificate with future timestamp Unknown subkey certificate type: %s + Removing subkey binding certificate due to invalid primary binding certificate + Removing subkey binding certificate due to bad primary binding certificate + Removing subkey binding certificate due to missing primary binding certificate No valid certificate found for %s, removing from ring Removing bad subkey revocation key Removing bad subkey revocation key -- cgit v1.2.3