diff options
Diffstat (limited to 'OpenKeychain/src')
107 files changed, 2006 insertions, 874 deletions
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 3cac4ed10..79b8dbb27 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -53,19 +53,32 @@ android:name="${applicationId}.WRITE_TEMPORARY_STORAGE" android:protectionLevel="signature" /> - <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.NFC" /> + <!-- CAMERA permission requested by ZXing library --> + + <!-- contact group --> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> - <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> - <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.WRITE_PROFILE" /> + <!-- storage group --> + <!-- + No need on >= Android 4.4 for WRITE_EXTERNAL_STORAGE, because we use Storage Access Framework, + but better not use maxSdkVersion as it causes problems: https://code.google.com/p/android/issues/detail?id=63895 + --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <!-- READ_EXTERNAL_STORAGE is now dangerous on Android >= 6 --> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + + <!-- other group (for free) --> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.NFC" /> + <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> + <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /> + <!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! --> <application android:name=".KeychainApplication" @@ -97,12 +110,12 @@ android:value=".ui.MainActivity" /> <!-- Connect with YubiKeys. This Activity will automatically show/import/create YubiKeys --> <intent-filter android:label="@string/app_name"> - <action android:name="android.nfc.action.NDEF_DISCOVERED"/> - <category android:name="android.intent.category.DEFAULT"/> + <action android:name="android.nfc.action.NDEF_DISCOVERED" /> + <category android:name="android.intent.category.DEFAULT" /> <data - android:scheme="https" android:host="my.yubico.com" - android:pathPrefix="/neo"/> + android:pathPrefix="/neo" + android:scheme="https" /> </intent-filter> </activity> <activity @@ -114,9 +127,7 @@ android:name=".ui.linked.LinkedIdWizard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:label="@string/title_linked_create" - android:parentActivityName=".ui.ViewKeyActivity" - > - </activity> + android:parentActivityName=".ui.ViewKeyActivity"></activity> <activity android:name=".ui.QrCodeViewActivity" android:label="@string/share_qr_code_dialog_title" /> @@ -210,6 +221,12 @@ <data android:mimeType="text/*" /> <data android:mimeType="message/*" /> </intent-filter> + <!-- Android 6 Floating Action Mode --> + <intent-filter> + <action android:name="android.intent.action.PROCESS_TEXT" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:mimeType="text/plain" /> + </intent-filter> </activity> <activity android:name=".ui.DisplayTextActivity" @@ -466,7 +483,7 @@ android:name=".ui.ImportKeysProxyActivity" android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:label="@string/app_name" - android:theme="@android:style/Theme.NoDisplay" + android:theme="@style/Theme.Keychain.Transparent" android:windowSoftInputMode="stateHidden"> <!-- VIEW with fingerprint scheme: @@ -499,7 +516,7 @@ <data android:mimeType="application/pgp-keys" /> </intent-filter> </activity> - + <activity android:name=".ui.QrCodeCaptureActivity" /> <activity android:name=".ui.ImportKeysActivity" android:configChanges="orientation|screenSize|keyboardHidden|keyboard" @@ -691,19 +708,19 @@ android:label="@string/title_log_display" /> <activity android:name=".ui.ConsolidateDialogActivity" - android:theme="@android:style/Theme.NoDisplay" /> + android:theme="@style/Theme.Keychain.Transparent" /> <activity android:name=".ui.PassphraseDialogActivity" - android:theme="@android:style/Theme.NoDisplay" /> + android:theme="@style/Theme.Keychain.Transparent" /> <activity android:name=".ui.RetryUploadDialogActivity" - android:theme="@android:style/Theme.NoDisplay" /> + android:theme="@style/Theme.Keychain.Transparent" /> <activity android:name=".ui.DeleteKeyDialogActivity" - android:theme="@android:style/Theme.NoDisplay" /> + android:theme="@style/Theme.Keychain.Transparent" /> <activity android:name=".ui.OrbotRequiredDialogActivity" - android:theme="@android:style/Theme.NoDisplay" /> + android:theme="@style/Theme.Keychain.Transparent" /> <!-- NOTE: singleTop is set to get NFC foreground dispatch to work. Then, all NFC intents will be broadcasted to onNewIntent() of this activity! @@ -713,10 +730,10 @@ --> <activity android:name=".ui.NfcOperationActivity" - android:theme="@style/Theme.Keychain.Light.Dialog" android:allowTaskReparenting="true" android:launchMode="singleTop" - android:taskAffinity=":Nfc" /> + android:taskAffinity=":Nfc" + android:theme="@style/Theme.Keychain.Light.Dialog" /> <activity android:name=".ui.HelpActivity" @@ -741,7 +758,7 @@ android:name=".provider.KeychainProvider" android:authorities="${applicationId}.provider" android:exported="false" - android:label="@string/keyserver_sync_settings_title"/> + android:label="@string/keyserver_sync_settings_title" /> <!-- Internal classes of the remote APIs (not exported) --> <activity diff --git a/OpenKeychain/src/main/java/org/spongycastle/openpgp/jcajce/JcaSkipMarkerPGPObjectFactory.java b/OpenKeychain/src/main/java/org/spongycastle/openpgp/jcajce/JcaSkipMarkerPGPObjectFactory.java new file mode 100644 index 000000000..a60c8e499 --- /dev/null +++ b/OpenKeychain/src/main/java/org/spongycastle/openpgp/jcajce/JcaSkipMarkerPGPObjectFactory.java @@ -0,0 +1,24 @@ +package org.spongycastle.openpgp.jcajce; + + +import java.io.IOException; +import java.io.InputStream; + +import org.spongycastle.openpgp.PGPMarker; + + +public class JcaSkipMarkerPGPObjectFactory extends JcaPGPObjectFactory { + + public JcaSkipMarkerPGPObjectFactory(InputStream in) { + super(in); + } + + @Override + public Object nextObject() throws IOException { + Object o = super.nextObject(); + while (o instanceof PGPMarker) { + o = super.nextObject(); + } + return o; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 79175e9ad..69f1862ce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -28,6 +28,7 @@ public final class Constants { public static final boolean DEBUG = BuildConfig.DEBUG; public static final boolean DEBUG_LOG_DB_QUERIES = false; + public static final boolean DEBUG_EXPLAIN_QUERIES = false; public static final boolean DEBUG_SYNC_REMOVE_CONTACTS = false; public static final boolean DEBUG_KEYSERVER_SYNC = false; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java index 4e68e76c5..869d107ab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java @@ -24,6 +24,9 @@ import java.net.Proxy; import java.util.ArrayList; import java.util.Vector; +import android.support.annotation.NonNull; + + /** * Search two or more types of server for online keys. */ @@ -31,8 +34,8 @@ public class CloudSearch { private final static long SECONDS = 1000; - public static ArrayList<ImportKeysListEntry> search(final String query, Preferences.CloudSearchPrefs cloudPrefs, - final Proxy proxy) + public static ArrayList<ImportKeysListEntry> search( + @NonNull final String query, Preferences.CloudSearchPrefs cloudPrefs, @NonNull Proxy proxy) throws Keyserver.CloudSearchFailureException { final ArrayList<Keyserver> servers = new ArrayList<>(); @@ -40,10 +43,10 @@ public class CloudSearch { final Vector<Keyserver.CloudSearchFailureException> problems = new Vector<>(); if (cloudPrefs.searchKeyserver) { - servers.add(new HkpKeyserver(cloudPrefs.keyserver)); + servers.add(new HkpKeyserver(cloudPrefs.keyserver, proxy)); } if (cloudPrefs.searchKeybase) { - servers.add(new KeybaseKeyserver()); + servers.add(new KeybaseKeyserver(proxy)); } final ImportKeysList results = new ImportKeysList(servers.size()); @@ -53,7 +56,7 @@ public class CloudSearch { @Override public void run() { try { - results.addAll(keyserver.search(query, proxy)); + results.addAll(keyserver.search(query)); } catch (Keyserver.CloudSearchFailureException e) { problems.add(e); } @@ -68,7 +71,7 @@ public class CloudSearch { // wait for either all the searches to come back, or 10 seconds. If using proxy, wait 30 seconds. synchronized (results) { try { - if (proxy != null) { + if (proxy == Proxy.NO_PROXY) { results.wait(30 * SECONDS); } else { results.wait(10 * SECONDS); 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 7473705f3..c2190318b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -46,6 +46,8 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; +import android.support.annotation.NonNull; + import de.measite.minidns.Client; import de.measite.minidns.Question; import de.measite.minidns.Record; @@ -74,6 +76,7 @@ public class HkpKeyserver extends Keyserver { private String mHost; private short mPort; + private Proxy mProxy; private boolean mSecure; /** @@ -150,17 +153,17 @@ public class HkpKeyserver extends Keyserver { * connect using {@link #PORT_DEFAULT}. However, port may be specified after colon * ("<code>hostname:port</code>", eg. "<code>p80.pool.sks-keyservers.net:80</code>"). */ - public HkpKeyserver(String hostAndPort) { + public HkpKeyserver(String hostAndPort, Proxy proxy) { String host = hostAndPort; short port = PORT_DEFAULT; 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")) { + if ("hkps".equalsIgnoreCase(parts[0]) || "https".equalsIgnoreCase(parts[0])) { secure = true; port = PORT_DEFAULT_HKPS; - } else if (!parts[0].equalsIgnoreCase("hkp") && !parts[0].equalsIgnoreCase("http")) { + } else if (!"hkp".equalsIgnoreCase(parts[0]) && !"http".equalsIgnoreCase(parts[0])) { throw new IllegalArgumentException("Protocol " + parts[0] + " is unknown"); } host = parts[1]; @@ -177,16 +180,18 @@ public class HkpKeyserver extends Keyserver { } mHost = host; mPort = port; + mProxy = proxy; mSecure = secure; } - public HkpKeyserver(String host, short port) { - this(host, port, false); + public HkpKeyserver(String host, short port, Proxy proxy) { + this(host, port, proxy, false); } - public HkpKeyserver(String host, short port, boolean secure) { + public HkpKeyserver(String host, short port, Proxy proxy, boolean secure) { mHost = host; mPort = port; + mProxy = proxy; mSecure = secure; } @@ -226,7 +231,7 @@ public class HkpKeyserver extends Keyserver { return client; } - private String query(String request, Proxy proxy) throws QueryFailedException, HttpError { + private String query(String request, @NonNull Proxy proxy) throws QueryFailedException, HttpError { try { URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + request); Log.d(Constants.TAG, "hkp keyserver query: " + url + " Proxy: " + proxy); @@ -243,7 +248,7 @@ public class HkpKeyserver extends Keyserver { } catch (IOException e) { Log.e(Constants.TAG, "IOException at HkpKeyserver", e); throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!" + - (proxy == null ? "" : " Using proxy " + proxy)); + (proxy == Proxy.NO_PROXY ? "" : " Using proxy " + proxy)); } } @@ -251,7 +256,7 @@ public class HkpKeyserver extends Keyserver { * Results are sorted by creation date of key! */ @Override - public ArrayList<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException, + public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException, QueryNeedsRepairException { ArrayList<ImportKeysListEntry> results = new ArrayList<>(); @@ -269,7 +274,7 @@ public class HkpKeyserver extends Keyserver { String data; try { - data = query(request, proxy); + data = query(request, mProxy); } catch (HttpError e) { if (e.getData() != null) { Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.ENGLISH)); @@ -373,12 +378,12 @@ public class HkpKeyserver extends Keyserver { } @Override - public String get(String keyIdHex, Proxy proxy) throws QueryFailedException { + public String get(String keyIdHex) throws QueryFailedException { String request = "/pks/lookup?op=get&options=mr&search=" + keyIdHex; - Log.d(Constants.TAG, "hkp keyserver get: " + request + " using Proxy: " + proxy); + Log.d(Constants.TAG, "hkp keyserver get: " + request + " using Proxy: " + mProxy); String data; try { - data = query(request, proxy); + data = query(request, mProxy); } catch (HttpError httpError) { Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", httpError); throw new QueryFailedException("not found"); @@ -394,7 +399,7 @@ public class HkpKeyserver extends Keyserver { } @Override - public void add(String armoredKey, Proxy proxy) throws AddKeyException { + public void add(String armoredKey) throws AddKeyException { try { String path = "/pks/add"; String params; @@ -405,7 +410,7 @@ public class HkpKeyserver extends Keyserver { } URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + path); - Log.d(Constants.TAG, "hkp keyserver add: " + url.toString()); + Log.d(Constants.TAG, "hkp keyserver add: " + url); Log.d(Constants.TAG, "params: " + params); RequestBody body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), params); @@ -417,7 +422,7 @@ public class HkpKeyserver extends Keyserver { .post(body) .build(); - Response response = getClient(url, proxy).newCall(request).execute(); + Response response = getClient(url, mProxy).newCall(request).execute(); Log.d(Constants.TAG, "response code: " + response.code()); Log.d(Constants.TAG, "answer: " + response.body().string()); @@ -434,16 +439,15 @@ public class HkpKeyserver extends Keyserver { @Override public String toString() { - return mHost + ":" + mPort; + return getUrlPrefix() + mHost + ":" + mPort; } /** * Tries to find a server responsible for a given domain * * @return A responsible Keyserver or null if not found. - * TODO: Add proxy functionality */ - public static HkpKeyserver resolve(String domain) { + public static HkpKeyserver resolve(String domain, Proxy proxy) { try { Record[] records = new Client().query(new Question("_hkp._tcp." + domain, Record.TYPE.SRV)).getAnswers(); if (records.length > 0) { @@ -458,7 +462,7 @@ public class HkpKeyserver extends Keyserver { Record record = records[0]; // This is our best choice if (record.getPayload().getType() == Record.TYPE.SRV) { return new HkpKeyserver(((SRV) record.getPayload()).getName(), - (short) ((SRV) record.getPayload()).getPort()); + (short) ((SRV) record.getPayload()).getPort(), proxy); } } } catch (Exception ignored) { 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 486d658f6..e4cd6738b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -33,10 +33,15 @@ import java.util.List; public class KeybaseKeyserver extends Keyserver { public static final String ORIGIN = "keybase:keybase.io"; - private String mQuery; + + Proxy mProxy; + + public KeybaseKeyserver(Proxy proxy) { + mProxy = proxy; + } @Override - public ArrayList<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException, + public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException, QueryNeedsRepairException { ArrayList<ImportKeysListEntry> results = new ArrayList<>(); @@ -47,14 +52,13 @@ public class KeybaseKeyserver extends Keyserver { if (query.isEmpty()) { throw new QueryTooShortException(); } - mQuery = query; try { KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient()); - keybaseQuery.setProxy(proxy); + keybaseQuery.setProxy(mProxy); Iterable<Match> matches = keybaseQuery.search(query); for (Match match : matches) { - results.add(makeEntry(match)); + results.add(makeEntry(match, query)); } } catch (KeybaseException e) { Log.e(Constants.TAG, "keybase result parsing error", e); @@ -64,9 +68,9 @@ public class KeybaseKeyserver extends Keyserver { return results; } - private ImportKeysListEntry makeEntry(Match match) throws KeybaseException { + private ImportKeysListEntry makeEntry(Match match, String query) throws KeybaseException { final ImportKeysListEntry entry = new ImportKeysListEntry(); - entry.setQuery(mQuery); + entry.setQuery(query); entry.addOrigin(ORIGIN); entry.setRevoked(false); // keybase doesn’t say anything about revoked keys @@ -102,10 +106,10 @@ public class KeybaseKeyserver extends Keyserver { } @Override - public String get(String id, Proxy proxy) throws QueryFailedException { + public String get(String id) throws QueryFailedException { try { KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient()); - keybaseQuery.setProxy(proxy); + keybaseQuery.setProxy(mProxy); return User.keyForUsername(keybaseQuery, id); } catch (KeybaseException e) { throw new QueryFailedException(e.getMessage()); @@ -113,7 +117,7 @@ public class KeybaseKeyserver extends Keyserver { } @Override - public void add(String armoredKey, Proxy proxy) throws AddKeyException { + public void add(String armoredKey) throws AddKeyException { throw new AddKeyException(); } } 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 15e0d94e9..00e8d6ac5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java @@ -69,12 +69,12 @@ public abstract class Keyserver { private static final long serialVersionUID = -507574859137295530L; } - public abstract List<ImportKeysListEntry> search(String query, Proxy proxy) + public abstract List<ImportKeysListEntry> search(String query) throws QueryFailedException, QueryNeedsRepairException; - public abstract String get(String keyIdHex, Proxy proxy) throws QueryFailedException; + public abstract String get(String keyIdHex) throws QueryFailedException; - public abstract void add(String armoredKey, Proxy proxy) throws AddKeyException; + public abstract void add(String armoredKey) throws AddKeyException; public static String readAll(InputStream in, String encoding) throws IOException { ByteArrayOutputStream raw = new ByteArrayOutputStream(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java index 998ec3ad4..e5a128e32 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java @@ -1,5 +1,7 @@ package org.sufficientlysecure.keychain.linked; +import android.content.Context; + import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; @@ -8,7 +10,6 @@ import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.json.JSONException; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.linked.resources.DnsResource; import org.sufficientlysecure.keychain.linked.resources.GenericHttpsResource; import org.sufficientlysecure.keychain.linked.resources.GithubResource; import org.sufficientlysecure.keychain.linked.resources.TwitterResource; @@ -32,8 +33,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import android.content.Context; - public abstract class LinkedTokenResource extends LinkedResource { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BackupOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BackupOperation.java index 206212387..ae9a2c180 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BackupOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BackupOperation.java @@ -252,7 +252,7 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> { ring.encode(arOutStream); } catch (PgpGeneralException e) { - log.add(LogType.MSG_BACKUP_ERROR_KEY, 2); + log.add(LogType.MSG_UPLOAD_ERROR_IO, 2); } finally { if (arOutStream != null) { arOutStream.close(); @@ -273,7 +273,7 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> { ring.encode(arOutStream); } catch (PgpGeneralException e) { - log.add(LogType.MSG_BACKUP_ERROR_KEY, 2); + log.add(LogType.MSG_UPLOAD_ERROR_IO, 2); } finally { if (arOutStream != null) { arOutStream.close(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BenchmarkOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BenchmarkOperation.java new file mode 100644 index 000000000..f6e157c74 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BenchmarkOperation.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.operations; + + +import java.util.Random; + +import android.content.Context; +import android.support.annotation.NonNull; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.S2K; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.operations.results.BenchmarkResult; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation; +import org.sufficientlysecure.keychain.pgp.Progressable; +import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.BenchmarkInputParcel; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; +import org.sufficientlysecure.keychain.util.ProgressScaler; + + +public class BenchmarkOperation extends BaseOperation<BenchmarkInputParcel> { + + public BenchmarkOperation(Context context, ProviderHelper providerHelper, Progressable + progressable) { + super(context, providerHelper, progressable); + } + + @NonNull + @Override + public BenchmarkResult execute(BenchmarkInputParcel consolidateInputParcel, + CryptoInputParcel cryptoInputParcel) { + OperationLog log = new OperationLog(); + log.add(LogType.MSG_BENCH, 0); + + // random data + byte[] buf = new byte[1024*1024*5]; + new Random().nextBytes(buf); + + Passphrase passphrase = new Passphrase("a"); + + int numRepeats = 5; + long totalTime = 0; + + // encrypt + SignEncryptResult encryptResult; + int i = 0; + do { + SignEncryptOperation op = + new SignEncryptOperation(mContext, mProviderHelper, + new ProgressScaler(mProgressable, i*(50/numRepeats), (i+1)*(50/numRepeats), 100), mCancelled); + SignEncryptParcel input = new SignEncryptParcel(); + input.setSymmetricPassphrase(passphrase); + input.setBytes(buf); + encryptResult = op.execute(input, new CryptoInputParcel()); + log.add(encryptResult, 1); + log.add(LogType.MSG_BENCH_ENC_TIME, 2, + String.format("%.2f", encryptResult.getResults().get(0).mOperationTime / 1000.0)); + totalTime += encryptResult.getResults().get(0).mOperationTime; + } while (++i < numRepeats); + + long encryptionTime = totalTime / numRepeats; + totalTime = 0; + + // decrypt + i = 0; + do { + DecryptVerifyResult decryptResult; + PgpDecryptVerifyOperation op = + new PgpDecryptVerifyOperation(mContext, mProviderHelper, + new ProgressScaler(mProgressable, 50 +i*(50/numRepeats), 50 +(i+1)*(50/numRepeats), 100)); + PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(encryptResult.getResultBytes()); + input.setAllowSymmetricDecryption(true); + decryptResult = op.execute(input, new CryptoInputParcel(passphrase)); + log.add(decryptResult, 1); + log.add(LogType.MSG_BENCH_DEC_TIME, 2, String.format("%.2f", decryptResult.mOperationTime / 1000.0)); + totalTime += decryptResult.mOperationTime; + } while (++i < numRepeats); + + long decryptionTime = totalTime / numRepeats; + totalTime = 0; + + int iterationsFor100ms; + try { + PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(); + PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( + digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + "".toCharArray()); + + byte[] iv = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + int iterations = 0; + while (iterations < 255 && totalTime < 100) { + iterations += 1; + + S2K s2k = new S2K(HashAlgorithmTags.SHA1, iv, iterations); + totalTime = System.currentTimeMillis(); + decryptorFactory.makeKeyFromPassPhrase(SymmetricKeyAlgorithmTags.AES_128, s2k); + totalTime = System.currentTimeMillis() -totalTime; + + if ((iterations % 10) == 0) { + log.add(LogType.MSG_BENCH_S2K_FOR_IT, 1, Integer.toString(iterations), Long.toString(totalTime)); + } + + } + iterationsFor100ms = iterations; + + } catch (PGPException e) { + Log.e(Constants.TAG, "internal error during benchmark", e); + log.add(LogType.MSG_INTERNAL_ERROR, 0); + return new BenchmarkResult(BenchmarkResult.RESULT_ERROR, log); + } + + log.add(LogType.MSG_BENCH_S2K_100MS_ITS, 1, Integer.toString(iterationsFor100ms)); + log.add(LogType.MSG_BENCH_ENC_TIME_AVG, 1, String.format("%.2f", encryptionTime/1000.0)); + log.add(LogType.MSG_BENCH_DEC_TIME_AVG, 1, String.format("%.2f", decryptionTime/1000.0)); + + log.add(LogType.MSG_BENCH_SUCCESS, 0); + return new BenchmarkResult(BenchmarkResult.RESULT_OK, log); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java index e1daac874..7d11fa1f1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -208,7 +208,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> { // these variables are used inside the following loop, but they need to be created only once UploadOperation uploadOperation = null; if (parcel.keyServerUri != null) { - uploadOperation = new UploadOperation(mContext, mProviderHelper, mProgressable); + uploadOperation = new UploadOperation(mContext, mProviderHelper, mProgressable, mCancelled); } // Write all certified keys into the database diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java index cf8928768..3b2c484be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java @@ -26,7 +26,6 @@ import android.support.annotation.NonNull; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; -import org.sufficientlysecure.keychain.operations.results.InputPendingResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; @@ -73,7 +72,7 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> { * @return the result of the operation */ @NonNull - public InputPendingResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) { + public EditKeyResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) { OperationLog log = new OperationLog(); log.add(LogType.MSG_ED, 0); @@ -100,7 +99,8 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> { modifyResult = keyOperations.modifySecretKeyRing(secRing, cryptoInput, saveParcel); if (modifyResult.isPending()) { - return modifyResult; + log.add(modifyResult, 1); + return new EditKeyResult(log, modifyResult); } } catch (NotFoundException e) { @@ -148,19 +148,16 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> { new UploadKeyringParcel(saveParcel.getUploadKeyserver(), keyringBytes); UploadResult uploadResult = - new UploadOperation(mContext, mProviderHelper, mProgressable) + new UploadOperation(mContext, mProviderHelper, mProgressable, mCancelled) .execute(exportKeyringParcel, cryptoInput); + log.add(uploadResult, 2); + if (uploadResult.isPending()) { - return uploadResult; + return new EditKeyResult(log, uploadResult); } else if (!uploadResult.success() && saveParcel.isUploadAtomic()) { // if atomic, update fail implies edit operation should also fail and not save - log.add(uploadResult, 2); - return new EditKeyResult(log, RequiredInputParcel.createRetryUploadOperation(), - cryptoInput); - } else { - // upload succeeded or not atomic so we continue - log.add(uploadResult, 2); + return new EditKeyResult(log, RequiredInputParcel.createRetryUploadOperation(), cryptoInput); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index 89575338f..19a05790f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -28,7 +28,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; -import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -82,6 +82,8 @@ import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; */ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { + public static final int MAX_THREADS = 10; + public ImportOperation(Context context, ProviderHelper providerHelper, Progressable progressable) { super(context, providerHelper, progressable); @@ -133,7 +135,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { @NonNull private ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num, String keyServerUri, Progressable progressable, - Proxy proxy) { + @NonNull Proxy proxy) { if (progressable != null) { progressable.setProgress(R.string.progress_importing, 0, 100); } @@ -188,7 +190,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { // Make sure we have the keyserver instance cached if (keyServer == null) { log.add(LogType.MSG_IMPORT_KEYSERVER, 1, keyServerUri); - keyServer = new HkpKeyserver(keyServerUri); + keyServer = new HkpKeyserver(keyServerUri, proxy); } try { @@ -197,11 +199,10 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { if (entry.mExpectedFingerprint != null) { log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" + entry.mExpectedFingerprint.substring(24)); - data = keyServer.get("0x" + entry.mExpectedFingerprint, proxy) - .getBytes(); + data = keyServer.get("0x" + entry.mExpectedFingerprint).getBytes(); } else { log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, entry.mKeyIdHex); - data = keyServer.get(entry.mKeyIdHex, proxy).getBytes(); + data = keyServer.get(entry.mKeyIdHex).getBytes(); } key = UncachedKeyRing.decodeFromData(data); if (key != null) { @@ -219,12 +220,12 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { if (entry.mKeybaseName != null) { // Make sure we have this cached if (keybaseServer == null) { - keybaseServer = new KeybaseKeyserver(); + keybaseServer = new KeybaseKeyserver(proxy); } try { log.add(LogType.MSG_IMPORT_FETCH_KEYBASE, 2, entry.mKeybaseName); - byte[] data = keybaseServer.get(entry.mKeybaseName, proxy).getBytes(); + byte[] data = keybaseServer.get(entry.mKeybaseName).getBytes(); UncachedKeyRing keybaseKey = UncachedKeyRing.decodeFromData(data); // If there already is a key, merge the two @@ -261,12 +262,6 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { continue; } - // Another check if we have been cancelled - if (checkCancelled()) { - cancelled = true; - break; - } - SaveKeyringResult result; // synchronizing prevents https://github.com/open-keychain/open-keychain/issues/1221 // and https://github.com/open-keychain/open-keychain/issues/1480 @@ -365,13 +360,15 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { } } - // Final log entry, it's easier to do this individually - if ((newKeys > 0 || updatedKeys > 0) && badKeys > 0) { - log.add(LogType.MSG_IMPORT_PARTIAL, 1); - } else if (newKeys > 0 || updatedKeys > 0) { - log.add(LogType.MSG_IMPORT_SUCCESS, 1); - } else { - log.add(LogType.MSG_IMPORT_ERROR, 1); + if (!cancelled) { + // Final log entry, it's easier to do this individually + if ((newKeys > 0 || updatedKeys > 0) && badKeys > 0) { + log.add(LogType.MSG_IMPORT_PARTIAL, 1); + } else if (newKeys > 0 || updatedKeys > 0) { + log.add(LogType.MSG_IMPORT_SUCCESS, 1); + } else { + log.add(LogType.MSG_IMPORT_ERROR, 1); + } } return new ImportKeyResult(resultType, log, newKeys, updatedKeys, badKeys, secret, @@ -400,8 +397,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { return new ImportKeyResult(null, RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput); } - proxy = Preferences.getPreferences(mContext).getProxyPrefs().parcelableProxy - .getProxy(); + proxy = Preferences.getPreferences(mContext).getProxyPrefs().getProxy(); } else { proxy = cryptoInput.getParcelableProxy().getProxy(); } @@ -414,62 +410,61 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { } @NonNull - private ImportKeyResult multiThreadedKeyImport(Iterator<ParcelableKeyRing> keyListIterator, + private ImportKeyResult multiThreadedKeyImport(@NonNull Iterator<ParcelableKeyRing> keyListIterator, int totKeys, final String keyServer, final Proxy proxy) { Log.d(Constants.TAG, "Multi-threaded key import starting"); - if (keyListIterator != null) { - KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable); - - final ProgressScaler ignoreProgressable = new ProgressScaler(); + KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable); - final int maxThreads = 200; - ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads, - 30L, TimeUnit.SECONDS, - new SynchronousQueue<Runnable>()); + final ProgressScaler ignoreProgressable = new ProgressScaler(); - ExecutorCompletionService<ImportKeyResult> importCompletionService = - new ExecutorCompletionService<>(importExecutor); + ExecutorService importExecutor = new ThreadPoolExecutor(0, MAX_THREADS, 30L, TimeUnit.SECONDS, + new LinkedBlockingQueue<Runnable>()); - while (keyListIterator.hasNext()) { // submit all key rings to be imported + ExecutorCompletionService<ImportKeyResult> importCompletionService = + new ExecutorCompletionService<>(importExecutor); - final ParcelableKeyRing pkRing = keyListIterator.next(); + while (keyListIterator.hasNext()) { // submit all key rings to be imported - Callable<ImportKeyResult> importOperationCallable = new Callable<ImportKeyResult> - () { + final ParcelableKeyRing pkRing = keyListIterator.next(); - @Override - public ImportKeyResult call() { + Callable<ImportKeyResult> importOperationCallable = new Callable<ImportKeyResult> + () { - ArrayList<ParcelableKeyRing> list = new ArrayList<>(); - list.add(pkRing); + @Override + public ImportKeyResult call() { - return serialKeyRingImport(list.iterator(), 1, keyServer, - ignoreProgressable, proxy); + if (checkCancelled()) { + return null; } - }; - importCompletionService.submit(importOperationCallable); - } + ArrayList<ParcelableKeyRing> list = new ArrayList<>(); + list.add(pkRing); - while (!accumulator.isImportFinished()) { // accumulate the results of each import - try { - accumulator.accumulateKeyImport(importCompletionService.take().get()); - } catch (InterruptedException | ExecutionException e) { - Log.e(Constants.TAG, "A key could not be imported during multi-threaded " + - "import", e); - // do nothing? - if (e instanceof ExecutionException) { - // Since serialKeyRingImport does not throw any exceptions, this is what - // would have happened if - // we were importing the key on this thread - throw new RuntimeException(); - } + return serialKeyRingImport(list.iterator(), 1, keyServer, ignoreProgressable, proxy); + } + }; + + importCompletionService.submit(importOperationCallable); + } + + while (!accumulator.isImportFinished()) { // accumulate the results of each import + try { + accumulator.accumulateKeyImport(importCompletionService.take().get()); + } catch (InterruptedException | ExecutionException e) { + Log.e(Constants.TAG, "A key could not be imported during multi-threaded " + + "import", e); + // do nothing? + if (e instanceof ExecutionException) { + // Since serialKeyRingImport does not throw any exceptions, this is what + // would have happened if + // we were importing the key on this thread + throw new RuntimeException(); } } - return accumulator.getConsolidatedResult(); } - return new ImportKeyResult(ImportKeyResult.RESULT_FAIL_NOTHING, new OperationLog()); + return accumulator.getConsolidatedResult(); + } /** @@ -486,6 +481,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { private int mUpdatedKeys = 0; private int mSecret = 0; private int mResultType = 0; + private boolean mHasCancelledResult; /** * Accumulates keyring imports and updates the progressable whenever a new key is imported. @@ -503,14 +499,25 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { } } - public synchronized void accumulateKeyImport(ImportKeyResult result) { + public void accumulateKeyImport(ImportKeyResult result) { mImportedKeys++; + if (result == null) { + return; + } + if (mProgressable != null) { mProgressable.setProgress(mImportedKeys, mTotalKeys); } - mImportLog.addAll(result.getLog().toList());//accumulates log + boolean notCancelledOrFirstCancelled = !result.cancelled() || !mHasCancelledResult; + if (notCancelledOrFirstCancelled) { + mImportLog.addAll(result.getLog().toList()); //accumulates log + if (result.cancelled()) { + mHasCancelledResult = true; + } + } + mBadKeys += result.mBadKeys; mNewKeys += result.mNewKeys; mUpdatedKeys += result.mUpdatedKeys; @@ -533,7 +540,9 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> { // adding required information to mResultType // special case,no keys requested for import - if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) { + if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0 + && (mResultType & ImportKeyResult.RESULT_CANCELLED) + != ImportKeyResult.RESULT_CANCELLED) { mResultType = ImportKeyResult.RESULT_FAIL_NOTHING; } else { if (mNewKeys > 0) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/InputDataOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/InputDataOperation.java index a09cf4f27..bb8d6ad73 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/InputDataOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/InputDataOperation.java @@ -110,10 +110,9 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> { if (decryptResult.isPending()) { return new InputDataResult(log, decryptResult); } - log.addByMerge(decryptResult, 2); + log.addByMerge(decryptResult, 1); - if (!decryptResult.success()) { - log.add(LogType.MSG_DATA_ERROR_OPENPGP, 1); + if ( ! decryptResult.success()) { return new InputDataResult(InputDataResult.RESULT_ERROR, log); } @@ -165,6 +164,7 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> { parser.setContentDecoding(true); parser.setRecurse(); parser.setContentHandler(new AbstractContentHandler() { + private boolean mFoundHeaderWithFields = false; private Uri uncheckedSignedDataUri; String mFilename; @@ -221,11 +221,19 @@ public class InputDataOperation extends BaseOperation<InputDataParcel> { } @Override + public void endHeader() throws MimeException { + if ( ! mFoundHeaderWithFields) { + parser.stop(); + } + } + + @Override public void field(Field field) throws MimeException { field = DefaultFieldParser.getParser().parse(field, DecodeMonitor.SILENT); if (field instanceof ContentDispositionField) { mFilename = ((ContentDispositionField) field).getFilename(); } + mFoundHeaderWithFields = true; } private void bodySignature(BodyDescriptor bd, InputStream is) throws MimeException, IOException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java index 975cf541a..3e787560a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java @@ -19,12 +19,13 @@ package org.sufficientlysecure.keychain.operations; + import android.content.Context; import android.net.Uri; import android.support.annotation.NonNull; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.operations.results.InputPendingResult; +import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.RevokeResult; import org.sufficientlysecure.keychain.pgp.Progressable; @@ -79,9 +80,8 @@ public class RevokeOperation extends BaseOperation<RevokeKeyringParcel> { saveKeyringParcel.mRevokeSubKeys.add(masterKeyId); - InputPendingResult revokeAndUploadResult = new EditKeyOperation(mContext, - mProviderHelper, mProgressable, mCancelled) - .execute(saveKeyringParcel, cryptoInputParcel); + EditKeyResult revokeAndUploadResult = new EditKeyOperation(mContext, + mProviderHelper, mProgressable, mCancelled).execute(saveKeyringParcel, cryptoInputParcel); if (revokeAndUploadResult.isPending()) { return revokeAndUploadResult; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/UploadOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/UploadOperation.java index bac9a7975..e5f11eaa6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/UploadOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/UploadOperation.java @@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import android.content.Context; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import org.spongycastle.bcpg.ArmoredOutputStream; import org.sufficientlysecure.keychain.Constants; @@ -47,26 +48,17 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Preferences; +import org.sufficientlysecure.keychain.util.Preferences.ProxyPrefs; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; /** - * An operation class which implements high level export operations. - * This class receives a source and/or destination of keys as input and performs - * all steps for this export. - * - * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries() - * For the export operation, the input consists of a set of key ids and - * either the name of a file or an output uri to write to. + * An operation class which implements the upload of a single key to a key server. */ public class UploadOperation extends BaseOperation<UploadKeyringParcel> { - public UploadOperation(Context context, ProviderHelper providerHelper, Progressable - progressable) { - super(context, providerHelper, progressable); - } - public UploadOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) { super(context, providerHelper, progressable, cancelled); @@ -74,57 +66,99 @@ public class UploadOperation extends BaseOperation<UploadKeyringParcel> { @NonNull public UploadResult execute(UploadKeyringParcel uploadInput, CryptoInputParcel cryptoInput) { + OperationLog log = new OperationLog(); + + log.add(LogType.MSG_UPLOAD, 0); + updateProgress(R.string.progress_uploading, 0, 1); + Proxy proxy; - if (cryptoInput.getParcelableProxy() == null) { - // explicit proxy not set - if (!OrbotHelper.isOrbotInRequiredState(mContext)) { - return new UploadResult(null, RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput); + { + boolean proxyIsTor = false; + + // Proxy priorities: + // 1. explicit proxy + // 2. orbot proxy state + // 3. proxy from preferences + ParcelableProxy parcelableProxy = cryptoInput.getParcelableProxy(); + if (parcelableProxy != null) { + proxy = parcelableProxy.getProxy(); + } else { + if ( ! OrbotHelper.isOrbotInRequiredState(mContext)) { + return new UploadResult(log, RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput); + } + ProxyPrefs proxyPrefs = Preferences.getPreferences(mContext).getProxyPrefs(); + if (proxyPrefs.torEnabled) { + proxyIsTor = true; + } + proxy = proxyPrefs.getProxy(); } - proxy = Preferences.getPreferences(mContext).getProxyPrefs().parcelableProxy.getProxy(); - } else { - proxy = cryptoInput.getParcelableProxy().getProxy(); + + if (proxyIsTor) { + log.add(LogType.MSG_UPLOAD_PROXY_TOR, 1); + } else if (proxy == Proxy.NO_PROXY) { + log.add(LogType.MSG_UPLOAD_PROXY_DIRECT, 1); + } else { + log.add(LogType.MSG_UPLOAD_PROXY, 1, proxy.toString()); + } + + } + + HkpKeyserver hkpKeyserver; + { + hkpKeyserver = new HkpKeyserver(uploadInput.mKeyserver, proxy); + log.add(LogType.MSG_UPLOAD_SERVER, 1, hkpKeyserver.toString()); + } + + CanonicalizedPublicKeyRing keyring = getPublicKeyringFromInput(log, uploadInput); + if (keyring == null) { + return new UploadResult(UploadResult.RESULT_ERROR, log); + } + + return uploadKeyRingToServer(log, hkpKeyserver, keyring); + } + + @Nullable + private CanonicalizedPublicKeyRing getPublicKeyringFromInput(OperationLog log, UploadKeyringParcel uploadInput) { + + boolean hasMasterKeyId = uploadInput.mMasterKeyId != null; + boolean hasKeyringBytes = uploadInput.mUncachedKeyringBytes != null; + if (hasMasterKeyId == hasKeyringBytes) { + throw new IllegalArgumentException("either keyid xor bytes must be non-null for this method call!"); } - HkpKeyserver hkpKeyserver = new HkpKeyserver(uploadInput.mKeyserver); try { - CanonicalizedPublicKeyRing keyring; - if (uploadInput.mMasterKeyId != null) { - keyring = mProviderHelper.getCanonicalizedPublicKeyRing( - uploadInput.mMasterKeyId); - } else if (uploadInput.mUncachedKeyringBytes != null) { - CanonicalizedKeyRing canonicalizedRing = - UncachedKeyRing.decodeFromData(uploadInput.mUncachedKeyringBytes) - .canonicalize(new OperationLog(), 0, true); - if ( ! CanonicalizedPublicKeyRing.class.isInstance(canonicalizedRing)) { - throw new AssertionError("keyring bytes must contain public key ring!"); - } - keyring = (CanonicalizedPublicKeyRing) canonicalizedRing; - } else { - throw new AssertionError("key id or bytes must be non-null!"); + + if (hasMasterKeyId) { + log.add(LogType.MSG_UPLOAD_KEY, 0, KeyFormattingUtils.convertKeyIdToHex(uploadInput.mMasterKeyId)); + return mProviderHelper.getCanonicalizedPublicKeyRing(uploadInput.mMasterKeyId); } - return uploadKeyRingToServer(hkpKeyserver, keyring, proxy); + + CanonicalizedKeyRing canonicalizedRing = + UncachedKeyRing.decodeFromData(uploadInput.mUncachedKeyringBytes) + .canonicalize(new OperationLog(), 0, true); + if ( ! CanonicalizedPublicKeyRing.class.isInstance(canonicalizedRing)) { + throw new IllegalArgumentException("keyring bytes must contain public key ring!"); + } + log.add(LogType.MSG_UPLOAD_KEY, 0, KeyFormattingUtils.convertKeyIdToHex(canonicalizedRing.getMasterKeyId())); + return (CanonicalizedPublicKeyRing) canonicalizedRing; + } catch (ProviderHelper.NotFoundException e) { + log.add(LogType.MSG_UPLOAD_ERROR_NOT_FOUND, 1); + return null; + } catch (IOException | PgpGeneralException e) { + log.add(LogType.MSG_UPLOAD_ERROR_IO, 1); Log.e(Constants.TAG, "error uploading key", e); - return new UploadResult(UploadResult.RESULT_ERROR, new OperationLog()); - } catch (IOException e) { - e.printStackTrace(); - return new UploadResult(UploadResult.RESULT_ERROR, new OperationLog()); - } catch (PgpGeneralException e) { - e.printStackTrace(); - return new UploadResult(UploadResult.RESULT_ERROR, new OperationLog()); + return null; } - } - UploadResult uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring, Proxy proxy) { + } - mProgressable.setProgress(R.string.progress_uploading, 0, 1); + @NonNull + private UploadResult uploadKeyRingToServer( + OperationLog log, HkpKeyserver server, CanonicalizedPublicKeyRing keyring) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ArmoredOutputStream aos = null; - OperationLog log = new OperationLog(); - log.add(LogType.MSG_BACKUP_UPLOAD_PUBLIC, 0, KeyFormattingUtils.convertKeyIdToHex( - keyring.getPublicKey().getKeyId() - )); try { aos = new ArmoredOutputStream(bos); @@ -132,22 +166,23 @@ public class UploadOperation extends BaseOperation<UploadKeyringParcel> { aos.close(); String armoredKey = bos.toString("UTF-8"); - server.add(armoredKey, proxy); + server.add(armoredKey); + + updateProgress(R.string.progress_uploading, 1, 1); - log.add(LogType.MSG_BACKUP_UPLOAD_SUCCESS, 1); + log.add(LogType.MSG_UPLOAD_SUCCESS, 1); return new UploadResult(UploadResult.RESULT_OK, log); } catch (IOException e) { Log.e(Constants.TAG, "IOException", e); - log.add(LogType.MSG_BACKUP_ERROR_KEY, 1); + log.add(LogType.MSG_UPLOAD_ERROR_IO, 1); return new UploadResult(UploadResult.RESULT_ERROR, log); } catch (AddKeyException e) { Log.e(Constants.TAG, "AddKeyException", e); - log.add(LogType.MSG_BACKUP_ERROR_UPLOAD, 1); + log.add(LogType.MSG_UPLOAD_ERROR_UPLOAD, 1); return new UploadResult(UploadResult.RESULT_ERROR, log); } finally { - mProgressable.setProgress(R.string.progress_uploading, 1, 1); try { if (aos != null) { aos.close(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/BenchmarkResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/BenchmarkResult.java new file mode 100644 index 000000000..473ae9886 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/BenchmarkResult.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.operations.results; + +import android.os.Parcel; + + +public class BenchmarkResult extends OperationResult { + + public BenchmarkResult(int result, OperationLog log) { + super(result, log); + } + + /** Construct from a parcel - trivial because we have no extra data. */ + public BenchmarkResult(Parcel source) { + super(source); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + } + + public static Creator<BenchmarkResult> CREATOR = new Creator<BenchmarkResult>() { + public BenchmarkResult createFromParcel(final Parcel source) { + return new BenchmarkResult(source); + } + + public BenchmarkResult[] newArray(final int size) { + return new BenchmarkResult[size]; + } + }; + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java index 95cf179af..f19ba5250 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java @@ -39,6 +39,8 @@ public class DecryptVerifyResult extends InputPendingResult { byte[] mOutputBytes; + public long mOperationTime; + public DecryptVerifyResult(int result, OperationLog log) { super(result, log); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java index 6098d59d5..fa383a7b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java @@ -38,6 +38,11 @@ public class EditKeyResult extends InputPendingResult { mMasterKeyId = null; } + public EditKeyResult(OperationLog log, InputPendingResult result) { + super(log, result); + mMasterKeyId = null; + } + public EditKeyResult(Parcel source) { super(source); mMasterKeyId = source.readInt() != 0 ? source.readLong() : null; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java index 0a8c1f653..ed6674ef7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.operations.results; import android.os.Parcel; +import android.support.annotation.NonNull; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; @@ -32,13 +33,13 @@ public class InputPendingResult extends OperationResult { // in case operation needs to add to/changes the cryptoInputParcel sent to it public final CryptoInputParcel mCryptoInputParcel; - public InputPendingResult(int result, OperationLog log) { + public InputPendingResult(int result, @NonNull OperationLog log) { super(result, log); mRequiredInput = null; mCryptoInputParcel = null; } - public InputPendingResult(OperationLog log, InputPendingResult result) { + public InputPendingResult(@NonNull OperationLog log, @NonNull InputPendingResult result) { super(RESULT_PENDING, log); if (!result.isPending()) { throw new AssertionError("sub result must be pending!"); @@ -47,7 +48,7 @@ public class InputPendingResult extends OperationResult { mCryptoInputParcel = result.mCryptoInputParcel; } - public InputPendingResult(OperationLog log, RequiredInputParcel requiredInput, + public InputPendingResult(@NonNull OperationLog log, RequiredInputParcel requiredInput, CryptoInputParcel cryptoInputParcel) { super(RESULT_PENDING, log); mRequiredInput = requiredInput; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 0b8c3e6c7..9877f2318 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -635,6 +635,8 @@ public abstract class OperationResult implements Parcelable { MSG_EK_ERROR_NOT_FOUND (LogLevel.ERROR, R.string.msg_ek_error_not_found), // decryptverify + MSG_DC_ASKIP_BAD_FLAGS (LogLevel.DEBUG, R.string.msg_dc_askip_bad_flags), + MSG_DC_ASKIP_UNAVAILABLE (LogLevel.DEBUG, R.string.msg_dc_askip_unavailable), MSG_DC_ASKIP_NO_KEY (LogLevel.DEBUG, R.string.msg_dc_askip_no_key), MSG_DC_ASKIP_NOT_ALLOWED (LogLevel.DEBUG, R.string.msg_dc_askip_not_allowed), MSG_DC_ASYM (LogLevel.DEBUG, R.string.msg_dc_asym), @@ -766,17 +768,24 @@ public abstract class OperationResult implements Parcelable { MSG_IMPORT_SUCCESS (LogLevel.OK, R.string.msg_import_success), MSG_BACKUP(LogLevel.START, R.plurals.msg_backup), - MSG_BACKUP_UPLOAD_PUBLIC(LogLevel.START, R.string.msg_backup_upload_public), MSG_BACKUP_PUBLIC(LogLevel.DEBUG, R.string.msg_backup_public), MSG_BACKUP_SECRET(LogLevel.DEBUG, R.string.msg_backup_secret), MSG_BACKUP_ALL(LogLevel.START, R.string.msg_backup_all), MSG_BACKUP_ERROR_URI_OPEN(LogLevel.ERROR, R.string.msg_backup_error_uri_open), MSG_BACKUP_ERROR_DB(LogLevel.ERROR, R.string.msg_backup_error_db), MSG_BACKUP_ERROR_IO(LogLevel.ERROR, R.string.msg_backup_error_io), - MSG_BACKUP_ERROR_KEY(LogLevel.ERROR, R.string.msg_backup_error_key), - MSG_BACKUP_ERROR_UPLOAD(LogLevel.ERROR, R.string.msg_backup_error_upload), MSG_BACKUP_SUCCESS(LogLevel.OK, R.string.msg_backup_success), - MSG_BACKUP_UPLOAD_SUCCESS(LogLevel.OK, R.string.msg_backup_upload_success), + + MSG_UPLOAD(LogLevel.START, R.string.msg_upload), + MSG_UPLOAD_KEY(LogLevel.INFO, R.string.msg_upload_key), + MSG_UPLOAD_PROXY_DIRECT(LogLevel.DEBUG, R.string.msg_upload_proxy_direct), + MSG_UPLOAD_PROXY_TOR(LogLevel.DEBUG, R.string.msg_upload_proxy_tor), + MSG_UPLOAD_PROXY(LogLevel.DEBUG, R.string.msg_upload_proxy), + MSG_UPLOAD_SERVER(LogLevel.DEBUG, R.string.msg_upload_server), + MSG_UPLOAD_SUCCESS(LogLevel.OK, R.string.msg_upload_success), + MSG_UPLOAD_ERROR_NOT_FOUND(LogLevel.ERROR, R.string.msg_upload_error_not_found), + MSG_UPLOAD_ERROR_IO(LogLevel.ERROR, R.string.msg_upload_error_key), + MSG_UPLOAD_ERROR_UPLOAD(LogLevel.ERROR, R.string.msg_upload_error_upload), MSG_CRT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_crt_upload_success), @@ -827,7 +836,6 @@ public abstract class OperationResult implements Parcelable { MSG_DATA (LogLevel.START, R.string.msg_data), MSG_DATA_OPENPGP (LogLevel.DEBUG, R.string.msg_data_openpgp), MSG_DATA_ERROR_IO (LogLevel.ERROR, R.string.msg_data_error_io), - MSG_DATA_ERROR_OPENPGP (LogLevel.ERROR, R.string.msg_data_error_openpgp), MSG_DATA_DETACHED (LogLevel.INFO, R.string.msg_data_detached), MSG_DATA_DETACHED_CLEAR (LogLevel.WARN, R.string.msg_data_detached_clear), MSG_DATA_DETACHED_SIG (LogLevel.DEBUG, R.string.msg_data_detached_sig), @@ -867,6 +875,16 @@ public abstract class OperationResult implements Parcelable { MSG_LV_FETCH_ERROR_IO (LogLevel.ERROR, R.string.msg_lv_fetch_error_io), MSG_LV_FETCH_ERROR_FORMAT(LogLevel.ERROR, R.string.msg_lv_fetch_error_format), MSG_LV_FETCH_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_lv_fetch_error_nothing), + + MSG_BENCH (LogLevel.START, R.string.msg_bench), + MSG_BENCH_ENC_TIME (LogLevel.DEBUG, R.string.msg_bench_enc_time), + MSG_BENCH_ENC_TIME_AVG (LogLevel.INFO, R.string.msg_bench_enc_time_avg), + MSG_BENCH_DEC_TIME (LogLevel.DEBUG, R.string.msg_bench_dec_time), + MSG_BENCH_DEC_TIME_AVG (LogLevel.INFO, R.string.msg_bench_enc_time_avg), + MSG_BENCH_S2K_FOR_IT (LogLevel.DEBUG, R.string.msg_bench_s2k_for_it), + MSG_BENCH_S2K_100MS_ITS (LogLevel.INFO, R.string.msg_bench_s2k_100ms_its), + MSG_BENCH_SUCCESS (LogLevel.OK, R.string.msg_bench_success), + ; public final int mMsgId; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java index 2b33b8ace..12b091e32 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java @@ -26,6 +26,7 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; public class PgpSignEncryptResult extends InputPendingResult { byte[] mDetachedSignature; + public long mOperationTime; public void setDetachedSignature(byte[] detachedSignature) { mDetachedSignature = detachedSignature; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java index 0e0c5d598..60f47be3c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java @@ -56,6 +56,10 @@ public class SignEncryptResult extends InputPendingResult { return mResultBytes; } + public ArrayList<PgpSignEncryptResult> getResults() { + return mResults; + } + public int describeContents() { return 0; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/UploadResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/UploadResult.java index a88072de3..ea2b373a9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/UploadResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/UploadResult.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.operations.results; import android.os.Parcel; +import android.support.annotation.NonNull; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; @@ -38,7 +39,7 @@ public class UploadResult extends InputPendingResult { } - public UploadResult(OperationLog log, RequiredInputParcel requiredInputParcel, + public UploadResult(@NonNull OperationLog log, RequiredInputParcel requiredInputParcel, CryptoInputParcel cryptoInputParcel) { super(log, requiredInputParcel, cryptoInputParcel); // we won't use these values diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index d85652a51..ea7465209 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -48,7 +48,7 @@ import org.spongycastle.openpgp.PGPPBEEncryptedData; import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPUtil; -import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.spongycastle.openpgp.jcajce.JcaSkipMarkerPGPObjectFactory; import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory; @@ -87,6 +87,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp InputData inputData; OutputStream outputStream; + long startTime = System.currentTimeMillis(); + if (input.getInputBytes() != null) { byte[] inputBytes = input.getInputBytes(); inputData = new InputData(new ByteArrayInputStream(inputBytes), inputBytes.length); @@ -122,6 +124,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp result.setOutputBytes(outputData); } + result.mOperationTime = System.currentTimeMillis() - startTime; + Log.d(Constants.TAG, "total time taken: " + String.format("%.2f", result.mOperationTime / 1000.0) + "s"); return result; } @@ -277,11 +281,11 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder(); - JcaPGPObjectFactory plainFact; + JcaSkipMarkerPGPObjectFactory plainFact; Object dataChunk; EncryptStreamResult esResult = null; { // resolve encrypted (symmetric and asymmetric) packets - JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in); + JcaSkipMarkerPGPObjectFactory pgpF = new JcaSkipMarkerPGPObjectFactory(in); Object obj = pgpF.nextObject(); if (obj instanceof PGPEncryptedDataList) { @@ -308,7 +312,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp decryptionResultBuilder.setInsecure(true); } - plainFact = new JcaPGPObjectFactory(esResult.cleartextStream); + plainFact = new JcaSkipMarkerPGPObjectFactory(esResult.cleartextStream); dataChunk = plainFact.nextObject(); } else { @@ -333,7 +337,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp PGPCompressedData compressedData = (PGPCompressedData) dataChunk; - JcaPGPObjectFactory fact = new JcaPGPObjectFactory(compressedData.getDataStream()); + JcaSkipMarkerPGPObjectFactory fact = new JcaSkipMarkerPGPObjectFactory(compressedData.getDataStream()); dataChunk = fact.nextObject(); plainFact = fact; } @@ -425,10 +429,12 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp InputStream dataIn = literalData.getInputStream(); + long opTime, startTime = System.currentTimeMillis(); + long alreadyWritten = 0; long wholeSize = 0; // TODO inputData.getSize() - inputData.getStreamPosition(); int length; - byte[] buffer = new byte[1 << 16]; + byte[] buffer = new byte[8192]; byte[] firstBytes = new byte[48]; while ((length = dataIn.read(buffer)) > 0) { // Log.d(Constants.TAG, "read bytes: " + length); @@ -456,6 +462,20 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp } } + if (signatureChecker.isInitialized()) { + + Object o = plainFact.nextObject(); + boolean signatureCheckOk = signatureChecker.verifySignatureOnePass(o, log, indent + 1); + + if (!signatureCheckOk) { + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + } + + opTime = System.currentTimeMillis()-startTime; + Log.d(Constants.TAG, "decrypt time taken: " + String.format("%.2f", opTime / 1000.0) + "s"); + // special treatment to detect pgp mime types if (matchesPrefix(firstBytes, "-----BEGIN PGP PUBLIC KEY BLOCK-----") || matchesPrefix(firstBytes, "-----BEGIN PGP PRIVATE KEY BLOCK-----")) { @@ -470,17 +490,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp metadata = new OpenPgpMetadata( originalFilename, mimeType, literalData.getModificationTime().getTime(), alreadyWritten, charset); - if (signatureChecker.isInitialized()) { - - Object o = plainFact.nextObject(); - boolean signatureCheckOk = signatureChecker.verifySignatureOnePass(o, log, indent + 1); - - if (!signatureCheckOk) { - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - } - indent -= 1; if (esResult != null) { @@ -513,6 +522,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp result.setSignatureResult(signatureChecker.getSignatureResult()); result.setDecryptionResult(decryptionResultBuilder.build()); result.setDecryptionMetadata(metadata); + result.mOperationTime = opTime; return result; @@ -581,6 +591,18 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp // get subkey which has been used for this encryption packet secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId); + if (!secretEncryptionKey.canEncrypt()) { + secretEncryptionKey = null; + log.add(LogType.MSG_DC_ASKIP_BAD_FLAGS, indent + 1); + continue; + } + + if (!secretEncryptionKey.getSecretKeyType().isUsable()) { + secretEncryptionKey = null; + log.add(LogType.MSG_DC_ASKIP_UNAVAILABLE, indent + 1); + continue; + } + /* secret key exists in database and is allowed! */ asymmetricPacketFound = true; @@ -817,7 +839,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp } updateProgress(R.string.progress_processing_signature, 60, 100); - JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(aIn); + JcaSkipMarkerPGPObjectFactory pgpFact = new JcaSkipMarkerPGPObjectFactory(aIn); PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mProviderHelper); @@ -869,12 +891,12 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp InputStream detachedSigIn = new ByteArrayInputStream(input.getDetachedSignature()); detachedSigIn = PGPUtil.getDecoderStream(detachedSigIn); - JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(detachedSigIn); + JcaSkipMarkerPGPObjectFactory pgpFact = new JcaSkipMarkerPGPObjectFactory(detachedSigIn); Object o = pgpFact.nextObject(); if (o instanceof PGPCompressedData) { PGPCompressedData c1 = (PGPCompressedData) o; - pgpFact = new JcaPGPObjectFactory(c1.getDataStream()); + pgpFact = new JcaSkipMarkerPGPObjectFactory(c1.getDataStream()); o = pgpFact.nextObject(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java index e8d1d3111..fbda90775 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java @@ -117,7 +117,7 @@ public class PgpHelper { } } - public static String getPgpContent(@NonNull CharSequence input) { + public static String getPgpMessageContent(@NonNull CharSequence input) { Log.dEscaped(Constants.TAG, "input: " + input); Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(input); @@ -141,4 +141,18 @@ public class PgpHelper { } } + public static String getPgpKeyContent(@NonNull CharSequence input) { + Log.dEscaped(Constants.TAG, "input: " + input); + + Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(input); + if (matcher.matches()) { + String text = matcher.group(1); + text = fixPgpMessage(text); + + Log.dEscaped(Constants.TAG, "input fixed: " + text); + return text; + } + return null; + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java index 25445a6c4..45641b33a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java @@ -321,6 +321,8 @@ public class PgpSignEncryptOperation extends BaseOperation { ArmoredOutputStream detachedArmorOut = null; BCPGOutputStream detachedBcpgOut = null; + long opTime, startTime = System.currentTimeMillis(); + try { if (enableEncryption) { @@ -516,6 +518,10 @@ public class PgpSignEncryptOperation extends BaseOperation { } } + opTime = System.currentTimeMillis() -startTime; + Log.d(Constants.TAG, "sign/encrypt time taken: " + String.format("%.2f", + opTime / 1000.0) + "s"); + // closing outputs // NOTE: closing needs to be done in the correct order! if (encryptionOut != null) { @@ -559,6 +565,7 @@ public class PgpSignEncryptOperation extends BaseOperation { log.add(LogType.MSG_PSE_OK, indent); PgpSignEncryptResult result = new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_OK, log); + result.mOperationTime = opTime; if (detachedByteOut != null) { try { detachedByteOut.flush(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 0f90f8141..56081dab9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -54,7 +54,7 @@ import java.io.IOException; */ public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 13; + private static final int DATABASE_VERSION = 14; static Boolean apgHack = false; private Context mContext; @@ -74,12 +74,14 @@ public class KeychainDatabase extends SQLiteOpenHelper { "CREATE TABLE IF NOT EXISTS keyrings_public (" + KeyRingsColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY," + KeyRingsColumns.KEY_RING_DATA + " BLOB" + + "PRIMARY KEY(" + KeyRingsColumns.MASTER_KEY_ID + ")," + ")"; private static final String CREATE_KEYRINGS_SECRET = "CREATE TABLE IF NOT EXISTS keyrings_secret (" + KeyRingsColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY," + KeyRingsColumns.KEY_RING_DATA + " BLOB," + + "PRIMARY KEY(" + KeyRingsColumns.MASTER_KEY_ID + ")," + "FOREIGN KEY(" + KeyRingsColumns.MASTER_KEY_ID + ") " + "REFERENCES keyrings_public(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + ")"; @@ -220,6 +222,13 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS_ACCOUNTS); db.execSQL(CREATE_API_APPS_ALLOWED_KEYS); + + db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ");"); + db.execSQL("CREATE INDEX uids_by_rank ON user_packets (" + UserPacketsColumns.RANK + ", " + + UserPacketsColumns.USER_ID + ", " + UserPacketsColumns.MASTER_KEY_ID + ");"); + db.execSQL("CREATE INDEX verified_certs ON certs (" + + CertsColumns.VERIFIED + ", " + CertsColumns.MASTER_KEY_ID + ");"); + } @Override @@ -291,13 +300,14 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL("DELETE FROM api_accounts WHERE key_id BETWEEN 0 AND 3"); case 12: db.execSQL(CREATE_UPDATE_KEYS); - if (oldVersion == 10) { - // no consolidate if we are updating from 10, we're just here for - // the api_accounts fix and the new update keys table - return; - } case 13: // do nothing here, just consolidate + case 14: + db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ");"); + db.execSQL("CREATE INDEX uids_by_rank ON user_packets (" + UserPacketsColumns.RANK + ", " + + UserPacketsColumns.USER_ID + ", " + UserPacketsColumns.MASTER_KEY_ID + ");"); + db.execSQL("CREATE INDEX verified_certs ON certs (" + + CertsColumns.VERIFIED + ", " + CertsColumns.MASTER_KEY_ID + ");"); } @@ -310,6 +320,10 @@ public class KeychainDatabase extends SQLiteOpenHelper { @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // Downgrade is ok for the debug version, makes it easier to work with branches + if (Constants.DEBUG) { + return; + } // NOTE: downgrading the database is explicitly not allowed to prevent // someone from exploiting old bugs to export the database throw new RuntimeException("Downgrading the database is not allowed!"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index d722fa9e7..104343074 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -303,14 +303,14 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT); projectionMap.put(KeyRings.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID); projectionMap.put(KeyRings.HAS_DUPLICATE_USER_ID, - "(SELECT COUNT (*) FROM " + Tables.USER_PACKETS + " AS dups" + "(EXISTS (SELECT * FROM " + Tables.USER_PACKETS + " AS dups" + " WHERE dups." + UserPackets.MASTER_KEY_ID + " != " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " AND dups." + UserPackets.RANK + " = 0" + " AND dups." + UserPackets.USER_ID + " = "+ Tables.USER_PACKETS + "." + UserPackets.USER_ID - + ") AS " + KeyRings.HAS_DUPLICATE_USER_ID); - projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED); + + ")) AS " + KeyRings.HAS_DUPLICATE_USER_ID); + projectionMap.put(KeyRings.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); projectionMap.put(KeyRings.PUBKEY_DATA, Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA + " AS " + KeyRings.PUBKEY_DATA); @@ -319,10 +319,8 @@ public class KeychainProvider extends ContentProvider { + " AS " + KeyRings.PRIVKEY_DATA); projectionMap.put(KeyRings.HAS_SECRET, Tables.KEYS + "." + KeyRings.HAS_SECRET); projectionMap.put(KeyRings.HAS_ANY_SECRET, - "(EXISTS (SELECT * FROM " + Tables.KEY_RINGS_SECRET - + " WHERE " + Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID - + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID - + ")) AS " + KeyRings.HAS_ANY_SECRET); + "(" + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NOT NULL)" + + " AS " + KeyRings.HAS_ANY_SECRET); projectionMap.put(KeyRings.HAS_ENCRYPT, "kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT); projectionMap.put(KeyRings.HAS_SIGN, @@ -363,7 +361,7 @@ public class KeychainProvider extends ContentProvider { + " = " + Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.MASTER_KEY_ID + ")" : "") - + (plist.contains(KeyRings.PRIVKEY_DATA) ? + + (plist.contains(KeyRings.PRIVKEY_DATA) || plist.contains(KeyRings.HAS_ANY_SECRET) ? " LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON (" + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = " @@ -712,20 +710,45 @@ public class KeychainProvider extends ContentProvider { } SQLiteDatabase db = getDb().getReadableDatabase(); + Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy); if (cursor != null) { // Tell the cursor what uri to watch, so it knows when its source data changes cursor.setNotificationUri(getContext().getContentResolver(), uri); } + Log.d(Constants.TAG, + "Query: " + qb.buildQuery(projection, selection, null, null, orderBy, null)); + if (Constants.DEBUG && Constants.DEBUG_LOG_DB_QUERIES) { - Log.d(Constants.TAG, - "Query: " - + qb.buildQuery(projection, selection, selectionArgs, null, null, - orderBy, null)); Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(cursor)); } + if (Constants.DEBUG && Constants.DEBUG_EXPLAIN_QUERIES) { + String rawQuery = qb.buildQuery(projection, selection, groupBy, having, orderBy, null); + Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + rawQuery, selectionArgs); + + // this is a debugging feature, we can be a little careless + explainCursor.moveToFirst(); + + StringBuilder line = new StringBuilder(); + for (int i = 0; i < explainCursor.getColumnCount(); i++) { + line.append(explainCursor.getColumnName(i)).append(", "); + } + Log.d(Constants.TAG, line.toString()); + + while (!explainCursor.isAfterLast()) { + line = new StringBuilder(); + for (int i = 0; i < explainCursor.getColumnCount(); i++) { + line.append(explainCursor.getString(i)).append(", "); + } + Log.d(Constants.TAG, line.toString()); + explainCursor.moveToNext(); + } + + explainCursor.close(); + } + return cursor; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index e7709e58e..b810f5a6a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -25,6 +25,7 @@ import android.net.Uri; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; @@ -34,13 +35,15 @@ import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpMetadata; import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.util.OpenPgpApi; +import org.spongycastle.bcpg.ArmoredOutputStream; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel; import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; -import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation; +import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; @@ -238,10 +241,8 @@ public class OpenPgpService extends RemoteService { PendingIntent.FLAG_CANCEL_CURRENT); } - private Intent signImpl(Intent data, ParcelFileDescriptor input, - ParcelFileDescriptor output, boolean cleartextSign) { - InputStream is = null; - OutputStream os = null; + private Intent signImpl(Intent data, InputStream inputStream, + OutputStream outputStream, boolean cleartextSign) { try { boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); @@ -277,14 +278,13 @@ public class OpenPgpService extends RemoteService { } // Get Input- and OutputStream from ParcelFileDescriptor - is = new ParcelFileDescriptor.AutoCloseInputStream(input); - if (cleartextSign) { + if (!cleartextSign) { // output stream only needed for cleartext signatures, // detached signatures are returned as extra - os = new ParcelFileDescriptor.AutoCloseOutputStream(output); + outputStream = null; } - long inputLength = is.available(); - InputData inputData = new InputData(is, inputLength); + long inputLength = inputStream.available(); + InputData inputData = new InputData(inputStream, inputLength); CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data); if (inputParcel == null) { @@ -298,7 +298,7 @@ public class OpenPgpService extends RemoteService { // execute PGP operation! PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null); - PgpSignEncryptResult pgpResult = pse.execute(pseInput, inputParcel, inputData, os); + PgpSignEncryptResult pgpResult = pse.execute(pseInput, inputParcel, inputData, outputStream); if (pgpResult.isPending()) { @@ -330,28 +330,11 @@ public class OpenPgpService extends RemoteService { new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException when closing InputStream", e); - } - } - if (os != null) { - try { - os.close(); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException when closing OutputStream", e); - } - } } } - private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input, - ParcelFileDescriptor output, boolean sign) { - InputStream is = null; - OutputStream os = null; + private Intent encryptAndSignImpl(Intent data, InputStream inputStream, + OutputStream outputStream, boolean sign) { try { boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); String originalFilename = data.getStringExtra(OpenPgpApi.EXTRA_ORIGINAL_FILENAME); @@ -383,13 +366,9 @@ public class OpenPgpService extends RemoteService { } } - // build InputData and write into OutputStream - // Get Input- and OutputStream from ParcelFileDescriptor - is = new ParcelFileDescriptor.AutoCloseInputStream(input); - os = new ParcelFileDescriptor.AutoCloseOutputStream(output); - - long inputLength = is.available(); - InputData inputData = new InputData(is, inputLength, originalFilename); + // TODO this is not correct! + long inputLength = inputStream.available(); + InputData inputData = new InputData(inputStream, inputLength, originalFilename); PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel(); pseInput.setEnableAsciiArmorOutput(asciiArmor) @@ -455,7 +434,7 @@ public class OpenPgpService extends RemoteService { PgpSignEncryptOperation op = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null); // execute PGP operation! - PgpSignEncryptResult pgpResult = op.execute(pseInput, inputParcel, inputData, os); + PgpSignEncryptResult pgpResult = op.execute(pseInput, inputParcel, inputData, outputStream); if (pgpResult.isPending()) { RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel(); @@ -482,37 +461,15 @@ public class OpenPgpService extends RemoteService { new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException when closing InputStream", e); - } - } - if (os != null) { - try { - os.close(); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException when closing OutputStream", e); - } - } } } - private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor inputDescriptor, - ParcelFileDescriptor output, boolean decryptMetadataOnly) { - InputStream inputStream = null; - OutputStream outputStream = null; + private Intent decryptAndVerifyImpl(Intent data, InputStream inputStream, + OutputStream outputStream, boolean decryptMetadataOnly) { try { - // Get Input- and OutputStream from ParcelFileDescriptor - inputStream = new ParcelFileDescriptor.AutoCloseInputStream(inputDescriptor); - // output is optional, e.g., for verifying detached signatures - if (decryptMetadataOnly || output == null) { + if (decryptMetadataOnly) { outputStream = null; - } else { - outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(output); } String currentPkg = getCurrentCallingPackage(); @@ -538,6 +495,7 @@ public class OpenPgpService extends RemoteService { PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(this, mProviderHelper, null); + // TODO this is not correct! long inputLength = inputStream.available(); InputData inputData = new InputData(inputStream, inputLength); @@ -604,6 +562,7 @@ public class OpenPgpService extends RemoteService { // case RESULT_NOT_ENCRYPTED, but a signature, fallback to deprecated signatureOnly variable if (decryptionResult.getResult() == OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED && signatureResult.getResult() != OpenPgpSignatureResult.RESULT_NO_SIGNATURE) { + // noinspection deprecation, TODO signatureResult.setSignatureOnly(true); } @@ -665,35 +624,40 @@ public class OpenPgpService extends RemoteService { result.putExtra(OpenPgpApi.RESULT_ERROR, new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; - } finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException when closing InputStream", e); - } - } - if (outputStream != null) { - try { - outputStream.close(); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException when closing OutputStream", e); - } - } } } - private Intent getKeyImpl(Intent data) { + private Intent getKeyImpl(Intent data, OutputStream outputStream) { try { long masterKeyId = data.getLongExtra(OpenPgpApi.EXTRA_KEY_ID, 0); try { // try to find key, throws NotFoundException if not in db! - mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId); + CanonicalizedPublicKeyRing keyRing = + mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId); Intent result = new Intent(); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + + boolean requestedKeyData = outputStream != null; + if (requestedKeyData) { + boolean requestAsciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, false); + + try { + if (requestAsciiArmor) { + outputStream = new ArmoredOutputStream(outputStream); + } + keyRing.encode(outputStream); + } finally { + try { + outputStream.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing OutputStream", e); + } + } + } + // also return PendingIntent that opens the key view activity result.putExtra(OpenPgpApi.RESULT_INTENT, getShowKeyPendingIntent(masterKeyId)); @@ -821,7 +785,7 @@ public class OpenPgpService extends RemoteService { OpenPgpError error = new OpenPgpError (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!\n" + "used API version: " + data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) + "\n" - + "supported API versions: " + supportedVersions.toString()); + + "supported API versions: " + supportedVersions); result.putExtra(OpenPgpApi.RESULT_ERROR, error); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; @@ -850,68 +814,88 @@ public class OpenPgpService extends RemoteService { return mBinder; } + @Nullable + protected Intent executeInternal( + @NonNull Intent data, + @Nullable ParcelFileDescriptor input, + @Nullable ParcelFileDescriptor output) { - protected Intent executeInternal(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) { - try { - Intent errorResult = checkRequirements(data); - if (errorResult != null) { - return errorResult; - } + OutputStream outputStream = + (output != null) ? new ParcelFileDescriptor.AutoCloseOutputStream(output) : null; + InputStream inputStream = + (input != null) ? new ParcelFileDescriptor.AutoCloseInputStream(input) : null; - String action = data.getAction(); - switch (action) { - case OpenPgpApi.ACTION_CLEARTEXT_SIGN: { - return signImpl(data, input, output, true); - } - case OpenPgpApi.ACTION_SIGN: { - // DEPRECATED: same as ACTION_CLEARTEXT_SIGN - Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!"); - return signImpl(data, input, output, true); - } - case OpenPgpApi.ACTION_DETACHED_SIGN: { - return signImpl(data, input, output, false); - } - case OpenPgpApi.ACTION_ENCRYPT: { - return encryptAndSignImpl(data, input, output, false); - } - case OpenPgpApi.ACTION_SIGN_AND_ENCRYPT: { - return encryptAndSignImpl(data, input, output, true); - } - case OpenPgpApi.ACTION_DECRYPT_VERIFY: { - return decryptAndVerifyImpl(data, input, output, false); - } - case OpenPgpApi.ACTION_DECRYPT_METADATA: { - return decryptAndVerifyImpl(data, input, output, true); - } - case OpenPgpApi.ACTION_GET_SIGN_KEY_ID: { - return getSignKeyIdImpl(data); - } - case OpenPgpApi.ACTION_GET_KEY_IDS: { - return getKeyIdsImpl(data); - } - case OpenPgpApi.ACTION_GET_KEY: { - return getKeyImpl(data); - } - default: { - return null; - } - } + try { + return executeInternalWithStreams(data, inputStream, outputStream); } finally { // always close input and output file descriptors even in error cases - if (input != null) { + if (inputStream != null) { try { - input.close(); + inputStream.close(); } catch (IOException e) { Log.e(Constants.TAG, "IOException when closing input ParcelFileDescriptor", e); } } - if (output != null) { + if (outputStream != null) { try { - output.close(); + outputStream.close(); } catch (IOException e) { Log.e(Constants.TAG, "IOException when closing output ParcelFileDescriptor", e); } } } } + + @Nullable + protected Intent executeInternalWithStreams( + @NonNull Intent data, + @Nullable InputStream inputStream, + @Nullable OutputStream outputStream) { + + Intent errorResult = checkRequirements(data); + if (errorResult != null) { + return errorResult; + } + + String action = data.getAction(); + switch (action) { + case OpenPgpApi.ACTION_CLEARTEXT_SIGN: { + return signImpl(data, inputStream, outputStream, true); + } + case OpenPgpApi.ACTION_SIGN: { + // DEPRECATED: same as ACTION_CLEARTEXT_SIGN + Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!"); + return signImpl(data, inputStream, outputStream, true); + } + case OpenPgpApi.ACTION_DETACHED_SIGN: { + return signImpl(data, inputStream, outputStream, false); + } + case OpenPgpApi.ACTION_ENCRYPT: { + return encryptAndSignImpl(data, inputStream, outputStream, false); + } + case OpenPgpApi.ACTION_SIGN_AND_ENCRYPT: { + return encryptAndSignImpl(data, inputStream, outputStream, true); + } + case OpenPgpApi.ACTION_DECRYPT_VERIFY: { + return decryptAndVerifyImpl(data, inputStream, outputStream, false); + } + case OpenPgpApi.ACTION_DECRYPT_METADATA: { + return decryptAndVerifyImpl(data, inputStream, outputStream, true); + } + case OpenPgpApi.ACTION_GET_SIGN_KEY_ID: { + return getSignKeyIdImpl(data); + } + case OpenPgpApi.ACTION_GET_KEY_IDS: { + return getKeyIdsImpl(data); + } + case OpenPgpApi.ACTION_GET_KEY: { + return getKeyImpl(data, outputStream); + } + default: { + return null; + } + } + + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/BenchmarkInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/BenchmarkInputParcel.java new file mode 100644 index 000000000..cfbdfefff --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/BenchmarkInputParcel.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.service; + +import android.os.Parcel; +import android.os.Parcelable; + + +public class BenchmarkInputParcel implements Parcelable { + + public BenchmarkInputParcel() { + } + + protected BenchmarkInputParcel(Parcel in) { + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + } + + public static final Creator<BenchmarkInputParcel> CREATOR = new Creator<BenchmarkInputParcel>() { + @Override + public BenchmarkInputParcel createFromParcel(Parcel in) { + return new BenchmarkInputParcel(in); + } + + @Override + public BenchmarkInputParcel[] newArray(int size) { + return new BenchmarkInputParcel[size]; + } + }; +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java index ee953b060..cf51e3b55 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java @@ -29,6 +29,7 @@ import android.os.RemoteException; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.operations.BaseOperation; +import org.sufficientlysecure.keychain.operations.BenchmarkOperation; import org.sufficientlysecure.keychain.operations.CertifyOperation; import org.sufficientlysecure.keychain.operations.ConsolidateOperation; import org.sufficientlysecure.keychain.operations.DeleteOperation; @@ -135,6 +136,8 @@ public class KeychainService extends Service implements Progressable { op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis), outerThis); } else if (inputParcel instanceof InputDataParcel) { op = new InputDataOperation(outerThis, new ProviderHelper(outerThis), outerThis); + } else if (inputParcel instanceof BenchmarkInputParcel) { + op = new BenchmarkOperation(outerThis, new ProviderHelper(outerThis), outerThis); } else { throw new AssertionError("Unrecognized input parcel in KeychainService!"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java index 8aebae7aa..122eb6cf4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java @@ -59,11 +59,12 @@ public class KeyserverSyncAdapterService extends Service { // time since last update after which a key should be updated again, in s public static final long KEY_UPDATE_LIMIT = Constants.DEBUG_KEYSERVER_SYNC ? 1 : TimeUnit.DAYS.toSeconds(7); - // time by which a sync is postponed in case of a + // time by which a sync is postponed in case screen is on public static final long SYNC_POSTPONE_TIME = Constants.DEBUG_KEYSERVER_SYNC ? 30 * 1000 : TimeUnit.MINUTES.toMillis(5); // Time taken by Orbot before a new circuit is created - public static final int ORBOT_CIRCUIT_TIMEOUT = (int) TimeUnit.MINUTES.toMillis(10); + public static final int ORBOT_CIRCUIT_TIMEOUT_SECONDS = + Constants.DEBUG_KEYSERVER_SYNC ? 2 : (int) TimeUnit.MINUTES.toSeconds(10); private static final String ACTION_IGNORE_TOR = "ignore_tor"; @@ -77,10 +78,14 @@ public class KeyserverSyncAdapterService extends Service { @Override public int onStartCommand(final Intent intent, int flags, final int startId) { + if (intent == null || intent.getAction() == null) { + // introduced due to https://github.com/open-keychain/open-keychain/issues/1573 + return START_NOT_STICKY; // we can't act on this Intent and don't want it redelivered + } switch (intent.getAction()) { case ACTION_CANCEL: { mCancelled.set(true); - break; + return START_NOT_STICKY; } // the reason for the separation betweyeen SYNC_NOW and UPDATE_ALL is so that starting // the sync directly from the notification is possible while the screen is on with @@ -92,44 +97,47 @@ public class KeyserverSyncAdapterService extends Service { Constants.PROVIDER_AUTHORITY, new Bundle() ); - break; + return START_NOT_STICKY; } case ACTION_UPDATE_ALL: { // does not check for screen on/off - asyncKeyUpdate(this, new CryptoInputParcel()); - break; + asyncKeyUpdate(this, new CryptoInputParcel(), startId); + // we depend on handleUpdateResult to call stopSelf when it is no longer necessary + // for the intent to be redelivered + return START_REDELIVER_INTENT; } case ACTION_IGNORE_TOR: { NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); - asyncKeyUpdate(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy())); - break; + asyncKeyUpdate(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy()), + startId); + // we depend on handleUpdateResult to call stopSelf when it is no longer necessary + // for the intent to be redelivered + return START_REDELIVER_INTENT; } case ACTION_START_ORBOT: { - NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + NotificationManager manager = (NotificationManager) + getSystemService(NOTIFICATION_SERVICE); manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); + Intent startOrbot = new Intent(this, OrbotRequiredDialogActivity.class); startOrbot.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_START_ORBOT, true); + Messenger messenger = new Messenger( new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case OrbotRequiredDialogActivity.MESSAGE_ORBOT_STARTED: { - asyncKeyUpdate(KeyserverSyncAdapterService.this, - new CryptoInputParcel()); - break; - } - case OrbotRequiredDialogActivity.MESSAGE_ORBOT_IGNORE: { - asyncKeyUpdate(KeyserverSyncAdapterService.this, - new CryptoInputParcel( - ParcelableProxy.getForNoProxy())); + startServiceWithUpdateAll(); break; } + case OrbotRequiredDialogActivity.MESSAGE_ORBOT_IGNORE: case OrbotRequiredDialogActivity.MESSAGE_DIALOG_CANCEL: { - // just stop service - stopSelf(); + // not possible since we proceed to Orbot's Activity + // directly, by starting OrbotRequiredDialogActivity with + // EXTRA_START_ORBOT set to true break; } } @@ -138,13 +146,17 @@ public class KeyserverSyncAdapterService extends Service { ); startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_MESSENGER, messenger); startActivity(startOrbot); - break; + // since we return START_NOT_STICKY, we also postpone the sync as a backup in case + // the service is killed before OrbotRequiredDialogActivity can get back to us + postponeSync(); + // if use START_REDELIVER_INTENT, we might annoy the user by repeatedly starting the + // Orbot Activity when our service is killed and restarted + return START_NOT_STICKY; } case ACTION_DISMISS_NOTIFICATION: { NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); - stopSelf(startId); - break; + return START_NOT_STICKY; } } return START_NOT_STICKY; @@ -167,10 +179,7 @@ public class KeyserverSyncAdapterService extends Service { boolean isScreenOn = pm.isScreenOn(); if (!isScreenOn) { - Intent serviceIntent = new Intent(KeyserverSyncAdapterService.this, - KeyserverSyncAdapterService.class); - serviceIntent.setAction(ACTION_UPDATE_ALL); - startService(serviceIntent); + startServiceWithUpdateAll(); } else { postponeSync(); } @@ -188,16 +197,24 @@ public class KeyserverSyncAdapterService extends Service { return new KeyserverSyncAdapter().getSyncAdapterBinder(); } - private void handleUpdateResult(ImportKeyResult result) { + /** + * Since we're returning START_REDELIVER_INTENT in onStartCommand, we need to remember to call + * stopSelf(int) to prevent the Intent from being redelivered if our work is already done + * + * @param result result of keyserver sync + * @param startId startId provided to the onStartCommand call which resulted in this sync + */ + private void handleUpdateResult(ImportKeyResult result, final int startId) { if (result.isPending()) { + Log.d(Constants.TAG, "Orbot required for sync but not running, attempting to start"); // result is pending due to Orbot not being started // try to start it silently, if disabled show notifications new OrbotHelper.SilentStartManager() { @Override protected void onOrbotStarted() { // retry the update - asyncKeyUpdate(KeyserverSyncAdapterService.this, - new CryptoInputParcel()); + startServiceWithUpdateAll(); + stopSelf(startId); // startServiceWithUpdateAll will deliver a new Intent } @Override @@ -207,16 +224,24 @@ public class KeyserverSyncAdapterService extends Service { (NotificationManager) getSystemService(NOTIFICATION_SERVICE); manager.notify(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT, getOrbotNoification(KeyserverSyncAdapterService.this)); + // further action on user interaction with notification, intent should not be + // redelivered, therefore: + stopSelf(startId); } }.startOrbotAndListen(this, false); + // if we're killed before we get a response from Orbot, we need the intent to be + // redelivered, so no stopSelf(int) here } else if (isUpdateCancelled()) { Log.d(Constants.TAG, "Keyserver sync cancelled, postponing by" + SYNC_POSTPONE_TIME + "ms"); postponeSync(); + // postponeSync creates a new intent, so we don't need this to be redelivered + stopSelf(startId); } else { Log.d(Constants.TAG, "Keyserver sync completed: Updated: " + result.mUpdatedKeys + " Failed: " + result.mBadKeys); - stopSelf(); + // key sync completed successfully, we can stop + stopSelf(startId); } } @@ -234,12 +259,12 @@ public class KeyserverSyncAdapterService extends Service { } private void asyncKeyUpdate(final Context context, - final CryptoInputParcel cryptoInputParcel) { + final CryptoInputParcel cryptoInputParcel, final int startId) { new Thread(new Runnable() { @Override public void run() { ImportKeyResult result = updateKeysFromKeyserver(context, cryptoInputParcel); - handleUpdateResult(result); + handleUpdateResult(result, startId); } }).start(); } @@ -278,7 +303,6 @@ public class KeyserverSyncAdapterService extends Service { ); } - /** * will perform a staggered update of user's keys using delays to ensure new Tor circuits, as * performed by parcimonie. Relevant issue and method at: @@ -290,17 +314,31 @@ public class KeyserverSyncAdapterService extends Service { CryptoInputParcel cryptoInputParcel) { Log.d(Constants.TAG, "Starting staggered update"); // final int WEEK_IN_SECONDS = (int) TimeUnit.DAYS.toSeconds(7); + // we are limiting our randomness to ORBOT_CIRCUIT_TIMEOUT_SECONDS for now final int WEEK_IN_SECONDS = 0; + ImportOperation.KeyImportAccumulator accumulator = new ImportOperation.KeyImportAccumulator(keyList.size(), null); + + // so that the first key can be updated without waiting. This is so that there isn't a + // large gap between a "Start Orbot" notification and the next key update + boolean first = true; + for (ParcelableKeyRing keyRing : keyList) { int waitTime; int staggeredTime = new Random().nextInt(1 + 2 * (WEEK_IN_SECONDS / keyList.size())); - if (staggeredTime >= ORBOT_CIRCUIT_TIMEOUT) { + if (staggeredTime >= ORBOT_CIRCUIT_TIMEOUT_SECONDS) { waitTime = staggeredTime; } else { - waitTime = ORBOT_CIRCUIT_TIMEOUT + new Random().nextInt(ORBOT_CIRCUIT_TIMEOUT); + waitTime = ORBOT_CIRCUIT_TIMEOUT_SECONDS + + new Random().nextInt(1 + ORBOT_CIRCUIT_TIMEOUT_SECONDS); + } + + if (first) { + waitTime = 0; + first = false; } + Log.d(Constants.TAG, "Updating key with fingerprint " + keyRing.mExpectedFingerprint + " with a wait time of " + waitTime + "s"); try { @@ -362,13 +400,15 @@ public class KeyserverSyncAdapterService extends Service { ); ArrayList<Long> ignoreMasterKeyIds = new ArrayList<>(); - while (updatedKeysCursor.moveToNext()) { + while (updatedKeysCursor != null && updatedKeysCursor.moveToNext()) { long masterKeyId = updatedKeysCursor.getLong(INDEX_UPDATED_KEYS_MASTER_KEY_ID); Log.d(Constants.TAG, "Keyserver sync: Ignoring {" + masterKeyId + "} last updated at {" + updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s"); ignoreMasterKeyIds.add(masterKeyId); } - updatedKeysCursor.close(); + if (updatedKeysCursor != null) { + updatedKeysCursor.close(); + } // 2. Make a list of public keys which should be updated final int INDEX_MASTER_KEY_ID = 0; @@ -413,7 +453,7 @@ public class KeyserverSyncAdapterService extends Service { /** * will cancel an update already in progress. We send an Intent to cancel it instead of simply - * modifying a static variable sync the service is running in a process that is different from + * modifying a static variable since the service is running in a process that is different from * the default application process where the UI code runs. * * @param context used to send an Intent to the service requesting cancellation. @@ -491,6 +531,12 @@ public class KeyserverSyncAdapterService extends Service { } } + private void startServiceWithUpdateAll() { + Intent serviceIntent = new Intent(this, KeyserverSyncAdapterService.class); + serviceIntent.setAction(ACTION_UPDATE_ALL); + this.startService(serviceIntent); + } + // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android private Bitmap getBitmap(int resId, Context context) { int mLargeIconWidth = (int) context.getResources().getDimension( diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java index 0d8569fe6..849418905 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Passphrase; +import java.net.Proxy; import java.nio.ByteBuffer; import java.util.Date; import java.util.HashMap; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java index a2aff2029..cf7a0b1d7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -200,7 +200,7 @@ public class DecryptActivity extends BaseActivity { } // clean up ascii armored message, fixing newlines and stuff - String cleanedText = PgpHelper.getPgpContent(text); + String cleanedText = PgpHelper.getPgpMessageContent(text); if (cleanedText == null) { return null; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index 1d2bf6b9c..c45a641e0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -24,12 +24,14 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.content.ClipDescription; import android.content.Context; import android.content.Intent; import android.content.pm.LabeledIntent; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.graphics.Point; @@ -37,9 +39,12 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -91,6 +96,22 @@ import org.sufficientlysecure.keychain.util.ParcelableHashMap; import org.sufficientlysecure.keychain.util.Preferences; +/** Displays a list of decrypted inputs. + * + * This class has a complex control flow to manage its input URIs. Each URI + * which is in mInputUris is also in exactly one of mPendingInputUris, + * mCancelledInputUris, mCurrentInputUri, or a key in mInputDataResults. + * + * Processing of URIs happens using a looping approach: + * - There is always exactly one method running which works on mCurrentInputUri + * - Processing starts in cryptoOperation(), which pops a new mCurrentInputUri + * from the list of mPendingInputUris. + * - Once a mCurrentInputUri is finished processing, it should be set to null and + * control handed back to cryptoOperation() + * - Control flow can move through asynchronous calls, and resume in callbacks + * like onActivityResult() or onPermissionRequestResult(). + * + */ public class DecryptListFragment extends QueueingCryptoOperationFragment<InputDataParcel,InputDataResult> implements OnMenuItemClickListener { @@ -102,7 +123,7 @@ public class DecryptListFragment public static final String ARG_CAN_DELETE = "can_delete"; private static final int REQUEST_CODE_OUTPUT = 0x00007007; - public static final String ARG_CURRENT_URI = "current_uri"; + private static final int REQUEST_PERMISSION_READ_EXTERNAL_STORAGE = 12; private ArrayList<Uri> mInputUris; private HashMap<Uri, InputDataResult> mInputDataResults; @@ -118,7 +139,7 @@ public class DecryptListFragment /** * Creates new instance of this fragment */ - public static DecryptListFragment newInstance(ArrayList<Uri> uris, boolean canDelete) { + public static DecryptListFragment newInstance(@NonNull ArrayList<Uri> uris, boolean canDelete) { DecryptListFragment frag = new DecryptListFragment(); Bundle args = new Bundle(); @@ -175,9 +196,12 @@ public class DecryptListFragment outState.putParcelable(ARG_RESULTS, new ParcelableHashMap<>(results)); outState.putParcelable(ARG_OUTPUT_URIS, new ParcelableHashMap<>(mInputDataResults)); outState.putParcelableArrayList(ARG_CANCELLED_URIS, mCancelledInputUris); - outState.putParcelable(ARG_CURRENT_URI, mCurrentInputUri); outState.putBoolean(ARG_CAN_DELETE, mCanDelete); + // this does not save mCurrentInputUri - if anything is being + // processed at fragment recreation time, the operation in + // progress will be lost! + } @Override @@ -189,20 +213,21 @@ public class DecryptListFragment ArrayList<Uri> inputUris = getArguments().getParcelableArrayList(ARG_INPUT_URIS); ArrayList<Uri> cancelledUris = args.getParcelableArrayList(ARG_CANCELLED_URIS); ParcelableHashMap<Uri,InputDataResult> results = args.getParcelable(ARG_RESULTS); - Uri currentInputUri = args.getParcelable(ARG_CURRENT_URI); mCanDelete = args.getBoolean(ARG_CAN_DELETE, false); - displayInputUris(inputUris, currentInputUri, cancelledUris, + displayInputUris(inputUris, cancelledUris, results != null ? results.getMap() : null ); } - private void displayInputUris(ArrayList<Uri> inputUris, Uri currentInputUri, - ArrayList<Uri> cancelledUris, HashMap<Uri,InputDataResult> results) { + private void displayInputUris( + ArrayList<Uri> inputUris, + ArrayList<Uri> cancelledUris, + HashMap<Uri,InputDataResult> results) { mInputUris = inputUris; - mCurrentInputUri = currentInputUri; + mCurrentInputUri = null; mInputDataResults = results != null ? results : new HashMap<Uri,InputDataResult>(inputUris.size()); mCancelledInputUris = cancelledUris != null ? cancelledUris : new ArrayList<Uri>(); @@ -211,30 +236,23 @@ public class DecryptListFragment for (final Uri uri : inputUris) { mAdapter.add(uri); - if (uri.equals(mCurrentInputUri)) { + boolean uriIsCancelled = mCancelledInputUris.contains(uri); + if (uriIsCancelled) { + mAdapter.setCancelled(uri, true); continue; } - if (mCancelledInputUris.contains(uri)) { - mAdapter.setCancelled(uri, new OnClickListener() { - @Override - public void onClick(View v) { - retryUri(uri); - } - }); + boolean uriHasResult = results != null && results.containsKey(uri); + if (uriHasResult) { + processResult(uri); continue; } - if (results != null && results.containsKey(uri)) { - processResult(uri); - } else { - mPendingInputUris.add(uri); - } + mPendingInputUris.add(uri); } - if (mCurrentInputUri == null) { - cryptoOperation(); - } + // check if there are any pending input uris + cryptoOperation(); } @Override @@ -364,12 +382,7 @@ public class DecryptListFragment mCurrentInputUri = null; mCancelledInputUris.add(uri); - mAdapter.setCancelled(uri, new OnClickListener() { - @Override - public void onClick(View v) { - retryUri(uri); - } - }); + mAdapter.setCancelled(uri, true); cryptoOperation(); @@ -463,8 +476,8 @@ public class DecryptListFragment mPendingInputUris.add(uri); mAdapter.resetItemData(uri); + // check if there are any pending input uris cryptoOperation(); - } public void displayBottomSheet(final InputDataResult result, final int index) { @@ -585,6 +598,11 @@ public class DecryptListFragment @Override public InputDataParcel createOperationInput() { + Activity activity = getActivity(); + if (activity == null) { + return null; + } + if (mCurrentInputUri == null) { if (mPendingInputUris.isEmpty()) { // nothing left to do @@ -594,7 +612,11 @@ public class DecryptListFragment mCurrentInputUri = mPendingInputUris.remove(0); } - Log.d(Constants.TAG, "mInputUri=" + mCurrentInputUri); + Log.d(Constants.TAG, "mCurrentInputUri=" + mCurrentInputUri); + + if ( ! checkAndRequestReadPermission(activity, mCurrentInputUri)) { + return null; + } PgpDecryptVerifyInputParcel decryptInput = new PgpDecryptVerifyInputParcel() .setAllowSymmetricDecryption(true); @@ -602,6 +624,87 @@ public class DecryptListFragment } + /** + * Request READ_EXTERNAL_STORAGE permission on Android >= 6.0 to read content from "file" Uris. + * + * This method returns true on Android < 6, or if permission is already granted. It + * requests the permission and returns false otherwise, taking over responsibility + * for mCurrentInputUri. + * + * see https://commonsware.com/blog/2015/10/07/runtime-permissions-files-action-send.html + */ + private boolean checkAndRequestReadPermission(Activity activity, final Uri uri) { + if ( ! "file".equals(uri.getScheme())) { + return true; + } + + if (Build.VERSION.SDK_INT < VERSION_CODES.M) { + return true; + } + + // Additional check due to https://commonsware.com/blog/2015/11/09/you-cannot-hold-nonexistent-permissions.html + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + return true; + } + + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) + == PackageManager.PERMISSION_GRANTED) { + return true; + } + + requestPermissions( + new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, + REQUEST_PERMISSION_READ_EXTERNAL_STORAGE); + + return false; + + } + + @Override + public void onRequestPermissionsResult(int requestCode, + @NonNull String[] permissions, + @NonNull int[] grantResults) { + + if (requestCode != REQUEST_PERMISSION_READ_EXTERNAL_STORAGE) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + return; + } + + boolean permissionWasGranted = grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED; + + if (permissionWasGranted) { + + // permission granted -> retry all cancelled file uris + for (Uri uri : mCancelledInputUris) { + if ( ! "file".equals(uri.getScheme())) { + continue; + } + mCancelledInputUris.remove(uri); + mPendingInputUris.add(uri); + mAdapter.setCancelled(uri, false); + } + + } else { + + // permission denied -> cancel current, and all pending file uris + mCurrentInputUri = null; + for (final Uri uri : mPendingInputUris) { + if ( ! "file".equals(uri.getScheme())) { + continue; + } + mPendingInputUris.remove(uri); + mCancelledInputUris.add(uri); + mAdapter.setCancelled(uri, true); + } + + } + + // hand control flow back + cryptoOperation(); + + } + @Override public boolean onMenuItemClick(MenuItem menuItem) { if (mAdapter.mMenuClickedModel == null || !mAdapter.mMenuClickedModel.hasResult()) { @@ -780,8 +883,10 @@ public class DecryptListFragment return false; } ViewModel viewModel = (ViewModel) o; - return !(mInputUri != null ? !mInputUri.equals(viewModel.mInputUri) - : viewModel.mInputUri != null); + if (mInputUri == null) { + return viewModel.mInputUri == null; + } + return mInputUri.equals(viewModel.mInputUri); } // Depends on inputUri only @@ -1017,10 +1122,19 @@ public class DecryptListFragment notifyItemChanged(pos); } - public void setCancelled(Uri uri, OnClickListener retryListener) { + public void setCancelled(final Uri uri, boolean isCancelled) { ViewModel newModel = new ViewModel(uri); int pos = mDataset.indexOf(newModel); - mDataset.get(pos).setCancelled(retryListener); + if (isCancelled) { + mDataset.get(pos).setCancelled(new OnClickListener() { + @Override + public void onClick(View v) { + retryUri(uri); + } + }); + } else { + mDataset.get(pos).setCancelled(null); + } notifyItemChanged(pos); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index 201465b52..50dbc9a8b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -28,6 +28,7 @@ import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import android.widget.Toast; +import org.apache.james.mime4j.util.MimeUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; @@ -59,12 +60,15 @@ public class EncryptTextActivity extends EncryptActivity { extras = new Bundle(); } + String textData = extras.getString(EXTRA_TEXT); + boolean returnProcessText = false; + // When sending to OpenKeychain Encrypt via share menu if (Intent.ACTION_SEND.equals(action) && type != null) { Log.logDebugBundle(extras, "extras"); // When sending to OpenKeychain Encrypt via share menu - if ("text/plain".equals(type)) { + if ( ! MimeUtil.isSameMimeType("text/plain", type)) { Toast.makeText(this, R.string.toast_wrong_mimetype, Toast.LENGTH_LONG).show(); finish(); return; @@ -94,12 +98,33 @@ public class EncryptTextActivity extends EncryptActivity { } // handle like normal text encryption, override action and extras to later // executeServiceMethod ACTION_ENCRYPT_TEXT in main actions - extras.putString(EXTRA_TEXT, sharedText); + textData = sharedText; } } - String textData = extras.getString(EXTRA_TEXT); + // Android 6, PROCESS_TEXT Intent + if (Intent.ACTION_PROCESS_TEXT.equals(action) && type != null) { + + String sharedText = null; + if (extras.containsKey(Intent.EXTRA_PROCESS_TEXT)) { + sharedText = extras.getString(Intent.EXTRA_PROCESS_TEXT); + returnProcessText = true; + } else if (extras.containsKey(Intent.EXTRA_PROCESS_TEXT_READONLY)) { + sharedText = extras.getString(Intent.EXTRA_PROCESS_TEXT_READONLY); + } + + if (sharedText != null) { + if (sharedText.length() > Constants.TEXT_LENGTH_LIMIT) { + sharedText = sharedText.substring(0, Constants.TEXT_LENGTH_LIMIT); + Notify.create(this, R.string.snack_shared_text_too_long, Style.WARN).show(); + } + // handle like normal text encryption, override action and extras to later + // executeServiceMethod ACTION_ENCRYPT_TEXT in main actions + textData = sharedText; + } + } + if (textData == null) { textData = ""; } @@ -107,7 +132,7 @@ public class EncryptTextActivity extends EncryptActivity { if (savedInstanceState == null) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - EncryptTextFragment encryptFragment = EncryptTextFragment.newInstance(textData); + EncryptTextFragment encryptFragment = EncryptTextFragment.newInstance(textData, returnProcessText); transaction.replace(R.id.encrypt_text_container, encryptFragment); transaction.commit(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index 4ce241c02..10d88253d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -56,8 +56,10 @@ public class EncryptTextFragment public static final String ARG_TEXT = "text"; public static final String ARG_USE_COMPRESSION = "use_compression"; + public static final String ARG_RETURN_PROCESS_TEXT = "return_process_text"; private boolean mShareAfterEncrypt; + private boolean mReturnProcessTextAfterEncrypt; private boolean mUseCompression; private boolean mHiddenRecipients = false; @@ -66,11 +68,12 @@ public class EncryptTextFragment /** * Creates new instance of this fragment */ - public static EncryptTextFragment newInstance(String text) { + public static EncryptTextFragment newInstance(String text, boolean returnProcessTextAfterEncrypt) { EncryptTextFragment frag = new EncryptTextFragment(); Bundle args = new Bundle(); args.putString(ARG_TEXT, text); + args.putBoolean(ARG_RETURN_PROCESS_TEXT, returnProcessTextAfterEncrypt); frag.setArguments(args); return frag; @@ -128,6 +131,7 @@ public class EncryptTextFragment super.onCreate(savedInstanceState); if (savedInstanceState == null) { mMessage = getArguments().getString(ARG_TEXT); + mReturnProcessTextAfterEncrypt = getArguments().getBoolean(ARG_RETURN_PROCESS_TEXT, false); } Preferences prefs = Preferences.getPreferences(getActivity()); @@ -151,6 +155,12 @@ public class EncryptTextFragment inflater.inflate(R.menu.encrypt_text_fragment, menu); menu.findItem(R.id.check_enable_compression).setChecked(mUseCompression); + + if (mReturnProcessTextAfterEncrypt) { + menu.findItem(R.id.encrypt_paste).setVisible(true); + menu.findItem(R.id.encrypt_copy).setVisible(false); + menu.findItem(R.id.encrypt_share).setVisible(false); + } } @Override @@ -177,6 +187,11 @@ public class EncryptTextFragment cryptoOperation(new CryptoInputParcel(new Date())); break; } + case R.id.encrypt_paste: { + hideKeyboard(); + cryptoOperation(new CryptoInputParcel(new Date())); + break; + } default: { return super.onOptionsItemSelected(item); } @@ -328,6 +343,11 @@ public class EncryptTextFragment // Share encrypted message/file startActivity(Intent.createChooser(createSendIntent(result.getResultBytes()), getString(R.string.title_share_message))); + } else if (mReturnProcessTextAfterEncrypt) { + Intent resultIntent = new Intent(); + resultIntent.putExtra(Intent.EXTRA_PROCESS_TEXT, new String(result.getResultBytes())); + getActivity().setResult(Activity.RESULT_OK, resultIntent); + getActivity().finish(); } else { // Copy to clipboard copyToClipboard(result); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java index 746c75600..8de60dfd3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -29,6 +29,9 @@ import android.view.ViewGroup; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FileHelper; public class ImportKeysFileFragment extends Fragment { @@ -78,12 +81,16 @@ public class ImportKeysFileFragment extends Fragment { String sendText = ""; if (clipboardText != null) { sendText = clipboardText.toString(); + sendText = PgpHelper.getPgpKeyContent(sendText); + if (sendText == null) { + Notify.create(mImportActivity, "Bad data!", Style.ERROR).show(); + return; + } mImportActivity.loadCallback(new ImportKeysListFragment.BytesLoaderState(sendText.getBytes(), null)); } } }); - return view; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index 864283b0a..7aed3176c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -17,6 +17,11 @@ package org.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + import android.app.Activity; import android.net.Uri; import android.os.Bundle; @@ -40,22 +45,12 @@ import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListCloudLoader; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader; -import org.sufficientlysecure.keychain.util.FileHelper; -import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; -import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - public class ImportKeysListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { @@ -180,8 +175,8 @@ public class ImportKeysListFragment extends ListFragment implements } static public class BytesLoaderState extends LoaderState { - byte[] mKeyBytes; - Uri mDataUri; + public byte[] mKeyBytes; + public Uri mDataUri; BytesLoaderState(byte[] keyBytes, Uri dataUri) { mKeyBytes = keyBytes; @@ -305,9 +300,7 @@ public class ImportKeysListFragment extends ListFragment implements onCreateLoader(int id, Bundle args) { switch (id) { case LOADER_ID_BYTES: { - BytesLoaderState ls = (BytesLoaderState) mLoaderState; - InputData inputData = getInputData(ls.mKeyBytes, ls.mDataUri); - return new ImportKeysListLoader(mActivity, inputData); + return new ImportKeysListLoader(mActivity, (BytesLoaderState) mLoaderState); } case LOADER_ID_CLOUD: { CloudLoaderState ls = (CloudLoaderState) mLoaderState; @@ -432,23 +425,4 @@ public class ImportKeysListFragment extends ListFragment implements } } - private InputData getInputData(byte[] importBytes, Uri dataUri) { - InputData inputData = null; - if (importBytes != null) { - inputData = new InputData(new ByteArrayInputStream(importBytes), importBytes.length); - } else if (dataUri != null) { - try { - InputStream inputStream = getActivity().getContentResolver().openInputStream(dataUri); - long length = FileHelper.getFileSize(getActivity(), dataUri, -1); - - inputData = new InputData(inputStream, length); - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "FileNotFoundException!", e); - return null; - } - } - - return inputData; - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index b60f3984c..45ce604c3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -87,12 +87,7 @@ public class ImportKeysProxyActivity extends FragmentActivity processScannedContent(dataUri); } else if (ACTION_SCAN_WITH_RESULT.equals(action) || ACTION_SCAN_IMPORT.equals(action) || ACTION_QR_CODE_API.equals(action)) { - IntentIntegrator integrator = new IntentIntegrator(this); - integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) - .setPrompt(getString(R.string.import_qr_code_text)) - .setResultDisplayDuration(0); - integrator.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - integrator.initiateScan(); + new IntentIntegrator(this).setCaptureActivity(QrCodeCaptureActivity.class).initiateScan(); } else if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { // Check to see if the Activity started due to an Android Beam if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 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 ce6994ba4..23c1250d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -30,6 +30,7 @@ import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; @@ -46,14 +47,17 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; +import android.widget.Button; import android.widget.ListView; import android.widget.TextView; +import android.widget.ViewAnimator; import com.getbase.floatingactionbutton.FloatingActionButton; import com.getbase.floatingactionbutton.FloatingActionsMenu; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; +import org.sufficientlysecure.keychain.operations.results.BenchmarkResult; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; @@ -61,6 +65,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.BenchmarkInputParcel; import org.sufficientlysecure.keychain.service.ConsolidateInputParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; @@ -97,6 +102,8 @@ public class KeyListFragment extends LoaderFragment // saves the mode object for multiselect, needed for reset at some point private ActionMode mActionMode = null; + private Button vSearchButton; + private ViewAnimator vSearchContainer; private String mQuery; private FloatingActionsMenu mFab; @@ -161,7 +168,9 @@ public class KeyListFragment extends LoaderFragment super.onActivityCreated(savedInstanceState); // show app name instead of "keys" from nav drawer - getActivity().setTitle(R.string.app_name); + final FragmentActivity activity = getActivity(); + + activity.setTitle(R.string.app_name); mStickyList.setOnItemClickListener(this); mStickyList.setAreHeadersSticky(true); @@ -170,7 +179,7 @@ public class KeyListFragment extends LoaderFragment // Adds an empty footer view so that the Floating Action Button won't block content // in last few rows. - View footer = new View(getActivity()); + View footer = new View(activity); int spacing = (int) android.util.TypedValue.applyDimension( android.util.TypedValue.COMPLEX_UNIT_DIP, 72, getResources().getDisplayMetrics() @@ -194,7 +203,7 @@ public class KeyListFragment extends LoaderFragment @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - android.view.MenuInflater inflater = getActivity().getMenuInflater(); + android.view.MenuInflater inflater = activity.getMenuInflater(); inflater.inflate(R.menu.key_list_multi, menu); mActionMode = mode; return true; @@ -234,7 +243,7 @@ public class KeyListFragment extends LoaderFragment @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { + boolean checked) { if (checked) { mAdapter.setNewSelection(position, true); } else { @@ -254,8 +263,21 @@ public class KeyListFragment extends LoaderFragment // Start out with a progress indicator. setContentShown(false); + // this view is made visible if no data is available + mStickyList.setEmptyView(activity.findViewById(R.id.key_list_empty)); + + // click on search button (in empty view) starts query for search string + vSearchContainer = (ViewAnimator) activity.findViewById(R.id.search_container); + vSearchButton = (Button) activity.findViewById(R.id.search_button); + vSearchButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startSearchForQuery(); + } + }); + // Create an empty adapter we will use to display the loaded data. - mAdapter = new KeyListAdapter(getActivity(), null, 0); + mAdapter = new KeyListAdapter(activity, null, 0); mStickyList.setAdapter(mAdapter); // Prepare the loader. Either re-connect with an existing one, @@ -263,8 +285,20 @@ public class KeyListFragment extends LoaderFragment getLoaderManager().initLoader(0, null, this); } + private void startSearchForQuery() { + Activity activity = getActivity(); + if (activity == null) { + return; + } + + Intent searchIntent = new Intent(activity, ImportKeysActivity.class); + searchIntent.putExtra(ImportKeysActivity.EXTRA_QUERY, mQuery); + searchIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER); + startActivity(searchIntent); + } + static final String ORDER = - KeyRings.HAS_ANY_SECRET + " DESC, UPPER(" + KeyRings.USER_ID + ") ASC"; + KeyRings.HAS_ANY_SECRET + " DESC, " + KeyRings.USER_ID + " COLLATE NOCASE ASC"; @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { @@ -318,9 +352,6 @@ public class KeyListFragment extends LoaderFragment mStickyList.setAdapter(mAdapter); - // this view is made visible if no data is available - mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty)); - // end action mode, if any if (mActionMode != null) { mActionMode.finish(); @@ -386,6 +417,7 @@ public class KeyListFragment extends LoaderFragment if (Constants.DEBUG) { menu.findItem(R.id.menu_key_list_debug_cons).setVisible(true); + menu.findItem(R.id.menu_key_list_debug_bench).setVisible(true); menu.findItem(R.id.menu_key_list_debug_read).setVisible(true); menu.findItem(R.id.menu_key_list_debug_write).setVisible(true); menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true); @@ -469,6 +501,10 @@ public class KeyListFragment extends LoaderFragment consolidate(); return true; + case R.id.menu_key_list_debug_bench: + benchmark(); + return true; + default: return super.onOptionsItemSelected(item); } @@ -482,17 +518,25 @@ public class KeyListFragment extends LoaderFragment @Override public boolean onQueryTextChange(String s) { Log.d(Constants.TAG, "onQueryTextChange s:" + s); - // Called when the action bar search text has changed. Update - // the search filter, and restart the loader to do a new query - // with this filter. + // Called when the action bar search text has changed. Update the + // search filter, and restart the loader to do a new query with this + // filter. // If the nav drawer is opened, onQueryTextChange("") is executed. // This hack prevents restarting the loader. - // TODO: better way to fix this? - String tmp = (mQuery == null) ? "" : mQuery; - if (!s.equals(tmp)) { + if (!s.equals(mQuery)) { mQuery = s; getLoaderManager().restartLoader(0, null, this); } + + if (s.length() > 2) { + vSearchButton.setText(getString(R.string.btn_search_for_query, mQuery)); + vSearchContainer.setDisplayedChild(1); + vSearchContainer.setVisibility(View.VISIBLE); + } else { + vSearchContainer.setDisplayedChild(0); + vSearchContainer.setVisibility(View.GONE); + } + return true; } @@ -559,8 +603,8 @@ public class KeyListFragment extends LoaderFragment mKeyserver = cloudPrefs.keyserver; } - mImportOpHelper = new CryptoOperationHelper<>(1, this, - this, R.string.progress_updating); + mImportOpHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_updating); + mImportOpHelper.setProgressCancellable(true); mImportOpHelper.cryptoOperation(); } @@ -601,6 +645,43 @@ public class KeyListFragment extends LoaderFragment mConsolidateOpHelper.cryptoOperation(); } + private void benchmark() { + + CryptoOperationHelper.Callback<BenchmarkInputParcel, BenchmarkResult> callback + = new CryptoOperationHelper.Callback<BenchmarkInputParcel, BenchmarkResult>() { + + @Override + public BenchmarkInputParcel createOperationInput() { + return new BenchmarkInputParcel(); // we want to perform a full consolidate + } + + @Override + public void onCryptoOperationSuccess(BenchmarkResult result) { + result.createNotify(getActivity()).show(); + } + + @Override + public void onCryptoOperationCancelled() { + + } + + @Override + public void onCryptoOperationError(BenchmarkResult result) { + result.createNotify(getActivity()).show(); + } + + @Override + public boolean onCryptoSetProgress(String msg, int progress, int max) { + return false; + } + }; + + CryptoOperationHelper opHelper = + new CryptoOperationHelper<>(2, this, callback, R.string.progress_importing); + + opHelper.cryptoOperation(); + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (mImportOpHelper != null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index 7e9b4953c..7bd7bafcc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> * Copyright (C) 2015 Kai Jiang <jiangkai@gmail.com> * @@ -27,13 +27,13 @@ import android.support.v4.app.FragmentManager.OnBackStackChangedListener; import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.Toolbar; import android.view.View; -import android.widget.AdapterView; import com.mikepenz.community_material_typeface_library.CommunityMaterial; +import com.mikepenz.fontawesome_typeface_library.FontAwesome; import com.mikepenz.google_material_typeface_library.GoogleMaterial; -import com.mikepenz.iconics.typeface.FontAwesome; import com.mikepenz.materialdrawer.Drawer; import com.mikepenz.materialdrawer.DrawerBuilder; +import com.mikepenz.materialdrawer.model.DividerDrawerItem; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; @@ -75,25 +75,23 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac .withToolbar(mToolbar) .addDrawerItems( new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key) - .withIdentifier(ID_KEYS).withCheckable(false), + .withIdentifier(ID_KEYS).withSelectable(false), new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock) - .withIdentifier(ID_ENCRYPT_DECRYPT).withCheckable(false), + .withIdentifier(ID_ENCRYPT_DECRYPT).withSelectable(false), new PrimaryDrawerItem().withName(R.string.title_api_registered_apps).withIcon(CommunityMaterial.Icon.cmd_apps) - .withIdentifier(ID_APPS).withCheckable(false), + .withIdentifier(ID_APPS).withSelectable(false), new PrimaryDrawerItem().withName(R.string.nav_backup).withIcon(CommunityMaterial.Icon.cmd_backup_restore) - .withIdentifier(ID_BACKUP).withCheckable(false) - ) - .addStickyDrawerItems( - // display and stick on bottom of drawer - new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(ID_SETTINGS).withCheckable(false), - new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(ID_HELP).withCheckable(false) + .withIdentifier(ID_BACKUP).withSelectable(false), + new DividerDrawerItem(), + new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(ID_SETTINGS).withSelectable(false), + new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(ID_HELP).withSelectable(false) ) .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { @Override - public boolean onItemClick(AdapterView<?> parent, View view, int position, long id, IDrawerItem drawerItem) { + public boolean onItemClick(View view, int position, IDrawerItem drawerItem) { if (drawerItem != null) { Intent intent = null; - switch(drawerItem.getIdentifier()) { + switch (drawerItem.getIdentifier()) { case ID_KEYS: onKeysSelected(); break; @@ -182,28 +180,28 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac private void onKeysSelected() { mToolbar.setTitle(R.string.app_name); - mDrawer.setSelectionByIdentifier(ID_KEYS, false); + mDrawer.setSelection(ID_KEYS, false); Fragment frag = new KeyListFragment(); setFragment(frag, false); } private void onEnDecryptSelected() { mToolbar.setTitle(R.string.nav_encrypt_decrypt); - mDrawer.setSelectionByIdentifier(ID_ENCRYPT_DECRYPT, false); + mDrawer.setSelection(ID_ENCRYPT_DECRYPT, false); Fragment frag = new EncryptDecryptFragment(); setFragment(frag, true); } private void onAppsSelected() { mToolbar.setTitle(R.string.nav_apps); - mDrawer.setSelectionByIdentifier(ID_APPS, false); + mDrawer.setSelection(ID_APPS, false); Fragment frag = new AppsListFragment(); setFragment(frag, true); } private void onBackupSelected() { mToolbar.setTitle(R.string.nav_backup); - mDrawer.setSelectionByIdentifier(ID_BACKUP, false); + mDrawer.setSelection(ID_BACKUP, false); Fragment frag = new BackupRestoreFragment(); setFragment(frag, true); } @@ -258,16 +256,16 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac // make sure the selected icon is the one shown at this point if (frag instanceof KeyListFragment) { mToolbar.setTitle(R.string.app_name); - mDrawer.setSelection(mDrawer.getPositionFromIdentifier(ID_KEYS), false); + mDrawer.setSelection(mDrawer.getPosition(ID_KEYS), false); } else if (frag instanceof EncryptDecryptFragment) { mToolbar.setTitle(R.string.nav_encrypt_decrypt); - mDrawer.setSelection(mDrawer.getPositionFromIdentifier(ID_ENCRYPT_DECRYPT), false); + mDrawer.setSelection(mDrawer.getPosition(ID_ENCRYPT_DECRYPT), false); } else if (frag instanceof AppsListFragment) { mToolbar.setTitle(R.string.nav_apps); - mDrawer.setSelection(mDrawer.getPositionFromIdentifier(ID_APPS), false); + mDrawer.setSelection(mDrawer.getPosition(ID_APPS), false); } else if (frag instanceof BackupRestoreFragment) { mToolbar.setTitle(R.string.nav_backup); - mDrawer.setSelection(mDrawer.getPositionFromIdentifier(ID_BACKUP), false); + mDrawer.setSelection(mDrawer.getPosition(ID_BACKUP), false); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeCaptureActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeCaptureActivity.java new file mode 100644 index 000000000..b5d3948be --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeCaptureActivity.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.ui; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.FragmentActivity; +import android.support.v4.content.ContextCompat; +import android.view.KeyEvent; + +import com.journeyapps.barcodescanner.CaptureManager; +import com.journeyapps.barcodescanner.CompoundBarcodeView; + +import org.sufficientlysecure.keychain.R; + +public class QrCodeCaptureActivity extends FragmentActivity { + private CaptureManager capture; + private CompoundBarcodeView barcodeScannerView; + + public static final int MY_PERMISSIONS_REQUEST_CAMERA = 42; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.qr_code_capture_activity); + + barcodeScannerView = (CompoundBarcodeView) findViewById(R.id.zxing_barcode_scanner); + barcodeScannerView.setStatusText(getString(R.string.import_qr_code_text)); + + if (savedInstanceState != null) { + init(barcodeScannerView, getIntent(), savedInstanceState); + } + + // check Android 6 permission + if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) + == PackageManager.PERMISSION_GRANTED) { + init(barcodeScannerView, getIntent(), null); + } else { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.CAMERA}, + MY_PERMISSIONS_REQUEST_CAMERA); + } + } + + private void init(CompoundBarcodeView barcodeScannerView, Intent intent, Bundle savedInstanceState) { + capture = new CaptureManager(this, barcodeScannerView); + capture.initializeFromIntent(intent, savedInstanceState); + capture.decode(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], + @NonNull int[] grantResults) { + switch (requestCode) { + case MY_PERMISSIONS_REQUEST_CAMERA: { + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // permission was granted + init(barcodeScannerView, getIntent(), null); + } else { + setResult(Activity.RESULT_CANCELED); + finish(); + } + } + } + } + + @Override + protected void onResume() { + super.onResume(); + if (capture != null) { + capture.onResume(); + } + } + + @Override + protected void onPause() { + super.onPause(); + if (capture != null) { + capture.onPause(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (capture != null) { + capture.onDestroy(); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (capture != null) { + capture.onSaveInstanceState(outState); + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + } +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 8118c2fa7..f5c239558 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -54,14 +54,8 @@ import java.util.List; public class SettingsActivity extends AppCompatPreferenceActivity { - public static final String ACTION_PREFS_CLOUD = "org.sufficientlysecure.keychain.ui.PREFS_CLOUD"; - public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV"; - public static final String ACTION_PREFS_PROXY = "org.sufficientlysecure.keychain.ui.PREFS_PROXY"; - public static final String ACTION_PREFS_GUI = "org.sufficientlysecure.keychain.ui.PREFS_GUI"; - public static final int REQUEST_CODE_KEYSERVER_PREF = 0x00007005; - private PreferenceScreen mKeyServerPreference = null; private static Preferences sPreferences; private ThemeChanger mThemeChanger; @@ -74,49 +68,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity { super.onCreate(savedInstanceState); setupToolbar(); - - String action = getIntent().getAction(); - - if (ACTION_PREFS_CLOUD.equals(action)) { - addPreferencesFromResource(R.xml.cloud_search_prefs); - - mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS); - mKeyServerPreference.setSummary(keyserverSummary(this)); - mKeyServerPreference - .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference preference) { - Intent intent = new Intent(SettingsActivity.this, - SettingsKeyServerActivity.class); - intent.putExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS, - sPreferences.getKeyServers()); - startActivityForResult(intent, REQUEST_CODE_KEYSERVER_PREF); - return false; - } - }); - initializeSearchKeyserver( - (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER) - ); - initializeSearchKeybase( - (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYBASE) - ); - - } else if (ACTION_PREFS_ADV.equals(action)) { - addPreferencesFromResource(R.xml.passphrase_preferences); - - initializePassphraseCacheSubs( - (CheckBoxPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_SUBS)); - - initializePassphraseCacheTtl( - (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL)); - - initializeUseNumKeypadForYubiKeyPin( - (CheckBoxPreference) findPreference(Constants.Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN)); - - } else if (ACTION_PREFS_GUI.equals(action)) { - addPreferencesFromResource(R.xml.gui_preferences); - - initializeTheme((ListPreference) findPreference(Constants.Pref.THEME)); - } } @Override @@ -448,23 +399,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } /** - * This fragment shows gui preferences. - */ - public static class GuiPrefsFragment extends PreferenceFragment { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - - // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.gui_preferences); - - initializeTheme((ListPreference) findPreference(Constants.Pref.THEME)); - } - } - - /** * This fragment shows the keyserver/contacts sync preferences */ public static class SyncPrefsFragment extends PreferenceFragment { @@ -576,7 +510,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity { return PassphrasePrefsFragment.class.getName().equals(fragmentName) || CloudSearchPrefsFragment.class.getName().equals(fragmentName) || ProxyPrefsFragment.class.getName().equals(fragmentName) - || GuiPrefsFragment.class.getName().equals(fragmentName) || SyncPrefsFragment.class.getName().equals(fragmentName) || ExperimentalPrefsFragment.class.getName().equals(fragmentName) || super.isValidFragment(fragmentName); 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 cbc7b88bf..0184527b7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -38,6 +38,7 @@ import android.os.Handler; import android.provider.ContactsContract; import android.support.design.widget.AppBarLayout; import android.support.design.widget.CollapsingToolbarLayout; +import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.ActivityCompat; import android.support.v4.app.FragmentManager; @@ -869,7 +870,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements mActionEncryptFile.setVisibility(View.INVISIBLE); mActionEncryptText.setVisibility(View.INVISIBLE); mActionNfc.setVisibility(View.INVISIBLE); - mFab.setVisibility(View.GONE); + hideFab(); mQrCodeLayout.setVisibility(View.GONE); } else if (mIsExpired) { if (mIsSecret) { @@ -885,7 +886,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements mActionEncryptFile.setVisibility(View.INVISIBLE); mActionEncryptText.setVisibility(View.INVISIBLE); mActionNfc.setVisibility(View.INVISIBLE); - mFab.setVisibility(View.GONE); + hideFab(); mQrCodeLayout.setVisibility(View.GONE); } else if (mIsSecret) { mStatusText.setText(R.string.view_key_my_key); @@ -927,7 +928,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements } else { mActionNfc.setVisibility(View.GONE); } - mFab.setVisibility(View.VISIBLE); + showFab(); // noinspection deprecation (no getDrawable with theme at current minApi level 15!) mFab.setImageDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); } else { @@ -944,7 +945,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements color = getResources().getColor(R.color.key_flag_green); photoTask.execute(mMasterKeyId); - mFab.setVisibility(View.GONE); + hideFab(); } else { mStatusText.setText(R.string.view_key_unverified); mStatusImage.setVisibility(View.VISIBLE); @@ -952,7 +953,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements State.UNVERIFIED, R.color.icons, true); color = getResources().getColor(R.color.key_flag_orange); - mFab.setVisibility(View.VISIBLE); + showFab(); } } @@ -982,6 +983,28 @@ public class ViewKeyActivity extends BaseNfcActivity implements } } + /** + * Helper to show Fab, from http://stackoverflow.com/a/31047038 + */ + private void showFab() { + CoordinatorLayout.LayoutParams p = (CoordinatorLayout.LayoutParams) mFab.getLayoutParams(); + p.setBehavior(new FloatingActionButton.Behavior()); + p.setAnchorId(R.id.app_bar_layout); + mFab.setLayoutParams(p); + mFab.setVisibility(View.VISIBLE); + } + + /** + * Helper to hide Fab, from http://stackoverflow.com/a/31047038 + */ + private void hideFab() { + CoordinatorLayout.LayoutParams p = (CoordinatorLayout.LayoutParams) mFab.getLayoutParams(); + p.setBehavior(null); //should disable default animations + p.setAnchorId(View.NO_ID); //should let you set visibility + mFab.setLayoutParams(p); + mFab.setVisibility(View.GONE); + } + @Override public void onLoaderReset(Loader<Cursor> loader) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index 139512ba9..038ebd5dd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -17,6 +17,14 @@ package org.sufficientlysecure.keychain.ui.adapter; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; + import android.content.Context; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.util.LongSparseArray; @@ -28,28 +36,26 @@ import org.sufficientlysecure.keychain.operations.results.GetKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow; +import org.sufficientlysecure.keychain.ui.ImportKeysListFragment.BytesLoaderState; +import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.PositionAwareInputStream; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.util.ArrayList; - public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { final Context mContext; - final InputData mInputData; + final BytesLoaderState mLoaderState; ArrayList<ImportKeysListEntry> mData = new ArrayList<>(); LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<>(); AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper; - public ImportKeysListLoader(Context context, InputData inputData) { + public ImportKeysListLoader(Context context, BytesLoaderState inputData) { super(context); this.mContext = context; - this.mInputData = inputData; + this.mLoaderState = inputData; } @Override @@ -62,12 +68,13 @@ public class ImportKeysListLoader GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null); mEntryListWrapper = new AsyncTaskResultWrapper<>(mData, getKeyResult); - if (mInputData == null) { + if (mLoaderState == null) { Log.e(Constants.TAG, "Input data is null!"); return mEntryListWrapper; } - generateListOfKeyrings(mInputData); + InputData inputData = getInputData(getContext(), mLoaderState); + generateListOfKeyrings(inputData); return mEntryListWrapper; } @@ -99,12 +106,7 @@ public class ImportKeysListLoader return mParcelableRings; } - /** - * Reads all PGPKeyRing objects from input - * - * @param inputData - * @return - */ + /** Reads all PGPKeyRing objects from the bytes of an InputData object. */ private void generateListOfKeyrings(InputData inputData) { PositionAwareInputStream progressIn = new PositionAwareInputStream( inputData.getInputStream()); @@ -132,4 +134,23 @@ public class ImportKeysListLoader } } + private static InputData getInputData(Context context, BytesLoaderState loaderState) { + InputData inputData = null; + if (loaderState.mKeyBytes != null) { + inputData = new InputData(new ByteArrayInputStream(loaderState.mKeyBytes), loaderState.mKeyBytes.length); + } else if (loaderState.mDataUri != null) { + try { + InputStream inputStream = context.getContentResolver().openInputStream(loaderState.mDataUri); + long length = FileHelper.getFileSize(context, loaderState.mDataUri, -1); + + inputData = new InputData(inputStream, length); + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "FileNotFoundException!", e); + return null; + } + } + + return inputData; + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java index 471a20411..7cc37b3a3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java @@ -1,13 +1,8 @@ package org.sufficientlysecure.keychain.ui.adapter; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - import android.content.Context; import android.database.Cursor; -import android.support.v7.internal.widget.AdapterViewCompat; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; @@ -18,6 +13,10 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.Log; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + public class KeySelectableAdapter extends KeyAdapter implements OnItemClickListener { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java index 5cf0e6e08..5566c725b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java @@ -24,6 +24,7 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.support.v4.content.CursorLoader; import android.util.Log; import android.view.LayoutInflater; @@ -228,9 +229,11 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { } public void seekAttention() { - ObjectAnimator anim = SubtleAttentionSeeker.tintText(vComment, 1000); - anim.setStartDelay(200); - anim.start(); + if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + ObjectAnimator anim = SubtleAttentionSeeker.tintText(vComment, 1000); + anim.setStartDelay(200); + anim.start(); + } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java index d2877d542..7ab9c7237 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java @@ -84,6 +84,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu public static final int REQUEST_CODE_RETRY_UPLOAD = 4; private Integer mProgressMessageResource; + private boolean mCancellable = false; private FragmentActivity mActivity; private Fragment mFragment; @@ -118,6 +119,10 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu mProgressMessageResource = id; } + public void setProgressCancellable(boolean cancellable) { + mCancellable = cancellable; + } + private void initiateInputActivity(RequiredInputParcel requiredInput, CryptoInputParcel cryptoInputParcel) { @@ -311,7 +316,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu if (mProgressMessageResource != null) { saveHandler.showProgressDialog( activity.getString(mProgressMessageResource), - ProgressDialog.STYLE_HORIZONTAL, false); + ProgressDialog.STYLE_HORIZONTAL, mCancellable); } activity.startService(intent); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index 48e6c2cee..01d51af48 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -23,11 +23,13 @@ import android.database.Cursor; import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -46,14 +48,14 @@ import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem; import org.sufficientlysecure.keychain.util.Log; -public class EncryptKeyCompletionView extends TokenCompleteTextView +public class EncryptKeyCompletionView extends TokenCompleteTextView<KeyItem> implements LoaderCallbacks<Cursor> { public static final String ARG_QUERY = "query"; private KeyAdapter mAdapter; private LoaderManager mLoaderManager; - private String mPrefix; + private CharSequence mPrefix; public EncryptKeyCompletionView(Context context) { super(context); @@ -79,30 +81,27 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView } @Override - public void setPrefix(String p) { + public void setPrefix(CharSequence p) { // this one is private in the superclass, but we need it here mPrefix = p; super.setPrefix(p); } @Override - protected View getViewForObject(Object object) { - if (object instanceof KeyItem) { - LayoutInflater l = LayoutInflater.from(getContext()); - View view = l.inflate(R.layout.recipient_box_entry, null); - ((TextView) view.findViewById(android.R.id.text1)).setText(((KeyItem) object).getReadableName()); - return view; - } - return null; + protected View getViewForObject(KeyItem keyItem) { + LayoutInflater l = LayoutInflater.from(getContext()); + View view = l.inflate(R.layout.recipient_box_entry, null); + ((TextView) view.findViewById(android.R.id.text1)).setText(keyItem.getReadableName()); + return view; } @Override - protected Object defaultObject(String completionText) { + protected KeyItem defaultObject(String completionText) { // TODO: We could try to automagically download the key if it's unknown but a key id /*if (completionText.startsWith("0x")) { }*/ - return ""; + return null; } @Override @@ -128,7 +127,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView // These are the rows that we will retrieve. Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); - String[] projection = KeyAdapter.getProjectionWith(new String[] { + String[] projection = KeyAdapter.getProjectionWith(new String[]{ KeychainContract.KeyRings.HAS_ENCRYPT, }); @@ -136,18 +135,19 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView + KeyRings.IS_EXPIRED + " = 0 AND " + Tables.KEYS + "." + KeyRings.IS_REVOKED + " = 0"; - if (args != null && args.containsKey(ARG_QUERY)) { - String query = args.getString(ARG_QUERY); - mAdapter.setSearchQuery(query); + if (args == null || !args.containsKey(ARG_QUERY)) { + // mAdapter.setSearchQuery(null); + // return new CursorLoader(getContext(), baseUri, projection, where, null, null); + return null; + } - where += " AND " + KeyRings.USER_ID + " LIKE ?"; + String query = args.getString(ARG_QUERY); + mAdapter.setSearchQuery(query); - return new CursorLoader(getContext(), baseUri, projection, where, - new String[]{"%" + query + "%"}, null); - } + where += " AND " + KeyRings.USER_ID + " LIKE ?"; - mAdapter.setSearchQuery(null); - return new CursorLoader(getContext(), baseUri, projection, where, null, null); + return new CursorLoader(getContext(), baseUri, projection, where, + new String[]{"%" + query + "%"}, null); } @@ -169,6 +169,8 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView super.showDropDown(); } + + @Override public void onFocusChanged(boolean hasFocus, int direction, Rect previous) { super.onFocusChanged(hasFocus, direction, previous); @@ -179,13 +181,18 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView } @Override - protected void performFiltering(CharSequence text, int start, int end, int keyCode) { + protected void performFiltering(@NonNull CharSequence text, int start, int end, int keyCode) { super.performFiltering(text, start, end, keyCode); if (start < mPrefix.length()) { start = mPrefix.length(); } + String query = text.subSequence(start, end).toString(); + if (TextUtils.isEmpty(query) || query.length() < 2) { + mLoaderManager.destroyLoader(0); + return; + } Bundle args = new Bundle(); - args.putString(ARG_QUERY, text.subSequence(start, end).toString()); + args.putString(ARG_QUERY, query); mLoaderManager.restartLoader(0, args, this); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java index 9a6d33260..a55249842 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java @@ -74,9 +74,9 @@ public class EmailKeyHelper { // Try _hkp._tcp SRV record first String[] mailparts = mail.split("@"); if (mailparts.length == 2) { - HkpKeyserver hkp = HkpKeyserver.resolve(mailparts[1]); + HkpKeyserver hkp = HkpKeyserver.resolve(mailparts[1], proxy); if (hkp != null) { - keys.addAll(getEmailKeys(mail, hkp, proxy)); + keys.addAll(getEmailKeys(mail, hkp)); } } @@ -84,18 +84,17 @@ public class EmailKeyHelper { // Most users don't have the SRV record, so ask a default server as well String server = Preferences.getPreferences(context).getPreferredKeyserver(); if (server != null) { - HkpKeyserver hkp = new HkpKeyserver(server); - keys.addAll(getEmailKeys(mail, hkp, proxy)); + HkpKeyserver hkp = new HkpKeyserver(server, proxy); + keys.addAll(getEmailKeys(mail, hkp)); } } return keys; } - public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer, - Proxy proxy) { + public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer) { Set<ImportKeysListEntry> keys = new HashSet<>(); try { - for (ImportKeysListEntry key : keyServer.search(mail, proxy)) { + for (ImportKeysListEntry key : keyServer.search(mail)) { if (key.isRevoked() || key.isExpired()) continue; for (String userId : key.getUserIds()) { if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java index 236913be7..7345faad9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java @@ -272,57 +272,21 @@ public class FileHelper { return true; } - /** - * Tests whether a file is readable by others - */ - @TargetApi(VERSION_CODES.LOLLIPOP) - public static boolean S_IROTH(int mode) { - return (mode & S_IROTH) == S_IROTH; - } - - /** - * A replacement for ContentResolver.openInputStream() that does not allow the usage of - * "file" Uris that point to private files owned by the application only. + /** A replacement for ContentResolver.openInputStream() that does not allow + * the usage of "file" Uris that point to private files owned by the + * application only, *on Lollipop devices*. * - * This is not allowed: - * am start -a android.intent.action.SEND -t text/plain -n - * "org.sufficientlysecure.keychain.debug/org.sufficientlysecure.keychain.ui.EncryptFilesActivity" --eu - * android.intent.extra.STREAM - * file:///data/data/org.sufficientlysecure.keychain.debug/databases/openkeychain.db + * The check will be performed on devices >= Lollipop only, which have the + * necessary API to stat filedescriptors. * - * @throws FileNotFoundException + * @see FileHelperLollipop */ - @TargetApi(VERSION_CODES.LOLLIPOP) public static InputStream openInputStreamSafe(ContentResolver resolver, Uri uri) - throws FileNotFoundException { + throws FileNotFoundException { // Not supported on Android < 5 - if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) { - return resolver.openInputStream(uri); - } - - String scheme = uri.getScheme(); - if (ContentResolver.SCHEME_FILE.equals(scheme)) { - ParcelFileDescriptor pfd = ParcelFileDescriptor.open( - new File(uri.getPath()), ParcelFileDescriptor.parseMode("r")); - - try { - final StructStat st = Os.fstat(pfd.getFileDescriptor()); - if (!S_IROTH(st.st_mode)) { - Log.e(Constants.TAG, "File is not readable by others, aborting!"); - throw new FileNotFoundException("Unable to create stream"); - } - } catch (ErrnoException e) { - Log.e(Constants.TAG, "fstat() failed: " + e); - throw new FileNotFoundException("fstat() failed"); - } - - AssetFileDescriptor fd = new AssetFileDescriptor(pfd, 0, -1); - try { - return fd.createInputStream(); - } catch (IOException e) { - throw new FileNotFoundException("Unable to create stream"); - } + if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + return FileHelperLollipop.openInputStreamSafe(resolver, uri); } else { return resolver.openInputStream(uri); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelperLollipop.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelperLollipop.java new file mode 100644 index 000000000..f89d679bc --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelperLollipop.java @@ -0,0 +1,82 @@ +package org.sufficientlysecure.keychain.util; + + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import android.annotation.TargetApi; +import android.content.ContentResolver; +import android.content.res.AssetFileDescriptor; +import android.net.Uri; +import android.os.Build; +import android.os.Build.VERSION_CODES; +import android.os.ParcelFileDescriptor; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; + +import org.sufficientlysecure.keychain.Constants; + +import static android.system.OsConstants.S_IROTH; + + +/** FileHelper methods which use Lollipop-exclusive API. + * Some of the methods and static fields used here cause VerifyErrors because + * they do not exist in pre-lollipop API, so they must be kept in a + * lollipop-only class. All methods here should only be called by FileHelper, + * and consequently have package visibility. + */ +@TargetApi(VERSION_CODES.LOLLIPOP) +class FileHelperLollipop { + /** + * Tests whether a file is readable by others + */ + private static boolean S_IROTH(int mode) { + return (mode & S_IROTH) == S_IROTH; + } + + /** + * A replacement for ContentResolver.openInputStream() that does not allow the usage of + * "file" Uris that point to private files owned by the application only. + * + * This is not allowed: + * am start -a android.intent.action.SEND -t text/plain -n + * "org.sufficientlysecure.keychain.debug/org.sufficientlysecure.keychain.ui.EncryptFilesActivity" --eu + * android.intent.extra.STREAM + * file:///data/data/org.sufficientlysecure.keychain.debug/databases/openkeychain.db + * + * @throws FileNotFoundException + */ + static InputStream openInputStreamSafe(ContentResolver resolver, Uri uri) + throws FileNotFoundException { + + String scheme = uri.getScheme(); + if (ContentResolver.SCHEME_FILE.equals(scheme)) { + ParcelFileDescriptor pfd = ParcelFileDescriptor.open( + new File(uri.getPath()), ParcelFileDescriptor.parseMode("r")); + + try { + final StructStat st = Os.fstat(pfd.getFileDescriptor()); + if (!S_IROTH(st.st_mode)) { + Log.e(Constants.TAG, "File is not readable by others, aborting!"); + throw new FileNotFoundException("Unable to create stream"); + } + } catch (ErrnoException e) { + Log.e(Constants.TAG, "fstat() failed: " + e); + throw new FileNotFoundException("fstat() failed"); + } + + AssetFileDescriptor fd = new AssetFileDescriptor(pfd, 0, -1); + try { + return fd.createInputStream(); + } catch (IOException e) { + throw new FileNotFoundException("Unable to create stream"); + } + } else { + return resolver.openInputStream(uri); + } + + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java index 7e788d04c..7e2328e99 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java @@ -17,12 +17,14 @@ package org.sufficientlysecure.keychain.util; -import android.os.Parcel; -import android.os.Parcelable; import java.net.InetSocketAddress; import java.net.Proxy; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + /** * used to simply transport java.net.Proxy objects created using InetSockets between services/activities */ @@ -47,9 +49,10 @@ public class ParcelableProxy implements Parcelable { return new ParcelableProxy(null, -1, null); } + @NonNull public Proxy getProxy() { if (mProxyHost == null) { - return null; + return Proxy.NO_PROXY; } /* * InetSocketAddress.createUnresolved so we can use this method even in the main thread diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 8b2c3c66a..559c5556f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -23,6 +23,9 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants.Pref; import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; @@ -322,6 +325,12 @@ public class Preferences { if (!torEnabled && !normalPorxyEnabled) this.parcelableProxy = new ParcelableProxy(null, -1, null); else this.parcelableProxy = new ParcelableProxy(hostName, port, type); } + + @NonNull + public Proxy getProxy() { + return parcelableProxy.getProxy(); + } + } // cloud prefs diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_action_encrypt_paste_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_encrypt_paste_24dp.png Binary files differnew file mode 100644 index 000000000..356cfbe43 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_encrypt_paste_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_action_encrypt_paste_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_encrypt_paste_24dp.png Binary files differnew file mode 100644 index 000000000..a546e152b --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_encrypt_paste_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_encrypt_paste_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_encrypt_paste_24dp.png Binary files differnew file mode 100644 index 000000000..ed7fdb732 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_encrypt_paste_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout_96dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout_96dp.png Binary files differindex e69de29bb..568b48c43 100644 --- a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout_96dp.png +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout_96dp.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_encrypt_paste_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_encrypt_paste_24dp.png Binary files differnew file mode 100644 index 000000000..50987bdd6 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_encrypt_paste_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_action_encrypt_paste_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_action_encrypt_paste_24dp.png Binary files differnew file mode 100644 index 000000000..11ad7e219 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_action_encrypt_paste_24dp.png diff --git a/OpenKeychain/src/main/res/layout/key_list_fragment.xml b/OpenKeychain/src/main/res/layout/key_list_fragment.xml index 26cedd362..6aaf5be25 100644 --- a/OpenKeychain/src/main/res/layout/key_list_fragment.xml +++ b/OpenKeychain/src/main/res/layout/key_list_fragment.xml @@ -3,6 +3,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="match_parent" android:layout_width="match_parent" + xmlns:tools="http://schemas.android.com/tools" + xmlns:custom="http://schemas.android.com/apk/res-auto" > <!--rebuild functionality of ListFragment --> @@ -23,10 +25,11 @@ <LinearLayout android:id="@+id/key_list_empty" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="240dp" android:gravity="center" android:orientation="vertical" - android:visibility="visible"> + android:animateLayoutChanges="true" + > <TextView android:layout_width="wrap_content" @@ -35,6 +38,30 @@ android:text="@string/key_list_empty_text1" android:textAppearance="?android:attr/textAppearanceLarge" /> + <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/search_container" + android:inAnimation="@anim/fade_in_delayed" + android:outAnimation="@anim/fade_out" + android:measureAllChildren="true" + custom:initialView="1"> + + <Space + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="24dp" + android:id="@+id/search_button" + android:gravity="center" + tools:text="@string/btn_search_for_query" + /> + + </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator> + </LinearLayout> </FrameLayout> diff --git a/OpenKeychain/src/main/res/layout/qr_code_capture_activity.xml b/OpenKeychain/src/main/res/layout/qr_code_capture_activity.xml new file mode 100644 index 000000000..094901740 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/qr_code_capture_activity.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.journeyapps.barcodescanner.CompoundBarcodeView + android:id="@+id/zxing_barcode_scanner" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentBottom="true" /> + +</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/toolbar_tabs.xml b/OpenKeychain/src/main/res/layout/toolbar_tabs.xml index ed42ef52e..315aaf75a 100644 --- a/OpenKeychain/src/main/res/layout/toolbar_tabs.xml +++ b/OpenKeychain/src/main/res/layout/toolbar_tabs.xml @@ -18,7 +18,7 @@ android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:textColor="?attr/colorTabText" - app:pstsTextColorSelected="?attr/colorTabTextSelected" + app:pstsTabTextColor="?attr/colorTabTextSelected" app:pstsIndicatorColor="?attr/colorTabIndicator" /> </RelativeLayout> diff --git a/OpenKeychain/src/main/res/menu/encrypt_text_fragment.xml b/OpenKeychain/src/main/res/menu/encrypt_text_fragment.xml index 80b78457d..4d3d53870 100644 --- a/OpenKeychain/src/main/res/menu/encrypt_text_fragment.xml +++ b/OpenKeychain/src/main/res/menu/encrypt_text_fragment.xml @@ -3,6 +3,14 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> <item + android:id="@+id/encrypt_paste" + android:title="@string/btn_paste_encrypted_signed" + android:icon="@drawable/ic_action_encrypt_paste_24dp" + android:orderInCategory="1" + android:visible="false" + app:showAsAction="ifRoom" /> + + <item android:id="@+id/encrypt_copy" android:title="@string/btn_copy_encrypted_signed" android:icon="@drawable/ic_action_encrypt_copy_24dp" diff --git a/OpenKeychain/src/main/res/menu/key_list.xml b/OpenKeychain/src/main/res/menu/key_list.xml index d3a2bd0fd..edaf0c999 100644 --- a/OpenKeychain/src/main/res/menu/key_list.xml +++ b/OpenKeychain/src/main/res/menu/key_list.xml @@ -26,6 +26,12 @@ app:showAsAction="never" /> <item + android:id="@+id/menu_key_list_debug_bench" + android:title="Debug / Benchmark" + android:visible="false" + app:showAsAction="never" /> + + <item android:id="@+id/menu_key_list_debug_read" android:title="Debug / DB restore" android:visible="false" diff --git a/OpenKeychain/src/main/res/raw-cs/help_about.md b/OpenKeychain/src/main/res/raw-cs/help_about.md index 06bed0021..3538e68cc 100644 --- a/OpenKeychain/src/main/res/raw-cs/help_about.md +++ b/OpenKeychain/src/main/res/raw-cs/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. License: GPLv3+ @@ -61,11 +61,11 @@ License: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-de/help_about.md b/OpenKeychain/src/main/res/raw-de/help_about.md index 3d5bedbfa..64af3c07f 100644 --- a/OpenKeychain/src/main/res/raw-de/help_about.md +++ b/OpenKeychain/src/main/res/raw-de/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) ist eine OpenPGP-Implementierung für Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Lizenz: GPLv3+ @@ -61,11 +61,11 @@ Lizenz: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache-Lizenz v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache-Lizenz v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache-Lizenz v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache-Lizenz v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache-Lizenz v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT-Lizenz) * [Snackbar](https://github.com/nispok/snackbar) (MIT-Lizenz) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11-Lizenz) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache-Lizenz v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache-Lizenz v2) * [ZXing](https://github.com/zxing/zxing) (Apache-Lizenz v2) diff --git a/OpenKeychain/src/main/res/raw-es/help_about.md b/OpenKeychain/src/main/res/raw-es/help_about.md index 194adfd3a..27dc7a59a 100644 --- a/OpenKeychain/src/main/res/raw-es/help_about.md +++ b/OpenKeychain/src/main/res/raw-es/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTA: ¡Ponga cada frase en su propia línea, Transifex pone cada línea en su propio campo de traducción!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) es una implementación de OpenPGP para Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Licencia: GPLv3+ @@ -61,11 +61,11 @@ Licencia: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Licencia Apache v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Licencia Apache v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Licencia Apache v2) - * [OkHttp](http://square.github.io/okhttp/) (Licencia Apache v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Licencia Apache v2) * [Librería SafeSlinger Exchange](https://github.com/SafeSlingerProject/exchange-android) (Licencia MIT) * [Snackbar](https://github.com/nispok/snackbar) (Licencia MIT) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (Licencia MIT X11) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licencia Apache v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licencia Apache v2) * [ZXing](https://github.com/zxing/zxing) (Licencia Apache v2) diff --git a/OpenKeychain/src/main/res/raw-eu/help_about.md b/OpenKeychain/src/main/res/raw-eu/help_about.md index 74890c2d7..f5c54b18c 100644 --- a/OpenKeychain/src/main/res/raw-eu/help_about.md +++ b/OpenKeychain/src/main/res/raw-eu/help_about.md @@ -1,8 +1,8 @@ [//]: # (OHARRA: Meseez jarri esaldi bakoitza bere lerroan, Transifex-ek lerroak bere itzulpen eremuan jartzen ditu!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) Android-rako OpenPGP egokitzapen bat da. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Baimena: GPLv3+ @@ -61,11 +61,11 @@ Baimena: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache Baimena v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache Baimena v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache Baimena v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache Baimena v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Diseinua) (Apache Baimena 2 bertsioa) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT Baimena) * [Snackbar](https://github.com/nispok/snackbar) (MIT Baimena) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 Baimena) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache Baimena v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache Baimena v2) * [ZXing](https://github.com/zxing/zxing) (Apache Baimena v2) diff --git a/OpenKeychain/src/main/res/raw-fa/help_about.md b/OpenKeychain/src/main/res/raw-fa/help_about.md index daecd5cbf..c1f4df2f2 100644 --- a/OpenKeychain/src/main/res/raw-fa/help_about.md +++ b/OpenKeychain/src/main/res/raw-fa/help_about.md @@ -1,8 +1,8 @@ [//]: # (تذکر: هر جمله در همان خط!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) یک نسخه از OpenPGP برای اندروید است. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. مجوز: GPLv3+ @@ -61,11 +61,11 @@ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-fi/help_about.md b/OpenKeychain/src/main/res/raw-fi/help_about.md index 06bed0021..3538e68cc 100644 --- a/OpenKeychain/src/main/res/raw-fi/help_about.md +++ b/OpenKeychain/src/main/res/raw-fi/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. License: GPLv3+ @@ -61,11 +61,11 @@ License: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-fr/help_about.md b/OpenKeychain/src/main/res/raw-fr/help_about.md index 5994ae4df..ec9a107ba 100644 --- a/OpenKeychain/src/main/res/raw-fr/help_about.md +++ b/OpenKeychain/src/main/res/raw-fr/help_about.md @@ -1,8 +1,8 @@ [//] : # (NOTE : veuillez mettre chaque phrase dans sa propre ligne. Transifex met chaque ligne dans son propre champ de traduction !) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) est une mise en œuvre d'OpenPGP pour Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Licence : GPLv3+ @@ -61,11 +61,11 @@ Licence : GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Licence Apache v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Licence Apache v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Licence Apache v2) - * [OkHttp](http://square.github.io/okhttp/) (licence Apache v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Conception matérielle)</a> (Licence Apache v2) * [Bibliothèque d'échange SafeSlinger](https://github.com/SafeSlingerProject/exchange-android) (Licence MIT) * [Snackbar](https://github.com/nispok/snackbar) (Licence MIT) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (Licence MIT X11) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licence Apache v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licence Apache v2) * [ZXing](https://github.com/zxing/zxing) (Licence Apache v2) diff --git a/OpenKeychain/src/main/res/raw-it/help_about.md b/OpenKeychain/src/main/res/raw-it/help_about.md index f1802ddb1..62a7a2687 100644 --- a/OpenKeychain/src/main/res/raw-it/help_about.md +++ b/OpenKeychain/src/main/res/raw-it/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTA: Si prega di mettere ogni frase in una propria linea, Transifex mette ogni riga nel proprio campo di traduzione!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) e un impelementazione OpenPGP per Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Licenza: GPLv3+ @@ -61,11 +61,11 @@ Licenza: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Design materiale) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-ja/help_about.md b/OpenKeychain/src/main/res/raw-ja/help_about.md index 17e3e17e3..5748aa299 100644 --- a/OpenKeychain/src/main/res/raw-ja/help_about.md +++ b/OpenKeychain/src/main/res/raw-ja/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) は Android における OpenPGP 実装です。 +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. ライセンス: GPLv3以降 @@ -61,11 +61,11 @@ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-ja/help_changelog.md b/OpenKeychain/src/main/res/raw-ja/help_changelog.md index 933100227..69efc6715 100644 --- a/OpenKeychain/src/main/res/raw-ja/help_changelog.md +++ b/OpenKeychain/src/main/res/raw-ja/help_changelog.md @@ -2,14 +2,14 @@ ## 3.6 - * Encrypted backups - * Security fixes based on external security audit - * YubiKey NEO key creation wizard - * Basic internal MIME support - * Automatic key synchronization - * Experimental feature: link keys to Github, Twitter accounts - * Experimental feature: key confirmation via phrases - * Experimental feature: dark theme + * 暗号化したバックアップ + * 外部セキュリティ監査によるセキュリティ修正 + * YubiKey NEO 鍵生成ウィザード + * 基本的な内部 MIME のサポート + * 自動鍵同期 + * 試験的機能: Github, Twitter アカウントと鍵のリンク + * 試験的機能: 語句による鍵検証 + * 試験的機能: ダークテーマ * API: Version 9 ## 3.5 diff --git a/OpenKeychain/src/main/res/raw-nl/help_about.md b/OpenKeychain/src/main/res/raw-nl/help_about.md index 5c0bc3327..2c88bed8f 100644 --- a/OpenKeychain/src/main/res/raw-nl/help_about.md +++ b/OpenKeychain/src/main/res/raw-nl/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) is een OpenPGP implementatie voor Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Licentie: GPLv3+ @@ -61,11 +61,11 @@ Licentie: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache licentie v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache licentie v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache licentie v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache licentie v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache licentie v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT licentie) * [Snackbar](https://github.com/nispok/snackbar) (MIT licentie) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 licentie) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache licentie v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache licentie v2) * [ZXing](https://github.com/zxing/zxing) (Apache licentie v2) diff --git a/OpenKeychain/src/main/res/raw-pl/help_about.md b/OpenKeychain/src/main/res/raw-pl/help_about.md index 7262dfefd..28e0b04c5 100644 --- a/OpenKeychain/src/main/res/raw-pl/help_about.md +++ b/OpenKeychain/src/main/res/raw-pl/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) jest implementacją OpenPGP dla Androida. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Licencja: GPLv3+ @@ -61,11 +61,11 @@ Licencja: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-ru/help_about.md b/OpenKeychain/src/main/res/raw-ru/help_about.md index ddb17fc09..6e7e3e832 100644 --- a/OpenKeychain/src/main/res/raw-ru/help_about.md +++ b/OpenKeychain/src/main/res/raw-ru/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) это OpenPGP имплементация для ОС Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Лицензия: GPLv3+ @@ -61,11 +61,11 @@ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-sl/help_about.md b/OpenKeychain/src/main/res/raw-sl/help_about.md index 3fe72beef..eae093b37 100644 --- a/OpenKeychain/src/main/res/raw-sl/help_about.md +++ b/OpenKeychain/src/main/res/raw-sl/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) je implementacija OpenPGP za Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Licenca: GPLv3+ @@ -61,11 +61,11 @@ Licenca: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Licenca Apache v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Licenca Apache v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (Licenca MIT) * [Snackbar](https://github.com/nispok/snackbar) (Licenca MIT) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (Licenca MIT X11) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licenca Apache v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licenca Apache v2) * [ZXing](https://github.com/zxing/zxing) (Licenca Apache v2) diff --git a/OpenKeychain/src/main/res/raw-sr/help_about.md b/OpenKeychain/src/main/res/raw-sr/help_about.md index 5bfbbc6ff..d88344739 100644 --- a/OpenKeychain/src/main/res/raw-sr/help_about.md +++ b/OpenKeychain/src/main/res/raw-sr/help_about.md @@ -1,8 +1,8 @@ [//]: # -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[Отворени кључарник (OpenKeychain)](http://www.openkeychain.org) је ОпенПГП имплементација за Андроид. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Лиценца: ГПЛв3+ @@ -61,11 +61,11 @@ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Апачи лиценца в2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Апачи лиценца в2) * [MiniDNS](https://github.com/rtreffer/minidns) (Апачи лиценца в2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Материјал дизајн) (Апачи лиценца в2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (МИТ лиценца) * [Snackbar](https://github.com/nispok/snackbar) (МИТ лиценца) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (МИТ Икс11 лиценца) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Апачи лиценца в2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Апачи лиценца в2) * [ZXing](https://github.com/zxing/zxing) (Апачи лиценца в2) diff --git a/OpenKeychain/src/main/res/raw-sv/help_about.md b/OpenKeychain/src/main/res/raw-sv/help_about.md index 0fd4713bb..22aae17a1 100644 --- a/OpenKeychain/src/main/res/raw-sv/help_about.md +++ b/OpenKeychain/src/main/res/raw-sv/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTERING: Var vänlig och sätt varje mening på sin egen rad, Transifex sätter varje rad i sitt eget fält för översättningar!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) är en OpenPGP-implementation till Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. Licens: GPLv3+ @@ -61,11 +61,11 @@ Licens: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger's bibliotek för utbyte](https://github.com/SafeSlingerProject/exchange-android) (MIT-licens) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11-licens) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-tr/help_about.md b/OpenKeychain/src/main/res/raw-tr/help_about.md index 06bed0021..3538e68cc 100644 --- a/OpenKeychain/src/main/res/raw-tr/help_about.md +++ b/OpenKeychain/src/main/res/raw-tr/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. License: GPLv3+ @@ -61,11 +61,11 @@ License: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-uk/help_about.md b/OpenKeychain/src/main/res/raw-uk/help_about.md index 06bed0021..3538e68cc 100644 --- a/OpenKeychain/src/main/res/raw-uk/help_about.md +++ b/OpenKeychain/src/main/res/raw-uk/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. License: GPLv3+ @@ -61,11 +61,11 @@ License: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md b/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md index ed32e0d5d..527539f12 100644 --- a/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md +++ b/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org)是一個Android的OpenPGP應用。 +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. 授權:GPLv3+ @@ -61,11 +61,11 @@ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/raw-zh/help_about.md b/OpenKeychain/src/main/res/raw-zh/help_about.md index d8bc20016..1600f19ce 100644 --- a/OpenKeychain/src/main/res/raw-zh/help_about.md +++ b/OpenKeychain/src/main/res/raw-zh/help_about.md @@ -1,8 +1,8 @@ [//]: # (注意: 请把每个句子放在其本行中, Transifex把每一行放在它自己的位置!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) 是安卓上的一个 OpenPGP 协议实现。 +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. 许可协议:GPLv3+ @@ -61,11 +61,11 @@ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache 许可证 v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache 许可证 v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache 许可证 v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache 许可证 v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT 许可证) * [Snackbar](https://github.com/nispok/snackbar) (MIT 许可证) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 许可证) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache 许可证 v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache 许可证 v2) * [ZXing](https://github.com/zxing/zxing) (Apache 许可证 v2) diff --git a/OpenKeychain/src/main/res/raw/help_about.md b/OpenKeychain/src/main/res/raw/help_about.md index 45d326daa..c5e8ebccf 100644 --- a/OpenKeychain/src/main/res/raw/help_about.md +++ b/OpenKeychain/src/main/res/raw/help_about.md @@ -1,8 +1,8 @@ [//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!) -[http://www.openkeychain.org](http://www.openkeychain.org) +[https://www.openkeychain.org](https://www.openkeychain.org) -[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android. +[OpenKeychain](https://www.openkeychain.org) is an OpenPGP implementation for Android. License: GPLv3+ @@ -61,11 +61,11 @@ License: GPLv3+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2) * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2) - * [OkHttp](http://square.github.io/okhttp/) (Apache License v2) + * [OkHttp](https://square.github.io/okhttp/) (Apache License v2) * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2) * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License) * [Snackbar](https://github.com/nispok/snackbar) (MIT License) - * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License) + * [SpongyCastle](https://rtyley.github.io/spongycastle/) (MIT X11 License) * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2) * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2) * [ZXing](https://github.com/zxing/zxing) (Apache License v2) diff --git a/OpenKeychain/src/main/res/values-cs/strings.xml b/OpenKeychain/src/main/res/values-cs/strings.xml index 04cfacd7c..ed9879ffb 100644 --- a/OpenKeychain/src/main/res/values-cs/strings.xml +++ b/OpenKeychain/src/main/res/values-cs/strings.xml @@ -404,7 +404,6 @@ <!--Key trust--> <string name="key_trust_start_cloud_search">Vyhledat</string> <string name="key_trust_results_prefix">Keybase.io nabízí “důkazy” které tvrdí, že vlastníkem tohoto klíče je: </string> - <string name="key_trust_header_text">Poznámka: Keybase.io důkazy jsou experimentální fíčura OpenKeychainu. Doporučujeme vám navíc potvrdit je pomocí naskenování QR kódu nebo si vyměnit klíče pomocí NFC.</string> <!--keybase proof stuff--> <string name="keybase_a_post">Příspěvek</string> <string name="keybase_twitter_proof">tweet</string> diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml index 9b4f06127..42c7cb790 100644 --- a/OpenKeychain/src/main/res/values-de/strings.xml +++ b/OpenKeychain/src/main/res/values-de/strings.xml @@ -189,20 +189,16 @@ <string name="pref_proxy_type_choice_http">HTTP</string> <string name="pref_proxy_type_choice_socks">SOCKS</string> <!--OrbotHelper strings--> - <string name="orbot_ignore_tor">Tor wird nicht verwendet</string> <!--InstallDialogFragment strings--> <string name="orbot_install_dialog_title">Orbot installieren, um Tor zu nutzen?</string> <string name="orbot_install_dialog_install">Installieren</string> <string name="orbot_install_dialog_content">Du musst Orbot installiert und aktiviert haben, um Netzwerverkehr hindurchleiten zu können. Möchtest du es installieren?</string> <string name="orbot_install_dialog_cancel">Abbrechen</string> - <string name="orbot_install_dialog_ignore_tor">Tor nicht verwendet</string> <!--StartOrbotDialogFragment strings--> <string name="orbot_start_dialog_title">Orbot starten?</string> - <string name="orbot_start_dialog_content">Orbot scheint nicht zu laufen. Möchtest du es starten und mit Tor verbinden?</string> <string name="orbot_start_btn">Orbot starten</string> <string name="orbot_start_dialog_start">Orbot starten</string> <string name="orbot_start_dialog_cancel">Abbrechen</string> - <string name="orbot_start_dialog_ignore_tor">Tor nicht verwendet</string> <string name="user_id_no_name"><![CDATA[<kein Name>]]></string> <string name="none"><![CDATA[<keine>]]></string> <plurals name="n_keys"> @@ -593,7 +589,6 @@ <string name="key_trust_no_cloud_evidence">Es gibt keinen Nachweis aus dem Internet zur Vertrauenswürdigkeit dieses Schlüssels.</string> <string name="key_trust_start_cloud_search">Suche beginnen</string> <string name="key_trust_results_prefix">Keybase.io bietet \"Nachweise\" die bestätigen, dass der Schlüsselinhaber:</string> - <string name="key_trust_header_text">Hinweis: Keybase.io-Nachweise sind ein experimentelles Feature von OpenKeychain. Wir rufen dazu auf, zusätzlich zur Bestätigung, QR-Codes zu nutzen oder Schlüssel via NFC auszutauschen.</string> <!--keybase proof stuff--> <string name="keybase_narrative_twitter">Auf Twitter schreibt, als %s</string> <string name="keybase_narrative_github">Auf GitHub bekannt ist, als %s</string> diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml index 5a2611507..706c0d226 100644 --- a/OpenKeychain/src/main/res/values-es/strings.xml +++ b/OpenKeychain/src/main/res/values-es/strings.xml @@ -189,20 +189,16 @@ <string name="pref_proxy_type_choice_http">HTTP</string> <string name="pref_proxy_type_choice_socks">SOCKS</string> <!--OrbotHelper strings--> - <string name="orbot_ignore_tor">No usar Tor</string> <!--InstallDialogFragment strings--> <string name="orbot_install_dialog_title">¿Instalar Orbot para usar Tor?</string> <string name="orbot_install_dialog_install">Instalar</string> <string name="orbot_install_dialog_content">Tiene que tener Orbot instalado y activado para proxyficar el tráfico a través de él. ¿Desea instalarlo?</string> <string name="orbot_install_dialog_cancel">Cancelar</string> - <string name="orbot_install_dialog_ignore_tor">No usar Tor</string> <!--StartOrbotDialogFragment strings--> <string name="orbot_start_dialog_title">¿Iniciar Orbot?</string> - <string name="orbot_start_dialog_content">Orbot no parece estar corriendo. ¿Desea iniciarlo y conectar a Tor?</string> <string name="orbot_start_btn">Iniciar Orbot</string> <string name="orbot_start_dialog_start">Iniciar Orbot</string> <string name="orbot_start_dialog_cancel">Cancelar</string> - <string name="orbot_start_dialog_ignore_tor">No usar Tor</string> <string name="user_id_no_name"><![CDATA[<no name>]]></string> <string name="none"><![CDATA[<none>]]></string> <plurals name="n_keys"> @@ -593,7 +589,6 @@ <string name="key_trust_no_cloud_evidence">No hay comprobante desde Internet de la confiabilidad de esta clave.</string> <string name="key_trust_start_cloud_search">Iniciar búsqueda</string> <string name="key_trust_results_prefix">Keybase.io ofrece \"comprobantes\" que sostienen que el propietario de esta clave:</string> - <string name="key_trust_header_text">Nota: Los comprobantes de Keybase.io son una característica experimental de OpenKeychain. Le animamos a que escanee los códigos QR o intercambie claves vía NFC además de confirmarlas.</string> <!--keybase proof stuff--> <string name="keybase_narrative_twitter">Publica en Twitter como %s</string> <string name="keybase_narrative_github">Es conocido en GitHub como %s</string> diff --git a/OpenKeychain/src/main/res/values-eu/strings.xml b/OpenKeychain/src/main/res/values-eu/strings.xml index 8378dfcad..b81f7364f 100644 --- a/OpenKeychain/src/main/res/values-eu/strings.xml +++ b/OpenKeychain/src/main/res/values-eu/strings.xml @@ -186,20 +186,16 @@ <string name="pref_proxy_type_choice_http">HTTP</string> <string name="pref_proxy_type_choice_socks">SOCKS</string> <!--OrbotHelper strings--> - <string name="orbot_ignore_tor">Ez erabili Tor</string> <!--InstallDialogFragment strings--> <string name="orbot_install_dialog_title">Ezarri Orbot edo erabili Tor?</string> <string name="orbot_install_dialog_install">Ezarri</string> <string name="orbot_install_dialog_content">Orbot ezarrita eta proxy trafiko igaropena gaituta eduki behar duzu. Nahi duzu ezartzea?</string> <string name="orbot_install_dialog_cancel">Ezeztatu</string> - <string name="orbot_install_dialog_ignore_tor">Ez erabili Tor</string> <!--StartOrbotDialogFragment strings--> <string name="orbot_start_dialog_title">Abiarazi Orbot?</string> - <string name="orbot_start_dialog_content">Ez dirudi Orbot ekinean dagoenik. Nahi duzu abiaraztea eta Tor-era elkartzea?</string> <string name="orbot_start_btn">Abiarazi Orbot</string> <string name="orbot_start_dialog_start">Abiarazi Orbot</string> <string name="orbot_start_dialog_cancel">Ezeztatu</string> - <string name="orbot_start_dialog_ignore_tor">Ez erabili Tor</string> <string name="user_id_no_name"><![CDATA[<izengabe>]]></string> <string name="none"><![CDATA[<ezer ez>]]></string> <plurals name="n_keys"> @@ -587,7 +583,6 @@ <!--Key trust--> <string name="key_trust_start_cloud_search">Hasi bilaketa</string> <string name="key_trust_results_prefix">Keybase.io \"probak\" eskaintzen ditu giltza honen jabea baieztatzeko:</string> - <string name="key_trust_header_text">Oharra: Keybase.io egiaztapenak OpenKeychain-en ezaugarri esperimental bat da. QR kodeak eskaneatzea edo giltzak NFC bidez aldatzea gomendatzen dizugu hauek baieztatu ahal izateko.</string> <!--keybase proof stuff--> <string name="keybase_narrative_twitter">Argitaratu Twitter-en honela %s</string> <string name="keybase_narrative_github">GitHub-en %s bezala ezagutzen da</string> diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml index dc29b3212..489774276 100644 --- a/OpenKeychain/src/main/res/values-fr/strings.xml +++ b/OpenKeychain/src/main/res/values-fr/strings.xml @@ -189,20 +189,16 @@ <string name="pref_proxy_type_choice_http">HTTP</string> <string name="pref_proxy_type_choice_socks">SOCKS</string> <!--OrbotHelper strings--> - <string name="orbot_ignore_tor">Ne pas utiliser Tor</string> <!--InstallDialogFragment strings--> <string name="orbot_install_dialog_title">Installer Orbot pour utiliser Tor ?</string> <string name="orbot_install_dialog_install">Installer</string> <string name="orbot_install_dialog_content">Orbot doit être installé et doit relayer le trafic. Voulez-vous l\'installer maintenant ?</string> <string name="orbot_install_dialog_cancel">Annuler</string> - <string name="orbot_install_dialog_ignore_tor">Ne pas utiliser Tor</string> <!--StartOrbotDialogFragment strings--> <string name="orbot_start_dialog_title">Démarrer Orbot ?</string> - <string name="orbot_start_dialog_content">Orbot ne semble pas tourner. Voulez-vous le démarrer et vous connecter à Tor ?</string> <string name="orbot_start_btn">Démarrer Orbot</string> <string name="orbot_start_dialog_start">Démarrer Orbot</string> <string name="orbot_start_dialog_cancel">Annuler</string> - <string name="orbot_start_dialog_ignore_tor">Ne pas utiliser Tor</string> <string name="user_id_no_name"><![CDATA[<no name>]]></string> <string name="none"><![CDATA[<none>]]></string> <plurals name="n_keys"> @@ -593,7 +589,6 @@ <string name="key_trust_no_cloud_evidence">Aucune preuve en provenance de l\'Internet sur la fiabilité de cette clef.</string> <string name="key_trust_start_cloud_search">Lancer la recherche</string> <string name="key_trust_results_prefix">Keybase.io offre des « preuves » affirmant que le propriétaire de cette clef : </string> - <string name="key_trust_header_text">Note : les preuves de keybase.io sont une fonction expérimentales d\'OpenKeychain. Nous vous encourageons à lire des codes QR ou à échanger des clefs via NFC en plus de les confirmer.</string> <!--keybase proof stuff--> <string name="keybase_narrative_twitter">Publie sur Twitter en tant que %s</string> <string name="keybase_narrative_github">Est connu sur GitHub en tant que %s</string> diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml index 4c5f48373..1b8d2729a 100644 --- a/OpenKeychain/src/main/res/values-it/strings.xml +++ b/OpenKeychain/src/main/res/values-it/strings.xml @@ -158,13 +158,11 @@ <string name="pref_proxy_type_choice_http">HTTP</string> <string name="pref_proxy_type_choice_socks">SOCKS</string> <!--OrbotHelper strings--> - <string name="orbot_ignore_tor">Non usare Tor</string> <!--InstallDialogFragment strings--> <string name="orbot_install_dialog_title">Installa Orbot per usare Tor?</string> <string name="orbot_install_dialog_install">Installa</string> <string name="orbot_install_dialog_content">Orbot deve essere installato e attivato per fare attraversare il traffico dei dati tramite proxy. Vuoi installare Orbot?</string> <string name="orbot_install_dialog_cancel">Annulla</string> - <string name="orbot_install_dialog_ignore_tor">Non usare Tor</string> <!--StartOrbotDialogFragment strings--> <string name="orbot_start_dialog_title">Attivare Orbot?</string> <string name="orbot_start_btn">Attivare Orbot</string> diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml index 2adf862e3..a2e68ac66 100644 --- a/OpenKeychain/src/main/res/values-ja/strings.xml +++ b/OpenKeychain/src/main/res/values-ja/strings.xml @@ -25,6 +25,7 @@ <string name="title_export_keys">鍵のバックアップ</string> <string name="title_key_not_found">鍵が見当りません</string> <string name="title_send_key">鍵サーバへアップロード</string> + <string name="title_backup">バックアップ鍵</string> <string name="title_certify_key">鍵の確認</string> <string name="title_key_details">鍵の概要</string> <string name="title_help">ヘルプ</string> @@ -56,6 +57,7 @@ <string name="section_share_key">鍵</string> <string name="section_key_server">鍵サーバ</string> <string name="section_fingerprint">指紋</string> + <string name="section_phrases">語句</string> <string name="section_encrypt">暗号化</string> <string name="section_decrypt">復号 / 検証</string> <string name="section_current_expiry">現在の期限</string> @@ -76,11 +78,14 @@ <string name="btn_back">戻る</string> <string name="btn_no">なし</string> <string name="btn_match">指紋一致</string> + <string name="btn_match_phrases">語句の一致</string> <string name="btn_share_encrypted_signed">暗号化/署名とテキストをシェア</string> <string name="btn_copy_encrypted_signed">暗号化/署名とテキストをコピー</string> <string name="btn_view_cert_key">検証した鍵を見る</string> <string name="btn_create_key">鍵の生成</string> <string name="btn_add_files">ファイルの追加</string> + <string name="btn_share_decrypted_text">共有</string> + <string name="btn_open_with">...で開く</string> <string name="btn_copy_decrypted_text">復号化したテキストのコピー</string> <string name="btn_decrypt_clipboard">クリップボードから読み取り</string> <string name="btn_decrypt_files">入力ファイルの選択</string> @@ -91,9 +96,11 @@ <string name="btn_add_keyserver">追加</string> <string name="btn_save_default">デフォルトとして保存</string> <string name="btn_saved">保存しました!</string> + <string name="btn_not_matching">一致せず</string> <!--menu--> <string name="menu_preferences">設定</string> <string name="menu_help">ヘルプ</string> + <string name="menu_export_key">バックアップ鍵</string> <string name="menu_delete_key">鍵の削除</string> <string name="menu_manage_keys">自分の鍵の管理</string> <string name="menu_search">検索</string> @@ -105,6 +112,8 @@ <string name="menu_update_all_keys">全部のキーをアップデートする</string> <string name="menu_advanced">拡張情報</string> <string name="menu_certify_fingerprint">指紋による確認</string> + <string name="menu_certify_fingerprint_phrases">語句による検証</string> + <string name="menu_share_log">共有ログ</string> <string name="menu_keyserver_add">追加</string> <!--label--> <string name="label_message">テキスト</string> @@ -113,6 +122,7 @@ <string name="label_file_colon">ファイル:</string> <string name="label_no_passphrase">パスワードなし</string> <string name="label_passphrase">パスワード</string> + <string name="label_pin">PIN</string> <string name="label_unlock">アンロック...</string> <string name="label_passphrase_again">再度パスワードを入力</string> <string name="label_show_passphrase">パスワード表示</string> @@ -155,6 +165,9 @@ <string name="label_enable_compression">圧縮を有効</string> <string name="label_encrypt_filenames">暗号化するファイル名</string> <string name="label_hidden_recipients">受信者を隠す</string> + <string name="label_verify_keyserver_connection">テスト接続</string> + <string name="label_only_trusted_keyserver">安全な鍵サーバのみ</string> + <string name="label_enter_keyserver_url">URL</string> <string name="label_keyserver_dialog_delete">鍵サーバの削除</string> <string name="label_theme">テーマ</string> <string name="pref_keyserver">OpenPGP鍵サーバ</string> @@ -171,6 +184,8 @@ <string name="keyserver_sync_settings_title">鍵の自動アップデート</string> <string name="label_experimental_settings_desc_title">注意</string> <string name="label_experimental_settings_desc_summary">これらの機能はまだ完成していないか、ユーザーエクスペリエンス/セキュリティ研究の結果ではありません。そのため、このセキュリティに依存したり、遭遇した問題を報告しないでください!</string> + <string name="label_experimental_settings_word_confirm_title">語句検証</string> + <string name="label_experimental_settings_word_confirm_summary">16進指紋の変りに語句で鍵を検証</string> <string name="label_experimental_settings_linked_identities_title">リンクしたユーザID</string> <string name="label_experimental_settings_linked_identities_summary">Twitter、GitHub、Webサイト、またはDNSに鍵をリンク (keybase.ioに似ていますが、分散型)</string> <string name="label_experimental_settings_keybase_title">Keybase.io 検証</string> @@ -195,10 +210,10 @@ <string name="orbot_install_dialog_install">インストール</string> <string name="orbot_install_dialog_content">Orbotをインストールして、それを介したプロキシトラフィックを有効にする必要があります。インストールしますか?</string> <string name="orbot_install_dialog_cancel">キャンセル</string> - <string name="orbot_install_dialog_ignore_tor">Torを使わない</string> + <string name="orbot_install_dialog_ignore_tor">Torを使わないあ</string> <!--StartOrbotDialogFragment strings--> <string name="orbot_start_dialog_title">Orbotを始めますか?</string> - <string name="orbot_start_dialog_content">Orbot は動作しても表れません。スタートしてTorに接続しますか?</string> + <string name="orbot_start_dialog_content">Orbot は動作していないようです。起動して Tor に接続しますか?</string> <string name="orbot_start_btn">Orbotを始める</string> <string name="orbot_start_dialog_start">Orbotを始める</string> <string name="orbot_start_dialog_cancel">キャンセル</string> @@ -247,6 +262,8 @@ <string name="no_filemanager_installed">互換性のないファイルマネージャがインストールされています。</string> <string name="passphrases_do_not_match">パスワードが一致しません。</string> <string name="passphrase_must_not_be_empty">パスワードを入れてください。</string> + <string name="passphrase_for_symmetric_encryption">パスワードの入力</string> + <string name="passphrase_for_backup">バックアップコードの入力</string> <string name="passphrase_for">\'%s\' にパスワードを入れてください</string> <string name="pin_for">\'%s\' にPINを入力してください</string> <string name="yubikey_pin_for">\'%s\' の Yubikey にアクセスするためのPINを入力してください</string> @@ -269,6 +286,7 @@ <string name="specify_backup_dest_secret_single">鍵のフルバックアップが行われます。保存先のファイルを指定してください。\n警告: ファイルが存在する場合、上書きされます!</string> <string name="specify_backup_dest_secret">あなたの鍵を含めて、すべての鍵のフルバックアップが行われます。保存先のファイルを指定してください。\n警告: ファイルが存在する場合、上書きされます!</string> <string name="key_deletion_confirmation_multi">選択したすべての鍵を本当に削除してよいですか?</string> + <string name="secret_key_deletion_confirmation">削除後はこの鍵で暗号化されたメッセージ/ファイルが読めなくなります、またその鍵で行われたすべての鍵確認を失います!</string> <string name="public_key_deletetion_confirmation">鍵 \'%s\' を削除しますか?</string> <string name="also_export_secret_keys">秘密鍵もエクスポートします</string> <string name="reinstall_openkeychain">あなたは既知のAndroidのバグに遭遇しました。もし鍵とあなたの連絡先をリンクさせたいならOpenKeychainを再インストールしてください。</string> @@ -388,6 +406,7 @@ <string name="progress_deleting">鍵の削除中...</string> <string name="progress_con_saving">統合: キャッシュへ保存…</string> <string name="progress_con_reimport">統合: 再インポート中…</string> + <string name="progress_verifying_keyserver_connection">接続の検証中...</string> <string name="progress_starting_orbot">Orbotを始める...</string> <!--action strings--> <string name="hint_cloud_search_hint">名前、Email...で検索</string> @@ -584,7 +603,6 @@ <string name="key_trust_no_cloud_evidence">この鍵の信頼性についてのインターネットでの検証がありません。</string> <string name="key_trust_start_cloud_search">検索開始</string> <string name="key_trust_results_prefix">Keybase.ioはこのキーのオーナーだと言う証拠を提供している:</string> - <string name="key_trust_header_text">注意:Keybase.ioのオーナー証拠は実験的な機会です。キーを確認することに加えて、QRコードや、NFCでキーを交換するのもお勧めする。</string> <!--keybase proof stuff--> <string name="keybase_narrative_twitter">Twitterへ以下のIDで投稿 %s</string> <string name="keybase_narrative_github">Githubでは %s で知られています</string> @@ -658,10 +676,14 @@ <string name="create_key_add_email_text">追加のEメールアドレスがこの鍵に紐付きそしてセキュアな通信に使うことができます。</string> <string name="create_key_email_already_exists_text">メールアドレスがすでに追加されている</string> <string name="create_key_email_invalid_email">メールアドレスのフォーマットが無効です</string> + <string name="create_key_yubi_key_pin_text">6桁の数字でPINを選んでください。</string> + <string name="create_key_yubi_key_admin_pin_text">管理者PINを書き留めて、安全な場所に保管してください (PINを3回間違ったときに必要になります)。</string> <string name="create_key_yubi_key_pin">PIN</string> <string name="create_key_yubi_key_admin_pin">管理者PIN</string> <string name="create_key_yubi_key_pin_repeat">再度PINを入力</string> <string name="create_key_yubi_key_pin_not_correct">PINが正しくありません!</string> + <string name="create_key_yubi_key_pin_too_short">PINは最低数字6字長は必要です!</string> + <string name="create_key_yubi_key_pin_insecure">000000、123456、のような似た組み合わせではない、安全なPINを選んでください。</string> <!--View key--> <string name="view_key_revoked">破棄: 鍵はもう使われません!</string> <string name="view_key_expired">期限切れ: この連絡先は鍵の妥当性を拡張する必要があります!</string> @@ -673,8 +695,10 @@ <!--Add/Edit keyserver--> <string name="add_keyserver_dialog_title">鍵サーバを追加</string> <string name="edit_keyserver_dialog_title">鍵サーバの編集</string> + <string name="add_keyserver_connection_verified">接続が検証されました!</string> <string name="add_keyserver_without_verification">鍵サーバを検証なしで追加した。</string> <string name="add_keyserver_invalid_url">無効なURLです!</string> + <string name="add_keyserver_keyserver_not_trusted">信頼された鍵サーバではありません (ピンニング証明書は利用できません)!</string> <string name="add_keyserver_connection_failed">鍵サーバへの接続に失敗しました。URLとインターネット接続をチェックしてください。</string> <string name="keyserver_preference_deleted">%s を削除</string> <string name="keyserver_preference_cannot_delete_last">最後の鍵サーバは削除できません。少なくとも一つは必要です!</string> @@ -685,6 +709,7 @@ <string name="drawer_open">ナビゲーションドロワーを開く</string> <string name="drawer_close">ナビゲーションドロワーを閉める</string> <string name="my_keys">自分の鍵</string> + <string name="nav_backup">バックアップ/リストア</string> <!--hints--> <string name="encrypt_content_edit_text_hint">テキストを入力</string> <!--certs--> @@ -710,6 +735,8 @@ <string name="msg_ip_error_op_exc">データベースエラーにより操作が失敗しました</string> <string name="msg_ip_error_remote_ex">内部エラーにより操作が失敗しました</string> <string name="msg_ip">公開鍵の鍵輪 %s をインポート</string> + <string name="msg_ip_fingerprint_error">インポートした鍵の鍵指紋が予期したものと一致しません!</string> + <string name="msg_ip_fingerprint_ok">指紋チェックOK</string> <string name="msg_ip_insert_keyring">鍵輪データのエンコード中</string> <string name="msg_ip_insert_keys">鍵の解析中</string> <string name="msg_ip_prepare">データベース操作の準備</string> @@ -865,6 +892,7 @@ <string name="msg_kc_uid_no_cert">ユーザID \'%s\' の正常な自己署名が見付かりませんでした、鍵輪から除去しました</string> <string name="msg_kc_uid_remove">無効なユーザID \'%s\' を破棄中</string> <string name="msg_kc_uid_dup">重複したユーザID \'%s\' を削除中。IDを二つもっています。この結果は署名を失っているためかもしれません!</string> + <string name="msg_kc_uid_too_many">ユーザーID \'%s\' を削除しています。100以上のユーザーIDがインポートされません!</string> <string name="msg_kc_uid_warn_encoding">ユーザIDをUTF-8として検証できませんでした!</string> <string name="msg_kc_uat_jpeg">JPEG型のユーザー属性の処理中</string> <string name="msg_kc_uat_unknown">不明な型のユーザー属性の処理中</string> @@ -896,6 +924,7 @@ <string name="msg_cr_error_no_user_id">鍵輪は最低でも1つのユーザIDの生成が必要です!</string> <string name="msg_cr_error_no_certify">主鍵は検証フラグが必須です!</string> <string name="msg_cr_error_null_expiry">鍵の生成時に期限を\'過去\'とすることはできません。これはプログラムエラーで、バグレポートでファイルを送ってください!</string> + <string name="msg_cr_error_keysize_2048">鍵サイズは2048かそれ以上が必須です!</string> <string name="msg_cr_error_no_curve">鍵サイズが不明です! これはプログラムエラーで、バグレポートでファイルの提出をお願いします!</string> <string name="msg_cr_error_no_keysize">楕円暗号が不明です! これはプログラムエラーで、バグレポートでファイルの提出をお願いします!</string> <string name="msg_cr_error_internal_pgp">PGP内部エラー!</string> @@ -1014,6 +1043,7 @@ <string name="msg_dc_askip_not_allowed">受け入れた鍵で暗号化されていないデータです、スキップします...</string> <string name="msg_dc_asym">鍵 %s で非対称の暗号化されたデータのブロックを検出</string> <string name="msg_dc_charset">文字セットヘッダを発見: \'%s\'</string> + <string name="msg_dc_backup_version">バックアップバージョンヘッダを発見: \'%s\'</string> <string name="msg_dc_clear_data">固定データを処理中</string> <string name="msg_dc_clear_decompress">圧縮データの展開中</string> <string name="msg_dc_clear_meta_file">ファイル名: %s</string> @@ -1036,6 +1066,7 @@ <string name="msg_dc_error_input">入力データストリームのオープンエラー!</string> <string name="msg_dc_error_no_data">ストリーム中に暗号化されたデータが見付からなかった!</string> <string name="msg_dc_error_no_key">ストリーム中に既知の秘密鍵で暗号化されたデータが見付からなかった!</string> + <string name="msg_dc_error_no_signature">署名データを見失いました!</string> <string name="msg_dc_error_pgp_exception">操作中にPGP例外に当たりました!</string> <string name="msg_dc_integrity_check_ok">完全性チェックOK!</string> <string name="msg_dc_ok_meta_only">メタデータだけが要求され、暗号化をスキップしました</string> @@ -1051,12 +1082,16 @@ <string name="msg_dc_trail_sym">追跡で遭遇、対称暗号化されたデータ</string> <string name="msg_dc_trail_unknown">追跡で未知のタイプのデータに遭遇</string> <string name="msg_dc_unlocking">秘密鍵のロック解除</string> + <string name="msg_dc_insecure_encryption_key">安全でない暗号化鍵が使用されました! 鍵が古くなっているか、攻撃のために発生することがあります。</string> <string name="msg_dc_insecure_symmetric_encryption_algo">安全でない暗号化アルゴリズムが使用されています! アプリケーションが古くなっているか、攻撃のために発生することがあります。</string> <string name="msg_dc_insecure_hash_algo">安全でないハッシュアルゴリズムが使用されています! アプリケーションが古くなっているか、攻撃のために発生することがあります。</string> <string name="msg_dc_insecure_mdc_missing">修正検出コード (MDC) パケットが欠落しています! 暗号化アプリケーションは古くなっているか、暗号強度低下攻撃がある場合に発生することがあります。</string> <string name="msg_dc_insecure_key">安全でない鍵: RSA/DSA/エルガマルのビット長が短すぎるか、ECC曲線/アルゴリズムが安全でないと考えられます! アプリケーションが古くなっているか、攻撃のために発生することがあります。</string> <!--Messages for VerifySignedLiteralData operation--> <string name="msg_vl">署名の確認開始</string> + <string name="msg_vl_error_no_siglist">署名済み固定データに署名リストがありません!</string> + <string name="msg_vl_error_wrong_key">メッセージが正しい鍵で署名されていません!</string> + <string name="msg_vl_error_no_signature">署名データの検証中</string> <string name="msg_vl_error_missing_literal">署名済み固定データにペイロードがありません</string> <string name="msg_vl_clear_meta_file">ファイル名: %s</string> <string name="msg_vl_clear_meta_mime">MIME種別: %s</string> @@ -1127,6 +1162,8 @@ </plurals> <string name="msg_import_fetch_error_decode">鍵輪のデコードエラー</string> <string name="msg_import_fetch_error">鍵の展開ができません! (ネットワークの問題?)</string> + <string name="msg_import_fetch_error_keyserver">鍵サーバから展開できません: %s</string> + <string name="msg_import_fetch_error_keyserver_secret">鍵サーバから秘密鍵をインポートできません!</string> <string name="msg_import_fetch_keybase">keybase.ioから回収: %s</string> <string name="msg_import_fetch_keyserver">鍵サーバからの回収: %s</string> <string name="msg_import_fetch_keyserver_ok">鍵の展開に成功</string> @@ -1137,6 +1174,20 @@ <string name="msg_import_error_io">I/Oエラーによりインポート操作が失敗しました!</string> <string name="msg_import_partial">インポート操作に成功、ただしエラーあり!</string> <string name="msg_import_success">インポート操作に成功!</string> + <plurals name="msg_backup"> + <item quantity="other">%d の鍵とバックアップ</item> + </plurals> + <string name="msg_backup_all">すべての鍵とバックアップ</string> + <string name="msg_backup_public">公開鍵 %s のバックアップを作成中</string> + <string name="msg_upload">公開鍵 %s をアップロード中</string> + <string name="msg_backup_secret">秘密鍵 %s のバックアップを作成中</string> + <string name="msg_backup_error_uri_open">URIオープン時にエラー!</string> + <string name="msg_backup_error_db">データベースエラー!</string> + <string name="msg_backup_error_io">入出力エラー!</string> + <string name="msg_upload_error_key">鍵データの事前処理のエラー!</string> + <string name="msg_upload_error_upload">サーバに鍵をアップロード中にエラー! インターネット接続を確認してください</string> + <string name="msg_backup_success">バックアップ操作に成功</string> + <string name="msg_upload_success">鍵サーバへアップロードに成功</string> <string name="msg_del_error_empty">削除するものがありません!</string> <string name="msg_del_error_multi_secret">秘密鍵は個別にしか削除できません!</string> <plurals name="msg_del"> @@ -1175,6 +1226,28 @@ <string name="msg_lv_fetch_error_io">IOエラー!</string> <string name="msg_lv_fetch_error_format">フォーマットエラー!</string> <string name="msg_lv_fetch_error_nothing">リソースが見つかりません!</string> + <string name="msg_data">入力データの処理中</string> + <string name="msg_data_openpgp">OpenPGPデータの処理を試みています</string> + <string name="msg_data_detached">分離署名に遭遇しました</string> + <string name="msg_data_detached_clear">以前の、未署名のデータをクリアしました!</string> + <string name="msg_data_detached_sig">分離署名の処理中</string> + <string name="msg_data_detached_raw">署名データの処理中</string> + <string name="msg_data_detached_nested">ネストされた署名データをスキップしています!</string> + <string name="msg_data_detached_trailing">署名された部分の後、末尾のデータをスキップしています!</string> + <string name="msg_data_detached_unsupported">サポートしない種別の分離署名!</string> + <string name="msg_data_error_io">入力データの読み込み時にエラー!</string> + <string name="msg_data_error_openpgp">OpenPGPデータの処理中にエラー!</string> + <string name="msg_data_mime_bad">MIMEデータとして解析できません</string> + <string name="msg_data_mime_filename">ファイル名: \'%s\'</string> + <string name="msg_data_mime_from_extension">拡張子からMIME種別を推測しています</string> + <string name="msg_data_mime_length">コンテンツ長: %s</string> + <string name="msg_data_mime">MIMEデータ構造の解析中</string> + <string name="msg_data_mime_ok">\"解析完了</string> + <string name="msg_data_mime_none">MIME構造が見つかりません</string> + <string name="msg_data_mime_part">MIME部分の処理中</string> + <string name="msg_data_mime_type">コンテンツ種別: %s</string> + <string name="msg_data_ok">データの処理に成功</string> + <string name="msg_data_skip_mime">MIME解析をスキップしています</string> <string name="msg_acc_saved">アカウント保存</string> <string name="msg_download_success">ダウンロードに成功しました!</string> <string name="msg_download_no_valid_keys">ファイル/クリップボードにて正しい鍵が見付かりません!</string> @@ -1195,6 +1268,9 @@ <string name="msg_keybase_error_specific">%s</string> <string name="msg_keybase_error_msg_payload_mismatch">復号化した検証ポストが指定した値と一致しない</string> <!--Messages for Mime parsing operation--> + <string name="msg_mime_parsing_start">MIME構造の解析中</string> + <string name="msg_mime_parsing_error">MIME解析に失敗</string> + <string name="msg_mime_parsing_success">MIME解析に成功!</string> <!--PassphraseCache--> <string name="passp_cache_notif_click_to_clear">タッチしてパスワードをクリア。</string> <plurals name="passp_cache_notif_n_keys"> @@ -1220,6 +1296,7 @@ <string name="backup_all">すべての鍵とあなた所有の鍵</string> <string name="backup_public_keys">すべての鍵</string> <string name="backup_section">バックアップ</string> + <string name="restore_section">リストア</string> <!--unsorted--> <string name="section_certifier_id">検証者</string> <string name="section_cert">証明の詳細</string> @@ -1229,6 +1306,8 @@ <string name="certs_text">あなたの検証された自己証明とあなたの鍵で生成された証明がここに表示されます</string> <string name="section_uids_to_certify">ユーザID</string> <string name="certify_text">インポートした鍵には\"アイデンティティ\": 名前とメールアドレス を含みます。正確に期待したものと一致しているか認定されているものを選択します。</string> + <string name="certify_fingerprint_text">鍵指紋を、文字対文字で、相手の端末に表示しているものと比較</string> + <string name="certify_fingerprint_text_phrases">これらの語句を、相手の端末に表示しているものと比較</string> <string name="label_revocation">破棄の理由</string> <string name="label_cert_type">種別</string> <string name="error_key_not_found">鍵が見当りません!</string> @@ -1249,6 +1328,7 @@ <string name="error_multi_clipboard">クリップボードへの複数ファイルの暗号化はサポートされていません。</string> <string name="error_detached_signature">バイナリファイルの署名のみの操作はサポートされません、最低1つは暗号化鍵を選択してください。</string> <string name="error_empty_text">テキストの入力を暗号化!</string> + <string name="error_log_share_internal">ログの準備中に内部エラー!</string> <string name="key_colon">鍵:</string> <string name="exchange_description">鍵交換の開始は、右側の参加者の番号を選択し、その後、\"交換開始\"ボタンを推します。\n\n2つ以上の質問で交換にいる右の参加者とその指紋が正しいかを確認してください。</string> <string name="btn_start_exchange">交換開始</string> @@ -1293,7 +1373,12 @@ <string name="yubikey_status_unbound">YubiKeyがマッチ、鍵に紐付けることができる</string> <string name="yubikey_status_partly">YubiKeyがマッチ、鍵に部分的に紐付いている</string> <string name="yubikey_create">あなたのデバイスの背面にYubiKeyを固定してください。</string> + <string name="yubikey_reset_or_import">このYubiKeyは既に鍵を含んでいます。クラウドを使用して鍵をインポートしたり、YubiKeyをリセットすることができます。</string> <string name="btn_import">インポート</string> + <string name="btn_reset">リセット</string> + <string name="yubikey_import_radio">鍵のインポート</string> + <string name="yubikey_reset_radio">YubiKeyをリセット</string> + <string name="yubikey_reset_warning">YubiKeyをリセットすると、その上のキーを完全に破壊します。その後は、この鍵で暗号化されたメッセージ/ファイルを復号化することができなくなります!</string> <string name="snack_yubi_other">違う鍵がYubiKeyに格納されています!</string> <string name="error_nfc">NFCエラー: %s</string> <plurals name="error_pin"> @@ -1310,7 +1395,9 @@ <string name="error_nfc_chaining_error">YubiKeyがチェーン中の最後のコマンドを待っています。</string> <string name="error_nfc_header">YubiKeyが不正な%sバイトを報告。</string> <string name="error_nfc_tag_lost">YubiKeyを取り出すのが早すぎました。操作が完了するまでYubiKeyを戻してしてください。</string> + <string name="error_nfc_iso_dep_not_supported">タグは ISO-DEP (ISO 14443-4) をサポートしていません</string> <string name="error_nfc_try_again">再実行</string> + <string name="error_pin_wrong">PINが正しくありません!</string> <string name="error_temp_file">一時ファイルの生成でエラーしました。</string> <string name="btn_delete_original">オリジナルのファイルを削除します</string> <string name="snack_encrypt_filenames_on">ファイル名を暗号化<b>した</b>。</string> @@ -1322,9 +1409,13 @@ <string name="error_loading_keys">鍵の読み込みエラー!</string> <string name="error_empty_log">(エラー、空のログ)</string> <string name="error_reading_text">復号化のための入力が読めない!</string> + <string name="error_reading_aosp">データの読み込みに失敗しました。これはAndroidメールクライアントのバグです! (Issue #290)</string> + <string name="error_reading_k9">不完全なデータを受信しました。 K-9 メールで、\'完全なメッセージをダウンロード\' を押してみてください!</string> <string name="filename_unknown">不明なファイル名 (クリックして開きます)</string> <string name="filename_unknown_text">テキスト(クリックして表示)</string> + <string name="filename_keys">鍵のバックアップ (クリックしてインポート)</string> <string name="intent_show">署名/暗号化した内容を表示</string> + <string name="intent_share">署名/暗号化した内容を共有</string> <string name="view_internal">OpenKeychainで閲覧</string> <string name="error_preparing_data">データの処理でエラー!</string> <string name="label_clip_title">暗号化データ</string> @@ -1370,6 +1461,7 @@ <item quantity="other">%d 以上の不明なユーザID種別があります</item> </plurals> <!--Other Linked Identity strings--> + <string name="linked_select_1">\'リンクしたユーザID\' は、あなたのPGP鍵をWeb上のリソースに接続します。</string> <string name="linked_select_2">種別を選択してください: </string> <string name="linked_id_generic_text">このファイルは、長いID %2$s のOpenPGP鍵の所有権を請求しています。\n\n検証のトークン:\n%1$s</string> <string name="linked_id_github_text">このGistは私のOpenPGP鍵でリンクされたIDを確認し、このGitHubアカウントにリンクします。\n\n検証のトークン:\n%1$s</string> @@ -1388,6 +1480,7 @@ <string name="card_linked_identity">リンクしたユーザID</string> <string name="linked_button_verify">検証</string> <string name="linked_button_retry">リトライ</string> + <string name="linked_button_retry_step">最後のステップを再試行</string> <string name="linked_button_confirm">確認</string> <string name="linked_button_view">閲覧</string> <string name="linked_text_verifying">検証中…</string> @@ -1395,4 +1488,36 @@ <string name="linked_text_confirming">確認中…</string> <string name="linked_ids_more_unknown">%d 以上の不明なユーザID種別</string> <string name="title_linked_id_create">リンクしたユーザIDを作成</string> + <string name="linked_github_text">この操作は、鍵をあなたのGitHubアカウントにリンクします。\nボタンを押すと続行します。</string> + <string name="linked_progress_auth_github">GitHubで認証…</string> + <string name="linked_progress_post_gist">Gistを投稿…</string> + <string name="linked_progress_update_key">鍵のアップデート…</string> + <string name="linked_button_start">Githubアカウントへリンク</string> + <string name="linked_error_auth_failed">認証に失敗!</string> + <string name="linked_error_timeout">接続タイムアウト!</string> + <string name="linked_error_network">ネットワークエラー!</string> + <string name="linked_error_http">通信エラー: %s</string> + <string name="linked_webview_title_github">GitHub認証</string> + <string name="linked_gist_description">OpenKeychain API テスト</string> + <string name="snack_btn_overwrite">上書</string> + <string name="backup_code_explanation">バックアップは、バックアップコードで保護されます。先に進む前に、それを書き留めてください!</string> + <string name="backup_code_enter">バックアップコードを入力してください:</string> + <string name="backup_code_ok">コードを受け付けました!</string> + <string name="btn_code_wrotedown">はい、書き留めました!</string> + <string name="backup_code_wrong">入力したバックアップコードが正しくありません!\n正しく書き留めましたか?</string> + <string name="btn_backup_share">バックアップを共有</string> + <string name="btn_backup_save">バックアップを保存</string> + <string name="snack_backup_error_saving">バックアップの保存中にエラー!</string> + <string name="snack_backup_saved">バックアップを保存しました</string> + <string name="snack_backup_exists">バックアップは既に存在します!</string> + <string name="snack_backup_saved_dir">OpenKeychainディレクトリに保存しました</string> + <string name="btn_backup_back">チェックに戻る</string> + <string name="snack_text_too_long">テキストが長すぎてすべて表示できません!</string> + <string name="snack_shared_text_too_long">長すぎたため、共有テキストをカットしました!</string> + <string name="share_log_dialog_title">ログを共有しますか?</string> + <string name="share_log_dialog_message">開発者がOpenKeychainのバグを見つけるためにログは非常に参考になりますが、更新された鍵に関するデータなど、潜在的な機密情報が含まれることがあります。この情報を共有しても大丈夫であることを確認してください。</string> + <string name="share_log_dialog_share_button">共有</string> + <string name="share_log_dialog_cancel_button">キャンセル</string> + <string name="toast_wrong_mimetype">データ種別が正しくありません。テキストを期待しました!</string> + <string name="toast_no_text">共有データにテキストはありません!</string> </resources> diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml index 5e5ab2a09..cf870718e 100644 --- a/OpenKeychain/src/main/res/values-nl/strings.xml +++ b/OpenKeychain/src/main/res/values-nl/strings.xml @@ -158,20 +158,16 @@ <string name="pref_proxy_type_choice_http">HTTP</string> <string name="pref_proxy_type_choice_socks">SOCKS</string> <!--OrbotHelper strings--> - <string name="orbot_ignore_tor">Gebruik Tor niet</string> <!--InstallDialogFragment strings--> <string name="orbot_install_dialog_title">Orbot installeren om Tor te gebruiken?</string> <string name="orbot_install_dialog_install">Installeren</string> <string name="orbot_install_dialog_content">Orbot moet geïnstalleerd en ingesteld zijn om verkeer erdoor te proxyen. Wil je het installeren?</string> <string name="orbot_install_dialog_cancel">Annuleren?</string> - <string name="orbot_install_dialog_ignore_tor">Gebruik Tor niet</string> <!--StartOrbotDialogFragment strings--> <string name="orbot_start_dialog_title">Orbot starten?</string> - <string name="orbot_start_dialog_content">Orbot wordt niet uitgevoerd. Wil je het opstarten en verbinden met Tor?</string> <string name="orbot_start_btn">Orbot starten</string> <string name="orbot_start_dialog_start">Orbot starten</string> <string name="orbot_start_dialog_cancel">Annuleren</string> - <string name="orbot_start_dialog_ignore_tor">Gebruik Tor niet</string> <plurals name="n_keys"> <item quantity="one">1 sleutel</item> <item quantity="other">%d sleutels</item> @@ -513,7 +509,6 @@ <!--Key trust--> <string name="key_trust_start_cloud_search">Zoeken starten</string> <string name="key_trust_results_prefix">Keybase.io geeft “bewijzen” die stellen dat de eigenaar van deze sleutel:</string> - <string name="key_trust_header_text">Let op: Keybase.io-bewijzen zijn een experimentele functie van OpenKeychain. We moedigen je aan QR-codes te scannen of sleutels uit te wisselen via NFC bovenop het bevestigen ervan.</string> <!--keybase proof stuff--> <string name="keybase_proof_failure">Helaas kan dit bewijs niet geverifieerd worden.</string> <string name="keybase_unknown_proof_failure">Niet-herkend probleem met de bewijschecker</string> diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml index 7dc1f09c5..8db2e64ae 100644 --- a/OpenKeychain/src/main/res/values-ru/strings.xml +++ b/OpenKeychain/src/main/res/values-ru/strings.xml @@ -9,6 +9,7 @@ <string name="title_decrypt">Расшифровать</string> <string name="title_add_subkey">Добавить подключ</string> <string name="title_edit_key">Изменить ключ</string> + <string name="title_linked_create">Создать связанный идентификатор</string> <string name="title_preferences">Настройки</string> <string name="title_api_registered_apps">Приложения</string> <string name="title_key_server_preference">Серверы OpenPGP</string> @@ -24,6 +25,7 @@ <string name="title_export_keys">Резервные ключи</string> <string name="title_key_not_found">Ключ не найден</string> <string name="title_send_key">Загрузить на сервер ключей</string> + <string name="title_backup">Резервировать ключ</string> <string name="title_certify_key">Сертифицировать ключ</string> <string name="title_key_details">Сведения о ключе</string> <string name="title_help">Помощь</string> @@ -36,12 +38,15 @@ <string name="section_user_ids">Идентификаторы</string> <string name="section_yubikey">YubiKey</string> <string name="section_linked_system_contact">Связанные контакты</string> + <string name="section_keybase_proofs">Доказательства Keybase.io</string> <string name="section_should_you_trust">Должны ли Вы доверять этому ключу?</string> <string name="section_proof_details">Подтвердить верификацию</string> <string name="section_keys">Доп. ключи</string> <string name="section_cloud_search">Поиск ключа</string> + <string name="section_cloud_search_summary">Сервер ключей, keybase.io</string> <string name="section_passphrase_cache">Пароли и PIN-коды</string> <string name="section_proxy_settings">Сетевая анонимность</string> + <string name="section_proxy_settings_summary">Tor, настройки прокси</string> <string name="section_gui">Интерфейс</string> <string name="section_sync_settings">Синхронизация</string> <string name="section_sync_settings_summary">Автоматическое обновление ключей, </string> @@ -51,6 +56,7 @@ <string name="section_share_key">Ключ</string> <string name="section_key_server">Сервер ключей</string> <string name="section_fingerprint">Отпечаток ключа</string> + <string name="section_phrases">Фразы</string> <string name="section_encrypt">Зашифровать</string> <string name="section_decrypt">Расшифровка / Подтверждение</string> <string name="section_current_expiry">Текущий срок годности</string> @@ -71,11 +77,14 @@ <string name="btn_back">Назад</string> <string name="btn_no">Нет</string> <string name="btn_match">Отпечатки совпадают</string> + <string name="btn_match_phrases">Совпадающие фразы</string> <string name="btn_share_encrypted_signed">Зашифровать/подписать и поделиться текстом</string> <string name="btn_copy_encrypted_signed">Зашифровать/подписать и скопировать текст</string> <string name="btn_view_cert_key">Просмотр ключа</string> <string name="btn_create_key">Создать ключ</string> <string name="btn_add_files">Добавить файл(ы)</string> + <string name="btn_share_decrypted_text">Опубликовать</string> + <string name="btn_open_with">Открыть с помощью...</string> <string name="btn_copy_decrypted_text">Копировать расшифрованный текст</string> <string name="btn_decrypt_clipboard">Прочитать из буфера</string> <string name="btn_decrypt_files">Выберите входной файл</string> @@ -86,9 +95,11 @@ <string name="btn_add_keyserver">Добавить</string> <string name="btn_save_default">Сохранить по умолчанию</string> <string name="btn_saved">Сохранено!</string> + <string name="btn_not_matching">Нет совпадений</string> <!--menu--> <string name="menu_preferences">Настройки</string> <string name="menu_help">Помощь</string> + <string name="menu_export_key">Резервировать ключ</string> <string name="menu_delete_key">Удалить ключ</string> <string name="menu_manage_keys">Управление ключами</string> <string name="menu_search">Поиск</string> @@ -100,6 +111,8 @@ <string name="menu_update_all_keys">Обновить все ключи</string> <string name="menu_advanced">Дополнительные сведения</string> <string name="menu_certify_fingerprint">Подтвердить через отпечаток</string> + <string name="menu_certify_fingerprint_phrases">Подтвердить фразами</string> + <string name="menu_share_log">Опубликовать отчёт</string> <string name="menu_keyserver_add">Добавить</string> <!--label--> <string name="label_message">Текст</string> @@ -125,6 +138,7 @@ <string name="label_hash_algorithm">ХЭШ-алгоритм</string> <string name="label_symmetric">Зашифровать с паролем</string> <string name="label_passphrase_cache_ttl">Запомнить время</string> + <string name="label_passphrase_cache_subs">Запомнить пароли для подключей</string> <string name="label_message_compression">Сжатие текста</string> <string name="label_file_compression">Сжатие файла</string> <string name="label_keyservers">Выберите серверы OpenPGP</string> @@ -139,6 +153,7 @@ <string name="label_name">Имя</string> <string name="label_comment">Комментарий</string> <string name="label_email">Email</string> + <string name="label_send_key">Синхронизация с Интернет</string> <string name="label_fingerprint">Отпечаток</string> <string name="expiry_date_dialog_title">Срок годности</string> <string name="label_keyservers_title">Серверы ключей</string> @@ -148,14 +163,30 @@ <string name="label_enable_compression">Использовать сжатие</string> <string name="label_encrypt_filenames">Шифровать имена файлов</string> <string name="label_hidden_recipients">Скрыть получателей</string> + <string name="label_verify_keyserver_connection">Тест соединения</string> + <string name="label_only_trusted_keyserver">Только доверенные сервера ключей</string> <string name="label_keyserver_dialog_delete">Удалить сервер ключей</string> <string name="label_theme">Тема</string> <string name="pref_keyserver">Серверы OpenPGP</string> <string name="pref_keyserver_summary">Искать ключи на выбранных серверах OpenPGP (протокол HKP)</string> <string name="pref_keybase">keybase.io</string> <string name="pref_keybase_summary">Искать ключи на Keybase.io</string> + <string name="label_sync_settings_keyserver_title">Автоматически обновлять ключи</string> + <string name="label_sync_settings_keyserver_summary_on">Ключи старше недели обновляются с предпочтительного сервера ключей</string> + <string name="label_sync_settings_keyserver_summary_off">Ключи не обновляются автоматически</string> + <string name="label_sync_settings_contacts_title">Связать ключи с контактами</string> + <string name="label_sync_settings_contacts_summary_on">Связывать ключи с контактами основываясь на именах и адресах электронной почты. Это происходит полностью в автономном режиме на вашем устройстве.</string> + <string name="label_sync_settings_contacts_summary_off">Новые ключи не будут связаны с контактами</string> <!--label shown in Android settings under the OpenKeychain account--> + <string name="keyserver_sync_settings_title">Автоматически обновлять ключи</string> <string name="label_experimental_settings_desc_title">Предупреждение</string> + <string name="label_experimental_settings_desc_summary">Эти новые возможности ещё не закончены и/или пока только изучаются. Проще говоря, не стоит полагаться на их безопасность. Пожалуйста, не сообщайте о связанных с ними проблемах!</string> + <string name="label_experimental_settings_word_confirm_title">Фраза подтверждения</string> + <string name="label_experimental_settings_word_confirm_summary">Подтвердить ключи с помощью фраз, вместо шестнадцатеричных отпечатков </string> + <string name="label_experimental_settings_linked_identities_title">Связанные идентификаторы</string> + <string name="label_experimental_settings_linked_identities_summary">Привязать ключи к Twitter, GitHub, веб-сайту или DNS (по аналогии с keybase.io но децентрализованно)</string> + <string name="label_experimental_settings_keybase_title">Keybase.io подтверждение</string> + <string name="label_experimental_settings_keybase_summary">Опрашивать keybase.io для подтверждения ключей и показывать это каждый раз при отображении ключей</string> <!--Proxy Preferences--> <string name="pref_proxy_tor_title">Использовать Tor</string> <string name="pref_proxy_tor_summary">Требуется установка Orbot</string> @@ -209,6 +240,7 @@ <string name="choice_4hours">4 часа</string> <string name="choice_8hours">8 часов</string> <string name="choice_forever">всегда</string> + <string name="choice_select_cert">Выбрать ключ</string> <string name="dsa">DSA</string> <string name="elgamal">ElGamal</string> <string name="rsa">RSA</string> @@ -229,6 +261,7 @@ <string name="no_filemanager_installed">Нет совместимого менеджера файлов.</string> <string name="passphrases_do_not_match">Пароли не совпадают.</string> <string name="passphrase_must_not_be_empty">Пожалуйста, введите пароль.</string> + <string name="passphrase_for_symmetric_encryption">Введите пароль</string> <string name="passphrase_for">Введите пароль для \'%s\'</string> <string name="pin_for">Введите PIN для \'%s\'</string> @@ -513,6 +546,7 @@ <string name="edit_key_error_add_identity">Добавьте хотя бы один идентификатор!</string> <string name="edit_key_error_add_subkey">Добавьте хотя бы один доп. ключ!</string> <!--Create key--> + <string name="create_key_upload">Синхронизация с Интернет</string> <string name="create_key_empty">Это обязательне поле</string> <string name="create_key_passphrases_not_equal">Пароли не совпадают</string> <string name="create_key_final_text">Вы указали следующие данные:</string> @@ -914,9 +948,11 @@ <string name="error_nfc">Ошибка NFC: %s</string> <!--Other Linked Identity strings--> <string name="linked_select_2">Пожалуйста, выберите тип:</string> + <string name="section_linked_identities">Связанные идентификаторы</string> <string name="btn_finish">Завершить</string> <string name="linked_title_twitter">Твиттер</string> <string name="linked_button_retry">Повторить</string> <string name="linked_button_confirm">Подтвердить</string> <string name="linked_text_error">Ошибка</string> + <string name="share_log_dialog_share_button">Опубликовать</string> </resources> diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml index 587cb364a..cceff9221 100644 --- a/OpenKeychain/src/main/res/values-sl/strings.xml +++ b/OpenKeychain/src/main/res/values-sl/strings.xml @@ -445,7 +445,6 @@ <!--Key trust--> <string name="key_trust_start_cloud_search">Začni iskanje</string> <string name="key_trust_results_prefix">Keybase.io ponuja \"dokazilo\", da je lastnik tega kluča:</string> - <string name="key_trust_header_text">Pozor: Dokazila Keybase.io v aplikaciji OpenKeychain so eksperimantalne narave. Za večjo zanesljivost priporočamo, da dodatno skenirate kode QR ali izmenjate ključe preko NFC.</string> <!--keybase proof stuff--> <string name="keybase_proof_failure">Dokazila se žal ne da preveriti.</string> <string name="keybase_unknown_proof_failure">Neznan problem pri pregledovalniku dokazil</string> diff --git a/OpenKeychain/src/main/res/values-sr/strings.xml b/OpenKeychain/src/main/res/values-sr/strings.xml index 0806b9f1a..7b9ef3238 100644 --- a/OpenKeychain/src/main/res/values-sr/strings.xml +++ b/OpenKeychain/src/main/res/values-sr/strings.xml @@ -173,20 +173,16 @@ <string name="pref_proxy_type_choice_http">ХТТП</string> <string name="pref_proxy_type_choice_socks">СОЦКС</string> <!--OrbotHelper strings--> - <string name="orbot_ignore_tor">Не користи Тор</string> <!--InstallDialogFragment strings--> <string name="orbot_install_dialog_title">Инсталирати Орбот да бих користио Тор?</string> <string name="orbot_install_dialog_install">Инсталирај</string> <string name="orbot_install_dialog_content">Морате имати Орбот инсталиран и активиран да бисте преусмерили саобраћај кроз њега. Желите ли да га инсталирате?</string> <string name="orbot_install_dialog_cancel">Одустани</string> - <string name="orbot_install_dialog_ignore_tor">Не користи Тор</string> <!--StartOrbotDialogFragment strings--> <string name="orbot_start_dialog_title">Да покренем Орбот?</string> - <string name="orbot_start_dialog_content">Чини се да Орбот није покренут. Желите ли да га покренете и повежете са Тором?</string> <string name="orbot_start_btn">Покрени Орбот</string> <string name="orbot_start_dialog_start">Покрени Орбот</string> <string name="orbot_start_dialog_cancel">Одустани</string> - <string name="orbot_start_dialog_ignore_tor">Не користи Тор</string> <string name="user_id_no_name"><![CDATA[<нема имена>]]></string> <string name="none"><![CDATA[<ништа>]]></string> <plurals name="n_keys"> @@ -570,7 +566,6 @@ <!--Key trust--> <string name="key_trust_start_cloud_search">Почни претрагу</string> <string name="key_trust_results_prefix">Keybase.io нуди „доказе“ који потврђују да власник овог кључа: </string> - <string name="key_trust_header_text">Напомена: Докази са Keybase.io су експериментална функција Отвореног кључарника. Препорука је да очитавате бар-кôдове или размењујете кључеве преко НФЦ-а како бисте их потврдили.</string> <!--keybase proof stuff--> <string name="keybase_narrative_twitter">Објављује на Твитеру као %s</string> <string name="keybase_narrative_github">Познат је на Гитхабу као %s</string> diff --git a/OpenKeychain/src/main/res/values-sv/strings.xml b/OpenKeychain/src/main/res/values-sv/strings.xml index ae34fdc13..0d44b6415 100644 --- a/OpenKeychain/src/main/res/values-sv/strings.xml +++ b/OpenKeychain/src/main/res/values-sv/strings.xml @@ -500,7 +500,6 @@ <!--Key trust--> <string name="key_trust_start_cloud_search">Påbörja sökning</string> <string name="key_trust_results_prefix">Keybase.io erbjuder \"bevis\" som hävdar att ägaren av den här nyckeln:</string> - <string name="key_trust_header_text">Observera: Keybase.io bevis är en experimentell funktion i OpenKeychain. Vi uppmanar dig att skanna QR-koder eller utbyta nycklar via NFC utöver att bekräfta dem.</string> <!--keybase proof stuff--> <string name="keybase_narrative_github">Är känd på GitHub som %s</string> <string name="keybase_proof_failure">Tyvärr kan detta bevis inte verifieras.</string> diff --git a/OpenKeychain/src/main/res/values-v23/themes.xml b/OpenKeychain/src/main/res/values-v23/themes.xml new file mode 100644 index 000000000..29bd762c0 --- /dev/null +++ b/OpenKeychain/src/main/res/values-v23/themes.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- see http://stackoverflow.com/questions/32169303/activity-did-not-call-finish-api-23 --> + <style name="Theme.Keychain.Transparent" parent="@android:style/Theme.Translucent.NoTitleBar" /> + +</resources> diff --git a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml index 1b23fe6ca..bc9f08a5e 100644 --- a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml +++ b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml @@ -156,20 +156,16 @@ <string name="pref_proxy_type_choice_http">HTTP</string> <string name="pref_proxy_type_choice_socks">SOCKS</string> <!--OrbotHelper strings--> - <string name="orbot_ignore_tor">不使用Tor</string> <!--InstallDialogFragment strings--> <string name="orbot_install_dialog_title">是否安裝Orbot以使用Tor?</string> <string name="orbot_install_dialog_install">安裝</string> <string name="orbot_install_dialog_content">您必須安裝Orbot並透過它進行網路代理。您是否要安裝Orbot?</string> <string name="orbot_install_dialog_cancel">取消</string> - <string name="orbot_install_dialog_ignore_tor">不使用Tor</string> <!--StartOrbotDialogFragment strings--> <string name="orbot_start_dialog_title">啟動Orbot?</string> - <string name="orbot_start_dialog_content">Orbot尚未執行,您希望啟動它並連結到Tor嗎?</string> <string name="orbot_start_btn">啟動Orbot</string> <string name="orbot_start_dialog_start">啟動Orbot</string> <string name="orbot_start_dialog_cancel">取消</string> - <string name="orbot_start_dialog_ignore_tor">不使用Tor</string> <plurals name="n_keys"> <item quantity="other">%d 金鑰</item> </plurals> @@ -500,7 +496,6 @@ <!--Key trust--> <string name="key_trust_start_cloud_search">開始搜尋</string> <string name="key_trust_results_prefix">Keybase.io提供“證據”證明這把金鑰的持有人:</string> - <string name="key_trust_header_text">注意:Keybase.io證據是個OpenKeychain實驗性功能。我們推薦你另外以QR條碼或是NFC交換金鑰進行確認。</string> <!--keybase proof stuff--> <string name="keybase_narrative_twitter">用下列身分在Twitter參與討論:%s</string> <string name="keybase_proof_failure">很不幸地我們沒法驗證這些證據。</string> diff --git a/OpenKeychain/src/main/res/values/colors.xml b/OpenKeychain/src/main/res/values/colors.xml index 06fe2a9cd..4eb9e7d68 100644 --- a/OpenKeychain/src/main/res/values/colors.xml +++ b/OpenKeychain/src/main/res/values/colors.xml @@ -33,4 +33,7 @@ <color name="translucent_scrim_bottom">#2A000000</color> <color name="translucent_scrim_bottom_center">#2A000000</color> + <!-- linked ID view --> + <color name="link_text_material_light">#ff009688</color> + </resources> diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index fa745cb5b..84b5b0376 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -88,6 +88,7 @@ <string name="btn_match_phrases">"Phrases match"</string> <string name="btn_share_encrypted_signed">"Encrypt/sign and share text"</string> <string name="btn_copy_encrypted_signed">"Encrypt/sign and copy text"</string> + <string name="btn_paste_encrypted_signed">"Encrypt/sign and paste text"</string> <string name="btn_view_cert_key">"View certification key"</string> <string name="btn_create_key">"Create key"</string> <string name="btn_add_files">"Add file(s)"</string> @@ -396,6 +397,7 @@ <string name="progress_cancelling">"cancelling…"</string> <string name="progress_saving">"saving…"</string> <string name="progress_importing">"importing…"</string> + <string name="progress_benchmarking">"benchmarking…"</string> <string name="progress_revoking_uploading">"Revoking and uploading key…"</string> <string name="progress_updating">"Updating keys…"</string> <string name="progress_exporting">"exporting…"</string> @@ -1148,6 +1150,8 @@ <string name="msg_ek_error_not_found">"Key not found!"</string> <!-- Messages for DecryptVerify operation --> + <string name="msg_dc_askip_bad_flags">"Key is not an encryption key, skipping…"</string> + <string name="msg_dc_askip_unavailable">"Key not available, skipping…"</string> <string name="msg_dc_askip_no_key">"Data not encrypted with known key, skipping…"</string> <string name="msg_dc_askip_not_allowed">"Data not encrypted with allowed key, skipping…"</string> <string name="msg_dc_asym">"Found block of asymmetrically encrypted data for key %s"</string> @@ -1299,15 +1303,22 @@ </plurals> <string name="msg_backup_all">"Backup with all keys"</string> <string name="msg_backup_public">"Creating backup of public key %s"</string> - <string name="msg_backup_upload_public">"Uploading public key %s"</string> <string name="msg_backup_secret">"Creating backup of secret key %s"</string> <string name="msg_backup_error_uri_open">"Error opening URI stream!"</string> <string name="msg_backup_error_db">"Database error!"</string> <string name="msg_backup_error_io">"Input/output error!"</string> - <string name="msg_backup_error_key">"Error preprocessing key data!"</string> - <string name="msg_backup_error_upload">"Error uploading key to server! Please check your Internet connection"</string> <string name="msg_backup_success">"Backup operation successful"</string> - <string name="msg_backup_upload_success">"Upload to keyserver successful"</string> + + <string name="msg_upload">"Uploading public key"</string> + <string name="msg_upload_proxy_direct">"Using proxy: None"</string> + <string name="msg_upload_proxy_tor">"Using proxy: TOR"</string> + <string name="msg_upload_proxy">"Using proxy: %s"</string> + <string name="msg_upload_server">"Server: %s"</string> + <string name="msg_upload_key">"Key id: %s"</string> + <string name="msg_upload_error_key">"Error preprocessing key data!"</string> + <string name="msg_upload_error_not_found">"Key not found!"</string> + <string name="msg_upload_error_upload">"Error uploading key to server! Please check your Internet connection"</string> + <string name="msg_upload_success">"Upload to keyserver successful"</string> <string name="msg_del_error_empty">"Nothing to delete!"</string> <string name="msg_del_error_multi_secret">"Secret keys can only be deleted individually!"</string> @@ -1355,6 +1366,15 @@ <string name="msg_lv_fetch_error_format">"Format error!"</string> <string name="msg_lv_fetch_error_nothing">"Resource not found!"</string> + <string name="msg_bench">"Benchmarking some operations…"</string> + <string name="msg_bench_enc_time">"Encryption time: %ss"</string> + <string name="msg_bench_enc_time_avg">"Average time to encrypt 5M: %ss"</string> + <string name="msg_bench_dec_time">"Decryption time: %ss"</string> + <string name="msg_bench_dec_time_avg">"Average time to decrypt 5M: %ss"</string> + <string name="msg_bench_s2k_100ms_its">"S2K Iteration Count for 100ms: %s"</string> + <string name="msg_bench_s2k_for_it">"Time for %1$s SHA1 S2K iterations: %2$sms"</string> + <string name="msg_bench_success">"Benchmarking complete!"</string> + <string name="msg_data">"Processing input data"</string> <string name="msg_data_openpgp">"Attempting to process OpenPGP data"</string> <string name="msg_data_detached">"Encountered detached signature"</string> @@ -1365,7 +1385,6 @@ <string name="msg_data_detached_trailing">"Skipping trailing data after signed part!"</string> <string name="msg_data_detached_unsupported">"Unsupported type of detached signature!"</string> <string name="msg_data_error_io">"Error reading input data!"</string> - <string name="msg_data_error_openpgp">"Error processing OpenPGP data!"</string> <string name="msg_data_mime_bad">"Could not parse as MIME data"</string> <string name="msg_data_mime_filename">"Filename: '%s'"</string> <string name="msg_data_mime_from_extension">"Guessing MIME type from extension"</string> @@ -1672,5 +1691,6 @@ <string name="share_log_dialog_cancel_button">"Cancel"</string> <string name="toast_wrong_mimetype">"Wrong data type, expected text!"</string> <string name="toast_no_text">"No text in shared data!"</string> + <string name="btn_search_for_query">"Search for\n'%s'"</string> </resources> diff --git a/OpenKeychain/src/main/res/values/themes.xml b/OpenKeychain/src/main/res/values/themes.xml index 38cf8a3db..fc6ae3846 100644 --- a/OpenKeychain/src/main/res/values/themes.xml +++ b/OpenKeychain/src/main/res/values/themes.xml @@ -85,11 +85,9 @@ <item name="alertDialogTheme">@style/Theme.Keychain.Dark.Dialog.Alert</item> </style> - <style name="Theme.Keychain.Light" parent="Base.Theme.Keychain.Light"> - </style> + <style name="Theme.Keychain.Light" parent="Base.Theme.Keychain.Light"></style> - <style name="Theme.Keychain.Dark" parent="Base.Theme.Keychain.Dark"> - </style> + <style name="Theme.Keychain.Dark" parent="Base.Theme.Keychain.Dark"></style> <!-- http://android-developers.blogspot.de/2014/10/appcompat-v21-material-design-for-pre.html --> <style name="Widget.Keychain.SearchView" parent="Widget.AppCompat.SearchView"> @@ -134,4 +132,6 @@ <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style> + + <style name="Theme.Keychain.Transparent" parent="@android:style/Theme.NoDisplay" /> </resources> diff --git a/OpenKeychain/src/main/res/xml/gui_preferences.xml b/OpenKeychain/src/main/res/xml/gui_preferences.xml deleted file mode 100644 index cda7beeef..000000000 --- a/OpenKeychain/src/main/res/xml/gui_preferences.xml +++ /dev/null @@ -1,10 +0,0 @@ -<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> - <ListPreference - android:persistent="false" - android:key="theme" - android:title="@string/label_theme" - android:entries="@array/theme_entries" - android:entryValues="@array/theme_values" - android:dialogTitle="@string/label_theme" /> - -</PreferenceScreen> diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java index be233d0b3..d3c3f1df5 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java @@ -46,6 +46,7 @@ import org.spongycastle.bcpg.PacketTags; import org.spongycastle.bcpg.PublicKeyEncSessionPacket; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPKeyFlags; import org.sufficientlysecure.keychain.WorkaroundBuildConfig; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; @@ -56,6 +57,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType; import org.sufficientlysecure.keychain.support.KeyringTestingHelper; @@ -558,8 +560,10 @@ public class PgpEncryptDecryptTest { String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true); + byte[] ciphertext; + long encKeyId1; + { // encrypt data with key - byte[] ciphertext; ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes()); @@ -589,7 +593,7 @@ public class PgpEncryptDecryptTest { Packet p; p = new BCPGInputStream(new ByteArrayInputStream(enc1.buf)).readPacket(); Assert.assertTrue("first packet must be session packet", p instanceof PublicKeyEncSessionPacket); - long encKeyId1 = ((PublicKeyEncSessionPacket) p).getKeyID(); + encKeyId1 = ((PublicKeyEncSessionPacket) p).getKeyID(); p = new BCPGInputStream(new ByteArrayInputStream(enc2.buf)).readPacket(); Assert.assertTrue("second packet must be session packet", p instanceof PublicKeyEncSessionPacket); @@ -604,6 +608,48 @@ public class PgpEncryptDecryptTest { } + { // strip first encrypted subkey, decryption should skip it + + SaveKeyringParcel parcel = new SaveKeyringParcel(mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint()); + parcel.mChangeSubKeys.add(new SubkeyChange(encKeyId1, true, false)); + UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(parcel, mStaticRing1, + new ArrayList<RawPacket>(), new ArrayList<RawPacket>(), + new CryptoInputParcel(new Date(), mKeyPhrase1)); + + ProviderHelper providerHelper = new ProviderHelper(RuntimeEnvironment.application); + providerHelper.saveSecretKeyRing(modified, new ProgressScaler()); + + PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application, + new ProviderHelper(RuntimeEnvironment.application), null); + PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(ciphertext); + DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1)); + + Assert.assertTrue("decryption must succeed", result.success()); + Assert.assertTrue("decryption must have skipped first key", + result.getLog().containsType(LogType.MSG_DC_ASKIP_UNAVAILABLE)); + } + + { // change flags of second encrypted subkey, decryption should skip it + + SaveKeyringParcel parcel = new SaveKeyringParcel(mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint()); + parcel.mChangeSubKeys.add(new SubkeyChange(encKeyId1, PGPKeyFlags.CAN_CERTIFY, null)); + UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(parcel, mStaticRing1, + new ArrayList<RawPacket>(), new ArrayList<RawPacket>(), + new CryptoInputParcel(new Date(), mKeyPhrase1)); + + ProviderHelper providerHelper = new ProviderHelper(RuntimeEnvironment.application); + providerHelper.saveSecretKeyRing(modified, new ProgressScaler()); + + PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application, + new ProviderHelper(RuntimeEnvironment.application), null); + PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(ciphertext); + DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1)); + + Assert.assertTrue("decryption must succeed", result.success()); + Assert.assertTrue("decryption must have skipped first key", + result.getLog().containsType(LogType.MSG_DC_ASKIP_BAD_FLAGS)); + } + { // revoke first encryption subkey of keyring in database SaveKeyringParcel parcel = new SaveKeyringParcel(mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint()); parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(mStaticRing1, 2)); @@ -616,7 +662,6 @@ public class PgpEncryptDecryptTest { } { // encrypt to this keyring, make sure it's not encrypted to the revoked subkey - byte[] ciphertext; ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes()); |