diff options
author | Thialfihar <thialfihar@gmail.com> | 2010-06-05 00:33:33 +0000 |
---|---|---|
committer | Thialfihar <thialfihar@gmail.com> | 2010-06-05 00:33:33 +0000 |
commit | a85ae5e0092057d1007ba62fada17c9f6a085c92 (patch) | |
tree | 0de13d1eb3bf345684ed83d6154a08fbebb2fd95 | |
parent | ed2cb1e525778d62949958bbf108687c571e4ebe (diff) | |
parent | a571ce7c5222d1f2246acdfada7c95e48a170dd4 (diff) | |
download | open-keychain-a85ae5e0092057d1007ba62fada17c9f6a085c92.tar.gz open-keychain-a85ae5e0092057d1007ba62fada17c9f6a085c92.tar.bz2 open-keychain-a85ae5e0092057d1007ba62fada17c9f6a085c92.zip |
branching trunk out of latest 1.0.x to get a clean start for it
41 files changed, 2840 insertions, 2595 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 03a653c66..f9a9e3f13 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -17,7 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.thialfihar.android.apg" - android:versionName="0.9.5" android:versionCode="10"> + android:versionCode="15" android:versionName="1.0.1"> <application android:icon="@drawable/icon" @@ -53,12 +53,28 @@ <activity android:name=".SelectPublicKeyListActivity" android:label="@string/title_selectRecipients" - android:configChanges="keyboardHidden|orientation|keyboard"/> + android:configChanges="keyboardHidden|orientation|keyboard"> + + <intent-filter> + <action android:name="org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS" /> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/*"/> + </intent-filter> + + </activity> <activity android:name=".SelectSecretKeyListActivity" android:label="@string/title_selectSignature" - android:configChanges="keyboardHidden|orientation|keyboard"/> + android:configChanges="keyboardHidden|orientation|keyboard"> + + <intent-filter> + <action android:name="org.thialfihar.android.apg.intent.SELECT_SECRET_KEY" /> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/*"/> + </intent-filter> + + </activity> <activity android:name=".EncryptActivity" @@ -68,6 +84,9 @@ <intent-filter> <action android:name="org.thialfihar.android.apg.intent.ENCRYPT" /> <action android:name="org.thialfihar.android.apg.intent.ENCRYPT_FILE" /> + <action android:name="org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN" /> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/*"/> </intent-filter> </activity> @@ -92,6 +111,9 @@ <intent-filter> <action android:name="org.thialfihar.android.apg.intent.DECRYPT" /> <action android:name="org.thialfihar.android.apg.intent.DECRYPT_FILE" /> + <action android:name="org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN" /> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/*"/> </intent-filter> </activity> @@ -106,13 +128,22 @@ android:label="@string/title_preferences" android:configChanges="keyboardHidden|orientation|keyboard"/> + <service android:name=".Service" /> + <provider + android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" android:name="org.thialfihar.android.apg.provider.DataProvider" - android:authorities="org.thialfihar.android.apg.provider" /> + android:authorities="org.thialfihar.android.apg.provider"/> </application> <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="5" /> -<uses-permission android:name="com.google.android.providers.gmail.permission.READ_GMAIL" /> -<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> -</manifest>
\ No newline at end of file + <permission android:name="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" + android:protectionLevel="dangerous" + android:label="@string/permission_read_key_details_label" + android:description="@string/permission_read_key_details_description"/> + + <uses-permission android:name="com.google.android.providers.gmail.permission.READ_GMAIL" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + +</manifest> diff --git a/res/layout/decrypt.xml b/res/layout/decrypt.xml index 03e2f6311..41bbee479 100644 --- a/res/layout/decrypt.xml +++ b/res/layout/decrypt.xml @@ -77,6 +77,7 @@ <EditText android:id="@+id/message" android:inputType="text|textCapSentences|textMultiLine|textLongMessage" + android:scrollHorizontally="true" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="top"/> diff --git a/res/layout/select_public_key_item.xml b/res/layout/select_public_key_item.xml index bb0dd30a4..beca23176 100644 --- a/res/layout/select_public_key_item.xml +++ b/res/layout/select_public_key_item.xml @@ -70,20 +70,6 @@ android:layout_height="fill_parent"/> <TextView - android:id="@+id/creation" - android:textAppearance="?android:attr/textAppearanceSmall" - android:text="31.12.2009" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - - <TextView - android:id="@+id/expiry" - android:textAppearance="?android:attr/textAppearanceSmall" - android:text="31.12.2010" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - - <TextView android:id="@+id/status" android:textAppearance="?android:attr/textAppearanceSmall" android:text="expired" diff --git a/res/layout/select_secret_key_item.xml b/res/layout/select_secret_key_item.xml index 35bf58e23..022545152 100644 --- a/res/layout/select_secret_key_item.xml +++ b/res/layout/select_secret_key_item.xml @@ -63,20 +63,6 @@ android:layout_height="fill_parent"/> <TextView - android:id="@+id/creation" - android:textAppearance="?android:attr/textAppearanceSmall" - android:text="31.12.2009" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - - <TextView - android:id="@+id/expiry" - android:textAppearance="?android:attr/textAppearanceSmall" - android:text="31.12.2010" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - - <TextView android:id="@+id/status" android:textAppearance="?android:attr/textAppearanceSmall" android:text="expired" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 5bb4dc2ba..008981b0e 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -38,10 +38,12 @@ <string name="title_importKeys">Import Keys</string> <string name="title_exportKey">Export Key</string> <string name="title_exportKeys">Export Keys</string> + <string name="title_keyNotFound">Key Not Found</string> <!-- section_lowerCase: capitalized words, no punctuation --> <string name="section_userIds">User IDs</string> <string name="section_keys">Keys</string> + <string name="section_general">General</string> <string name="section_defaults">Defaults</string> <!-- btn_lowerCase: capitalized words, no punctuation --> @@ -92,6 +94,9 @@ <string name="label_hashAlgorithm">Hash Algorithm</string> <string name="label_asymmetric">Public Key</string> <string name="label_symmetric">Pass Phrase</string> + <string name="label_passPhraseCacheTtl">Pass Phrase Cache</string> + <string name="label_messageCompression">Message Compression</string> + <string name="label_fileCompression">File Compression</string> <string name="noKeysSelected">Select</string> <string name="oneKeySelected">1 Selected</string> @@ -108,9 +113,16 @@ <string name="notValid">not valid</string> <!-- choice_lowerCase: capitalized firwst word, no punctuation --> + <string name="choice_none">None</string> <string name="choice_signOnly">Sign only</string> <string name="choice_encryptOnly">Encrypt only</string> <string name="choice_signAndEncrypt">Sign and Encrypt</string> + <string name="choice_15secs">15 secs</string> + <string name="choice_1min">1 min</string> + <string name="choice_3mins">3 mins</string> + <string name="choice_5mins">5 mins</string> + <string name="choice_10mins">10 mins</string> + <string name="choice_untilQuit">until quit</string> <string name="dsa">DSA</string> <string name="elgamal">ElGamal</string> @@ -133,7 +145,7 @@ <string name="usingClipboardContent">Using clipboard content.</string> <string name="keySaved">Key saved.</string> <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> - <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> + <string name="noFilemanagerInstalled">No compatible file manager installed.</string> <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string> <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string> <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> @@ -150,19 +162,20 @@ <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> - <string name="specifyFileToImportFrom">Please specify which file to import from.</string> + <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string> <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string> <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string> <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> - <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> - <string name="keysAdded">Succssfully added %s keys.</string> - <string name="keysUpdated">Succssfully updated %s keys.</string> + <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string> + <string name="keysAdded">Succssfully added %s key(s).</string> + <string name="keysUpdated">Succssfully updated %s key(s).</string> <string name="noKeysAddedOrUpdated">No keys added or updated.</string> <string name="keyExported">Succssfully exported 1 key.</string> <string name="keysExported">Succssfully exported %s keys.</string> <string name="noKeysExported">No keys exported.</string> <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> + <string name="keyNotFound">Couldn't find key %08X.</string> <!-- error_lowerCase: phrases, no punctuation, all lowercase, they will be put after "errorMessage", e.g. "Error: file not found" --> @@ -227,4 +240,3 @@ <string name="progress_verifyingIntegrity">verifying integrity...</string> </resources> - diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 5bb4dc2ba..008981b0e 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -38,10 +38,12 @@ <string name="title_importKeys">Import Keys</string> <string name="title_exportKey">Export Key</string> <string name="title_exportKeys">Export Keys</string> + <string name="title_keyNotFound">Key Not Found</string> <!-- section_lowerCase: capitalized words, no punctuation --> <string name="section_userIds">User IDs</string> <string name="section_keys">Keys</string> + <string name="section_general">General</string> <string name="section_defaults">Defaults</string> <!-- btn_lowerCase: capitalized words, no punctuation --> @@ -92,6 +94,9 @@ <string name="label_hashAlgorithm">Hash Algorithm</string> <string name="label_asymmetric">Public Key</string> <string name="label_symmetric">Pass Phrase</string> + <string name="label_passPhraseCacheTtl">Pass Phrase Cache</string> + <string name="label_messageCompression">Message Compression</string> + <string name="label_fileCompression">File Compression</string> <string name="noKeysSelected">Select</string> <string name="oneKeySelected">1 Selected</string> @@ -108,9 +113,16 @@ <string name="notValid">not valid</string> <!-- choice_lowerCase: capitalized firwst word, no punctuation --> + <string name="choice_none">None</string> <string name="choice_signOnly">Sign only</string> <string name="choice_encryptOnly">Encrypt only</string> <string name="choice_signAndEncrypt">Sign and Encrypt</string> + <string name="choice_15secs">15 secs</string> + <string name="choice_1min">1 min</string> + <string name="choice_3mins">3 mins</string> + <string name="choice_5mins">5 mins</string> + <string name="choice_10mins">10 mins</string> + <string name="choice_untilQuit">until quit</string> <string name="dsa">DSA</string> <string name="elgamal">ElGamal</string> @@ -133,7 +145,7 @@ <string name="usingClipboardContent">Using clipboard content.</string> <string name="keySaved">Key saved.</string> <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> - <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> + <string name="noFilemanagerInstalled">No compatible file manager installed.</string> <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string> <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string> <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> @@ -150,19 +162,20 @@ <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> - <string name="specifyFileToImportFrom">Please specify which file to import from.</string> + <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string> <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string> <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string> <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> - <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> - <string name="keysAdded">Succssfully added %s keys.</string> - <string name="keysUpdated">Succssfully updated %s keys.</string> + <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string> + <string name="keysAdded">Succssfully added %s key(s).</string> + <string name="keysUpdated">Succssfully updated %s key(s).</string> <string name="noKeysAddedOrUpdated">No keys added or updated.</string> <string name="keyExported">Succssfully exported 1 key.</string> <string name="keysExported">Succssfully exported %s keys.</string> <string name="noKeysExported">No keys exported.</string> <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> + <string name="keyNotFound">Couldn't find key %08X.</string> <!-- error_lowerCase: phrases, no punctuation, all lowercase, they will be put after "errorMessage", e.g. "Error: file not found" --> @@ -227,4 +240,3 @@ <string name="progress_verifyingIntegrity">verifying integrity...</string> </resources> - diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 5bb4dc2ba..008981b0e 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -38,10 +38,12 @@ <string name="title_importKeys">Import Keys</string> <string name="title_exportKey">Export Key</string> <string name="title_exportKeys">Export Keys</string> + <string name="title_keyNotFound">Key Not Found</string> <!-- section_lowerCase: capitalized words, no punctuation --> <string name="section_userIds">User IDs</string> <string name="section_keys">Keys</string> + <string name="section_general">General</string> <string name="section_defaults">Defaults</string> <!-- btn_lowerCase: capitalized words, no punctuation --> @@ -92,6 +94,9 @@ <string name="label_hashAlgorithm">Hash Algorithm</string> <string name="label_asymmetric">Public Key</string> <string name="label_symmetric">Pass Phrase</string> + <string name="label_passPhraseCacheTtl">Pass Phrase Cache</string> + <string name="label_messageCompression">Message Compression</string> + <string name="label_fileCompression">File Compression</string> <string name="noKeysSelected">Select</string> <string name="oneKeySelected">1 Selected</string> @@ -108,9 +113,16 @@ <string name="notValid">not valid</string> <!-- choice_lowerCase: capitalized firwst word, no punctuation --> + <string name="choice_none">None</string> <string name="choice_signOnly">Sign only</string> <string name="choice_encryptOnly">Encrypt only</string> <string name="choice_signAndEncrypt">Sign and Encrypt</string> + <string name="choice_15secs">15 secs</string> + <string name="choice_1min">1 min</string> + <string name="choice_3mins">3 mins</string> + <string name="choice_5mins">5 mins</string> + <string name="choice_10mins">10 mins</string> + <string name="choice_untilQuit">until quit</string> <string name="dsa">DSA</string> <string name="elgamal">ElGamal</string> @@ -133,7 +145,7 @@ <string name="usingClipboardContent">Using clipboard content.</string> <string name="keySaved">Key saved.</string> <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> - <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> + <string name="noFilemanagerInstalled">No compatible file manager installed.</string> <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string> <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string> <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> @@ -150,19 +162,20 @@ <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> - <string name="specifyFileToImportFrom">Please specify which file to import from.</string> + <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string> <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string> <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string> <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> - <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> - <string name="keysAdded">Succssfully added %s keys.</string> - <string name="keysUpdated">Succssfully updated %s keys.</string> + <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string> + <string name="keysAdded">Succssfully added %s key(s).</string> + <string name="keysUpdated">Succssfully updated %s key(s).</string> <string name="noKeysAddedOrUpdated">No keys added or updated.</string> <string name="keyExported">Succssfully exported 1 key.</string> <string name="keysExported">Succssfully exported %s keys.</string> <string name="noKeysExported">No keys exported.</string> <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> + <string name="keyNotFound">Couldn't find key %08X.</string> <!-- error_lowerCase: phrases, no punctuation, all lowercase, they will be put after "errorMessage", e.g. "Error: file not found" --> @@ -227,4 +240,3 @@ <string name="progress_verifyingIntegrity">verifying integrity...</string> </resources> - diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml new file mode 100644 index 000000000..8bb198ad6 --- /dev/null +++ b/res/values-sl/strings.xml @@ -0,0 +1,242 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <string name="app_name">APG</string> + + <!-- title_lowerCase: capitalized words, no punctuation --> + <string name="title_mailInbox">Poštni nabiralnik</string> + <string name="title_managePublicKeys">Upravljanje javnih ključev</string> + <string name="title_manageSecretKeys">Upravljanje zasebnih ključev</string> + <string name="title_selectRecipients">Izberi prejemnike</string> + <string name="title_selectSignature">Izberi podpis</string> + <string name="title_encrypt">Šifriraj</string> + <string name="title_decrypt">Dešifriraj</string> + <string name="title_authentification">Avtentikacija</string> + <string name="title_createKey">Ustvari ključ</string> + <string name="title_editKey">Uredi ključ</string> + <string name="title_preferences">Nastavitve</string> + <string name="title_changePassPhrase">Spremeni geslo</string> + <string name="title_setPassPhrase">Določi geslo</string> + <string name="title_sendEmail">"Pošlji e-pošto..."</string> + <string name="title_encryptToFile">Šifriraj v datoteko</string> + <string name="title_decryptToFile">Dešifriraj v datoteko</string> + <string name="title_addAccount">Dodaj račun</string> + <string name="title_importKeys">Uvozi ključe</string> + <string name="title_exportKey">Izvozi ključ</string> + <string name="title_exportKeys">Izvozi ključe</string> + <string name="title_keyNotFound">Ključ ni bil najden</string> + + <!-- section_lowerCase: capitalized words, no punctuation --> + <string name="section_userIds">Uporabniške identitete</string> + <string name="section_keys">Ključi</string> + <string name="section_general">Splošno</string> + <string name="section_defaults">Privzete nastavitve</string> + + <!-- btn_lowerCase: capitalized words, no punctuation --> + <string name="btn_encryptToClipboard">Šifriraj v odložišče</string> + <string name="btn_send">Šifriraj in pošlji</string> + <string name="btn_encrypt">Šifriraj</string> + <string name="btn_decrypt">Dešifriraj</string> + <string name="btn_verify">Overi</string> + <string name="btn_selectEncryptKeys">Izberi prejemnike</string> + <string name="btn_reply">Odgovori</string> + <string name="btn_encryptMessage">Šifriraj sporočilo</string> + <string name="btn_decryptMessage">Dešifriraj sporočilo</string> + <string name="btn_encryptFile">Šifriraj datoteko</string> + <string name="btn_decryptFile">Dešifriraj datoteko</string> + <string name="btn_save">Shrani</string> + <string name="btn_doNotSave">Prekliči</string> + <string name="btn_delete">Izbriši</string> + <string name="btn_noDate">Brez</string> + + <!-- menu_lowerCase: capitalized words, no punctuation --> + <string name="menu_about">O programu</string> + <string name="menu_addAccount">Dodaj GMail račun</string> + <string name="menu_deleteAccount">Izbriši račun</string> + <string name="menu_managePublicKeys">Upravljanje javnih ključev</string> + <string name="menu_manageSecretKeys">Upravljanje zasebnih ključev</string> + <string name="menu_preferences">Nastavitve</string> + <string name="menu_changePassPhrase">Spremeni geslo</string> + <string name="menu_setPassPhrase">Določi geslo</string> + <string name="menu_importKeys">Uvozi ključe</string> + <string name="menu_exportKeys">Izvozi ključe</string> + <string name="menu_exportKey">Izvozi ključ</string> + <string name="menu_deleteKey">Izbriši ključ</string> + <string name="menu_createKey">Ustvari ključ</string> + <string name="menu_editKey">Uredi ključ</string> + + <!-- label_lowerCase: capitalized words, no punctuation --> + <string name="label_sign">Podpiši</string> + <string name="label_message">Sporočilo</string> + <string name="label_file">Datoteka</string> + <string name="label_passPhrase">Geslo</string> + <string name="label_passPhraseAgain">Ponovi</string> + <string name="label_algorithm">Algoritem</string> + <string name="label_asciiArmour">ASCII Armour</string> + <string name="label_selectPublicKeys">Javni ključ(i)</string> + <string name="label_deleteAfterEncryption">Po šifriranju izbriši</string> + <string name="label_deleteAfterDecryption">Po dešifriranju izbriši</string> + <string name="label_encryptionAlgorithm">Šifrirni algoritem</string> + <string name="label_hashAlgorithm">Hash algoritem</string> + <string name="label_asymmetric">Javni ključ</string> + <string name="label_symmetric">Geslo</string> + <string name="label_passPhraseCacheTtl">Predpomnilnik gesel</string> + <string name="label_messageCompression">Zgoščevanje sporočil</string> + <string name="label_fileCompression">Zgoščevanje datotek</string> + + <string name="noKeysSelected">Izberi</string> + <string name="oneKeySelected">1 izbran</string> + <string name="nKeysSelected">Izbrani</string> + <string name="unknownUserId"><nepoznan></string> + <string name="none"><brez></string> + <string name="noKey"><brez ključa></string> + <string name="noDate">-</string> + <string name="noExpiry"><nikoli></string> + <string name="unknownStatus"></string> + <string name="canEncrypt">lahko šifrira</string> + <string name="canSign">lahko podpiše</string> + <string name="expired">potečeno</string> + <string name="notValid">neveljavno</string> + + <!-- choice_lowerCase: capitalized firwst word, no punctuation --> + <string name="choice_none">Brez</string> + <string name="choice_signOnly">Samo podpis</string> + <string name="choice_encryptOnly">Samo šifriranje</string> + <string name="choice_signAndEncrypt">Podpis in šifriranje</string> + <string name="choice_15secs">15 sek</string> + <string name="choice_1min">1 min</string> + <string name="choice_3mins">3 min</string> + <string name="choice_5mins">5 min</string> + <string name="choice_10mins">10 min</string> + <string name="choice_untilQuit">do izhoda</string> + + <string name="dsa">DSA</string> + <string name="elgamal">ElGamal</string> + <string name="rsa">RSA</string> + + <string name="filemanager_titleOpen">Odpri...</string> + <string name="filemanager_titleSave">Shrani kot...</string> + <string name="filemanager_titleEncrypt">Izberi datoteko za šifriranje...</string> + <string name="filemanager_titleDecrypt">Izberi datoteko za dešifriranje...</string> + <string name="filemanager_btnOpen">Odpri</string> + <string name="filemanager_btnSave">Shrani</string> + + <string name="warning">Opozorilo</string> + <string name="error">Napaka</string> + <string name="warningMessage">Opozorilo: %s</string> + <string name="errorMessage">Napaka: %s</string> + + <!-- sentences --> + <string name="wrongPassPhrase">Napačno geslo.</string> + <string name="usingClipboardContent">Uporabljam vsebino odložišča.</string> + <string name="keySaved">Ključ shranjen.</string> + <string name="setAPassPhrase">Najprej preko menija možnosti določite geslo.</string> + <string name="noFilemanagerInstalled">Nameščen ni noben združljiv upravitelj datotek.</string> + <string name="passPhrasesDoNotMatch">Gesli se ne ujemata.</string> + <string name="passPhraseMustNotBeEmpty">Prazna gesla niso dovoljena.</string> + <string name="passPhraseForSymmetricEncryption">Geslo za simetrično enkripcijo:</string> + <string name="passPhraseFor">Geslo za %s:</string> + <string name="fileDeleteConfirmation">Ali ste prepričani, da želite izbrisati\n%s?</string> + <string name="fileDeleteSuccessful">Uspešno izbrisano.</string> + <string name="noFileSelected">Najprej izberite datoteko.</string> + <string name="decryptionSuccessful">Uspešno dešifrirano.</string> + <string name="encryptionSuccessful">Uspešno šifrirano.</string> + <string name="encryptionToClipboardSuccessful">Uspešno šifrirano v odložišče.</string> + <string name="enterPassPhraseTwice">Vstavite geslo dvakrat.</string> + <string name="selectEncryptionKey">Izberite vsaj en šifrirni ključ.</string> + <string name="selectEncryptionOrSignatureKey">Izberite vsaj en šifrirni ključ ali ključ za podpis.</string> + <string name="specifyFileToEncryptTo">Določite datoteko v katero želite šifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string> + <string name="specifyFileToDecryptTo">Določite datoteko v katero želite dešifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string> + <string name="specifyGoogleMailAccount">Določite Google Mail račun, ki ga želite dodati.</string> + <string name="specifyFileToImportFrom">Določite iz katere datoteke želite uvoziti ključe. (.asc ali .gpg)</string> + <string name="specifyFileToExportTo">Določite v katero datoteko želite izvoziti.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string> + <string name="specifyFileToExportSecretKeysTo">Določite v katero datoteko želite izvoziti.\nPOZOR! Izvozili boste ZASEBNI ključ.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string> + <string name="keyDeletionConfirmation">Ali zares želite izbrisati ključ '%s'?\nTega ne boste mogli popraviti!</string> + <string name="secretKeyDeletionConfirmation">Ali zares želite izbrisati ZASEBNI ključ '%s'?\nTega ne boste mogli popraviti!</string> + <string name="keysAddedAndUpdated">Uspešno dodani ključi: %s. Uspešno posodobljeni ključi: %s."</string> + <string name="keysAdded">Uspešno dodani ključi: %s.</string> + <string name="keysUpdated">Uspešno posodobljeni ključi: %s.</string> + <string name="noKeysAddedOrUpdated">Noben ključ ni bil dodan ali posodobljen.</string> + <string name="keyExported">Uspešno izvožen 1 ključ.</string> + <string name="keysExported">Uspešno izvoženi ključi: %s</string> + <string name="noKeysExported">Noben ključ ni bil izvožen.</string> + <string name="keyCreationElGamalInfo">Opomba: le podključi podpirajo ElGamal. Za ElGamal bo uporabljena velikost najbližja 1536, 2048, 3072, 4096, ali 8192.</string> + <string name="keyNotFound">Ne najdem ključa %08X.</string> + + <!-- error_lowerCase: phrases, no punctuation, all lowercase, + they will be put after "errorMessage", e.g. "Error: file not found" --> + <string name="error_fileDeleteFailed">izbris '%s' ni uspel</string> + <string name="error_fileNotFound">ne najdem datoteke</string> + <string name="error_noSecretKeyFound">najden ni bil noben ustrezen zasebni kluč</string> + <string name="error_noKnownEncryptionFound">najdena ni bila nobena poznana vrsta enkripcije</string> + <string name="error_externalStorageNotReady">zunanji pomnilnik ni pripravljen</string> + <string name="error_accountNotFound">račun '%s' ni najden</string> + <string name="error_addingAccountFailed">dodajanje računa '%s' ni uspelo</string> + <string name="error_invalidEmail">neveljaven e-naslov '%s'</string> + <string name="error_keySizeMinimum512bit">velikost ključa mora biti vsaj 512bit</string> + <string name="error_masterKeyMustNotBeElGamal">statični ključ ne more biti ključ ElGamal</string> + <string name="error_unknownAlgorithmChoice">neznana izbira algoritma</string> + <string name="error_userIdNeedsAName">določiti morate ime</string> + <string name="error_userIdNeedsAnEmailAddress">določiti morate naslov e-pošte</string> + <string name="error_keyNeedsAUserId">potrebujem vsaj eno uporabniško identiteto</string> + <string name="error_mainUserIdMustNotBeEmpty">glavna uporabniška identiteta ne more biti prazna</string> + <string name="error_keyNeedsMasterKey">potrebujem vsaj statični ključ</string> + <string name="error_expiryMustComeAfterCreation">datum poteka mora biti kasnejši od datuma nastanka</string> + <string name="error_noEncryptionKeysOrPassPhrase">dan ni bil noben šifrirni ključ ali geslo</string> + <string name="error_signatureFailed">podpis ni bil uspešen</string> + <string name="error_noSignaturePassPhrase">dano ni bilo nobeno geslo</string> + <string name="error_noSignatureKey">dan ni bil noben podpisni ključ</string> + <string name="error_invalidData">neveljavni šifrirni podatki</string> + <string name="error_corruptData">pokvarjeni podatki</string> + <string name="error_noSymmetricEncryptionPacket">ne najdem podatkov s simetrično enkripcijo</string> + <string name="error_wrongPassPhrase">napačno geslo</string> + <string name="error_savingKeys">napaka pri shranjevanju nakaterih ključev</string> + + <!-- progress_lowerCase: lowercase, phrases, usually ending in '...' --> + <string name="progress_done">končano.</string> + <string name="progress_initializing">inicializiram...</string> + <string name="progress_saving">shranjujem...</string> + <string name="progress_importing">uvažam...</string> + <string name="progress_exporting">izvažam...</string> + <string name="progress_generating">generiram ključ, to lahko traja nekaj časa...</string> + <string name="progress_buildingKey">gradim ključ...</string> + <string name="progress_preparingMasterKey">pripravljam statični ključ...</string> + <string name="progress_certifyingMasterKey">potrjujem statični ključ...</string> + <string name="progress_buildingMasterKeyRing">gradim datoteko s statičnimi ključi...</string> + <string name="progress_addingSubKeys">dodajam podključe...</string> + <string name="progress_savingKeyRing">shranjujem datoteko s ključi...</string> + <string name="progress_importingSecretKeys">uvažam zasebne ključe...</string> + <string name="progress_importingPublicKeys">uvažam javne ključe...</string> + <string name="progress_reloadingKeys">reloading keys...</string> + <string name="progress_exportingKey">izvažam ključ...</string> + <string name="progress_exportingKeys">izvažam ključe...</string> + <string name="progress_extractingSignatureKey">izvlačim podpisni kluč...</string> + <string name="progress_extractingKey">izvlačim ključ...</string> + <string name="progress_preparingStreams">pripravljam tok...</string> + <string name="progress_encrypting">šifriram podatke...</string> + <string name="progress_decrypting">dešifriram podatke...</string> + <string name="progress_preparingSignature">pripravljam podpis...</string> + <string name="progress_generatingSignature">generiram podpis...</string> + <string name="progress_processingSignature">obdelujem podpis...</string> + <string name="progress_verifyingSignature">overovljam podpis...</string> + <string name="progress_signing">podpisujem...</string> + <string name="progress_readingData">berem podatke...</string> + <string name="progress_findingKey">iščem ključ...</string> + <string name="progress_decompressingData">raztezam podatke...</string> + <string name="progress_verifyingIntegrity">overovljam integriteto...</string> + +</resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 856ed0c4e..72c1d1f9c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -38,6 +38,7 @@ <string name="title_importKeys">Import Keys</string> <string name="title_exportKey">Export Key</string> <string name="title_exportKeys">Export Keys</string> + <string name="title_keyNotFound">Key Not Found</string> <!-- section_lowerCase: capitalized words, no punctuation --> <string name="section_userIds">User IDs</string> @@ -144,7 +145,7 @@ <string name="usingClipboardContent">Using clipboard content.</string> <string name="keySaved">Key saved.</string> <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> - <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> + <string name="noFilemanagerInstalled">No compatible file manager installed.</string> <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string> <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string> <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> @@ -161,19 +162,20 @@ <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> - <string name="specifyFileToImportFrom">Please specify which file to import from.</string> + <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string> <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string> <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string> <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> - <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> - <string name="keysAdded">Succssfully added %s keys.</string> - <string name="keysUpdated">Succssfully updated %s keys.</string> + <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string> + <string name="keysAdded">Succssfully added %s key(s).</string> + <string name="keysUpdated">Succssfully updated %s key(s).</string> <string name="noKeysAddedOrUpdated">No keys added or updated.</string> <string name="keyExported">Succssfully exported 1 key.</string> <string name="keysExported">Succssfully exported %s keys.</string> <string name="noKeysExported">No keys exported.</string> <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> + <string name="keyNotFound">Couldn't find key %08X.</string> <!-- error_lowerCase: phrases, no punctuation, all lowercase, they will be put after "errorMessage", e.g. "Error: file not found" --> @@ -237,5 +239,9 @@ <string name="progress_decompressingData">decompressing data...</string> <string name="progress_verifyingIntegrity">verifying integrity...</string> + <!-- permission strings --> + <string name="permission_read_key_details_label">Read key details from APG.</string> + <string name="permission_read_key_details_description">Read of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read.</string> + </resources> diff --git a/src/org/openintents/intents/FileManager.java b/src/org/openintents/intents/FileManager.java deleted file mode 100644 index 3a5cc0d86..000000000 --- a/src/org/openintents/intents/FileManager.java +++ /dev/null @@ -1,78 +0,0 @@ -/*
- * Copyright (C) 2008 OpenIntents.org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.openintents.intents;
-
-// Version Dec 9, 2008
-
-/**
- * Provides OpenIntents actions, extras, and categories used by providers.
- * <p>
- * These specifiers extend the standard Android specifiers.
- * </p>
- */
-public final class FileManager {
-
- /**
- * Activity Action: Pick a file through the file manager, or let user
- * specify a custom file name. Data is the current file name or file name
- * suggestion. Returns a new file name as file URI in data.
- *
- * <p>
- * Constant Value: "org.openintents.action.PICK_FILE"
- * </p>
- */
- public static final String ACTION_PICK_FILE = "org.openintents.action.PICK_FILE";
-
- /**
- * Activity Action: Pick a directory through the file manager, or let user
- * specify a custom file name. Data is the current directory name or
- * directory name suggestion. Returns a new directory name as file URI in
- * data.
- *
- * <p>
- * Constant Value: "org.openintents.action.PICK_DIRECTORY"
- * </p>
- */
- public static final String ACTION_PICK_DIRECTORY = "org.openintents.action.PICK_DIRECTORY";
-
- /**
- * The title to display.
- *
- * <p>
- * This is shown in the title bar of the file manager.
- * </p>
- *
- * <p>
- * Constant Value: "org.openintents.extra.TITLE"
- * </p>
- */
- public static final String EXTRA_TITLE = "org.openintents.extra.TITLE";
-
- /**
- * The text on the button to display.
- *
- * <p>
- * Depending on the use, it makes sense to set this to "Open" or "Save".
- * </p>
- *
- * <p>
- * Constant Value: "org.openintents.extra.BUTTON_TEXT"
- * </p>
- */
- public static final String EXTRA_BUTTON_TEXT = "org.openintents.extra.BUTTON_TEXT";
-
-}
diff --git a/src/org/thialfihar/android/apg/Apg.java b/src/org/thialfihar/android/apg/Apg.java index e290a501e..3c35064a4 100644 --- a/src/org/thialfihar/android/apg/Apg.java +++ b/src/org/thialfihar/android/apg/Apg.java @@ -19,6 +19,7 @@ package org.thialfihar.android.apg; import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -35,8 +36,6 @@ import java.security.SecureRandom; import java.security.Security;
import java.security.SignatureException;
import java.util.Calendar;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
@@ -80,18 +79,19 @@ import org.bouncycastle2.openpgp.PGPSignatureList; import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle2.openpgp.PGPUtil;
-import org.thialfihar.android.apg.provider.PublicKeys;
-import org.thialfihar.android.apg.provider.SecretKeys;
+import org.thialfihar.android.apg.provider.Database;
+import org.thialfihar.android.apg.provider.KeyRings;
+import org.thialfihar.android.apg.provider.Keys;
+import org.thialfihar.android.apg.provider.UserIds;
import org.thialfihar.android.apg.ui.widget.KeyEditor;
import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.ui.widget.UserIdEditor;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.Activity;
-import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.net.Uri;
+import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Environment;
import android.view.ViewGroup;
@@ -102,9 +102,35 @@ public class Apg { public static final String ENCRYPT = "org.thialfihar.android.apg.intent.ENCRYPT";
public static final String DECRYPT_FILE = "org.thialfihar.android.apg.intent.DECRYPT_FILE";
public static final String ENCRYPT_FILE = "org.thialfihar.android.apg.intent.ENCRYPT_FILE";
- }
-
- public static String VERSION = "0.9.5";
+ public static final String DECRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN";
+ public static final String ENCRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN";
+ public static final String SELECT_PUBLIC_KEYS = "org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS";
+ public static final String SELECT_SECRET_KEY = "org.thialfihar.android.apg.intent.SELECT_SECRET_KEY";
+ }
+
+ public static final String EXTRA_DATA = "data";
+ public static final String EXTRA_STATUS = "status";
+ public static final String EXTRA_ERROR = "error";
+ public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage";
+ public static final String EXTRA_ENCRYPTED_MESSAGE = "decryptedMessage";
+ public static final String EXTRA_SIGNATURE = "signature";
+ public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId";
+ public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId";
+ public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess";
+ public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown";
+ public static final String EXTRA_USER_ID = "userId";
+ public static final String EXTRA_KEY_ID = "keyId";
+ public static final String EXTRA_REPLY_TO = "replyTo";
+ public static final String EXTRA_SEND_TO = "sendTo";
+ public static final String EXTRA_SUBJECT = "subject";
+ public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds";
+ public static final String EXTRA_SELECTION = "selection";
+ public static final String EXTRA_MESSAGE = "message";
+ public static final String EXTRA_PROGRESS = "progress";
+ public static final String EXTRA_MAX = "max";
+ public static final String EXTRA_ACCOUNT = "account";
+
+ public static String VERSION = "1.0.1";
public static String FULL_VERSION = "APG v" + VERSION;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS =
@@ -125,41 +151,20 @@ public class Apg { CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP };
- protected static Vector<PGPPublicKeyRing> mPublicKeyRings = new Vector<PGPPublicKeyRing>();
- protected static Vector<PGPSecretKeyRing> mSecretKeyRings = new Vector<PGPSecretKeyRing>();
-
public static Pattern PGP_MESSAGE =
Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
Pattern.DOTALL);
public static Pattern PGP_SIGNED_MESSAGE =
- Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
- Pattern.DOTALL);
-
- protected static boolean mInitialized = false;
-
- protected static HashMap<Long, Integer> mSecretKeyIdToIdMap;
- protected static HashMap<Long, PGPSecretKeyRing> mSecretKeyIdToKeyRingMap;
- protected static HashMap<Long, Integer> mPublicKeyIdToIdMap;
- protected static HashMap<Long, PGPPublicKeyRing> mPublicKeyIdToKeyRingMap;
-
- public static final String PUBLIC_KEY_PROJECTION[] =
- new String[] {
- PublicKeys._ID,
- PublicKeys.KEY_ID,
- PublicKeys.KEY_DATA,
- PublicKeys.WHO_ID, };
- public static final String SECRET_KEY_PROJECTION[] =
- new String[] {
- PublicKeys._ID,
- PublicKeys.KEY_ID,
- PublicKeys.KEY_DATA,
- PublicKeys.WHO_ID, };
+ Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
+ Pattern.DOTALL);
private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
new HashMap<Long, CachedPassPhrase>();
private static String mEditPassPhrase = null;
+ private static Database mDatabase = null;
+
public static class GeneralException extends Exception {
static final long serialVersionUID = 0xf812773342L;
@@ -176,102 +181,14 @@ public class Apg { }
}
- static {
- mPublicKeyRings = new Vector<PGPPublicKeyRing>();
- mSecretKeyRings = new Vector<PGPSecretKeyRing>();
- mSecretKeyIdToIdMap = new HashMap<Long, Integer>();
- mSecretKeyIdToKeyRingMap = new HashMap<Long, PGPSecretKeyRing>();
- mPublicKeyIdToIdMap = new HashMap<Long, Integer>();
- mPublicKeyIdToKeyRingMap = new HashMap<Long, PGPPublicKeyRing>();
- }
-
- public static void initialize(Activity context) {
- if (mInitialized) {
- return;
- }
-
- if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- File dir = new File(Constants.path.app_dir);
- if (!dir.exists() && !dir.mkdirs()) {
- // ignore this for now, it's not crucial
- // that the directory doesn't exist at this point
- }
- }
-
- loadKeyRings(context, Id.type.public_key);
- loadKeyRings(context, Id.type.secret_key);
-
- mInitialized = true;
- }
-
- public static class PublicKeySorter implements Comparator<PGPPublicKeyRing> {
- @Override
- public int compare(PGPPublicKeyRing object1, PGPPublicKeyRing object2) {
- PGPPublicKey key1 = getMasterKey(object1);
- PGPPublicKey key2 = getMasterKey(object2);
- if (key1 == null && key2 == null) {
- return 0;
- }
-
- if (key1 == null) {
- return -1;
- }
-
- if (key2 == null) {
- return 1;
- }
-
- String uid1 = getMainUserId(key1);
- String uid2 = getMainUserId(key2);
- if (uid1 == null && uid2 == null) {
- return 0;
- }
-
- if (uid1 == null) {
- return -1;
- }
-
- if (uid2 == null) {
- return 1;
- }
-
- return uid1.compareTo(uid2);
+ public static void initialize(Context context) {
+ if (mDatabase == null) {
+ mDatabase = new Database(context);
}
}
- public static class SecretKeySorter implements Comparator<PGPSecretKeyRing> {
- @Override
- public int compare(PGPSecretKeyRing object1, PGPSecretKeyRing object2) {
- PGPSecretKey key1 = getMasterKey(object1);
- PGPSecretKey key2 = getMasterKey(object2);
- if (key1 == null && key2 == null) {
- return 0;
- }
-
- if (key1 == null) {
- return -1;
- }
-
- if (key2 == null) {
- return 1;
- }
-
- String uid1 = getMainUserId(key1);
- String uid2 = getMainUserId(key2);
- if (uid1 == null && uid2 == null) {
- return 0;
- }
-
- if (uid1 == null) {
- return -1;
- }
-
- if (uid2 == null) {
- return 1;
- }
-
- return uid1.compareTo(uid2);
- }
+ public static Database getDatabase() {
+ return mDatabase;
}
public static void setEditPassPhrase(String passPhrase) {
@@ -289,7 +206,7 @@ public class Apg { public static String getCachedPassPhrase(long keyId) {
long realId = keyId;
if (realId != Id.key.symmetric) {
- PGPSecretKeyRing keyRing = findSecretKeyRing(keyId);
+ PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
if (keyRing == null) {
return null;
}
@@ -308,19 +225,30 @@ public class Apg { return cpp.passPhrase;
}
- public static void cleanUpCache(int ttl) {
+ public static int cleanUpCache(int ttl, int initialDelay) {
+ int delay = initialDelay;
+ long realTtl = ttl * 1000;
long now = new Date().getTime();
-
Vector<Long> oldKeys = new Vector<Long>();
for (Map.Entry<Long, CachedPassPhrase> pair : mPassPhraseCache.entrySet()) {
- if ((now - pair.getValue().timestamp) >= 1000 * ttl) {
+ long lived = now - pair.getValue().timestamp;
+ if (lived >= realTtl) {
oldKeys.add(pair.getKey());
+ } else {
+ // see, whether the remaining time for this cache entry improves our
+ // check delay
+ long nextCheck = realTtl - lived + 1000;
+ if (nextCheck < delay) {
+ delay = (int)nextCheck;
+ }
}
}
for (long keyId : oldKeys) {
mPassPhraseCache.remove(keyId);
}
+
+ return delay;
}
public static PGPSecretKey createKey(Context context,
@@ -413,11 +341,10 @@ public class Apg { secretKey = (PGPSecretKey) it.next();
}
-
return secretKey;
}
- private static long getNumDatesBetween(GregorianCalendar first, GregorianCalendar second) {
+ private static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) {
GregorianCalendar tmp = new GregorianCalendar();
tmp.setTime(first.getTime());
long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
@@ -434,7 +361,7 @@ public class Apg { String oldPassPhrase, String newPassPhrase,
ProgressDialogUpdater progress)
throws Apg.GeneralException, NoSuchProviderException, PGPException,
- NoSuchAlgorithmException, SignatureException {
+ NoSuchAlgorithmException, SignatureException, IOException, Database.GeneralException {
progress.setProgress(R.string.progress_buildingKey, 0, 100);
@@ -465,7 +392,7 @@ public class Apg { } catch (UserIdEditor.NoEmailException e) {
throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress));
} catch (UserIdEditor.InvalidEmailException e) {
- throw new Apg.GeneralException(e.getMessage());
+ throw new Apg.GeneralException("" + e);
}
if (userId.equals("")) {
@@ -549,11 +476,12 @@ public class Apg { hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
+ // TODO: this doesn't work quite right yet
if (keyEditor.getExpiryDate() != null) {
GregorianCalendar creationDate = new GregorianCalendar();
creationDate.setTime(getCreationDate(masterKey));
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
- long numDays = getNumDatesBetween(creationDate, expiryDate);
+ long numDays = getNumDaysBetween(creationDate, expiryDate);
if (numDays <= 0) {
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
}
@@ -600,11 +528,12 @@ public class Apg { }
hashedPacketsGen.setKeyFlags(true, keyFlags);
+ // TODO: this doesn't work quite right yet
if (keyEditor.getExpiryDate() != null) {
GregorianCalendar creationDate = new GregorianCalendar();
creationDate.setTime(getCreationDate(masterKey));
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
- long numDays = getNumDatesBetween(creationDate, expiryDate);
+ long numDays = getNumDaysBetween(creationDate, expiryDate);
if (numDays <= 0) {
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
}
@@ -619,79 +548,16 @@ public class Apg { PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
- saveKeyRing(context, secretKeyRing);
- saveKeyRing(context, publicKeyRing);
+ mDatabase.saveKeyRing(secretKeyRing);
+ mDatabase.saveKeyRing(publicKeyRing);
- loadKeyRings(context, Id.type.public_key);
- loadKeyRings(context, Id.type.secret_key);
progress.setProgress(R.string.progress_done, 100, 100);
}
- private static int saveKeyRing(Activity context, PGPPublicKeyRing keyRing) {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ContentValues values = new ContentValues();
-
- PGPPublicKey masterKey = getMasterKey(keyRing);
- if (masterKey == null) {
- return Id.return_value.no_master_key;
- }
-
- try {
- keyRing.encode(out);
- out.close();
- } catch (IOException e) {
- return Id.return_value.error;
- }
-
- values.put(PublicKeys.KEY_ID, masterKey.getKeyID());
- values.put(PublicKeys.KEY_DATA, out.toByteArray());
-
- Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
- Cursor cursor = context.managedQuery(uri, PUBLIC_KEY_PROJECTION, null, null, null);
- if (cursor != null && cursor.getCount() > 0) {
- context.getContentResolver().update(uri, values, null, null);
- return Id.return_value.updated;
- } else {
- context.getContentResolver().insert(PublicKeys.CONTENT_URI, values);
- return Id.return_value.ok;
- }
- }
-
- private static int saveKeyRing(Activity context, PGPSecretKeyRing keyRing) {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ContentValues values = new ContentValues();
-
- PGPSecretKey masterKey = getMasterKey(keyRing);
- if (masterKey == null) {
- return Id.return_value.no_master_key;
- }
-
- try {
- keyRing.encode(out);
- out.close();
- } catch (IOException e) {
- return Id.return_value.error;
- }
-
- values.put(SecretKeys.KEY_ID, masterKey.getKeyID());
- values.put(SecretKeys.KEY_DATA, out.toByteArray());
-
- Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
- Cursor cursor = context.managedQuery(uri, SECRET_KEY_PROJECTION, null, null, null);
- if (cursor != null && cursor.getCount() > 0) {
- context.getContentResolver().update(uri, values, null, null);
- return Id.return_value.updated;
- } else {
- context.getContentResolver().insert(SecretKeys.CONTENT_URI, values);
- return Id.return_value.ok;
- }
- }
-
public static Bundle importKeyRings(Activity context, int type, String filename,
ProgressDialogUpdater progress)
throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle();
- PGPObjectFactory objectFactory = null;
if (type == Id.type.secret_key) {
progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
@@ -704,53 +570,61 @@ public class Apg { }
FileInputStream fileIn = new FileInputStream(filename);
- InputStream in = PGPUtil.getDecoderStream(fileIn);
- objectFactory = new PGPObjectFactory(in);
-
- Vector<Object> objects = new Vector<Object>();
- Object obj = objectFactory.nextObject();
- while (obj != null) {
- objects.add(obj);
- obj = objectFactory.nextObject();
- }
-
+ long fileSize = new File(filename).length();
+ PositionAwareInputStream progressIn = new PositionAwareInputStream(fileIn);
+ // need to have access to the bufferedInput, so we can reuse it for the possible
+ // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
+ // armour blocks
+ BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
int newKeys = 0;
int oldKeys = 0;
- for (int i = 0; i < objects.size(); ++i) {
- progress.setProgress(i * 100 / objects.size(), 100);
- obj = objects.get(i);
- PGPPublicKeyRing publicKeyRing;
- PGPSecretKeyRing secretKeyRing;
- int retValue;
-
- if (type == Id.type.secret_key) {
- if (!(obj instanceof PGPSecretKeyRing)) {
- continue;
- }
- secretKeyRing = (PGPSecretKeyRing) obj;
- retValue = saveKeyRing(context, secretKeyRing);
- } else {
- if (!(obj instanceof PGPPublicKeyRing)) {
- continue;
+ try {
+ while (true) {
+ InputStream in = PGPUtil.getDecoderStream(bufferedInput);
+ PGPObjectFactory objectFactory = new PGPObjectFactory(in);
+ Object obj = objectFactory.nextObject();
+ // if the first is already a null object, then we can stop trying
+ if (obj == null) {
+ break;
}
- publicKeyRing = (PGPPublicKeyRing) obj;
- retValue = saveKeyRing(context, publicKeyRing);
- }
+ while (obj != null) {
+ PGPPublicKeyRing publicKeyRing;
+ PGPSecretKeyRing secretKeyRing;
+ // a return value that doesn't match any Id.return_value.* values, in case
+ // saveKeyRing is never called
+ int retValue = 2107;
- if (retValue == Id.return_value.error) {
- throw new GeneralException(context.getString(R.string.error_savingKeys));
- }
+ try {
+ if (type == Id.type.secret_key && obj instanceof PGPSecretKeyRing) {
+ secretKeyRing = (PGPSecretKeyRing) obj;
+ retValue = mDatabase.saveKeyRing(secretKeyRing);
+ } else if (type == Id.type.public_key && obj instanceof PGPPublicKeyRing) {
+ publicKeyRing = (PGPPublicKeyRing) obj;
+ retValue = mDatabase.saveKeyRing(publicKeyRing);
+ }
+ } catch (IOException e) {
+ retValue = Id.return_value.error;
+ } catch (Database.GeneralException e) {
+ retValue = Id.return_value.error;
+ }
+
+ if (retValue == Id.return_value.error) {
+ throw new GeneralException(context.getString(R.string.error_savingKeys));
+ }
- if (retValue == Id.return_value.updated) {
- ++oldKeys;
- } else if (retValue == Id.return_value.ok) {
- ++newKeys;
+ if (retValue == Id.return_value.updated) {
+ ++oldKeys;
+ } else if (retValue == Id.return_value.ok) {
+ ++newKeys;
+ }
+ progress.setProgress((int)(100 * progressIn.position() / fileSize), 100);
+ obj = objectFactory.nextObject();
+ }
}
+ } catch (EOFException e) {
+ // nothing to do, we are done
}
- progress.setProgress(R.string.progress_reloadingKeys, 100, 100);
- loadKeyRings(context, type);
-
returnData.putInt("added", newKeys);
returnData.putInt("updated", oldKeys);
@@ -759,12 +633,13 @@ public class Apg { return returnData;
}
- public static Bundle exportKeyRings(Activity context, Vector<Object> keys, String filename,
+ public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds,
+ String filename,
ProgressDialogUpdater progress)
throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle();
- if (keys.size() == 1) {
+ if (keyRingIds.size() == 1) {
progress.setProgress(R.string.progress_exportingKey, 0, 100);
} else {
progress.setProgress(R.string.progress_exportingKeys, 0, 100);
@@ -777,9 +652,9 @@ public class Apg { ArmoredOutputStream out = new ArmoredOutputStream(fileOut);
int numKeys = 0;
- for (int i = 0; i < keys.size(); ++i) {
- progress.setProgress(i * 100 / keys.size(), 100);
- Object obj = keys.get(i);
+ for (int i = 0; i < keyRingIds.size(); ++i) {
+ progress.setProgress(i * 100 / keyRingIds.size(), 100);
+ Object obj = mDatabase.getKeyRing(keyRingIds.get(i));
PGPPublicKeyRing publicKeyRing;
PGPSecretKeyRing secretKeyRing;
@@ -803,61 +678,6 @@ public class Apg { return returnData;
}
- private static void loadKeyRings(Activity context, int type) {
- Cursor cursor;
- if (type == Id.type.secret_key) {
- mSecretKeyRings.clear();
- mSecretKeyIdToIdMap.clear();
- mSecretKeyIdToKeyRingMap.clear();
- cursor = context.managedQuery(SecretKeys.CONTENT_URI, SECRET_KEY_PROJECTION,
- null, null, null);
- } else {
- mPublicKeyRings.clear();
- mPublicKeyIdToIdMap.clear();
- mPublicKeyIdToKeyRingMap.clear();
- cursor = context.managedQuery(PublicKeys.CONTENT_URI, PUBLIC_KEY_PROJECTION,
- null, null, null);
- }
-
- for (int i = 0; i < cursor.getCount(); ++i) {
- cursor.moveToPosition(i);
- String sharedIdColumn = PublicKeys._ID; // same in both
- String sharedKeyIdColumn = PublicKeys.KEY_ID; // same in both
- String sharedKeyDataColumn = PublicKeys.KEY_DATA; // same in both
- int idIndex = cursor.getColumnIndex(sharedIdColumn);
- int keyIdIndex = cursor.getColumnIndex(sharedKeyIdColumn);
- int keyDataIndex = cursor.getColumnIndex(sharedKeyDataColumn);
-
- byte keyData[] = cursor.getBlob(keyDataIndex);
- int id = cursor.getInt(idIndex);
- long keyId = cursor.getLong(keyIdIndex);
-
- try {
- if (type == Id.type.secret_key) {
- PGPSecretKeyRing key = new PGPSecretKeyRing(keyData);
- mSecretKeyRings.add(key);
- mSecretKeyIdToIdMap.put(keyId, id);
- mSecretKeyIdToKeyRingMap.put(keyId, key);
- } else {
- PGPPublicKeyRing key = new PGPPublicKeyRing(keyData);
- mPublicKeyRings.add(key);
- mPublicKeyIdToIdMap.put(keyId, id);
- mPublicKeyIdToKeyRingMap.put(keyId, key);
- }
- } catch (IOException e) {
- // TODO: some error handling
- } catch (PGPException e) {
- // TODO: some error handling
- }
- }
-
- if (type == Id.type.secret_key) {
- Collections.sort(mSecretKeyRings, new SecretKeySorter());
- } else {
- Collections.sort(mPublicKeyRings, new PublicKeySorter());
- }
- }
-
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
@@ -867,6 +687,9 @@ public class Apg { }
public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
+ if (keyRing == null) {
+ return null;
+ }
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
if (key.isMasterKey()) {
return key;
@@ -877,6 +700,9 @@ public class Apg { }
public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
+ if (keyRing == null) {
+ return null;
+ }
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (key.isMasterKey()) {
return key;
@@ -982,7 +808,7 @@ public class Apg { }
public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
- PGPPublicKeyRing keyRing = mPublicKeyIdToKeyRingMap.get(masterKeyId);
+ PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
if (keyRing == null) {
return null;
}
@@ -994,7 +820,7 @@ public class Apg { }
public static PGPSecretKey getSigningKey(long masterKeyId) {
- PGPSecretKeyRing keyRing = mSecretKeyIdToKeyRingMap.get(masterKeyId);
+ PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
if (keyRing == null) {
return null;
}
@@ -1035,14 +861,6 @@ public class Apg { return userId;
}
- public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
- return mPublicKeyIdToKeyRingMap.get(keyId);
- }
-
- public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
- return mSecretKeyIdToKeyRingMap.get(keyId);
- }
-
public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
return false;
@@ -1122,9 +940,17 @@ public class Apg { }
public static String getAlgorithmInfo(PGPPublicKey key) {
+ return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength());
+ }
+
+ public static String getAlgorithmInfo(PGPSecretKey key) {
+ return getAlgorithmInfo(key.getPublicKey());
+ }
+
+ public static String getAlgorithmInfo(int algorithm, int keySize) {
String algorithmStr = null;
- switch (key.getAlgorithm()) {
+ switch (algorithm) {
case PGPPublicKey.RSA_ENCRYPT:
case PGPPublicKey.RSA_GENERAL:
case PGPPublicKey.RSA_SIGN: {
@@ -1148,97 +974,121 @@ public class Apg { break;
}
}
- return algorithmStr + ", " + key.getBitStrength() + "bit";
- }
-
- public static String getAlgorithmInfo(PGPSecretKey key) {
- return getAlgorithmInfo(key.getPublicKey());
+ return algorithmStr + ", " + keySize + "bit";
}
- public static void deleteKey(Activity context, PGPPublicKeyRing keyRing) {
- PGPPublicKey masterKey = getMasterKey(keyRing);
- Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
- context.getContentResolver().delete(uri, null, null);
- loadKeyRings(context, Id.type.public_key);
+ public static void deleteKey(int keyRingId) {
+ mDatabase.deleteKeyRing(keyRingId);
}
- public static void deleteKey(Activity context, PGPSecretKeyRing keyRing) {
- PGPSecretKey masterKey = getMasterKey(keyRing);
- Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
- context.getContentResolver().delete(uri, null, null);
- loadKeyRings(context, Id.type.secret_key);
+ public static Object getKeyRing(int keyRingId) {
+ return mDatabase.getKeyRing(keyRingId);
}
- public static PGPPublicKey findPublicKey(long keyId) {
- PGPPublicKey key = null;
- for (int i = 0; i < mPublicKeyRings.size(); ++i) {
- PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
- try {
- key = keyRing.getPublicKey(keyId);
- if (key != null) {
- return key;
- }
- } catch (PGPException e) {
- // just not found, can ignore this
- }
+ public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
+ byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_secret, keyId);
+ if (data == null) {
+ return null;
+ }
+ try {
+ return new PGPSecretKeyRing(data);
+ } catch (IOException e) {
+ // no good way to handle this, return null
+ // TODO: some info?
+ } catch (PGPException e) {
+ // no good way to handle this, return null
+ // TODO: some info?
}
return null;
}
- public static PGPSecretKey findSecretKey(long keyId) {
- PGPSecretKey key = null;
- for (int i = 0; i < mSecretKeyRings.size(); ++i) {
- PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
- key = keyRing.getSecretKey(keyId);
- if (key != null) {
- return key;
- }
+ public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
+ byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_public, keyId);
+ if (data == null) {
+ return null;
+ }
+ try {
+ return new PGPPublicKeyRing(data);
+ } catch (IOException e) {
+ // no good way to handle this, return null
+ // TODO: some info?
}
return null;
}
- public static PGPSecretKeyRing findSecretKeyRing(long keyId) {
- for (int i = 0; i < mSecretKeyRings.size(); ++i) {
- PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
- PGPSecretKey key = null;
- key = keyRing.getSecretKey(keyId);
- if (key != null) {
- return keyRing;
- }
+ public static PGPSecretKey getSecretKey(long keyId) {
+ PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
+ if (keyRing == null) {
+ return null;
}
- return null;
+ return keyRing.getSecretKey(keyId);
}
- public static PGPPublicKeyRing findPublicKeyRing(long keyId) {
- for (int i = 0; i < mPublicKeyRings.size(); ++i) {
- PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
- PGPPublicKey key = null;
- try {
- key = keyRing.getPublicKey(keyId);
- if (key != null) {
- return keyRing;
- }
- } catch (PGPException e) {
- // key not found
- }
+ public static PGPPublicKey getPublicKey(long keyId) {
+ PGPPublicKeyRing keyRing = getPublicKeyRing(keyId);
+ if (keyRing == null) {
+ return null;
+ }
+ try {
+ return keyRing.getPublicKey(keyId);
+ } catch (PGPException e) {
+ return null;
}
- return null;
}
- public static PGPPublicKey getPublicMasterKey(long keyId) {
- PGPPublicKey key = null;
- for (int i = 0; i < mPublicKeyRings.size(); ++i) {
- PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
- try {
- key = keyRing.getPublicKey(keyId);
- if (key != null) {
- return getMasterKey(keyRing);
- }
- } catch (PGPException e) {
- // just not found, can ignore this
- }
+ public static Vector<Integer> getKeyRingIds(int type) {
+ SQLiteDatabase db = mDatabase.db();
+ Vector<Integer> keyIds = new Vector<Integer>();
+ Cursor c = db.query(KeyRings.TABLE_NAME,
+ new String[] { KeyRings._ID },
+ KeyRings.TYPE + " = ?", new String[] { "" + type },
+ null, null, null);
+ if (c != null && c.moveToFirst()) {
+ do {
+ keyIds.add(c.getInt(0));
+ } while (c.moveToNext());
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return keyIds;
+ }
+
+ public static String getMainUserId(long keyId, int type) {
+ SQLiteDatabase db = mDatabase.db();
+ Cursor c = db.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ") " +
+ " INNER JOIN " + Keys.TABLE_NAME + " AS masterKey ON (" +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ "masterKey." + Keys.KEY_RING_ID + " AND " +
+ "masterKey." + Keys.IS_MASTER_KEY + " = '1') " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON (" +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " = " +
+ "masterKey." + Keys._ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0')",
+ new String[] { UserIds.USER_ID },
+ Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] {
+ "" + keyId,
+ "" + type,
+ },
+ null, null, null);
+ String userId = "";
+ if (c != null && c.moveToFirst()) {
+ do {
+ userId = c.getString(0);
+ } while (c.moveToNext());
}
- return null;
+
+ if (c != null) {
+ c.close();
+ }
+
+ return userId;
}
public static void encrypt(Context context,
@@ -1277,7 +1127,7 @@ public class Apg { }
if (signatureKeyId != 0) {
- signingKeyRing = findSecretKeyRing(signatureKeyId);
+ signingKeyRing = getSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) {
throw new GeneralException(context.getString(R.string.error_signatureFailed));
@@ -1329,7 +1179,7 @@ public class Apg { if (compression == Id.choice.compression.none) {
bcpgOut = new BCPGOutputStream(encryptOut);
} else {
- compressGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZLIB);
+ compressGen = new PGPCompressedDataGenerator(compression);
bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
}
if (signatureKeyId != 0) {
@@ -1393,7 +1243,7 @@ public class Apg { throw new GeneralException(context.getString(R.string.error_noSignatureKey));
}
- signingKeyRing = findSecretKeyRing(signatureKeyId);
+ signingKeyRing = getSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) {
throw new GeneralException(context.getString(R.string.error_signatureFailed));
@@ -1479,7 +1329,7 @@ public class Apg { if (obj instanceof PGPPublicKeyEncryptedData) {
gotAsymmetricEncryption = true;
PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
- secretKey = findSecretKey(pbe.getKeyID());
+ secretKey = getSecretKey(pbe.getKeyID());
if (secretKey != null) {
break;
}
@@ -1532,6 +1382,9 @@ public class Apg { String passPhrase, ProgressDialogUpdater progress,
boolean assumeSymmetric)
throws IOException, GeneralException, PGPException, SignatureException {
+ if (passPhrase == null) {
+ passPhrase = "";
+ }
Bundle returnData = new Bundle();
InputStream in = PGPUtil.getDecoderStream(inStream);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
@@ -1589,7 +1442,7 @@ public class Apg { Object obj = it.next();
if (obj instanceof PGPPublicKeyEncryptedData) {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- secretKey = findSecretKey(encData.getKeyID());
+ secretKey = getSecretKey(encData.getKeyID());
if (secretKey != null) {
pbe = encData;
break;
@@ -1634,11 +1487,11 @@ public class Apg { if (dataChunk instanceof PGPOnePassSignatureList) {
progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
- returnData.putBoolean("signature", true);
+ returnData.putBoolean(EXTRA_SIGNATURE, true);
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
- signatureKey = findPublicKey(signature.getKeyID());
+ signatureKey = getPublicKey(signature.getKeyID());
if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID();
}
@@ -1648,21 +1501,21 @@ public class Apg { signatureIndex = i;
signatureKeyId = signature.getKeyID();
String userId = null;
- PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId);
+ PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
if (sigKeyRing != null) {
userId = getMainUserId(getMasterKey(sigKeyRing));
}
- returnData.putString("signatureUserId", userId);
+ returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
break;
}
}
- returnData.putLong("signatureKeyId", signatureKeyId);
+ returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
if (signature != null) {
signature.initVerify(signatureKey, new BouncyCastleProvider());
} else {
- returnData.putBoolean("signatureUnknown", true);
+ returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
}
dataChunk = plainFact.nextObject();
@@ -1694,7 +1547,7 @@ public class Apg { try {
signature.update(buffer, 0, n);
} catch (SignatureException e) {
- returnData.putBoolean("signatureSuccess", false);
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
signature = null;
}
}
@@ -1714,9 +1567,9 @@ public class Apg { PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex);
if (signature.verify(messageSignature)) {
- returnData.putBoolean("signatureSuccess", true);
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, true);
} else {
- returnData.putBoolean("signatureSuccess", false);
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
}
}
}
@@ -1769,7 +1622,7 @@ public class Apg { byte[] clearText = out.toByteArray();
outStream.write(clearText);
- returnData.putBoolean("signature", true);
+ returnData.putBoolean(EXTRA_SIGNATURE, true);
progress.setProgress(R.string.progress_processingSignature, 60, 100);
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
@@ -1783,7 +1636,7 @@ public class Apg { PGPPublicKey signatureKey = null;
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
- signatureKey = findPublicKey(signature.getKeyID());
+ signatureKey = getPublicKey(signature.getKeyID());
if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID();
}
@@ -1792,19 +1645,19 @@ public class Apg { } else {
signatureKeyId = signature.getKeyID();
String userId = null;
- PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId);
+ PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
if (sigKeyRing != null) {
userId = getMainUserId(getMasterKey(sigKeyRing));
}
- returnData.putString("signatureUserId", userId);
+ returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
break;
}
}
- returnData.putLong("signatureKeyId", signatureKeyId);
+ returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
if (signature == null) {
- returnData.putBoolean("signatureUnknown", true);
+ returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
progress.setProgress(R.string.progress_done, 100, 100);
return returnData;
}
@@ -1829,21 +1682,12 @@ public class Apg { while (lookAhead != -1);
}
- returnData.putBoolean("signatureSuccess", signature.verify());
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, signature.verify());
progress.setProgress(R.string.progress_done, 100, 100);
return returnData;
}
- public static Vector<PGPPublicKeyRing> getPublicKeyRings() {
- return mPublicKeyRings;
- }
-
- public static Vector<PGPSecretKeyRing> getSecretKeyRings() {
- return mSecretKeyRings;
- }
-
-
// taken from ClearSignedFileProcessor in BC
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
throws IOException {
diff --git a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java index 67aad7529..01cd2de25 100644 --- a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java +++ b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java @@ -25,6 +25,7 @@ import android.app.AlertDialog; import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
@@ -42,14 +43,24 @@ public class AskForSecretKeyPassPhrase { alert.setTitle(R.string.title_authentification);
final PGPSecretKey secretKey;
+ final Activity activity = context;
if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) {
secretKey = null;
alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
} else {
- secretKey = Apg.getMasterKey(Apg.findSecretKeyRing(secretKeyId));
+ secretKey = Apg.getMasterKey(Apg.getSecretKeyRing(secretKeyId));
if (secretKey == null) {
- return null;
+ alert.setTitle(R.string.title_keyNotFound);
+ alert.setMessage(context.getString(R.string.keyNotFound, secretKeyId));
+ alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ activity.removeDialog(Id.dialog.pass_phrase);
+ }
+ });
+ alert.setCancelable(false);
+ return alert.create();
}
String userId = Apg.getMainUserIdSafe(context, secretKey);
alert.setMessage(context.getString(R.string.passPhraseFor, userId));
@@ -65,30 +76,29 @@ public class AskForSecretKeyPassPhrase { alert.setView(view);
final PassPhraseCallbackInterface cb = callback;
- final Activity activity = context;
alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- activity.removeDialog(Id.dialog.pass_phrase);
- String passPhrase = "" + input.getText();
- long keyId;
- if (secretKey != null) {
- try {
- secretKey.extractPrivateKey(passPhrase.toCharArray(),
- new BouncyCastleProvider());
- } catch (PGPException e) {
- Toast.makeText(activity,
- R.string.wrongPassPhrase,
- Toast.LENGTH_SHORT).show();
- return;
- }
- keyId = secretKey.getKeyID();
- } else {
- keyId = Id.key.symmetric;
- }
- cb.passPhraseCallback(keyId, passPhrase);
- }
- });
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ activity.removeDialog(Id.dialog.pass_phrase);
+ String passPhrase = "" + input.getText();
+ long keyId;
+ if (secretKey != null) {
+ try {
+ secretKey.extractPrivateKey(passPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ } catch (PGPException e) {
+ Toast.makeText(activity,
+ R.string.wrongPassPhrase,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ keyId = secretKey.getKeyID();
+ } else {
+ keyId = Id.key.symmetric;
+ }
+ cb.passPhraseCallback(keyId, passPhrase);
+ }
+ });
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
diff --git a/src/org/thialfihar/android/apg/BaseActivity.java b/src/org/thialfihar/android/apg/BaseActivity.java index 64705ba1f..210e09409 100644 --- a/src/org/thialfihar/android/apg/BaseActivity.java +++ b/src/org/thialfihar/android/apg/BaseActivity.java @@ -17,10 +17,7 @@ package org.thialfihar.android.apg;
import java.io.File;
-import java.util.Timer;
-import java.util.TimerTask;
-import org.bouncycastle2.bcpg.CompressionAlgorithmTags;
import org.bouncycastle2.bcpg.HashAlgorithmTags;
import org.bouncycastle2.openpgp.PGPEncryptedData;
@@ -33,6 +30,7 @@ import android.content.DialogInterface; import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
@@ -53,8 +51,6 @@ public class BaseActivity extends Activity private String mDeleteFile = null;
protected static SharedPreferences mPreferences = null;
- private static Timer mCacheTimer = new Timer();
-
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -66,33 +62,23 @@ public class BaseActivity extends Activity protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Apg.initialize(this);
+
if (mPreferences == null) {
mPreferences = getPreferences(MODE_PRIVATE);
}
- Apg.initialize(this);
- if (mCacheTimer == null) {
- setPassPhraseCacheTimer();
- }
- }
- private void setPassPhraseCacheTimer() {
- if (mCacheTimer != null) {
- mCacheTimer.cancel();
- mCacheTimer = null;
- }
- int ttl = getPassPhraseCacheTtl();
- if (ttl == 0) {
- // no timer needed
- return;
- }
- // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
- // and makes sure the longest a pass phrase survives int the cache is 1.5 * ttl
- mCacheTimer = new Timer();
- mCacheTimer.scheduleAtFixedRate(new TimerTask() {
- public void run() {
- Apg.cleanUpCache(BaseActivity.this.getPassPhraseCacheTtl());
+ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ File dir = new File(Constants.path.app_dir);
+ if (!dir.exists() && !dir.mkdirs()) {
+ // ignore this for now, it's not crucial
+ // that the directory doesn't exist at this point
}
- }, 0, ttl * 1000 / 2);
+ }
+
+ Intent intent = new Intent(this, Service.class);
+ intent.putExtra(Service.EXTRA_TTL, getPassPhraseCacheTtl());
+ startService(intent);
}
@Override
@@ -282,7 +268,7 @@ public class BaseActivity extends Activity case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
- setSecretKeyId(bundle.getLong("selectedKeyId"));
+ setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
} else {
setSecretKeyId(Id.key.none);
}
@@ -304,9 +290,9 @@ public class BaseActivity extends Activity public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
- data.putInt("type", Id.message.progress_update);
- data.putInt("progress", progress);
- data.putInt("max", max);
+ data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
+ data.putInt(Apg.EXTRA_PROGRESS, progress);
+ data.putInt(Apg.EXTRA_MAX, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
@@ -314,10 +300,10 @@ public class BaseActivity extends Activity public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
- data.putInt("type", Id.message.progress_update);
- data.putString("message", message);
- data.putInt("progress", progress);
- data.putInt("max", max);
+ data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
+ data.putString(Apg.EXTRA_MESSAGE, message);
+ data.putInt(Apg.EXTRA_PROGRESS, progress);
+ data.putInt(Apg.EXTRA_MAX, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
@@ -328,16 +314,16 @@ public class BaseActivity extends Activity return;
}
- int type = data.getInt("type");
+ int type = data.getInt(Apg.EXTRA_STATUS);
switch (type) {
case Id.message.progress_update: {
- String message = data.getString("message");
+ String message = data.getString(Apg.EXTRA_MESSAGE);
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
- mProgressDialog.setMax(data.getInt("max"));
- mProgressDialog.setProgress(data.getInt("progress"));
+ mProgressDialog.setMax(data.getInt(Apg.EXTRA_MAX));
+ mProgressDialog.setProgress(data.getInt(Apg.EXTRA_PROGRESS));
}
break;
}
@@ -382,7 +368,14 @@ public class BaseActivity extends Activity }
public int getPassPhraseCacheTtl() {
- return mPreferences.getInt(Constants.pref.pass_phrase_cache_ttl, 300);
+ int ttl = mPreferences.getInt(Constants.pref.pass_phrase_cache_ttl, 180);
+ // fix the value if it was set to "never" in previous versions, which currently is not
+ // supported
+ if (ttl == 0) {
+ ttl = 180;
+ setPassPhraseCacheTtl(ttl);
+ }
+ return ttl;
}
public void setPassPhraseCacheTtl(int value) {
@@ -390,7 +383,9 @@ public class BaseActivity extends Activity editor.putInt(Constants.pref.pass_phrase_cache_ttl, value);
editor.commit();
- setPassPhraseCacheTimer();
+ Intent intent = new Intent(this, Service.class);
+ intent.putExtra(Service.EXTRA_TTL, value);
+ startService(intent);
}
public int getDefaultEncryptionAlgorithm() {
@@ -417,7 +412,7 @@ public class BaseActivity extends Activity public int getDefaultMessageCompression() {
return mPreferences.getInt(Constants.pref.default_message_compression,
- CompressionAlgorithmTags.ZLIB);
+ Id.choice.compression.zlib);
}
public void setDefaultMessageCompression(int value) {
@@ -428,7 +423,7 @@ public class BaseActivity extends Activity public int getDefaultFileCompression() {
return mPreferences.getInt(Constants.pref.default_file_compression,
- CompressionAlgorithmTags.ZLIB);
+ Id.choice.compression.none);
}
public void setDefaultFileCompression(int value) {
diff --git a/src/org/thialfihar/android/apg/CachedPassPhrase.java b/src/org/thialfihar/android/apg/CachedPassPhrase.java index e7566220e..74248aee3 100644 --- a/src/org/thialfihar/android/apg/CachedPassPhrase.java +++ b/src/org/thialfihar/android/apg/CachedPassPhrase.java @@ -10,6 +10,12 @@ public class CachedPassPhrase { this.passPhrase = passPhrase;
}
+ public int hashCode() {
+ int hc1 = (int)(this.timestamp & 0xffffffff);
+ int hc2 = (this.passPhrase == null ? 0 : this.passPhrase.hashCode());
+ return (hc1 + hc2) * hc2 + hc1;
+ }
+
public boolean equals(Object other) {
if (!(other instanceof CachedPassPhrase)) {
return false;
diff --git a/src/org/thialfihar/android/apg/DecryptActivity.java b/src/org/thialfihar/android/apg/DecryptActivity.java index 80ad13d5e..de8dcb3ff 100644 --- a/src/org/thialfihar/android/apg/DecryptActivity.java +++ b/src/org/thialfihar/android/apg/DecryptActivity.java @@ -31,8 +31,6 @@ import java.util.regex.Matcher; import org.bouncycastle2.jce.provider.BouncyCastleProvider; import org.bouncycastle2.openpgp.PGPException; -import org.bouncycastle2.util.Strings; -import org.openintents.intents.FileManager; import android.app.Dialog; import android.content.ActivityNotFoundException; @@ -57,6 +55,9 @@ import android.widget.ViewFlipper; public class DecryptActivity extends BaseActivity { private long mSignatureKeyId = 0; + private Intent mIntent; + + private boolean mReturnResult = false; private String mReplyTo = null; private String mSubject = null; private boolean mSignedOnly = false; @@ -158,9 +159,9 @@ public class DecryptActivity extends BaseActivity { mSource.showNext(); } - Intent intent = getIntent(); - if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) { - Uri uri = intent.getData(); + mIntent = getIntent(); + if (Intent.ACTION_VIEW.equals(mIntent.getAction())) { + Uri uri = mIntent.getData(); try { InputStream attachment = getContentResolver().openInputStream(uri); ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); @@ -170,15 +171,15 @@ public class DecryptActivity extends BaseActivity { byteOut.write(bytes, 0, length); } byteOut.close(); - String data = Strings.fromUTF8ByteArray(byteOut.toByteArray()); + String data = new String(byteOut.toByteArray()); mMessage.setText(data); } catch (FileNotFoundException e) { // ignore, then } catch (IOException e) { // ignore, then } - } else if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SEND)) { - Bundle extras = intent.getExtras(); + } else if (Intent.ACTION_SEND.equals(mIntent.getAction())) { + Bundle extras = mIntent.getExtras(); if (extras == null) { extras = new Bundle(); } @@ -187,15 +188,15 @@ public class DecryptActivity extends BaseActivity { mMessage.setText(data); } mSubject = extras.getString(Intent.EXTRA_SUBJECT); - if (mSubject.startsWith("Fwd: ")) { + if (mSubject != null && mSubject.startsWith("Fwd: ")) { mSubject = mSubject.substring(5); } - } else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT)) { - Bundle extras = intent.getExtras(); + } else if (Apg.Intent.DECRYPT.equals(mIntent.getAction())) { + Bundle extras = mIntent.getExtras(); if (extras == null) { extras = new Bundle(); } - String data = extras.getString("data"); + String data = extras.getString(Apg.EXTRA_DATA); if (data != null) { Matcher matcher = Apg.PGP_MESSAGE.matcher(data); if (matcher.matches()) { @@ -214,14 +215,39 @@ public class DecryptActivity extends BaseActivity { } } } - mReplyTo = extras.getString("replyTo"); - mSubject = extras.getString("subject"); - } else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT_FILE)) { + mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO); + mSubject = extras.getString(Apg.EXTRA_SUBJECT); + } else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) { mSource.setInAnimation(null); mSource.setOutAnimation(null); while (mSource.getCurrentView().getId() != R.id.sourceFile) { mSource.showNext(); } + } else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) { + Bundle extras = mIntent.getExtras(); + if (extras == null) { + extras = new Bundle(); + } + String data = extras.getString(Apg.EXTRA_DATA); + if (data != null) { + Matcher matcher = Apg.PGP_MESSAGE.matcher(data); + if (matcher.matches()) { + data = matcher.group(1); + // replace non breakable spaces + data = data.replaceAll("\\xa0", " "); + mMessage.setText(data); + } else { + matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data); + if (matcher.matches()) { + data = matcher.group(1); + // replace non breakable spaces + data = data.replaceAll("\\xa0", " "); + mMessage.setText(data); + mDecryptButton.setText(R.string.btn_verify); + } + } + } + mReturnResult = true; } if (mSource.getCurrentView().getId() == R.id.sourceMessage && @@ -256,29 +282,41 @@ public class DecryptActivity extends BaseActivity { }); mReplyButton.setVisibility(View.INVISIBLE); - if (mSource.getCurrentView().getId() == R.id.sourceMessage && - mMessage.getText().length() > 0) { - mDecryptButton.performClick(); + if (mReturnResult) { + mSourcePrevious.setClickable(false); + mSourcePrevious.setEnabled(false); + mSourcePrevious.setVisibility(View.INVISIBLE); + + mSourceNext.setClickable(false); + mSourceNext.setEnabled(false); + mSourceNext.setVisibility(View.INVISIBLE); + + mSourceLabel.setClickable(false); + mSourceLabel.setEnabled(false); } updateSource(); + + if (mSource.getCurrentView().getId() == R.id.sourceMessage && + mMessage.getText().length() > 0) { + mDecryptButton.performClick(); + } } private void openFile() { String filename = mFilename.getText().toString(); - Intent intent = new Intent(FileManager.ACTION_PICK_FILE); + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setData(Uri.parse("file://" + filename)); - - intent.putExtra(FileManager.EXTRA_TITLE, getString(R.string.filemanager_titleDecrypt)); - intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen); + intent.setType("*/*"); try { startActivityForResult(intent, Id.request.filename); } catch (ActivityNotFoundException e) { // No compatible file manager was found. - Toast.makeText(this, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show(); + Toast.makeText(this, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show(); } } @@ -373,9 +411,9 @@ public class DecryptActivity extends BaseActivity { // look at the file/message again to check whether there's // symmetric encryption data in there if (mDecryptTarget == Id.target.file) { - ((FileInputStream) in).reset(); + in = new FileInputStream(mInputFilename); } else { - ((ByteArrayInputStream) in).reset(); + in = new ByteArrayInputStream(mMessage.getText().toString().getBytes()); } if (!Apg.hasSymmetricEncryption(this, in)) { throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound)); @@ -396,9 +434,9 @@ public class DecryptActivity extends BaseActivity { } catch (FileNotFoundException e) { error = getString(R.string.error_fileNotFound); } catch (IOException e) { - error = e.getLocalizedMessage(); + error = "" + e; } catch (Apg.GeneralException e) { - error = e.getLocalizedMessage(); + error = "" + e; } if (error != null) { Toast.makeText(this, getString(R.string.errorMessage, error), @@ -412,12 +450,11 @@ public class DecryptActivity extends BaseActivity { String data = mMessage.getText().toString(); data = data.replaceAll("(?m)^", "> "); data = "\n\n" + data; - intent.putExtra("data", data); - intent.putExtra("subject", "Re: " + mSubject); - intent.putExtra("sendTo", mReplyTo); - intent.putExtra("eyId", mSignatureKeyId); - intent.putExtra("signatureKeyId", getSecretKeyId()); - intent.putExtra("encryptionKeyIds", new long[] { mSignatureKeyId }); + intent.putExtra(Apg.EXTRA_DATA, data); + intent.putExtra(Apg.EXTRA_SUBJECT, "Re: " + mSubject); + intent.putExtra(Apg.EXTRA_SEND_TO, mReplyTo); + intent.putExtra(Apg.EXTRA_SIGNATURE_KEY_ID, getSecretKeyId()); + intent.putExtra(Apg.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId }); startActivity(intent); } @@ -474,25 +511,23 @@ public class DecryptActivity extends BaseActivity { out.close(); if (mDecryptTarget == Id.target.message) { - data.putString("decryptedMessage", - Strings.fromUTF8ByteArray(((ByteArrayOutputStream) - out).toByteArray())); + data.putByteArray(Apg.EXTRA_DECRYPTED_MESSAGE, + ((ByteArrayOutputStream) out).toByteArray()); } } catch (PGPException e) { - error = e.getMessage(); + error = "" + e; } catch (IOException e) { - error = e.getMessage(); + error = "" + e; } catch (SignatureException e) { - error = e.getMessage(); - e.printStackTrace(); + error = "" + e; } catch (Apg.GeneralException e) { - error = e.getMessage(); + error = "" + e; } - data.putInt("type", Id.message.done); + data.putInt(Apg.EXTRA_STATUS, Id.message.done); if (error != null) { - data.putString("error", error); + data.putString(Apg.EXTRA_ERROR, error); } msg.setData(data); @@ -509,20 +544,20 @@ public class DecryptActivity extends BaseActivity { mSignatureLayout.setVisibility(View.GONE); mReplyButton.setVisibility(View.INVISIBLE); - String error = data.getString("error"); + String error = data.getString(Apg.EXTRA_ERROR); if (error != null) { Toast.makeText(DecryptActivity.this, - getString(R.string.errorMessage, - data.getString("error")), - Toast.LENGTH_SHORT).show(); + getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show(); return; } Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show(); switch (mDecryptTarget) { case Id.target.message: { - String decryptedMessage = data.getString("decryptedMessage"); + String decryptedMessage = + new String(data.getByteArray(Apg.EXTRA_DECRYPTED_MESSAGE)); mMessage.setText(decryptedMessage); + mMessage.setHorizontallyScrolling(false); mReplyButton.setVisibility(View.VISIBLE); break; } @@ -541,9 +576,9 @@ public class DecryptActivity extends BaseActivity { } } - if (data.getBoolean("signature")) { - String userId = data.getString("signatureUserId"); - mSignatureKeyId = data.getLong("signatureKeyId"); + if (data.getBoolean(Apg.EXTRA_SIGNATURE)) { + String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID); + mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID); mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL)); if (userId == null) { userId = getResources().getString(R.string.unknownUserId); @@ -555,15 +590,22 @@ public class DecryptActivity extends BaseActivity { } mUserId.setText(userId); - if (data.getBoolean("signatureSuccess")) { + if (data.getBoolean(Apg.EXTRA_SIGNATURE_SUCCESS)) { mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); - } else if (data.getBoolean("signatureUnknown")) { + } else if (data.getBoolean(Apg.EXTRA_SIGNATURE_UNKNOWN)) { mSignatureStatusImage.setImageResource(R.drawable.overlay_error); } else { mSignatureStatusImage.setImageResource(R.drawable.overlay_error); } mSignatureLayout.setVisibility(View.VISIBLE); } + + if (mReturnResult) { + Intent intent = new Intent(); + intent.putExtras(data); + setResult(RESULT_OK, intent); + finish(); + } } @Override diff --git a/src/org/thialfihar/android/apg/EditKeyActivity.java b/src/org/thialfihar/android/apg/EditKeyActivity.java index b1e37e8a1..e71fd8e8c 100644 --- a/src/org/thialfihar/android/apg/EditKeyActivity.java +++ b/src/org/thialfihar/android/apg/EditKeyActivity.java @@ -16,6 +16,7 @@ package org.thialfihar.android.apg; +import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SignatureException; @@ -24,6 +25,7 @@ import java.util.Vector; import org.bouncycastle2.openpgp.PGPException; import org.bouncycastle2.openpgp.PGPSecretKey; import org.bouncycastle2.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.provider.Database; import org.thialfihar.android.apg.ui.widget.KeyEditor; import org.thialfihar.android.apg.ui.widget.SectionView; import org.thialfihar.android.apg.utils.IterableIterator; @@ -69,7 +71,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener { Intent intent = getIntent(); long keyId = 0; if (intent.getExtras() != null) { - keyId = intent.getExtras().getLong("keyId"); + keyId = intent.getExtras().getLong(Apg.EXTRA_KEY_ID); } if (keyId != 0) { @@ -115,7 +117,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener { Toast.makeText(this, "Warning: Key editing is still kind of beta.", Toast.LENGTH_LONG).show(); } - public long getMasterKeyId() { + private long getMasterKeyId() { if (mKeys.getEditors().getChildCount() == 0) { return 0; } @@ -243,22 +245,27 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener { newPassPhrase = oldPassPhrase; } Apg.buildSecretKey(this, mUserIds, mKeys, oldPassPhrase, newPassPhrase, this); + Apg.setCachedPassPhrase(getMasterKeyId(), newPassPhrase); } catch (NoSuchProviderException e) { - error = e.getMessage(); + error = "" + e; } catch (NoSuchAlgorithmException e) { - error = e.getMessage(); + error = "" + e; } catch (PGPException e) { - error = e.getMessage(); + error = "" + e; } catch (SignatureException e) { - error = e.getMessage(); + error = "" + e; } catch (Apg.GeneralException e) { - error = e.getMessage(); + error = "" + e; + } catch (Database.GeneralException e) { + error = "" + e; + } catch (IOException e) { + error = "" + e; } - data.putInt("type", Id.message.done); + data.putInt(Apg.EXTRA_STATUS, Id.message.done); if (error != null) { - data.putString("error", error); + data.putString(Apg.EXTRA_ERROR, error); } msg.setData(data); @@ -272,11 +279,10 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener { Bundle data = msg.getData(); removeDialog(Id.dialog.saving); - String error = data.getString("error"); + String error = data.getString(Apg.EXTRA_ERROR); if (error != null) { Toast.makeText(EditKeyActivity.this, - getString(R.string.errorMessage, data.getString("error")), - Toast.LENGTH_SHORT).show(); + getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show(); setResult(RESULT_OK); diff --git a/src/org/thialfihar/android/apg/EncryptActivity.java b/src/org/thialfihar/android/apg/EncryptActivity.java index 073e9dc9c..926d79599 100644 --- a/src/org/thialfihar/android/apg/EncryptActivity.java +++ b/src/org/thialfihar/android/apg/EncryptActivity.java @@ -35,7 +35,6 @@ import org.bouncycastle2.openpgp.PGPPublicKeyRing; import org.bouncycastle2.openpgp.PGPSecretKey; import org.bouncycastle2.openpgp.PGPSecretKeyRing; import org.bouncycastle2.util.Strings; -import org.openintents.intents.FileManager; import org.thialfihar.android.apg.Apg.GeneralException; import org.thialfihar.android.apg.utils.Choice; @@ -62,11 +61,13 @@ import android.widget.Toast; import android.widget.ViewFlipper; public class EncryptActivity extends BaseActivity { + private Intent mIntent = null; private String mSubject = null; private String mSendTo = null; private long mEncryptionKeyIds[] = null; + private boolean mReturnResult = false; private EditText mMessage = null; private Button mSelectKeysButton = null; private Button mEncryptButton = null; @@ -265,21 +266,26 @@ public class EncryptActivity extends BaseActivity { } }); - Intent intent = getIntent(); - if (intent.getAction() != null && - (intent.getAction().equals(Apg.Intent.ENCRYPT) || - intent.getAction().equals(Apg.Intent.ENCRYPT_FILE))) { - Bundle extras = intent.getExtras(); + mIntent = getIntent(); + if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || + Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) || + Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { + Bundle extras = mIntent.getExtras(); if (extras == null) { extras = new Bundle(); } - String data = extras.getString("data"); - mSendTo = extras.getString("sendTo"); - mSubject = extras.getString("subject"); - long signatureKeyId = extras.getLong("signatureKeyId"); - long encryptionKeyIds[] = extras.getLongArray("encryptionKeyIds"); + + if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { + mReturnResult = true; + } + + String data = extras.getString(Apg.EXTRA_DATA); + mSendTo = extras.getString(Apg.EXTRA_SEND_TO); + mSubject = extras.getString(Apg.EXTRA_SUBJECT); + long signatureKeyId = extras.getLong(Apg.EXTRA_SIGNATURE_KEY_ID); + long encryptionKeyIds[] = extras.getLongArray(Apg.EXTRA_ENCRYPTION_KEY_IDS); if (signatureKeyId != 0) { - PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId); + PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(signatureKeyId); PGPSecretKey masterKey = null; if (keyRing != null) { masterKey = Apg.getMasterKey(keyRing); @@ -295,7 +301,7 @@ public class EncryptActivity extends BaseActivity { if (encryptionKeyIds != null) { Vector<Long> goodIds = new Vector<Long>(); for (int i = 0; i < encryptionKeyIds.length; ++i) { - PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]); + PGPPublicKeyRing keyRing = Apg.getPublicKeyRing(encryptionKeyIds[i]); PGPPublicKey masterKey = null; if (keyRing == null) { continue; @@ -318,7 +324,8 @@ public class EncryptActivity extends BaseActivity { } } - if (intent.getAction().equals(Apg.Intent.ENCRYPT)) { + if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || + Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { if (data != null) { mMessage.setText(data); } @@ -327,7 +334,7 @@ public class EncryptActivity extends BaseActivity { while (mSource.getCurrentView().getId() != R.id.sourceMessage) { mSource.showNext(); } - } else if (intent.getAction().equals(Apg.Intent.ENCRYPT_FILE)) { + } else if (Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())) { mSource.setInAnimation(null); mSource.setOutAnimation(null); while (mSource.getCurrentView().getId() != R.id.sourceFile) { @@ -339,23 +346,47 @@ public class EncryptActivity extends BaseActivity { updateView(); updateSource(); updateMode(); + + if (mReturnResult) { + mSourcePrevious.setClickable(false); + mSourcePrevious.setEnabled(false); + mSourcePrevious.setVisibility(View.INVISIBLE); + + mSourceNext.setClickable(false); + mSourceNext.setEnabled(false); + mSourceNext.setVisibility(View.INVISIBLE); + + mSourceLabel.setClickable(false); + mSourceLabel.setEnabled(false); + + mEncryptToClipboardButton.setEnabled(false); + mEncryptToClipboardButton.setVisibility(View.INVISIBLE); + mEncryptButton.setText(R.string.btn_encrypt); + } + + if (mReturnResult && + mMessage.getText().length() > 0 && + ((mEncryptionKeyIds != null && + mEncryptionKeyIds.length > 0) || + getSecretKeyId() != 0)) { + encryptClicked(); + } } private void openFile() { String filename = mFilename.getText().toString(); - Intent intent = new Intent(FileManager.ACTION_PICK_FILE); + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setData(Uri.parse("file://" + filename)); - - intent.putExtra(FileManager.EXTRA_TITLE, R.string.filemanager_titleEncrypt); - intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen); + intent.setType("*/*"); try { startActivityForResult(intent, Id.request.filename); } catch (ActivityNotFoundException e) { // No compatible file manager was found. - Toast.makeText(this, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show(); + Toast.makeText(this, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show(); } } @@ -457,7 +488,7 @@ public class EncryptActivity extends BaseActivity { return; } } else { - boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0; + boolean encryptIt = (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0); // for now require at least one form of encryption for files if (!encryptIt && mEncryptTarget == Id.target.file) { Toast.makeText(this, R.string.selectEncryptionKey, Toast.LENGTH_SHORT).show(); @@ -527,7 +558,7 @@ public class EncryptActivity extends BaseActivity { } else { encryptionKeyIds = mEncryptionKeyIds; signatureKeyId = getSecretKeyId(); - signOnly = mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0; + signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0); } if (mEncryptTarget == Id.target.file) { @@ -548,7 +579,7 @@ public class EncryptActivity extends BaseActivity { } else { String message = mMessage.getText().toString(); - if (signOnly) { + if (signOnly && mReturnResult) { // fix the message a bit, trailing spaces and newlines break stuff, // because GMail sends as HTML and such things fuck up the signature, // TODO: things like "<" and ">" also fuck up the signature @@ -582,26 +613,27 @@ public class EncryptActivity extends BaseActivity { out.close(); if (mEncryptTarget != Id.target.file) { - data.putString("message", new String(((ByteArrayOutputStream)out).toByteArray())); + data.putByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE, + ((ByteArrayOutputStream)out).toByteArray()); } } catch (IOException e) { - error = e.getMessage(); + error = "" + e; } catch (PGPException e) { - error = e.getMessage(); + error = "" + e; } catch (NoSuchProviderException e) { - error = e.getMessage(); + error = "" + e; } catch (NoSuchAlgorithmException e) { - error = e.getMessage(); + error = "" + e; } catch (SignatureException e) { - error = e.getMessage(); + error = "" + e; } catch (Apg.GeneralException e) { - error = e.getMessage(); + error = "" + e; } - data.putInt("type", Id.message.done); + data.putInt(Apg.EXTRA_STATUS, Id.message.done); if (error != null) { - data.putString("error", error); + data.putString(Apg.EXTRA_ERROR, error); } msg.setData(data); @@ -645,7 +677,7 @@ public class EncryptActivity extends BaseActivity { private void selectPublicKeys() { Intent intent = new Intent(this, SelectPublicKeyListActivity.class); - intent.putExtra("selection", mEncryptionKeyIds); + intent.putExtra(Apg.EXTRA_SELECTION, mEncryptionKeyIds); startActivityForResult(intent, Id.request.public_keys); } @@ -702,7 +734,7 @@ public class EncryptActivity extends BaseActivity { case Id.request.public_keys: { if (resultCode == RESULT_OK) { Bundle bundle = data.getExtras(); - mEncryptionKeyIds = bundle.getLongArray("selection"); + mEncryptionKeyIds = bundle.getLongArray(Apg.EXTRA_SELECTION); } updateView(); break; @@ -723,53 +755,61 @@ public class EncryptActivity extends BaseActivity { removeDialog(Id.dialog.encrypting); Bundle data = msg.getData(); - String error = data.getString("error"); + String error = data.getString(Apg.EXTRA_ERROR); if (error != null) { Toast.makeText(EncryptActivity.this, - getString(R.string.errorMessage, data.getString("error")), - Toast.LENGTH_SHORT).show(); + getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show(); return; - } else { - String message = data.getString("message"); - switch (mEncryptTarget) { - case Id.target.clipboard: { - ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - clip.setText(message); - Toast.makeText(this, R.string.encryptionToClipboardSuccessful, - Toast.LENGTH_SHORT).show(); - break; - } + } + switch (mEncryptTarget) { + case Id.target.clipboard: { + String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE)); + ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + clip.setText(message); + Toast.makeText(this, R.string.encryptionToClipboardSuccessful, + Toast.LENGTH_SHORT).show(); + break; + } - case Id.target.email: { - Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); - emailIntent.setType("text/plain; charset=utf-8"); - emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message); - if (mSubject != null) { - emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, - mSubject); - } - if (mSendTo != null) { - emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, - new String[] { mSendTo }); - } - EncryptActivity.this. - startActivity(Intent.createChooser(emailIntent, - getString(R.string.title_sendEmail))); + case Id.target.email: { + if (mReturnResult) { + Intent intent = new Intent(); + intent.putExtras(data); + setResult(RESULT_OK, intent); + finish(); + return; } - case Id.target.file: { - Toast.makeText(this, R.string.encryptionSuccessful, Toast.LENGTH_SHORT).show(); - if (mDeleteAfter.isChecked()) { - setDeleteFile(mInputFilename); - showDialog(Id.dialog.delete_file); - } - break; + String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE)); + Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); + emailIntent.setType("text/plain; charset=utf-8"); + emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message); + if (mSubject != null) { + emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, + mSubject); + } + if (mSendTo != null) { + emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, + new String[] { mSendTo }); } + EncryptActivity.this. + startActivity(Intent.createChooser(emailIntent, + getString(R.string.title_sendEmail))); + break; + } - default: { - // shouldn't happen - break; + case Id.target.file: { + Toast.makeText(this, R.string.encryptionSuccessful, Toast.LENGTH_SHORT).show(); + if (mDeleteAfter.isChecked()) { + setDeleteFile(mInputFilename); + showDialog(Id.dialog.delete_file); } + break; + } + + default: { + // shouldn't happen + break; } } } diff --git a/src/org/thialfihar/android/apg/FileDialog.java b/src/org/thialfihar/android/apg/FileDialog.java index 22d64fc84..b6bbbf3f1 100644 --- a/src/org/thialfihar/android/apg/FileDialog.java +++ b/src/org/thialfihar/android/apg/FileDialog.java @@ -16,8 +16,6 @@ package org.thialfihar.android.apg;
-import org.openintents.intents.FileManager;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
@@ -48,6 +46,8 @@ public class FileDialog { String defaultFile, OnClickListener onClickListener,
String fileManagerTitle, String fileManagerButton,
int requestCode) {
+ // TODO: fileManagerTitle and fileManagerButton are deprecated, no use for them right now,
+ // but maybe the Intent now used will someday support them again, so leaving them in
LayoutInflater inflater =
(LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
@@ -102,18 +102,17 @@ public class FileDialog { private static void openFile() {
String filename = mFilename.getText().toString();
- Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setData(Uri.parse("file://" + filename));
-
- intent.putExtra(FileManager.EXTRA_TITLE, mFileManagerTitle);
- intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, mFileManagerButton);
+ intent.setType("*/*");
try {
mActivity.startActivityForResult(intent, mRequestCode);
} catch (ActivityNotFoundException e) {
// No compatible file manager was found.
- Toast.makeText(mActivity, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show();
+ Toast.makeText(mActivity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
}
}
}
diff --git a/src/org/thialfihar/android/apg/Id.java b/src/org/thialfihar/android/apg/Id.java index 47cd0a890..4567f937d 100644 --- a/src/org/thialfihar/android/apg/Id.java +++ b/src/org/thialfihar/android/apg/Id.java @@ -80,6 +80,11 @@ public final class Id { public static final int export_keys = 0x21070002;
}
+ public static final class database {
+ public static final int type_public = 0;
+ public static final int type_secret = 1;
+ }
+
public static final class type {
public static final int public_key = 0x21070001;
public static final int secret_key = 0x21070002;
diff --git a/src/org/thialfihar/android/apg/KeyListActivity.java b/src/org/thialfihar/android/apg/KeyListActivity.java new file mode 100644 index 000000000..7e86504b3 --- /dev/null +++ b/src/org/thialfihar/android/apg/KeyListActivity.java @@ -0,0 +1,642 @@ +/*
+ * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.thialfihar.android.apg;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Vector;
+
+import org.bouncycastle2.openpgp.PGPException;
+import org.bouncycastle2.openpgp.PGPPublicKeyRing;
+import org.bouncycastle2.openpgp.PGPSecretKeyRing;
+import org.thialfihar.android.apg.provider.KeyRings;
+import org.thialfihar.android.apg.provider.Keys;
+import org.thialfihar.android.apg.provider.UserIds;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+
+public class KeyListActivity extends BaseActivity {
+ protected ExpandableListView mList;
+ protected KeyListAdapter mListAdapter;
+
+ protected int mSelectedItem = -1;
+ protected int mTask = 0;
+
+ protected String mImportFilename = Constants.path.app_dir + "/";
+ protected String mExportFilename = Constants.path.app_dir + "/";
+
+ protected int mKeyType = Id.type.public_key;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.key_list);
+
+ mList = (ExpandableListView) findViewById(R.id.list);
+ mListAdapter = new KeyListAdapter(this);
+ mList.setAdapter(mListAdapter);
+ registerForContextMenu(mList);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case Id.menu.option.import_keys: {
+ showDialog(Id.dialog.import_keys);
+ return true;
+ }
+
+ case Id.menu.option.export_keys: {
+ showDialog(Id.dialog.export_keys);
+ return true;
+ }
+
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
+ int type = ExpandableListView.getPackedPositionType(info.packedPosition);
+ int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
+
+ if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ return super.onContextItemSelected(menuItem);
+ }
+
+ switch (menuItem.getItemId()) {
+ case Id.menu.export: {
+ mSelectedItem = groupPosition;
+ showDialog(Id.dialog.export_key);
+ return true;
+ }
+
+ case Id.menu.delete: {
+ mSelectedItem = groupPosition;
+ showDialog(Id.dialog.delete_key);
+ return true;
+ }
+
+ default: {
+ return super.onContextItemSelected(menuItem);
+ }
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ boolean singleKeyExport = false;
+
+ switch (id) {
+ case Id.dialog.delete_key: {
+ final int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
+ mSelectedItem = -1;
+ // TODO: better way to do this?
+ String userId = "<unknown>";
+ Object keyRing = Apg.getKeyRing(keyRingId);
+ if (keyRing != null) {
+ if (keyRing instanceof PGPPublicKeyRing) {
+ userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey((PGPPublicKeyRing) keyRing));
+ } else {
+ userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey((PGPSecretKeyRing) keyRing));
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.warning);
+ builder.setMessage(getString(mKeyType == Id.type.public_key ?
+ R.string.keyDeletionConfirmation :
+ R.string.secretKeyDeletionConfirmation, userId));
+ builder.setIcon(android.R.drawable.ic_dialog_alert);
+ builder.setPositiveButton(R.string.btn_delete,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ deleteKey(keyRingId);
+ removeDialog(Id.dialog.delete_key);
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.delete_key);
+ }
+ });
+ return builder.create();
+ }
+
+ case Id.dialog.import_keys: {
+ return FileDialog.build(this, getString(R.string.title_importKeys),
+ getString(R.string.specifyFileToImportFrom),
+ mImportFilename,
+ new FileDialog.OnClickListener() {
+
+ @Override
+ public void onOkClick(String filename) {
+ removeDialog(Id.dialog.import_keys);
+ mImportFilename = filename;
+ importKeys();
+ }
+
+ @Override
+ public void onCancelClick() {
+ removeDialog(Id.dialog.import_keys);
+ }
+ },
+ getString(R.string.filemanager_titleOpen),
+ getString(R.string.filemanager_btnOpen),
+ Id.request.filename);
+ }
+
+ case Id.dialog.export_key: {
+ singleKeyExport = true;
+ // break intentionally omitted, to use the Id.dialog.export_keys dialog
+ }
+
+ case Id.dialog.export_keys: {
+ String title = (singleKeyExport ?
+ getString(R.string.title_exportKey) :
+ getString(R.string.title_exportKeys));
+
+ final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
+
+ return FileDialog.build(this, title,
+ getString(mKeyType == Id.type.public_key ?
+ R.string.specifyFileToExportTo :
+ R.string.specifyFileToExportSecretKeysTo),
+ mExportFilename,
+ new FileDialog.OnClickListener() {
+ @Override
+ public void onOkClick(String filename) {
+ removeDialog(thisDialogId);
+ mExportFilename = filename;
+ exportKeys();
+ }
+
+ @Override
+ public void onCancelClick() {
+ removeDialog(thisDialogId);
+ }
+ },
+ getString(R.string.filemanager_titleSave),
+ getString(R.string.filemanager_btnSave),
+ Id.request.filename);
+ }
+
+ default: {
+ return super.onCreateDialog(id);
+ }
+ }
+ }
+
+ public void importKeys() {
+ showDialog(Id.dialog.importing);
+ mTask = Id.task.import_keys;
+ startThread();
+ }
+
+ public void exportKeys() {
+ showDialog(Id.dialog.exporting);
+ mTask = Id.task.export_keys;
+ startThread();
+ }
+
+ @Override
+ public void run() {
+ String error = null;
+ Bundle data = new Bundle();
+ Message msg = new Message();
+
+ String filename = null;
+ if (mTask == Id.task.import_keys) {
+ filename = mImportFilename;
+ } else {
+ filename = mExportFilename;
+ }
+
+ try {
+ if (mTask == Id.task.import_keys) {
+ data = Apg.importKeyRings(this, mKeyType, filename, this);
+ } else {
+ Vector<Integer> keyRingIds = new Vector<Integer>();
+ if (mSelectedItem == -1) {
+ keyRingIds = Apg.getKeyRingIds(mKeyType == Id.type.public_key ?
+ Id.database.type_public :
+ Id.database.type_secret);
+ } else {
+ int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
+ keyRingIds.add(keyRingId);
+ mSelectedItem = -1;
+ }
+ data = Apg.exportKeyRings(this, keyRingIds, filename, this);
+ }
+ } catch (FileNotFoundException e) {
+ error = getString(R.string.error_fileNotFound);
+ } catch (IOException e) {
+ error = "" + e;
+ } catch (PGPException e) {
+ error = "" + e;
+ } catch (Apg.GeneralException e) {
+ error = "" + e;
+ }
+
+ if (mTask == Id.task.import_keys) {
+ data.putInt(Apg.EXTRA_STATUS, Id.message.import_done);
+ } else {
+ data.putInt(Apg.EXTRA_STATUS, Id.message.export_done);
+ }
+
+ if (error != null) {
+ data.putString(Apg.EXTRA_ERROR, error);
+ }
+
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ protected void deleteKey(int keyRingId) {
+ Apg.deleteKey(keyRingId);
+ refreshList();
+ }
+
+ protected void refreshList() {
+ mListAdapter.rebuild(true);
+ mListAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ Bundle data = msg.getData();
+ if (data != null) {
+ int type = data.getInt(Apg.EXTRA_STATUS);
+ switch (type) {
+ case Id.message.import_done: {
+ removeDialog(Id.dialog.importing);
+
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(KeyListActivity.this,
+ getString(R.string.errorMessage, error),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ int added = data.getInt("added");
+ int updated = data.getInt("updated");
+ String message;
+ if (added > 0 && updated > 0) {
+ message = getString(R.string.keysAddedAndUpdated, added, updated);
+ } else if (added > 0) {
+ message = getString(R.string.keysAdded, added);
+ } else if (updated > 0) {
+ message = getString(R.string.keysUpdated, updated);
+ } else {
+ message = getString(R.string.noKeysAddedOrUpdated);
+ }
+ Toast.makeText(KeyListActivity.this, message,
+ Toast.LENGTH_SHORT).show();
+ }
+ refreshList();
+ break;
+ }
+
+ case Id.message.export_done: {
+ removeDialog(Id.dialog.exporting);
+
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(KeyListActivity.this,
+ getString(R.string.errorMessage, error),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ int exported = data.getInt("exported");
+ String message;
+ if (exported == 1) {
+ message = getString(R.string.keyExported);
+ } else if (exported > 0) {
+ message = getString(R.string.keysExported, exported);
+ } else{
+ message = getString(R.string.noKeysExported);
+ }
+ Toast.makeText(KeyListActivity.this, message,
+ Toast.LENGTH_SHORT).show();
+ }
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ protected class KeyListAdapter extends BaseExpandableListAdapter {
+ private LayoutInflater mInflater;
+ private Vector<Vector<KeyChild>> mChildren;
+ private SQLiteDatabase mDatabase;
+ private Cursor mCursor;
+
+ private class KeyChild {
+ public static final int KEY = 0;
+ public static final int USER_ID = 1;
+
+ public int type;
+ public String userId;
+ public long keyId;
+ public boolean isMasterKey;
+ public int algorithm;
+ public int keySize;
+ public boolean canSign;
+ public boolean canEncrypt;
+
+ public KeyChild(long keyId, boolean isMasterKey, int algorithm, int keySize,
+ boolean canSign, boolean canEncrypt) {
+ this.keyId = keyId;
+ this.isMasterKey = isMasterKey;
+ this.algorithm = algorithm;
+ this.keySize = keySize;
+ this.canSign = canSign;
+ this.canEncrypt = canEncrypt;
+ }
+
+ public KeyChild(String userId) {
+ type = USER_ID;
+ this.userId = userId;
+ }
+ }
+
+ public KeyListAdapter(Context context) {
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mDatabase = Apg.getDatabase().db();
+ mCursor = mDatabase.query(
+ KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
+ "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
+ Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
+ ") " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
+ "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
+ new String[] {
+ KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
+ },
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] { "" + (mKeyType == Id.type.public_key ?
+ Id.database.type_public : Id.database.type_secret) },
+ null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
+
+ startManagingCursor(mCursor);
+ rebuild(false);
+ }
+
+ public void rebuild(boolean requery) {
+ if (requery) {
+ mCursor.requery();
+ }
+ mChildren = new Vector<Vector<KeyChild>>();
+ for (int i = 0; i < mCursor.getCount(); ++i) {
+ mChildren.add(null);
+ }
+ }
+
+ protected Vector<KeyChild> getChildrenOfGroup(int groupPosition) {
+ Vector<KeyChild> children = mChildren.get(groupPosition);
+ if (children != null) {
+ return children;
+ }
+
+ mCursor.moveToPosition(groupPosition);
+ children = new Vector<KeyChild>();
+ Cursor c = mDatabase.query(Keys.TABLE_NAME,
+ new String[] {
+ Keys._ID, // 0
+ Keys.KEY_ID, // 1
+ Keys.IS_MASTER_KEY, // 2
+ Keys.ALGORITHM, // 3
+ Keys.KEY_SIZE, // 4
+ Keys.CAN_SIGN, // 5
+ Keys.CAN_ENCRYPT, // 6
+ },
+ Keys.KEY_RING_ID + " = ?",
+ new String[] { mCursor.getString(0) },
+ null, null, Keys.RANK + " ASC");
+
+ long masterKeyId = -1;
+ for (int i = 0; i < c.getCount(); ++i) {
+ c.moveToPosition(i);
+ children.add(new KeyChild(c.getLong(1), c.getInt(2) == 1, c.getInt(3), c.getInt(4),
+ c.getInt(5) == 1, c.getInt(6) == 1));
+ if (i == 0) {
+ masterKeyId = c.getInt(0);
+ }
+ }
+ c.close();
+
+ if (masterKeyId != -1) {
+ c = mDatabase.query(UserIds.TABLE_NAME,
+ new String[] {
+ UserIds.USER_ID, // 0
+ },
+ UserIds.KEY_ID + " = ? AND " + UserIds.RANK + " > 0",
+ new String[] { "" + masterKeyId },
+ null, null, UserIds.RANK + " ASC");
+
+ for (int i = 0; i < c.getCount(); ++i) {
+ c.moveToPosition(i);
+ children.add(new KeyChild(c.getString(0)));
+ }
+ c.close();
+ }
+
+ mChildren.set(groupPosition, children);
+ return children;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+
+ public int getGroupCount() {
+ return mCursor.getCount();
+ }
+
+ public Object getChild(int groupPosition, int childPosition) {
+ return null;
+ }
+
+ public long getChildId(int groupPosition, int childPosition) {
+ return childPosition;
+ }
+
+ public int getChildrenCount(int groupPosition) {
+ return getChildrenOfGroup(groupPosition).size();
+ }
+
+ public Object getGroup(int position) {
+ return position;
+ }
+
+ public long getGroupId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(1); // MASTER_KEY_ID
+ }
+
+ public int getKeyRingId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(0); // _ID
+ }
+
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
+ ViewGroup parent) {
+ mCursor.moveToPosition(groupPosition);
+
+ View view = mInflater.inflate(R.layout.key_list_group_item, null);
+ view.setBackgroundResource(android.R.drawable.list_selector_background);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText("");
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+
+ String userId = mCursor.getString(2); // USER_ID
+ if (userId != null) {
+ String chunks[] = userId.split(" <", 2);
+ userId = chunks[0];
+ if (chunks.length > 1) {
+ mainUserIdRest.setText("<" + chunks[1]);
+ }
+ mainUserId.setText(userId);
+ }
+
+ if (mainUserId.getText().length() == 0) {
+ mainUserId.setText(R.string.unknownUserId);
+ }
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ }
+ return view;
+ }
+
+ public View getChildView(int groupPosition, int childPosition,
+ boolean isLastChild, View convertView,
+ ViewGroup parent) {
+ mCursor.moveToPosition(groupPosition);
+
+ Vector<KeyChild> children = getChildrenOfGroup(groupPosition);
+
+ KeyChild child = children.get(childPosition);
+ View view = null;
+ switch (child.type) {
+ case KeyChild.KEY: {
+ if (child.isMasterKey) {
+ view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
+ } else {
+ view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
+ }
+
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ String keyIdStr = Long.toHexString(child.keyId & 0xffffffffL);
+ while (keyIdStr.length() < 8) {
+ keyIdStr = "0" + keyIdStr;
+ }
+ keyId.setText(keyIdStr);
+ TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
+ String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);
+ keyDetails.setText("(" + algorithmStr + ")");
+
+ ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
+ if (!child.canEncrypt) {
+ encryptIcon.setVisibility(View.GONE);
+ }
+
+ ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
+ if (!child.canSign) {
+ signIcon.setVisibility(View.GONE);
+ }
+ break;
+ }
+
+ case KeyChild.USER_ID: {
+ view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
+ TextView userId = (TextView) view.findViewById(R.id.userId);
+ userId.setText(child.userId);
+ break;
+ }
+ }
+ return view;
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.request.filename: {
+ if (resultCode == RESULT_OK && data != null) {
+ String filename = data.getDataString();
+ if (filename != null) {
+ // Get rid of URI prefix:
+ if (filename.startsWith("file://")) {
+ filename = filename.substring(7);
+ }
+ // replace %20 and so on
+ filename = Uri.decode(filename);
+
+ FileDialog.setFilename(filename);
+ }
+ }
+ return;
+ }
+
+ default: {
+ break;
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+}
diff --git a/src/org/thialfihar/android/apg/MailListActivity.java b/src/org/thialfihar/android/apg/MailListActivity.java index f0abe8f45..d166feede 100644 --- a/src/org/thialfihar/android/apg/MailListActivity.java +++ b/src/org/thialfihar/android/apg/MailListActivity.java @@ -87,7 +87,7 @@ public class MailListActivity extends ListActivity { mconversations = new Vector<Conversation>();
mmessages = new Vector<Message>();
- String account = getIntent().getExtras().getString("account");
+ String account = getIntent().getExtras().getString(Apg.EXTRA_ACCOUNT);
// TODO: what if account is null?
Uri uri = Uri.parse("content://gmail-ls/conversations/" + account);
Cursor cursor =
@@ -153,9 +153,9 @@ public class MailListActivity extends ListActivity { Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
intent.setAction(Apg.Intent.DECRYPT);
Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
- intent.putExtra("data", message.data);
- intent.putExtra("subject", message.subject);
- intent.putExtra("replyTo", message.replyTo);
+ intent.putExtra(Apg.EXTRA_DATA, message.data);
+ intent.putExtra(Apg.EXTRA_SUBJECT, message.subject);
+ intent.putExtra(Apg.EXTRA_REPLY_TO, message.replyTo);
startActivity(intent);
}
});
diff --git a/src/org/thialfihar/android/apg/MainActivity.java b/src/org/thialfihar/android/apg/MainActivity.java index a4d584304..65dd20d95 100644 --- a/src/org/thialfihar/android/apg/MainActivity.java +++ b/src/org/thialfihar/android/apg/MainActivity.java @@ -47,6 +47,8 @@ import android.widget.AdapterView.OnItemClickListener; public class MainActivity extends BaseActivity { private ListView mAccounts = null; + private AccountListAdapter mListAdapter = null; + private Cursor mAccountCursor; @Override public void onCreate(Bundle savedInstanceState) { @@ -95,22 +97,22 @@ public class MainActivity extends BaseActivity { } }); - Cursor accountCursor = managedQuery(Accounts.CONTENT_URI, null, null, null, null); + mAccountCursor = + Apg.getDatabase().db().query(Accounts.TABLE_NAME, + new String[] { + Accounts._ID, + Accounts.NAME, + }, null, null, null, null, Accounts.NAME + " ASC"); + startManagingCursor(mAccountCursor); - mAccounts.setAdapter(new AccountListAdapter(this, accountCursor)); + mListAdapter = new AccountListAdapter(this, mAccountCursor); + mAccounts.setAdapter(mListAdapter); mAccounts.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View view, int index, long id) { - Cursor cursor = - managedQuery(Uri.withAppendedPath(Accounts.CONTENT_URI, "" + id), null, - null, null, null); - if (cursor != null && cursor.getCount() > 0) { - cursor.moveToFirst(); - int nameIndex = cursor.getColumnIndex(Accounts.NAME); - String accountName = cursor.getString(nameIndex); - startActivity(new Intent(MainActivity.this, MailListActivity.class) - .putExtra("account", accountName)); - } + String accountName = (String) mAccounts.getItemAtPosition(index); + startActivity(new Intent(MainActivity.this, MailListActivity.class) + .putExtra(Apg.EXTRA_ACCOUNT, accountName)); } }); registerForContextMenu(mAccounts); @@ -154,9 +156,10 @@ public class MainActivity extends BaseActivity { ContentValues values = new ContentValues(); values.put(Accounts.NAME, accountName); try { - MainActivity.this.getContentResolver() - .insert(Accounts.CONTENT_URI, - values); + Apg.getDatabase().db().insert(Accounts.TABLE_NAME, + Accounts.NAME, values); + mAccountCursor.requery(); + mListAdapter.notifyDataSetChanged(); } catch (SQLException e) { Toast.makeText(MainActivity.this, getString(R.string.errorMessage, @@ -188,6 +191,12 @@ public class MainActivity extends BaseActivity { message.setText("Read the warnings!\n\n" + "Changes:\n" + + "* k9mail integration, k9mail beta build is available on the k9mail website\n" + + "* support of other file managers (e.g. ASTRO)\n" + + "* Slovenian translation (thanks, 359)\n" + + "* new database, much faster, less memory usage\n" + + "* defined Intents and content provider for other apps\n" + + "* bugfixes\n" + "\n" + "WARNING: be careful editing your existing keys, as they " + "WILL be stripped of certificates right now.\n" + @@ -277,8 +286,11 @@ public class MainActivity extends BaseActivity { switch (menuItem.getItemId()) { case Id.menu.delete: { - Uri uri = Uri.withAppendedPath(Accounts.CONTENT_URI, "" + info.id); - this.getContentResolver().delete(uri, null, null); + Apg.getDatabase().db().delete(Accounts.TABLE_NAME, + Accounts._ID + " = ?", + new String[] { "" + info.id }); + mAccountCursor.requery(); + mListAdapter.notifyDataSetChanged(); return true; } @@ -298,6 +310,13 @@ public class MainActivity extends BaseActivity { } @Override + public Object getItem(int position) { + Cursor c = getCursor(); + c.moveToPosition(position); + return c.getString(c.getColumnIndex(Accounts.NAME)); + } + + @Override public int getCount() { return super.getCount(); } diff --git a/src/org/thialfihar/android/apg/PreferencesActivity.java b/src/org/thialfihar/android/apg/PreferencesActivity.java index fae63d63b..e80e1ad5f 100644 --- a/src/org/thialfihar/android/apg/PreferencesActivity.java +++ b/src/org/thialfihar/android/apg/PreferencesActivity.java @@ -50,7 +50,6 @@ public class PreferencesActivity extends BaseActivity { new Choice(180, getString(R.string.choice_3mins)),
new Choice(300, getString(R.string.choice_5mins)),
new Choice(600, getString(R.string.choice_10mins)),
- new Choice(0, getString(R.string.choice_untilQuit)),
};
ArrayAdapter<Choice> adapter =
new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
diff --git a/src/org/thialfihar/android/apg/PublicKeyListActivity.java b/src/org/thialfihar/android/apg/PublicKeyListActivity.java index 67bc608ad..4997f60b7 100644 --- a/src/org/thialfihar/android/apg/PublicKeyListActivity.java +++ b/src/org/thialfihar/android/apg/PublicKeyListActivity.java @@ -16,54 +16,19 @@ package org.thialfihar.android.apg;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Vector;
-
-import org.bouncycastle2.openpgp.PGPException;
-import org.bouncycastle2.openpgp.PGPPublicKey;
-import org.bouncycastle2.openpgp.PGPPublicKeyRing;
-import org.thialfihar.android.apg.utils.IterableIterator;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
import android.view.ContextMenu;
-import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
-import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
-
-public class PublicKeyListActivity extends BaseActivity {
- ExpandableListView mList;
-
- protected int mSelectedItem = -1;
- protected int mTask = 0;
-
- private String mImportFilename = Constants.path.app_dir + "/pubring.gpg";
- private String mExportFilename = Constants.path.app_dir + "/pubexport.asc";
+public class PublicKeyListActivity extends KeyListActivity {
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ public void onCreate(Bundle savedInstanceState) {
+ mExportFilename = Constants.path.app_dir + "/pubexport.asc";
+ mKeyType = Id.type.public_key;
super.onCreate(savedInstanceState);
- setContentView(R.layout.key_list);
-
- mList = (ExpandableListView) findViewById(R.id.list);
- mList.setAdapter(new PublicKeyListAdapter(this));
- registerForContextMenu(mList);
}
@Override
@@ -80,506 +45,16 @@ public class PublicKeyListActivity extends BaseActivity { }
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case Id.menu.option.import_keys: {
- showDialog(Id.dialog.import_keys);
- return true;
- }
-
- case Id.menu.option.export_keys: {
- showDialog(Id.dialog.export_keys);
- return true;
- }
-
- default: {
- return super.onOptionsItemSelected(item);
- }
- }
- }
-
- @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
- int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
- String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
- menu.setHeaderTitle(userId);
+ // TODO: user id? menu.setHeaderTitle("Key");
menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey);
}
}
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
- int type = ExpandableListView.getPackedPositionType(info.packedPosition);
- int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
-
- if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- return super.onContextItemSelected(menuItem);
- }
-
- switch (menuItem.getItemId()) {
- case Id.menu.export: {
- mSelectedItem = groupPosition;
- showDialog(Id.dialog.export_key);
- return true;
- }
-
- case Id.menu.delete: {
- mSelectedItem = groupPosition;
- showDialog(Id.dialog.delete_key);
- return true;
- }
-
- default: {
- return super.onContextItemSelected(menuItem);
- }
- }
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- boolean singleKeyExport = false;
-
- switch (id) {
- case Id.dialog.delete_key: {
- PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(mSelectedItem);
- String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.warning);
- builder.setMessage(getString(R.string.keyDeletionConfirmation, userId));
- builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.setPositiveButton(R.string.btn_delete,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- deleteKey(mSelectedItem);
- mSelectedItem = -1;
- removeDialog(Id.dialog.delete_key);
- }
- });
- builder.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- mSelectedItem = -1;
- removeDialog(Id.dialog.delete_key);
- }
- });
- return builder.create();
- }
-
- case Id.dialog.import_keys: {
- return FileDialog.build(this, getString(R.string.title_importKeys),
- getString(R.string.specifyFileToImportFrom),
- mImportFilename,
- new FileDialog.OnClickListener() {
-
- @Override
- public void onOkClick(String filename) {
- removeDialog(Id.dialog.import_keys);
- mImportFilename = filename;
- importKeys();
- }
-
- @Override
- public void onCancelClick() {
- removeDialog(Id.dialog.import_keys);
- }
- },
- getString(R.string.filemanager_titleOpen),
- getString(R.string.filemanager_btnOpen),
- Id.request.filename);
- }
-
- case Id.dialog.export_key: {
- singleKeyExport = true;
- // break intentionally omitted, to use the Id.dialog.export_keys dialog
- }
-
- case Id.dialog.export_keys: {
- String title = (singleKeyExport ?
- getString(R.string.title_exportKey) :
- getString(R.string.title_exportKeys));
-
- final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
-
- return FileDialog.build(this, title,
- getString(R.string.specifyFileToExportTo),
- mExportFilename,
- new FileDialog.OnClickListener() {
-
- @Override
- public void onOkClick(String filename) {
- removeDialog(thisDialogId);
- mExportFilename = filename;
- exportKeys();
- }
-
- @Override
- public void onCancelClick() {
- removeDialog(thisDialogId);
- }
- },
- getString(R.string.filemanager_titleSave),
- getString(R.string.filemanager_btnSave),
- Id.request.filename);
- }
-
- default: {
- return super.onCreateDialog(id);
- }
- }
- }
-
- public void importKeys() {
- showDialog(Id.dialog.importing);
- mTask = Id.task.import_keys;
- startThread();
- }
-
- public void exportKeys() {
- showDialog(Id.dialog.exporting);
- mTask = Id.task.export_keys;
- startThread();
- }
-
- @Override
- public void run() {
- String error = null;
- Bundle data = new Bundle();
- Message msg = new Message();
-
- String filename = null;
- if (mTask == Id.task.import_keys) {
- filename = mImportFilename;
- } else {
- filename = mExportFilename;
- }
-
- try {
- if (mTask == Id.task.import_keys) {
- data = Apg.importKeyRings(this, Id.type.public_key, filename, this);
- } else {
- Vector<Object> keys = new Vector<Object>();
- if (mSelectedItem == -1) {
- for (PGPPublicKeyRing key : Apg.getPublicKeyRings()) {
- keys.add(key);
- }
- } else {
- keys.add(Apg.getPublicKeyRings().get(mSelectedItem));
- }
- data = Apg.exportKeyRings(this, keys, filename, this);
- }
- } catch (FileNotFoundException e) {
- error = getString(R.string.error_fileNotFound);
- } catch (IOException e) {
- error = e.getMessage();
- } catch (PGPException e) {
- error = e.getMessage();
- } catch (Apg.GeneralException e) {
- error = e.getMessage();
- }
-
- if (mTask == Id.task.import_keys) {
- data.putInt("type", Id.message.import_done);
- } else {
- data.putInt("type", Id.message.export_done);
- }
-
- if (error != null) {
- data.putString("error", error);
- }
-
- msg.setData(data);
- sendMessage(msg);
- }
-
- private void deleteKey(int index) {
- PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(index);
- Apg.deleteKey(this, keyRing);
- refreshList();
- }
-
- private void refreshList() {
- ((PublicKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
- }
-
- @Override
- public void doneCallback(Message msg) {
- super.doneCallback(msg);
-
- Bundle data = msg.getData();
- if (data != null) {
- int type = data.getInt("type");
- switch (type) {
- case Id.message.import_done: {
- removeDialog(Id.dialog.importing);
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(PublicKeyListActivity.this,
- getString(R.string.errorMessage, data.getString("error")),
- Toast.LENGTH_SHORT).show();
- } else {
- int added = data.getInt("added");
- int updated = data.getInt("updated");
- String message;
- if (added > 0 && updated > 0) {
- message = getString(R.string.keysAddedAndUpdated, added, updated);
- } else if (added > 0) {
- message = getString(R.string.keysAdded, added);
- } else if (updated > 0) {
- message = getString(R.string.keysUpdated, updated);
- } else {
- message = getString(R.string.noKeysAddedOrUpdated);
- }
- Toast.makeText(PublicKeyListActivity.this, message,
- Toast.LENGTH_SHORT).show();
- }
- refreshList();
- break;
- }
-
- case Id.message.export_done: {
- removeDialog(Id.dialog.exporting);
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(PublicKeyListActivity.this,
- getString(R.string.errorMessage, data.getString("error")),
- Toast.LENGTH_SHORT).show();
- } else {
- int exported = data.getInt("exported");
- String message;
- if (exported == 1) {
- message = getString(R.string.keyExported);
- } else if (exported > 0) {
- message = getString(R.string.keysExported);
- } else{
- message = getString(R.string.noKeysExported);
- }
- Toast.makeText(PublicKeyListActivity.this, message,
- Toast.LENGTH_SHORT).show();
- }
- break;
- }
-
- default: {
- break;
- }
- }
- }
- }
-
- private static class PublicKeyListAdapter extends BaseExpandableListAdapter {
- private LayoutInflater mInflater;
-
- private class KeyChild {
- public static final int KEY = 0;
- public static final int USER_ID = 1;
-
- public int type;
- public PGPPublicKey key;
- public String userId;
-
- public KeyChild(PGPPublicKey key) {
- type = KEY;
- this.key = key;
- }
-
- public KeyChild(String userId) {
- type = USER_ID;
- this.userId = userId;
- }
- }
-
- public PublicKeyListAdapter(Context context) {
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
-
- protected Vector<KeyChild> getChildrenOfKeyRing(PGPPublicKeyRing keyRing) {
- Vector<KeyChild> children = new Vector<KeyChild>();
- PGPPublicKey masterKey = null;
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- children.add(new KeyChild(key));
- if (key.isMasterKey()) {
- masterKey = key;
- }
- }
-
- if (masterKey != null) {
- boolean isFirst = true;
- for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
- if (isFirst) {
- // ignore first, it's in the group already
- isFirst = false;
- continue;
- }
- children.add(new KeyChild(userId));
- }
- }
-
- return children;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
-
- public int getGroupCount() {
- return Apg.getPublicKeyRings().size();
- }
-
- public Object getChild(int groupPosition, int childPosition) {
- PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
- Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
-
- KeyChild child = children.get(childPosition);
- return child;
- }
-
- public long getChildId(int groupPosition, int childPosition) {
- return childPosition;
- }
-
- public int getChildrenCount(int groupPosition) {
- return getChildrenOfKeyRing(Apg.getPublicKeyRings().get(groupPosition)).size();
- }
-
- public Object getGroup(int position) {
- return position;
- }
-
- public long getGroupId(int position) {
- return position;
- }
-
- public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
- ViewGroup parent) {
- PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- View view;
- if (!key.isMasterKey()) {
- continue;
- }
- view = mInflater.inflate(R.layout.key_list_group_item, null);
- view.setBackgroundResource(android.R.drawable.list_selector_background);
-
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText("");
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
-
- String userId = Apg.getMainUserId(key);
- if (userId != null) {
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mainUserIdRest.setText("<" + chunks[1]);
- }
- mainUserId.setText(userId);
- }
-
- if (mainUserId.getText().length() == 0) {
- mainUserId.setText(R.string.unknownUserId);
- }
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- }
- return view;
- }
- return null;
- }
-
- public View getChildView(int groupPosition, int childPosition,
- boolean isLastChild, View convertView,
- ViewGroup parent) {
- PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
- Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
-
- KeyChild child = children.get(childPosition);
- View view = null;
- switch (child.type) {
- case KeyChild.KEY: {
- PGPPublicKey key = child.key;
- if (key.isMasterKey()) {
- view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
- } else {
- view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
- }
-
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- String keyIdStr = Long.toHexString(key.getKeyID() & 0xffffffffL);
- while (keyIdStr.length() < 8) {
- keyIdStr = "0" + keyIdStr;
- }
- keyId.setText(keyIdStr);
- TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
- String algorithmStr = Apg.getAlgorithmInfo(key);
- keyDetails.setText("(" + algorithmStr + ")");
-
- ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
- if (!Apg.isEncryptionKey(key)) {
- encryptIcon.setVisibility(View.GONE);
- }
-
- ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
- if (!Apg.isSigningKey(key)) {
- signIcon.setVisibility(View.GONE);
- }
- break;
- }
-
- case KeyChild.USER_ID: {
- view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
- TextView userId = (TextView) view.findViewById(R.id.userId);
- userId.setText(child.userId);
- break;
- }
- }
- return view;
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.filename: {
- if (resultCode == RESULT_OK && data != null) {
- String filename = data.getDataString();
- if (filename != null) {
- // Get rid of URI prefix:
- if (filename.startsWith("file://")) {
- filename = filename.substring(7);
- }
- // replace %20 and so on
- filename = Uri.decode(filename);
-
- FileDialog.setFilename(filename);
- }
-
- }
- return;
- }
-
- default: {
- break;
- }
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
}
diff --git a/src/org/thialfihar/android/apg/SecretKeyListActivity.java b/src/org/thialfihar/android/apg/SecretKeyListActivity.java index a69fc5b9c..0252c46d2 100644 --- a/src/org/thialfihar/android/apg/SecretKeyListActivity.java +++ b/src/org/thialfihar/android/apg/SecretKeyListActivity.java @@ -16,55 +16,24 @@ package org.thialfihar.android.apg;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Vector;
-
-import org.bouncycastle2.openpgp.PGPException;
-import org.bouncycastle2.openpgp.PGPSecretKey;
-import org.bouncycastle2.openpgp.PGPSecretKeyRing;
-import org.thialfihar.android.apg.utils.IterableIterator;
-
-import android.app.AlertDialog;
import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
import android.view.ContextMenu;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
-import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView.OnChildClickListener;
-public class SecretKeyListActivity extends BaseActivity implements OnChildClickListener {
- ExpandableListView mList;
-
- protected int mSelectedItem = -1;
- protected int mTask = 0;
-
- private String mImportFilename = Constants.path.app_dir + "/secring.gpg";
- private String mExportFilename = Constants.path.app_dir + "/secexport.asc";
-
+public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ public void onCreate(Bundle savedInstanceState) {
+ mExportFilename = Constants.path.app_dir + "/secexport.asc";
+ mKeyType = Id.type.secret_key;
super.onCreate(savedInstanceState);
- setContentView(R.layout.key_list);
-
- mList = (ExpandableListView) findViewById(R.id.list);
- mList.setAdapter(new SecretKeyListAdapter(this));
- registerForContextMenu(mList);
mList.setOnChildClickListener(this);
}
@@ -86,16 +55,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case Id.menu.option.import_keys: {
- showDialog(Id.dialog.import_keys);
- return true;
- }
-
- case Id.menu.option.export_keys: {
- showDialog(Id.dialog.export_keys);
- return true;
- }
-
case Id.menu.option.create: {
createKey();
return true;
@@ -113,12 +72,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
- int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
- String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
- menu.setHeaderTitle(userId);
+ // TODO: user id? menu.setHeaderTitle("Key");
menu.add(0, Id.menu.edit, 0, R.string.menu_editKey);
menu.add(0, Id.menu.export, 1, R.string.menu_exportKey);
menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey);
@@ -142,18 +98,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL return true;
}
- case Id.menu.export: {
- mSelectedItem = groupPosition;
- showDialog(Id.dialog.export_key);
- return true;
- }
-
- case Id.menu.delete: {
- mSelectedItem = groupPosition;
- showDialog(Id.dialog.delete_key);
- return true;
- }
-
default: {
return super.onContextItemSelected(menuItem);
}
@@ -170,96 +114,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL @Override
protected Dialog onCreateDialog(int id) {
- boolean singleKeyExport = false;
-
switch (id) {
- case Id.dialog.delete_key: {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
-
- String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.warning);
- builder.setMessage(getString(R.string.secretKeyDeletionConfirmation, userId));
- builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.setPositiveButton(R.string.btn_delete,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- deleteKey(mSelectedItem);
- mSelectedItem = -1;
- removeDialog(Id.dialog.delete_key);
- }
- });
- builder.setNegativeButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- mSelectedItem = -1;
- removeDialog(Id.dialog.delete_key);
- }
- });
- return builder.create();
- }
-
- case Id.dialog.import_keys: {
- return FileDialog.build(this, getString(R.string.title_importKeys),
- getString(R.string.specifyFileToImportFrom),
- mImportFilename,
- new FileDialog.OnClickListener() {
-
- @Override
- public void onOkClick(String filename) {
- removeDialog(Id.dialog.import_keys);
- mImportFilename = filename;
- importKeys();
- }
-
- @Override
- public void onCancelClick() {
- removeDialog(Id.dialog.import_keys);
- }
- },
- getString(R.string.filemanager_titleOpen),
- getString(R.string.filemanager_btnOpen),
- Id.request.filename);
- }
-
- case Id.dialog.export_key: {
- singleKeyExport = true;
- // break intentionally omitted, to use the Id.dialog.export_keys dialog
- }
-
- case Id.dialog.export_keys: {
- String title = (singleKeyExport ?
- getString(R.string.title_exportKey) :
- getString(R.string.title_exportKeys));
-
- final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
-
- return FileDialog.build(this, title,
- getString(R.string.specifyFileToExportSecretKeysTo),
- mExportFilename,
- new FileDialog.OnClickListener() {
-
- @Override
- public void onOkClick(String filename) {
- removeDialog(thisDialogId);
- mExportFilename = filename;
- exportKeys();
- }
-
- @Override
- public void onCancelClick() {
- removeDialog(thisDialogId);
- }
- },
- getString(R.string.filemanager_titleSave),
- getString(R.string.filemanager_btnSave),
- Id.request.filename);
- }
-
case Id.dialog.pass_phrase: {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
- long keyId = keyRing.getSecretKey().getKeyID();
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
}
@@ -270,8 +127,7 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL }
public void checkPassPhraseAndEdit() {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
- long keyId = keyRing.getSecretKey().getKeyID();
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
String passPhrase = Apg.getCachedPassPhrase(keyId);
if (passPhrase == null) {
showDialog(Id.dialog.pass_phrase);
@@ -295,10 +151,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL }
private void editKey() {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
- long keyId = keyRing.getSecretKey().getKeyID();
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
Intent intent = new Intent(this, EditKeyActivity.class);
- intent.putExtra("keyId", keyId);
+ intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
startActivityForResult(intent, Id.message.edit_key);
}
@@ -313,24 +168,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL break;
}
- case Id.request.filename: {
- if (resultCode == RESULT_OK && data != null) {
- String filename = data.getDataString();
- if (filename != null) {
- // Get rid of URI prefix:
- if (filename.startsWith("file://")) {
- filename = filename.substring(7);
- }
- // replace %20 and so on
- filename = Uri.decode(filename);
-
- FileDialog.setFilename(filename);
- }
-
- }
- return;
- }
-
default: {
break;
}
@@ -338,320 +175,4 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL super.onActivityResult(requestCode, resultCode, data);
}
-
- public void importKeys() {
- showDialog(Id.dialog.importing);
- mTask = Id.task.import_keys;
- startThread();
- }
-
- public void exportKeys() {
- showDialog(Id.dialog.exporting);
- mTask = Id.task.export_keys;
- startThread();
- }
-
- @Override
- public void run() {
- String error = null;
- Bundle data = new Bundle();
- Message msg = new Message();
-
- String filename = null;
- if (mTask == Id.task.import_keys) {
- filename = mImportFilename;
- } else {
- filename = mExportFilename;
- }
-
- try {
- if (mTask == Id.task.import_keys) {
- data = Apg.importKeyRings(this, Id.type.secret_key, filename, this);
- } else {
- Vector<Object> keys = new Vector<Object>();
- if (mSelectedItem == -1) {
- for (PGPSecretKeyRing key : Apg.getSecretKeyRings()) {
- keys.add(key);
- }
- } else {
- keys.add(Apg.getSecretKeyRings().get(mSelectedItem));
- }
- data = Apg.exportKeyRings(this, keys, filename, this);
- }
- } catch (FileNotFoundException e) {
- error = getString(R.string.error_fileNotFound);
- } catch (IOException e) {
- error = e.getMessage();
- } catch (PGPException e) {
- error = e.getMessage();
- } catch (Apg.GeneralException e) {
- error = e.getMessage();
- }
-
- if (mTask == Id.task.import_keys) {
- data.putInt("type", Id.message.import_done);
- } else {
- data.putInt("type", Id.message.export_done);
- }
-
- if (error != null) {
- data.putString("error", error);
- }
-
- msg.setData(data);
- sendMessage(msg);
- }
-
- private void deleteKey(int index) {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(index);
- Apg.deleteKey(this, keyRing);
- refreshList();
- }
-
- private void refreshList() {
- ((SecretKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
- }
-
- @Override
- public void doneCallback(Message msg) {
- super.doneCallback(msg);
-
- Bundle data = msg.getData();
- if (data != null) {
- int type = data.getInt("type");
- switch (type) {
- case Id.message.import_done: {
- removeDialog(Id.dialog.importing);
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(SecretKeyListActivity.this,
- getString(R.string.errorMessage, data.getString("error")),
- Toast.LENGTH_SHORT).show();
- } else {
- int added = data.getInt("added");
- int updated = data.getInt("updated");
- String message;
- if (added > 0 && updated > 0) {
- message = getString(R.string.keysAddedAndUpdated, added, updated);
- } else if (added > 0) {
- message = getString(R.string.keysAdded, added);
- } else if (updated > 0) {
- message = getString(R.string.keysUpdated, updated);
- } else {
- message = getString(R.string.noKeysAddedOrUpdated);
- }
- Toast.makeText(SecretKeyListActivity.this, message,
- Toast.LENGTH_SHORT).show();
- }
- refreshList();
- break;
- }
-
- case Id.message.export_done: {
- removeDialog(Id.dialog.exporting);
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(SecretKeyListActivity.this,
- getString(R.string.errorMessage, data.getString("error")),
- Toast.LENGTH_SHORT).show();
- } else {
- int exported = data.getInt("exported");
- String message;
- if (exported == 1) {
- message = getString(R.string.keyExported);
- } else if (exported > 0) {
- message = getString(R.string.keysExported);
- } else{
- message = getString(R.string.noKeysExported);
- }
- Toast.makeText(SecretKeyListActivity.this, message,
- Toast.LENGTH_SHORT).show();
- }
- break;
- }
-
- default: {
- break;
- }
- }
- }
- }
-
- private static class SecretKeyListAdapter extends BaseExpandableListAdapter {
- private LayoutInflater mInflater;
-
- private class KeyChild {
- static final int KEY = 0;
- static final int USER_ID = 1;
-
- public int type;
- public PGPSecretKey key;
- public String userId;
-
- public KeyChild(PGPSecretKey key) {
- type = KEY;
- this.key = key;
- }
-
- public KeyChild(String userId) {
- type = USER_ID;
- this.userId = userId;
- }
- }
-
- public SecretKeyListAdapter(Context context) {
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
-
- protected Vector<KeyChild> getChildrenOfKeyRing(PGPSecretKeyRing keyRing) {
- Vector<KeyChild> children = new Vector<KeyChild>();
- PGPSecretKey masterKey = null;
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- children.add(new KeyChild(key));
- if (key.isMasterKey()) {
- masterKey = key;
- }
- }
-
- if (masterKey != null) {
- boolean isFirst = true;
- for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
- if (isFirst) {
- // ignore first, it's in the group already
- isFirst = false;
- continue;
- }
- children.add(new KeyChild(userId));
- }
- }
-
- return children;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
-
- public int getGroupCount() {
- return Apg.getSecretKeyRings().size();
- }
-
- public Object getChild(int groupPosition, int childPosition) {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
- Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
- KeyChild child = children.get(childPosition);
- return child;
- }
-
- public long getChildId(int groupPosition, int childPosition) {
- return childPosition;
- }
-
- public int getChildrenCount(int groupPosition) {
- return getChildrenOfKeyRing(Apg.getSecretKeyRings().get(groupPosition)).size();
- }
-
- public Object getGroup(int position) {
- return position;
- }
-
- public long getGroupId(int position) {
- return position;
- }
-
- public View getGroupView(int groupPosition, boolean isExpanded,
- View convertView, ViewGroup parent) {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- View view;
- if (!key.isMasterKey()) {
- continue;
- }
- view = mInflater.inflate(R.layout.key_list_group_item, null);
- view.setBackgroundResource(android.R.drawable.list_selector_background);
-
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText("");
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
-
- String userId = Apg.getMainUserId(key);
- if (userId != null) {
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mainUserIdRest.setText("<" + chunks[1]);
- }
- mainUserId.setText(userId);
- }
-
- if (mainUserId.getText().length() == 0) {
- mainUserId.setText(R.string.unknownUserId);
- }
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- }
- return view;
- }
- return null;
- }
-
- public View getChildView(int groupPosition, int childPosition,
- boolean isLastChild, View convertView,
- ViewGroup parent) {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
- Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
-
- KeyChild child = children.get(childPosition);
- View view = null;
- switch (child.type) {
- case KeyChild.KEY: {
- PGPSecretKey key = child.key;
- if (key.isMasterKey()) {
- view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
- } else {
- view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
- }
-
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- String keyIdStr = Long.toHexString(key.getKeyID() & 0xffffffffL);
- while (keyIdStr.length() < 8) {
- keyIdStr = "0" + keyIdStr;
- }
- keyId.setText(keyIdStr);
- TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
- String algorithmStr = Apg.getAlgorithmInfo(key);
- keyDetails.setText("(" + algorithmStr + ")");
-
- ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
- if (!Apg.isEncryptionKey(key)) {
- encryptIcon.setVisibility(View.GONE);
- }
-
- ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
- if (!Apg.isSigningKey(key)) {
- signIcon.setVisibility(View.GONE);
- }
- break;
- }
-
- case KeyChild.USER_ID: {
- view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
- TextView userId = (TextView) view.findViewById(R.id.userId);
- userId.setText(child.userId);
- break;
- }
- }
- return view;
- }
- }
}
diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java b/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java index fbb0b6fe0..aeb6d59a3 100644 --- a/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java +++ b/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java @@ -16,12 +16,8 @@ package org.thialfihar.android.apg; -import java.util.Collections; import java.util.Vector; -import org.bouncycastle2.openpgp.PGPPublicKey; -import org.bouncycastle2.openpgp.PGPPublicKeyRing; - import android.content.Intent; import android.os.Bundle; import android.view.View; @@ -42,27 +38,21 @@ public class SelectPublicKeyListActivity extends BaseActivity { mIntent = getIntent(); long selectedKeyIds[] = null; if (mIntent.getExtras() != null) { - selectedKeyIds = mIntent.getExtras().getLongArray("selection"); + selectedKeyIds = mIntent.getExtras().getLongArray(Apg.EXTRA_SELECTION); } mList = (ListView) findViewById(R.id.list); // needed in Android 1.5, where the XML attribute gets ignored mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - Vector<PGPPublicKeyRing> keyRings = - (Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone(); - Collections.sort(keyRings, new Apg.PublicKeySorter()); - mList.setAdapter(new SelectPublicKeyListAdapter(mList, keyRings)); + SelectPublicKeyListAdapter adapter = new SelectPublicKeyListAdapter(this, mList); + mList.setAdapter(adapter); if (selectedKeyIds != null) { - for (int i = 0; i < keyRings.size(); ++i) { - PGPPublicKeyRing keyRing = keyRings.get(i); - PGPPublicKey key = Apg.getMasterKey(keyRing); - if (key == null) { - continue; - } + for (int i = 0; i < adapter.getCount(); ++i) { + long keyId = adapter.getItemId(i); for (int j = 0; j < selectedKeyIds.length; ++j) { - if (key.getKeyID() == selectedKeyIds[j]) { + if (keyId == selectedKeyIds[j]) { mList.setItemChecked(i, true); break; } @@ -106,8 +96,8 @@ public class SelectPublicKeyListActivity extends BaseActivity { for (int i = 0; i < vector.size(); ++i) { selectedKeyIds[i] = vector.get(i); } - data.putExtra("selection", selectedKeyIds); + data.putExtra(Apg.EXTRA_SELECTION, selectedKeyIds); setResult(RESULT_OK, data); finish(); } -}
\ No newline at end of file +} diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java index 1b0b82fd8..ffc344ead 100644 --- a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java +++ b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java @@ -16,15 +16,16 @@ package org.thialfihar.android.apg;
-import java.text.DateFormat;
import java.util.Date;
-import java.util.Vector;
-import org.bouncycastle2.openpgp.PGPPublicKey;
-import org.bouncycastle2.openpgp.PGPPublicKeyRing;
-import org.thialfihar.android.apg.utils.IterableIterator;
+import org.thialfihar.android.apg.provider.KeyRings;
+import org.thialfihar.android.apg.provider.Keys;
+import org.thialfihar.android.apg.provider.UserIds;
+import android.app.Activity;
import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -34,40 +35,55 @@ import android.widget.ListView; import android.widget.TextView;
public class SelectPublicKeyListAdapter extends BaseAdapter {
- protected Vector<PGPPublicKeyRing> mKeyRings;
protected LayoutInflater mInflater;
protected ListView mParent;
+ protected SQLiteDatabase mDatabase;
+ protected Cursor mCursor;
- public SelectPublicKeyListAdapter(ListView parent,
- Vector<PGPPublicKeyRing> keyRings) {
- setKeyRings(keyRings);
+ public SelectPublicKeyListAdapter(Activity activity, ListView parent) {
mParent = parent;
+ mDatabase = Apg.getDatabase().db();
mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
-
- public void setKeyRings(Vector<PGPPublicKeyRing> keyRings) {
- mKeyRings = keyRings;
- notifyDataSetChanged();
- }
-
- public Vector<PGPPublicKeyRing> getKeyRings() {
- return mKeyRings;
+ long now = new Date().getTime() / 1000;
+ mCursor = mDatabase.query(
+ KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
+ "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
+ Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
+ ") " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
+ "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
+ new String[] {
+ KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_ENCRYPT + " = '1')", // 3
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_ENCRYPT + " = '1' AND " +
+ "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
+ "(tmp." + Keys.EXPIRY + " IS NULL OR " +
+ "tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
+ },
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] { "" + Id.database.type_public },
+ null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
+
+ activity.startManagingCursor(mCursor);
}
@Override
public boolean isEnabled(int position) {
- PGPPublicKeyRing keyRing = mKeyRings.get(position);
-
- if (Apg.getMasterKey(keyRing) == null) {
- return false;
- }
-
- Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- return false;
- }
-
- return true;
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(4) > 0; // valid CAN_ENCRYPT
}
@Override
@@ -77,93 +93,62 @@ public class SelectPublicKeyListAdapter extends BaseAdapter { @Override
public int getCount() {
- return mKeyRings.size();
+ return mCursor.getCount();
}
@Override
public Object getItem(int position) {
- return mKeyRings.get(position);
+ return position;
}
@Override
public long getItemId(int position) {
- PGPPublicKeyRing keyRing = mKeyRings.get(position);
- PGPPublicKey key = Apg.getMasterKey(keyRing);
- if (key != null) {
- return key.getKeyID();
- }
-
- return 0;
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(1); // MASTER_KEY_ID
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
+ mCursor.moveToPosition(position);
+
View view = mInflater.inflate(R.layout.select_public_key_item, null);
boolean enabled = isEnabled(position);
- PGPPublicKeyRing keyRing = mKeyRings.get(position);
- PGPPublicKey key = null;
- for (PGPPublicKey tKey : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- if (tKey.isMasterKey()) {
- key = tKey;
- break;
- }
- }
-
- Vector<PGPPublicKey> encryptKeys = Apg.getEncryptKeys(keyRing);
- Vector<PGPPublicKey> usableKeys = Apg.getUsableEncryptKeys(keyRing);
-
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText(R.string.unknownUserId);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.keyId);
keyId.setText(R.string.noKey);
- TextView creation = (TextView) view.findViewById(R.id.creation);
- creation.setText(R.string.noDate);
- TextView expiry = (TextView) view.findViewById(R.id.expiry);
- expiry.setText(R.string.noExpiry);
TextView status = (TextView) view.findViewById(R.id.status);
status.setText(R.string.unknownStatus);
- if (key != null) {
- String userId = Apg.getMainUserId(key);
- if (userId != null) {
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mainUserIdRest.setText("<" + chunks[1]);
- }
- mainUserId.setText(userId);
+ String userId = mCursor.getString(2); // USER_ID
+ if (userId != null) {
+ String chunks[] = userId.split(" <", 2);
+ userId = chunks[0];
+ if (chunks.length > 1) {
+ mainUserIdRest.setText("<" + chunks[1]);
}
-
- keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL));
+ mainUserId.setText(userId);
}
+ long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
+ keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
+
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
- PGPPublicKey timespanKey = key;
- if (usableKeys.size() > 0) {
- timespanKey = usableKeys.get(0);
+ if (enabled) {
status.setText(R.string.canEncrypt);
- } else if (encryptKeys.size() > 0) {
- timespanKey = encryptKeys.get(0);
- Date now = new Date();
- if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
- status.setText(R.string.notValid);
- } else {
+ } else {
+ if (mCursor.getInt(3) > 0) {
+ // has some CAN_ENCRYPT keys, but col(4) = 0, so must be revoked or expired
status.setText(R.string.expired);
+ } else {
+ status.setText(R.string.noKey);
}
- } else {
- status.setText(R.string.noKey);
- }
-
- creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey)));
- Date expiryDate = Apg.getExpiryDate(timespanKey);
- if (expiryDate != null) {
- expiry.setText(DateFormat.getDateInstance().format(expiryDate));
}
status.setText(status.getText() + " ");
@@ -176,8 +161,6 @@ public class SelectPublicKeyListAdapter extends BaseAdapter { mainUserId.setEnabled(enabled);
mainUserIdRest.setEnabled(enabled);
keyId.setEnabled(enabled);
- creation.setEnabled(enabled);
- expiry.setEnabled(enabled);
selected.setEnabled(enabled);
status.setEnabled(enabled);
diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java index b6811d6e3..cd87a94b6 100644 --- a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java +++ b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java @@ -16,32 +16,16 @@ package org.thialfihar.android.apg; -import java.text.DateFormat; -import java.util.Collections; -import java.util.Date; -import java.util.Vector; - -import org.bouncycastle2.openpgp.PGPSecretKey; -import org.bouncycastle2.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.utils.IterableIterator; - -import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.BaseAdapter; import android.widget.ListView; -import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; public class SelectSecretKeyListActivity extends BaseActivity { - protected Vector<PGPSecretKeyRing> mKeyRings; - protected LayoutInflater mInflater; - protected Intent mIntent; protected ListView mList; + protected SelectSecretKeyListAdapter mListAdapter; protected long mSelectedKeyId = 0; @@ -49,158 +33,20 @@ public class SelectSecretKeyListActivity extends BaseActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - // fill things - mIntent = getIntent(); - - mKeyRings = (Vector<PGPSecretKeyRing>) Apg.getSecretKeyRings().clone(); - Collections.sort(mKeyRings, new Apg.SecretKeySorter()); - setContentView(R.layout.select_secret_key); mList = (ListView) findViewById(R.id.list); - mList.setAdapter(new SecretKeyListAdapter(this)); + mListAdapter = new SelectSecretKeyListAdapter(this, mList); + mList.setAdapter(mListAdapter); mList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { Intent data = new Intent(); - data.putExtra("selectedKeyId", id); + data.putExtra(Apg.EXTRA_KEY_ID, id); setResult(RESULT_OK, data); finish(); } }); } - - private class SecretKeyListAdapter extends BaseAdapter { - - public SecretKeyListAdapter(Context context) { - } - - @Override - public boolean isEnabled(int position) { - PGPSecretKeyRing keyRing = mKeyRings.get(position); - - if (Apg.getMasterKey(keyRing) == null) { - return false; - } - - Vector<PGPSecretKey> usableKeys = Apg.getUsableSigningKeys(keyRing); - if (usableKeys.size() == 0) { - return false; - } - - return true; - } - - @Override - public boolean hasStableIds() { - return true; - } - - @Override - public int getCount() { - return mKeyRings.size(); - } - - @Override - public Object getItem(int position) { - return mKeyRings.get(position); - } - - @Override - public long getItemId(int position) { - PGPSecretKeyRing keyRing = mKeyRings.get(position); - PGPSecretKey key = Apg.getMasterKey(keyRing); - if (key != null) { - return key.getKeyID(); - } - - return 0; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View view = mInflater.inflate(R.layout.select_secret_key_item, null); - boolean enabled = isEnabled(position); - - PGPSecretKeyRing keyRing = mKeyRings.get(position); - PGPSecretKey key = null; - for (PGPSecretKey tKey : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) { - if (tKey.isMasterKey()) { - key = tKey; - break; - } - } - - TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); - mainUserId.setText(R.string.unknownUserId); - TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - mainUserIdRest.setText(""); - TextView keyId = (TextView) view.findViewById(R.id.keyId); - keyId.setText(R.string.noKey); - TextView creation = (TextView) view.findViewById(R.id.creation); - creation.setText(R.string.noDate); - TextView expiry = (TextView) view.findViewById(R.id.expiry); - expiry.setText(R.string.noExpiry); - TextView status = (TextView) view.findViewById(R.id.status); - status.setText(R.string.unknownStatus); - - if (key != null) { - String userId = Apg.getMainUserId(key); - if (userId != null) { - String chunks[] = userId.split(" <", 2); - userId = chunks[0]; - if (chunks.length > 1) { - mainUserIdRest.setText("<" + chunks[1]); - } - mainUserId.setText(userId); - } - - keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL)); - } - - if (mainUserIdRest.getText().length() == 0) { - mainUserIdRest.setVisibility(View.GONE); - } - - Vector<PGPSecretKey> signingKeys = Apg.getSigningKeys(keyRing); - Vector<PGPSecretKey> usableKeys = Apg.getUsableSigningKeys(keyRing); - - PGPSecretKey timespanKey = key; - if (usableKeys.size() > 0) { - timespanKey = usableKeys.get(0); - status.setText(R.string.canSign); - } else if (signingKeys.size() > 0) { - timespanKey = signingKeys.get(0); - Date now = new Date(); - if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) { - status.setText(R.string.notValid); - } else { - status.setText(R.string.expired); - } - } else { - status.setText(R.string.noKey); - } - - creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey))); - Date expiryDate = Apg.getExpiryDate(timespanKey); - if (expiryDate != null) { - expiry.setText(DateFormat.getDateInstance().format(expiryDate)); - } - - status.setText(status.getText() + " "); - - view.setEnabled(enabled); - mainUserId.setEnabled(enabled); - mainUserIdRest.setEnabled(enabled); - keyId.setEnabled(enabled); - creation.setEnabled(enabled); - expiry.setEnabled(enabled); - status.setEnabled(enabled); - - return view; - } - } -}
\ No newline at end of file +} diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java new file mode 100644 index 000000000..33cd15b40 --- /dev/null +++ b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java @@ -0,0 +1,147 @@ +package org.thialfihar.android.apg;
+
+import java.util.Date;
+
+import org.thialfihar.android.apg.provider.KeyRings;
+import org.thialfihar.android.apg.provider.Keys;
+import org.thialfihar.android.apg.provider.UserIds;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class SelectSecretKeyListAdapter extends BaseAdapter {
+ protected LayoutInflater mInflater;
+ protected ListView mParent;
+ protected SQLiteDatabase mDatabase;
+ protected Cursor mCursor;
+
+ public SelectSecretKeyListAdapter(Activity activity, ListView parent) {
+ mParent = parent;
+ mDatabase = Apg.getDatabase().db();
+ mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ long now = new Date().getTime() / 1000;
+ mCursor = mDatabase.query(
+ KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
+ "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
+ Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
+ ") " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
+ "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
+ new String[] {
+ KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_SIGN + " = '1')", // 3,
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_SIGN + " = '1' AND " +
+ "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
+ "(tmp." + Keys.EXPIRY + " IS NULL OR " +
+ "tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
+ },
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] { "" + Id.database.type_secret },
+ null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
+
+ activity.startManagingCursor(mCursor);
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(4) > 0; // valid CAN_SIGN
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return position;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(1); // MASTER_KEY_ID
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ mCursor.moveToPosition(position);
+
+ View view = mInflater.inflate(R.layout.select_secret_key_item, null);
+ boolean enabled = isEnabled(position);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknownUserId);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ keyId.setText(R.string.noKey);
+ TextView status = (TextView) view.findViewById(R.id.status);
+ status.setText(R.string.unknownStatus);
+
+ String userId = mCursor.getString(2); // USER_ID
+ if (userId != null) {
+ String chunks[] = userId.split(" <", 2);
+ userId = chunks[0];
+ if (chunks.length > 1) {
+ mainUserIdRest.setText("<" + chunks[1]);
+ }
+ mainUserId.setText(userId);
+ }
+
+ long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
+ keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ }
+
+ if (enabled) {
+ status.setText(R.string.canSign);
+ } else {
+ if (mCursor.getInt(3) > 0) {
+ // has some CAN_SIGN keys, but col(4) = 0, so must be revoked or expired
+ status.setText(R.string.expired);
+ } else {
+ status.setText(R.string.noKey);
+ }
+ }
+
+ status.setText(status.getText() + " ");
+
+ view.setEnabled(enabled);
+ mainUserId.setEnabled(enabled);
+ mainUserIdRest.setEnabled(enabled);
+ keyId.setEnabled(enabled);
+ status.setEnabled(enabled);
+
+ return view;
+ }
+}
\ No newline at end of file diff --git a/src/org/thialfihar/android/apg/Service.java b/src/org/thialfihar/android/apg/Service.java new file mode 100644 index 000000000..4457274ff --- /dev/null +++ b/src/org/thialfihar/android/apg/Service.java @@ -0,0 +1,81 @@ +package org.thialfihar.android.apg;
+
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+
+public class Service extends android.app.Service {
+ private final IBinder mBinder = new LocalBinder();
+
+ public static final String EXTRA_TTL = "ttl";
+
+ private int mPassPhraseCacheTtl = 15;
+ private Handler mCacheHandler = new Handler();
+ private Runnable mCacheTask = new Runnable() {
+ public void run() {
+ // TODO: I suppose we could read out the time left until the first cache entry
+ // expiration, then use that for the timer...
+
+ // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
+ // and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl
+ int delay = mPassPhraseCacheTtl * 1000 / 2;
+ // also make sure the delay is not longer than one minute
+ if (delay > 60000) {
+ delay = 60000;
+ }
+
+ delay = Apg.cleanUpCache(mPassPhraseCacheTtl, delay);
+ // don't check too often, even if we were close
+ if (delay < 5000) {
+ delay = 5000;
+ }
+
+ mCacheHandler.postDelayed(this, delay);
+ }
+ };
+
+ static private boolean mIsRunning = false;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mIsRunning = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mIsRunning = false;
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+
+ if (intent != null) {
+ mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15);
+ }
+ if (mPassPhraseCacheTtl < 15) {
+ mPassPhraseCacheTtl = 15;
+ }
+ mCacheHandler.removeCallbacks(mCacheTask);
+ mCacheHandler.postDelayed(mCacheTask, 1000);
+ }
+
+ static public boolean isRunning() {
+ return mIsRunning;
+ }
+
+ public class LocalBinder extends Binder {
+ Service getService() {
+ return Service.this;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/provider/Accounts.java b/src/org/thialfihar/android/apg/provider/Accounts.java index 4fce2b607..8162472ec 100644 --- a/src/org/thialfihar/android/apg/provider/Accounts.java +++ b/src/org/thialfihar/android/apg/provider/Accounts.java @@ -16,7 +16,12 @@ package org.thialfihar.android.apg.provider;
-public class Accounts extends Accounts1 {
- private Accounts() {
- }
-}
\ No newline at end of file +import android.provider.BaseColumns;
+
+public class Accounts implements BaseColumns {
+ public static final String TABLE_NAME = "accounts";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String NAME = "c_name";
+ public static final String NAME_type = "TEXT";
+}
diff --git a/src/org/thialfihar/android/apg/provider/DataProvider.java b/src/org/thialfihar/android/apg/provider/DataProvider.java index fbc1be047..8a3fefdff 100644 --- a/src/org/thialfihar/android/apg/provider/DataProvider.java +++ b/src/org/thialfihar/android/apg/provider/DataProvider.java @@ -18,15 +18,12 @@ package org.thialfihar.android.apg.provider; import java.util.HashMap; +import org.thialfihar.android.apg.Id; + import android.content.ContentProvider; -import android.content.ContentUris; import android.content.ContentValues; -import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; @@ -34,153 +31,190 @@ import android.text.TextUtils; public class DataProvider extends ContentProvider { public static final String AUTHORITY = "org.thialfihar.android.apg.provider"; - private static final String DATABASE_NAME = "apg"; - private static final int DATABASE_VERSION = 1; - - private static final int PUBLIC_KEYS = 101; - private static final int PUBLIC_KEY_ID = 102; - private static final int PUBLIC_KEY_BY_KEY_ID = 103; - - private static final int SECRET_KEYS = 201; - private static final int SECRET_KEY_ID = 202; - private static final int SECRET_KEY_BY_KEY_ID = 203; - - private static final int ACCOUNTS = 301; - private static final int ACCOUNT_ID = 302; + private static final int PUBLIC_KEY_RINGS = 101; + private static final int PUBLIC_KEY_RING_ID = 102; + private static final int PUBLIC_KEY_RING_BY_KEY_ID = 103; + private static final int PUBLIC_KEY_RING_KEYS = 111; + private static final int PUBLIC_KEY_RING_KEY_RANK = 112; + private static final int PUBLIC_KEY_RING_USER_IDS = 121; + private static final int PUBLIC_KEY_RING_USER_ID_RANK = 122; + + private static final int SECRET_KEY_RINGS = 201; + private static final int SECRET_KEY_RING_ID = 202; + private static final int SECRET_KEY_RING_BY_KEY_ID = 203; + private static final int SECRET_KEY_RING_KEYS = 211; + private static final int SECRET_KEY_RING_KEY_RANK = 212; + private static final int SECRET_KEY_RING_USER_IDS = 221; + private static final int SECRET_KEY_RING_USER_ID_RANK = 222; + + private static final String PUBLIC_KEY_RING_CONTENT_DIR_TYPE = + "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key_ring"; + private static final String PUBLIC_KEY_RING_CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/vnd.thialfihar.apg.public.key_ring"; + + private static final String PUBLIC_KEY_CONTENT_DIR_TYPE = + "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key"; + private static final String PUBLIC_KEY_CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/vnd.thialfihar.apg.public.key"; + + private static final String SECRET_KEY_RING_CONTENT_DIR_TYPE = + "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key_ring"; + private static final String SECRET_KEY_RING_CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key_ring"; + + private static final String SECRET_KEY_CONTENT_DIR_TYPE = + "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key"; + private static final String SECRET_KEY_CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key"; + + private static final String USER_ID_CONTENT_DIR_TYPE = + "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id"; + private static final String USER_ID_CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/vnd.thialfihar.apg.user_id"; + + public static final String MASTER_KEY_ID = "master_key_id"; + public static final String KEY_ID = "key_id"; + public static final String USER_ID = "user_id"; private static final UriMatcher mUriMatcher; - private static final HashMap<String, String> mPublicKeysProjectionMap; - private static final HashMap<String, String> mSecretKeysProjectionMap; - private static final HashMap<String, String> mAccountsProjectionMap; - private DatabaseHelper mdbHelper; + private Database mDb; static { mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys", PUBLIC_KEYS); - mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/#", PUBLIC_KEY_ID); - mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/key_id/*", PUBLIC_KEY_BY_KEY_ID); - - mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys", SECRET_KEYS); - mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/#", SECRET_KEY_ID); - mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/key_id/*", SECRET_KEY_BY_KEY_ID); - - mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts", ACCOUNTS); - mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts/#", ACCOUNT_ID); - - mPublicKeysProjectionMap = new HashMap<String, String>(); - mPublicKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID); - mPublicKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID); - mPublicKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA); - mPublicKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID); - - mSecretKeysProjectionMap = new HashMap<String, String>(); - mSecretKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID); - mSecretKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID); - mSecretKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA); - mSecretKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID); - - mAccountsProjectionMap = new HashMap<String, String>(); - mAccountsProjectionMap.put(Accounts._ID, Accounts._ID); - mAccountsProjectionMap.put(Accounts.NAME, Accounts.NAME); - } + mUriMatcher.addURI(AUTHORITY, "key_rings/public/key_id/*", PUBLIC_KEY_RING_BY_KEY_ID); - /** - * This class helps open, create, and upgrade the database file. - */ - private static class DatabaseHelper extends SQLiteOpenHelper { + mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys", PUBLIC_KEY_RING_KEYS); + mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys/#", PUBLIC_KEY_RING_KEY_RANK); - DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } + mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids", PUBLIC_KEY_RING_USER_IDS); + mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids/#", PUBLIC_KEY_RING_USER_ID_RANK); - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + PublicKeys.TABLE_NAME + " (" + - PublicKeys._ID + " " + PublicKeys._ID_type + "," + - PublicKeys.KEY_ID + " " + PublicKeys.KEY_ID_type + ", " + - PublicKeys.KEY_DATA + " " + PublicKeys.KEY_DATA_type + ", " + - PublicKeys.WHO_ID + " " + PublicKeys.WHO_ID_type + ");"); - - db.execSQL("CREATE TABLE " + SecretKeys.TABLE_NAME + " (" + - SecretKeys._ID + " " + SecretKeys._ID_type + "," + - SecretKeys.KEY_ID + " " + SecretKeys.KEY_ID_type + ", " + - SecretKeys.KEY_DATA + " " + SecretKeys.KEY_DATA_type + ", " + - SecretKeys.WHO_ID + " " + SecretKeys.WHO_ID_type + ");"); - - db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" + - Accounts._ID + " " + Accounts._ID_type + "," + - Accounts.NAME + " " + Accounts.NAME_type + ");"); - } + mUriMatcher.addURI(AUTHORITY, "key_rings/public", PUBLIC_KEY_RINGS); + mUriMatcher.addURI(AUTHORITY, "key_rings/public/*", PUBLIC_KEY_RING_ID); - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // TODO: upgrade db if necessary, and do that in a clever way - } + mUriMatcher.addURI(AUTHORITY, "key_rings/secret/key_id/*", SECRET_KEY_RING_BY_KEY_ID); + + mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys", SECRET_KEY_RING_KEYS); + mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys/#", SECRET_KEY_RING_KEY_RANK); + + mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids", SECRET_KEY_RING_USER_IDS); + mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids/#", SECRET_KEY_RING_USER_ID_RANK); + + mUriMatcher.addURI(AUTHORITY, "key_rings/secret", SECRET_KEY_RINGS); + mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*", SECRET_KEY_RING_ID); } @Override public boolean onCreate() { - mdbHelper = new DatabaseHelper(getContext()); + mDb = new Database(getContext()); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + // TODO: implement the others, then use them for the lists SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - - switch (mUriMatcher.match(uri)) { - case PUBLIC_KEYS: { - qb.setTables(PublicKeys.TABLE_NAME); - qb.setProjectionMap(mPublicKeysProjectionMap); + HashMap<String, String> projectionMap = new HashMap<String, String>(); + int match = mUriMatcher.match(uri); + int type; + switch (match) { + case PUBLIC_KEY_RINGS: + case PUBLIC_KEY_RING_ID: + case PUBLIC_KEY_RING_BY_KEY_ID: + case PUBLIC_KEY_RING_KEYS: + case PUBLIC_KEY_RING_KEY_RANK: + case PUBLIC_KEY_RING_USER_IDS: + case PUBLIC_KEY_RING_USER_ID_RANK: + type = Id.database.type_public; break; - } - case PUBLIC_KEY_ID: { - qb.setTables(PublicKeys.TABLE_NAME); - qb.setProjectionMap(mPublicKeysProjectionMap); - qb.appendWhere(PublicKeys._ID + "=" + uri.getPathSegments().get(1)); + case SECRET_KEY_RINGS: + case SECRET_KEY_RING_ID: + case SECRET_KEY_RING_BY_KEY_ID: + case SECRET_KEY_RING_KEYS: + case SECRET_KEY_RING_KEY_RANK: + case SECRET_KEY_RING_USER_IDS: + case SECRET_KEY_RING_USER_ID_RANK: + type = Id.database.type_secret; break; - } - case PUBLIC_KEY_BY_KEY_ID: { - qb.setTables(PublicKeys.TABLE_NAME); - qb.setProjectionMap(mPublicKeysProjectionMap); - qb.appendWhere(PublicKeys.KEY_ID + "=" + uri.getPathSegments().get(2)); - break; + default: { + throw new IllegalArgumentException("Unknown URI " + uri); } + } - case SECRET_KEYS: { - qb.setTables(SecretKeys.TABLE_NAME); - qb.setProjectionMap(mSecretKeysProjectionMap); - break; - } + qb.appendWhere(KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = " + type); + + switch (match) { + case PUBLIC_KEY_RINGS: + case SECRET_KEY_RINGS: { + qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + + "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + + Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + + ") " + + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + + "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + + UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); + + projectionMap.put(MASTER_KEY_ID, + KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID); + projectionMap.put(USER_ID, + UserIds.TABLE_NAME + "." + UserIds.USER_ID); - case SECRET_KEY_ID: { - qb.setTables(SecretKeys.TABLE_NAME); - qb.setProjectionMap(mSecretKeysProjectionMap); - qb.appendWhere(SecretKeys._ID + "=" + uri.getPathSegments().get(1)); break; } - case SECRET_KEY_BY_KEY_ID: { - qb.setTables(SecretKeys.TABLE_NAME); - qb.setProjectionMap(mSecretKeysProjectionMap); - qb.appendWhere(SecretKeys.KEY_ID + "=" + uri.getPathSegments().get(2)); + case PUBLIC_KEY_RING_ID: + case SECRET_KEY_RING_ID: { + qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + + "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + + Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + + ") " + + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + + "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + + UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); + + projectionMap.put(MASTER_KEY_ID, + KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID); + projectionMap.put(USER_ID, + UserIds.TABLE_NAME + "." + UserIds.USER_ID); + + qb.appendWhere(" AND " + + KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); break; } - case ACCOUNTS: { - qb.setTables(Accounts.TABLE_NAME); - qb.setProjectionMap(mAccountsProjectionMap); - break; - } + case SECRET_KEY_RING_BY_KEY_ID: + case PUBLIC_KEY_RING_BY_KEY_ID: { + qb.setTables(Keys.TABLE_NAME + " AS tmp INNER JOIN " + + KeyRings.TABLE_NAME + " ON (" + + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + + "tmp." + Keys.KEY_RING_ID + ")" + + " INNER JOIN " + Keys.TABLE_NAME + " ON " + + "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + + Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + + ") " + + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + + "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + + UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); + + projectionMap.put(MASTER_KEY_ID, + KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID); + projectionMap.put(USER_ID, + UserIds.TABLE_NAME + "." + UserIds.USER_ID); + + qb.appendWhere(" AND tmp." + Keys.KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(3)); - case ACCOUNT_ID: { - qb.setTables(Accounts.TABLE_NAME); - qb.setProjectionMap(mAccountsProjectionMap); - qb.appendWhere(Accounts._ID + "=" + uri.getPathSegments().get(1)); break; } @@ -189,20 +223,20 @@ public class DataProvider extends ContentProvider { } } + qb.setProjectionMap(projectionMap); + // If no sort order is specified use the default String orderBy; if (TextUtils.isEmpty(sortOrder)) { - orderBy = PublicKeys.DEFAULT_SORT_ORDER; + orderBy = null; } else { orderBy = sortOrder; } - // Get the database and run the query - SQLiteDatabase db = mdbHelper.getReadableDatabase(); - Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); + //System.out.println(qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null).replace("WHERE", "WHERE\n")); + Cursor c = qb.query(mDb.db(), projection, selection, selectionArgs, null, null, orderBy); - // Tell the cursor what uri to watch, so it knows when its source data - // changes + // Tell the cursor what uri to watch, so it knows when its source data changes c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @@ -210,278 +244,68 @@ public class DataProvider extends ContentProvider { @Override public String getType(Uri uri) { switch (mUriMatcher.match(uri)) { - case PUBLIC_KEYS: { - return PublicKeys.CONTENT_TYPE; - } + case PUBLIC_KEY_RINGS: + return PUBLIC_KEY_RING_CONTENT_DIR_TYPE; - case PUBLIC_KEY_ID: { - return PublicKeys.CONTENT_ITEM_TYPE; - } + case PUBLIC_KEY_RING_ID: + return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE; - case PUBLIC_KEY_BY_KEY_ID: { - return PublicKeys.CONTENT_ITEM_TYPE; - } + case PUBLIC_KEY_RING_BY_KEY_ID: + return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE; - case SECRET_KEYS: { - return SecretKeys.CONTENT_TYPE; - } + case PUBLIC_KEY_RING_KEYS: + return PUBLIC_KEY_CONTENT_DIR_TYPE; - case SECRET_KEY_ID: { - return SecretKeys.CONTENT_ITEM_TYPE; - } + case PUBLIC_KEY_RING_KEY_RANK: + return PUBLIC_KEY_CONTENT_ITEM_TYPE; - case SECRET_KEY_BY_KEY_ID: { - return SecretKeys.CONTENT_ITEM_TYPE; - } + case PUBLIC_KEY_RING_USER_IDS: + return USER_ID_CONTENT_DIR_TYPE; - case ACCOUNTS: { - return Accounts.CONTENT_TYPE; - } + case PUBLIC_KEY_RING_USER_ID_RANK: + return USER_ID_CONTENT_ITEM_TYPE; - case ACCOUNT_ID: { - return Accounts.CONTENT_ITEM_TYPE; - } + case SECRET_KEY_RINGS: + return SECRET_KEY_RING_CONTENT_DIR_TYPE; - default: { - throw new IllegalArgumentException("Unknown URI " + uri); - } - } - } + case SECRET_KEY_RING_ID: + return SECRET_KEY_RING_CONTENT_ITEM_TYPE; - @Override - public Uri insert(Uri uri, ContentValues initialValues) { - switch (mUriMatcher.match(uri)) { - case PUBLIC_KEYS: { - ContentValues values; - if (initialValues != null) { - values = new ContentValues(initialValues); - } else { - values = new ContentValues(); - } - - if (!values.containsKey(PublicKeys.WHO_ID)) { - values.put(PublicKeys.WHO_ID, ""); - } - - SQLiteDatabase db = mdbHelper.getWritableDatabase(); - long rowId = db.insert(PublicKeys.TABLE_NAME, PublicKeys.WHO_ID, values); - if (rowId > 0) { - Uri transferUri = ContentUris.withAppendedId(PublicKeys.CONTENT_URI, rowId); - getContext().getContentResolver().notifyChange(transferUri, null); - return transferUri; - } - - throw new SQLException("Failed to insert row into " + uri); - } + case SECRET_KEY_RING_BY_KEY_ID: + return SECRET_KEY_RING_CONTENT_ITEM_TYPE; - case SECRET_KEYS: { - ContentValues values; - if (initialValues != null) { - values = new ContentValues(initialValues); - } else { - values = new ContentValues(); - } - - if (!values.containsKey(SecretKeys.WHO_ID)) { - values.put(SecretKeys.WHO_ID, ""); - } - - SQLiteDatabase db = mdbHelper.getWritableDatabase(); - long rowId = db.insert(SecretKeys.TABLE_NAME, SecretKeys.WHO_ID, values); - if (rowId > 0) { - Uri transferUri = ContentUris.withAppendedId(SecretKeys.CONTENT_URI, rowId); - getContext().getContentResolver().notifyChange(transferUri, null); - return transferUri; - } - - throw new SQLException("Failed to insert row into " + uri); - } + case SECRET_KEY_RING_KEYS: + return SECRET_KEY_CONTENT_DIR_TYPE; - case ACCOUNTS: { - ContentValues values; - if (initialValues != null) { - values = new ContentValues(initialValues); - } else { - values = new ContentValues(); - } - - SQLiteDatabase db = mdbHelper.getWritableDatabase(); - long rowId = db.insert(Accounts.TABLE_NAME, null, values); - if (rowId > 0) { - Uri transferUri = ContentUris.withAppendedId(Accounts.CONTENT_URI, rowId); - getContext().getContentResolver().notifyChange(transferUri, null); - return transferUri; - } - - throw new SQLException("Failed to insert row into " + uri); - } + case SECRET_KEY_RING_KEY_RANK: + return SECRET_KEY_CONTENT_ITEM_TYPE; - default: { + case SECRET_KEY_RING_USER_IDS: + return USER_ID_CONTENT_DIR_TYPE; + + case SECRET_KEY_RING_USER_ID_RANK: + return USER_ID_CONTENT_ITEM_TYPE; + + default: throw new IllegalArgumentException("Unknown URI " + uri); - } } } @Override - public int delete(Uri uri, String where, String[] whereArgs) { - SQLiteDatabase db = mdbHelper.getWritableDatabase(); - int count; - switch (mUriMatcher.match(uri)) { - case PUBLIC_KEYS: { - count = db.delete(PublicKeys.TABLE_NAME, where, whereArgs); - break; - } - - case PUBLIC_KEY_ID: { - String publicKeyId = uri.getPathSegments().get(1); - count = db.delete(PublicKeys.TABLE_NAME, - PublicKeys._ID + "=" + publicKeyId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - case PUBLIC_KEY_BY_KEY_ID: { - String publicKeyKeyId = uri.getPathSegments().get(2); - count = db.delete(PublicKeys.TABLE_NAME, - PublicKeys.KEY_ID + "=" + publicKeyKeyId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - case SECRET_KEYS: { - count = db.delete(SecretKeys.TABLE_NAME, where, whereArgs); - break; - } - - case SECRET_KEY_ID: { - String secretKeyId = uri.getPathSegments().get(1); - count = db.delete(SecretKeys.TABLE_NAME, - SecretKeys._ID + "=" + secretKeyId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - case SECRET_KEY_BY_KEY_ID: { - String secretKeyKeyId = uri.getPathSegments().get(2); - count = db.delete(SecretKeys.TABLE_NAME, - SecretKeys.KEY_ID + "=" + secretKeyKeyId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - case ACCOUNTS: { - count = db.delete(Accounts.TABLE_NAME, where, whereArgs); - break; - } - - case ACCOUNT_ID: { - String accountId = uri.getPathSegments().get(1); - count = db.delete(Accounts.TABLE_NAME, - Accounts._ID + "=" + accountId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - default: { - throw new IllegalArgumentException("Unknown URI " + uri); - } - } + public Uri insert(Uri uri, ContentValues initialValues) { + // not supported + return null; + } - getContext().getContentResolver().notifyChange(uri, null); - return count; + @Override + public int delete(Uri uri, String where, String[] whereArgs) { + // not supported + return 0; } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { - SQLiteDatabase db = mdbHelper.getWritableDatabase(); - int count; - switch (mUriMatcher.match(uri)) { - case PUBLIC_KEYS: { - count = db.update(PublicKeys.TABLE_NAME, values, where, whereArgs); - break; - } - - case PUBLIC_KEY_ID: { - String publicKeyId = uri.getPathSegments().get(1); - - count = db.update(PublicKeys.TABLE_NAME, values, - PublicKeys._ID + "=" + publicKeyId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - case PUBLIC_KEY_BY_KEY_ID: { - String publicKeyKeyId = uri.getPathSegments().get(2); - - count = db.update(PublicKeys.TABLE_NAME, values, - PublicKeys.KEY_ID + "=" + publicKeyKeyId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - case SECRET_KEYS: { - count = db.update(SecretKeys.TABLE_NAME, values, where, whereArgs); - break; - } - - case SECRET_KEY_ID: { - String secretKeyId = uri.getPathSegments().get(1); - - count = db.update(SecretKeys.TABLE_NAME, values, - SecretKeys._ID + "=" + secretKeyId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - case SECRET_KEY_BY_KEY_ID: { - String secretKeyKeyId = uri.getPathSegments().get(2); - - count = db.update(SecretKeys.TABLE_NAME, values, - SecretKeys.KEY_ID + "=" + secretKeyKeyId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - case ACCOUNTS: { - count = db.update(Accounts.TABLE_NAME, values, where, whereArgs); - break; - } - - case ACCOUNT_ID: { - String accountId = uri.getPathSegments().get(1); - - count = db.update(Accounts.TABLE_NAME, values, - Accounts._ID + "=" + accountId + - (!TextUtils.isEmpty(where) ? - " AND (" + where + ')' : ""), - whereArgs); - break; - } - - default: { - throw new IllegalArgumentException("Unknown URI " + uri); - } - } - - getContext().getContentResolver().notifyChange(uri, null); - return count; + // not supported + return 0; } } diff --git a/src/org/thialfihar/android/apg/provider/Database.java b/src/org/thialfihar/android/apg/provider/Database.java new file mode 100644 index 000000000..810ebebbf --- /dev/null +++ b/src/org/thialfihar/android/apg/provider/Database.java @@ -0,0 +1,605 @@ +package org.thialfihar.android.apg.provider;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Vector;
+
+import org.bouncycastle2.openpgp.PGPException;
+import org.bouncycastle2.openpgp.PGPPublicKey;
+import org.bouncycastle2.openpgp.PGPPublicKeyRing;
+import org.bouncycastle2.openpgp.PGPSecretKey;
+import org.bouncycastle2.openpgp.PGPSecretKeyRing;
+import org.thialfihar.android.apg.Apg;
+import org.thialfihar.android.apg.Id;
+import org.thialfihar.android.apg.utils.IterableIterator;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+public class Database extends SQLiteOpenHelper {
+ public static class GeneralException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ public GeneralException(String message) {
+ super(message);
+ }
+ }
+
+ private static final String DATABASE_NAME = "apg";
+ private static final int DATABASE_VERSION = 2;
+
+ public static final String AUTHORITY = "org.thialfihar.android.apg.database";
+
+ public static HashMap<String, String> sKeyRingsProjection;
+ public static HashMap<String, String> sKeysProjection;
+ public static HashMap<String, String> sUserIdsProjection;
+
+ private SQLiteDatabase mDb = null;
+ private int mStatus = 0;
+
+ static {
+ sKeyRingsProjection = new HashMap<String, String>();
+ sKeyRingsProjection.put(KeyRings._ID, KeyRings._ID);
+ sKeyRingsProjection.put(KeyRings.MASTER_KEY_ID, KeyRings.MASTER_KEY_ID);
+ sKeyRingsProjection.put(KeyRings.TYPE, KeyRings.TYPE);
+ sKeyRingsProjection.put(KeyRings.WHO_ID, KeyRings.WHO_ID);
+ sKeyRingsProjection.put(KeyRings.KEY_RING_DATA, KeyRings.KEY_RING_DATA);
+
+ sKeysProjection = new HashMap<String, String>();
+ sKeysProjection.put(Keys._ID, Keys._ID);
+ sKeysProjection.put(Keys.KEY_ID, Keys.KEY_ID);
+ sKeysProjection.put(Keys.TYPE, Keys.TYPE);
+ sKeysProjection.put(Keys.IS_MASTER_KEY, Keys.IS_MASTER_KEY);
+ sKeysProjection.put(Keys.ALGORITHM, Keys.ALGORITHM);
+ sKeysProjection.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
+ sKeysProjection.put(Keys.CAN_SIGN, Keys.CAN_SIGN);
+ sKeysProjection.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
+ sKeysProjection.put(Keys.IS_REVOKED, Keys.IS_REVOKED);
+ sKeysProjection.put(Keys.CREATION, Keys.CREATION);
+ sKeysProjection.put(Keys.EXPIRY, Keys.EXPIRY);
+ sKeysProjection.put(Keys.KEY_DATA, Keys.KEY_DATA);
+ sKeysProjection.put(Keys.RANK, Keys.RANK);
+
+ sUserIdsProjection = new HashMap<String, String>();
+ sUserIdsProjection.put(UserIds._ID, UserIds._ID);
+ sUserIdsProjection.put(UserIds.KEY_ID, UserIds.KEY_ID);
+ sUserIdsProjection.put(UserIds.USER_ID, UserIds.USER_ID);
+ sUserIdsProjection.put(UserIds.RANK, UserIds.RANK);
+ }
+
+ public Database(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ // force upgrade to test things
+ //onUpgrade(getWritableDatabase(), 1, 2);
+ mDb = getWritableDatabase();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ mDb.close();
+ super.finalize();
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
+ KeyRings._ID + " " + KeyRings._ID_type + "," +
+ KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
+ KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
+ KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
+ KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
+
+ db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
+ Keys._ID + " " + Keys._ID_type + "," +
+ Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
+ Keys.TYPE + " " + Keys.TYPE_type + ", " +
+ Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
+ Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
+ Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
+ Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
+ Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
+ Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
+ Keys.CREATION + " " + Keys.CREATION_type + ", " +
+ Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
+ Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
+ Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
+ Keys.RANK + " " + Keys.RANK_type + ");");
+
+ db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
+ UserIds._ID + " " + UserIds._ID_type + "," +
+ UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
+ UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
+ UserIds.RANK + " " + UserIds.RANK_type + ");");
+
+ db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" +
+ Accounts._ID + " " + Accounts._ID_type + "," +
+ Accounts.NAME + " " + Accounts.NAME_type + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ mDb = db;
+ for (int version = oldVersion; version < newVersion; ++version) {
+ switch (version) {
+ case 1: { // upgrade 1 to 2
+ db.execSQL("DROP TABLE IF EXISTS " + KeyRings.TABLE_NAME + ";");
+ db.execSQL("DROP TABLE IF EXISTS " + Keys.TABLE_NAME + ";");
+ db.execSQL("DROP TABLE IF EXISTS " + UserIds.TABLE_NAME + ";");
+
+ db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
+ KeyRings._ID + " " + KeyRings._ID_type + "," +
+ KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
+ KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
+ KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
+ KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
+
+ db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
+ Keys._ID + " " + Keys._ID_type + "," +
+ Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
+ Keys.TYPE + " " + Keys.TYPE_type + ", " +
+ Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
+ Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
+ Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
+ Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
+ Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
+ Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
+ Keys.CREATION + " " + Keys.CREATION_type + ", " +
+ Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
+ Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
+ Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
+ Keys.RANK + " " + Keys.RANK_type + ");");
+
+ db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
+ UserIds._ID + " " + UserIds._ID_type + "," +
+ UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
+ UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
+ UserIds.RANK + " " + UserIds.RANK_type + ");");
+
+ Cursor cursor = db.query("public_keys", new String[] { "c_key_data" },
+ null, null, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ byte[] data = cursor.getBlob(0);
+ try {
+ PGPPublicKeyRing keyRing = new PGPPublicKeyRing(data);
+ saveKeyRing(keyRing);
+ } catch (IOException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ } catch (GeneralException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ }
+ } while (cursor.moveToNext());
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ cursor = db.query("secret_keys", new String[]{ "c_key_data" },
+ null, null, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ byte[] data = cursor.getBlob(0);
+ try {
+ PGPSecretKeyRing keyRing = new PGPSecretKeyRing(data);
+ saveKeyRing(keyRing);
+ } catch (IOException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ } catch (PGPException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ } catch (GeneralException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ }
+ } while (cursor.moveToNext());
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ db.execSQL("DROP TABLE IF EXISTS public_keys;");
+ db.execSQL("DROP TABLE IF EXISTS secret_keys;");
+
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+ mDb = null;
+ }
+
+ public int saveKeyRing(PGPPublicKeyRing keyRing) throws IOException, GeneralException {
+ mDb.beginTransaction();
+ ContentValues values = new ContentValues();
+ PGPPublicKey masterKey = keyRing.getPublicKey();
+ long masterKeyId = masterKey.getKeyID();
+
+ values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRings.TYPE, Id.database.type_public);
+ values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
+
+ long rowId = insertOrUpdateKeyRing(values);
+ int returnValue = mStatus;
+
+ if (rowId == -1) {
+ throw new GeneralException("saving public key ring " + masterKeyId + " failed");
+ }
+
+ Vector<Integer> seenIds = new Vector<Integer>();
+ int rank = 0;
+ for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
+ seenIds.add(saveKey(rowId, key, rank));
+ ++rank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(Keys.TABLE_NAME,
+ Keys.KEY_RING_ID + " = ? AND " +
+ Keys._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ mDb.setTransactionSuccessful();
+ mDb.endTransaction();
+ return returnValue;
+ }
+
+ public int saveKeyRing(PGPSecretKeyRing keyRing) throws IOException, GeneralException {
+ mDb.beginTransaction();
+ ContentValues values = new ContentValues();
+ PGPSecretKey masterKey = keyRing.getSecretKey();
+ long masterKeyId = masterKey.getKeyID();
+
+ values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRings.TYPE, Id.database.type_secret);
+ values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
+
+ long rowId = insertOrUpdateKeyRing(values);
+ int returnValue = mStatus;
+
+ if (rowId == -1) {
+ throw new GeneralException("saving secret key ring " + masterKeyId + " failed");
+ }
+
+ Vector<Integer> seenIds = new Vector<Integer>();
+ int rank = 0;
+ for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
+ seenIds.add(saveKey(rowId, key, rank));
+ ++rank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(Keys.TABLE_NAME,
+ Keys.KEY_RING_ID + " = ? AND " +
+ Keys._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ mDb.setTransactionSuccessful();
+ mDb.endTransaction();
+ return returnValue;
+ }
+
+ private int saveKey(long keyRingId, PGPPublicKey key, int rank)
+ throws IOException, GeneralException {
+ ContentValues values = new ContentValues();
+
+ values.put(Keys.KEY_ID, key.getKeyID());
+ values.put(Keys.TYPE, Id.database.type_public);
+ values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
+ values.put(Keys.ALGORITHM, key.getAlgorithm());
+ values.put(Keys.KEY_SIZE, key.getBitStrength());
+ values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
+ values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
+ values.put(Keys.IS_REVOKED, key.isRevoked());
+ values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
+ Date expiryDate = Apg.getExpiryDate(key);
+ if (expiryDate != null) {
+ values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
+ }
+ values.put(Keys.KEY_RING_ID, keyRingId);
+ values.put(Keys.KEY_DATA, key.getEncoded());
+ values.put(Keys.RANK, rank);
+
+ long rowId = insertOrUpdateKey(values);
+
+ if (rowId == -1) {
+ throw new GeneralException("saving public key " + key.getKeyID() + " failed");
+ }
+
+ Vector<Integer> seenIds = new Vector<Integer>();
+ int userIdRank = 0;
+ for (String userId : new IterableIterator<String>(key.getUserIDs())) {
+ seenIds.add(saveUserId(rowId, userId, userIdRank));
+ ++userIdRank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(UserIds.TABLE_NAME,
+ UserIds.KEY_ID + " = ? AND " +
+ UserIds._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ return (int)rowId;
+ }
+
+ private int saveKey(long keyRingId, PGPSecretKey key, int rank)
+ throws IOException, GeneralException {
+ ContentValues values = new ContentValues();
+
+ values.put(Keys.KEY_ID, key.getPublicKey().getKeyID());
+ values.put(Keys.TYPE, Id.database.type_secret);
+ values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
+ values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
+ values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
+ values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
+ values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
+ values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
+ values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
+ Date expiryDate = Apg.getExpiryDate(key);
+ if (expiryDate != null) {
+ values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
+ }
+ values.put(Keys.KEY_RING_ID, keyRingId);
+ values.put(Keys.KEY_DATA, key.getEncoded());
+ values.put(Keys.RANK, rank);
+
+ long rowId = insertOrUpdateKey(values);
+
+ if (rowId == -1) {
+ throw new GeneralException("saving secret key " + key.getPublicKey().getKeyID() + " failed");
+ }
+
+ Vector<Integer> seenIds = new Vector<Integer>();
+ int userIdRank = 0;
+ for (String userId : new IterableIterator<String>(key.getUserIDs())) {
+ seenIds.add(saveUserId(rowId, userId, userIdRank));
+ ++userIdRank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(UserIds.TABLE_NAME,
+ UserIds.KEY_ID + " = ? AND " +
+ UserIds._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ return (int)rowId;
+ }
+
+ private int saveUserId(long keyId, String userId, int rank) throws GeneralException {
+ ContentValues values = new ContentValues();
+
+ values.put(UserIds.KEY_ID, keyId);
+ values.put(UserIds.USER_ID, userId);
+ values.put(UserIds.RANK, rank);
+
+ long rowId = insertOrUpdateUserId(values);
+
+ if (rowId == -1) {
+ throw new GeneralException("saving user id " + userId + " failed");
+ }
+
+ return (int)rowId;
+ }
+
+ private long insertOrUpdateKeyRing(ContentValues values) {
+ Cursor c = mDb.query(KeyRings.TABLE_NAME, new String[] { KeyRings._ID },
+ KeyRings.MASTER_KEY_ID + " = ? AND " + KeyRings.TYPE + " = ?",
+ new String[] {
+ values.getAsString(KeyRings.MASTER_KEY_ID),
+ values.getAsString(KeyRings.TYPE),
+ },
+ null, null, null);
+ long rowId = -1;
+ if (c != null && c.moveToFirst()) {
+ rowId = c.getLong(0);
+ mDb.update(KeyRings.TABLE_NAME, values,
+ KeyRings._ID + " = ?", new String[] { "" + rowId });
+ mStatus = Id.return_value.updated;
+ } else {
+ rowId = mDb.insert(KeyRings.TABLE_NAME, KeyRings.WHO_ID, values);
+ mStatus = Id.return_value.ok;
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return rowId;
+ }
+
+ private long insertOrUpdateKey(ContentValues values) {
+ Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
+ Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
+ new String[] {
+ values.getAsString(Keys.KEY_ID),
+ values.getAsString(Keys.TYPE),
+ },
+ null, null, null);
+ long rowId = -1;
+ if (c != null && c.moveToFirst()) {
+ rowId = c.getLong(0);
+ mDb.update(Keys.TABLE_NAME, values,
+ Keys._ID + " = ?", new String[] { "" + rowId });
+ } else {
+ rowId = mDb.insert(Keys.TABLE_NAME, Keys.KEY_DATA, values);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return rowId;
+ }
+
+ private long insertOrUpdateUserId(ContentValues values) {
+ Cursor c = mDb.query(UserIds.TABLE_NAME, new String[] { UserIds._ID },
+ UserIds.KEY_ID + " = ? AND " + UserIds.USER_ID + " = ?",
+ new String[] {
+ values.getAsString(UserIds.KEY_ID),
+ values.getAsString(UserIds.USER_ID),
+ },
+ null, null, null);
+ long rowId = -1;
+ if (c != null && c.moveToFirst()) {
+ rowId = c.getLong(0);
+ mDb.update(UserIds.TABLE_NAME, values,
+ UserIds._ID + " = ?", new String[] { "" + rowId });
+ } else {
+ rowId = mDb.insert(UserIds.TABLE_NAME, UserIds.USER_ID, values);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return rowId;
+ }
+
+ public Object getKeyRing(int keyRingId) {
+ Cursor c = mDb.query(KeyRings.TABLE_NAME,
+ new String[] { KeyRings.KEY_RING_DATA, KeyRings.TYPE },
+ KeyRings._ID + " = ?",
+ new String[] {
+ "" + keyRingId,
+ },
+ null, null, null);
+ byte[] data = null;
+ Object keyRing = null;
+ if (c != null && c.moveToFirst()) {
+ data = c.getBlob(0);
+ if (data != null) {
+ try {
+ if (c.getInt(1) == Id.database.type_public) {
+ keyRing = new PGPPublicKeyRing(data);
+ } else {
+ keyRing = new PGPSecretKeyRing(data);
+ }
+ } catch (IOException e) {
+ // can't load it, then
+ } catch (PGPException e) {
+ // can't load it, then
+ }
+ }
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return keyRing;
+ }
+
+ public byte[] getKeyRingDataFromKeyId(int type, long keyId) {
+ Cursor c = mDb.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ")",
+ new String[] { KeyRings.TABLE_NAME + "." + KeyRings.KEY_RING_DATA },
+ Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] {
+ "" + keyId,
+ "" + type,
+ },
+ null, null, null);
+
+ byte[] data = null;
+ if (c != null && c.moveToFirst()) {
+ data = c.getBlob(0);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return data;
+ }
+
+ public byte[] getKeyDataFromKeyId(int type, long keyId) {
+ Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys.KEY_DATA },
+ Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
+ new String[] {
+ "" + keyId,
+ "" + type,
+ },
+ null, null, null);
+ byte[] data = null;
+ if (c != null && c.moveToFirst()) {
+ data = c.getBlob(0);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return data;
+ }
+
+ public void deleteKeyRing(int keyRingId) {
+ mDb.beginTransaction();
+ mDb.delete(KeyRings.TABLE_NAME,
+ KeyRings._ID + " = ?", new String[] { "" + keyRingId });
+
+ Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
+ Keys.KEY_RING_ID + " = ?",
+ new String[] {
+ "" + keyRingId,
+ },
+ null, null, null);
+ if (c != null && c.moveToFirst()) {
+ do {
+ int keyId = c.getInt(0);
+ deleteKey(keyId);
+ } while (c.moveToNext());
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ mDb.setTransactionSuccessful();
+ mDb.endTransaction();
+ }
+
+ private void deleteKey(int keyId) {
+ mDb.delete(Keys.TABLE_NAME,
+ Keys._ID + " = ?", new String[] { "" + keyId });
+
+ mDb.delete(UserIds.TABLE_NAME,
+ UserIds.KEY_ID + " = ?", new String[] { "" + keyId });
+ }
+
+ public SQLiteDatabase db() {
+ return mDb;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/provider/Accounts1.java b/src/org/thialfihar/android/apg/provider/KeyRings.java index 9009e4598..a4eae6695 100644 --- a/src/org/thialfihar/android/apg/provider/Accounts1.java +++ b/src/org/thialfihar/android/apg/provider/KeyRings.java @@ -1,36 +1,33 @@ -/* - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -import android.net.Uri; -import android.provider.BaseColumns; - -class Accounts1 implements BaseColumns { - public static final String TABLE_NAME = "accounts"; - - public static final String _ID_type = "INTEGER PRIMARY KEY"; - public static final String NAME = "c_name"; - public static final String NAME_type = "TEXT"; - - public static final Uri CONTENT_URI = - Uri.parse("content://" + DataProvider.AUTHORITY + "/accounts"); - public static final String CONTENT_TYPE = - "vnd.android.cursor.dir/vnd.thialfihar.apg.account"; - public static final String CONTENT_ITEM_TYPE = - "vnd.android.cursor.item/vnd.thialfihar.apg.account"; - public static final String DEFAULT_SORT_ORDER = _ID + " DESC"; -}
\ No newline at end of file +/*
+ * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.thialfihar.android.apg.provider;
+
+import android.provider.BaseColumns;
+
+public class KeyRings implements BaseColumns {
+ public static final String TABLE_NAME = "key_rings";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String MASTER_KEY_ID = "c_master_key_id";
+ public static final String MASTER_KEY_ID_type = "INT64";
+ public static final String TYPE = "c_type";
+ public static final String TYPE_type = "INTEGER";
+ public static final String WHO_ID = "c_who_id";
+ public static final String WHO_ID_type = "INTEGER";
+ public static final String KEY_RING_DATA = "c_key_ring_data";
+ public static final String KEY_RING_DATA_type = "BLOB";
+}
diff --git a/src/org/thialfihar/android/apg/provider/Keys.java b/src/org/thialfihar/android/apg/provider/Keys.java new file mode 100644 index 000000000..618c5e920 --- /dev/null +++ b/src/org/thialfihar/android/apg/provider/Keys.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.provider; + +import android.provider.BaseColumns; + +public class Keys implements BaseColumns { + public static final String TABLE_NAME = "keys"; + + public static final String _ID_type = "INTEGER PRIMARY KEY"; + public static final String KEY_ID = "c_key_id"; + public static final String KEY_ID_type = "INT64"; + public static final String TYPE = "c_type"; + public static final String TYPE_type = "INTEGER"; + public static final String IS_MASTER_KEY = "c_is_master_key"; + public static final String IS_MASTER_KEY_type = "INTEGER"; + public static final String ALGORITHM = "c_algorithm"; + public static final String ALGORITHM_type = "INTEGER"; + public static final String KEY_SIZE = "c_key_size"; + public static final String KEY_SIZE_type = "INTEGER"; + public static final String CAN_SIGN = "c_can_sign"; + public static final String CAN_SIGN_type = "INTEGER"; + public static final String CAN_ENCRYPT = "c_can_encrypt"; + public static final String CAN_ENCRYPT_type = "INTEGER"; + public static final String IS_REVOKED = "c_is_revoked"; + public static final String IS_REVOKED_type = "INTEGER"; + public static final String CREATION = "c_creation"; + public static final String CREATION_type = "INTEGER"; + public static final String EXPIRY = "c_expiry"; + public static final String EXPIRY_type = "INTEGER"; + public static final String KEY_RING_ID = "c_key_ring_id"; + public static final String KEY_RING_ID_type = "INTEGER"; + public static final String KEY_DATA = "c_key_data"; + public static final String KEY_DATA_type = "BLOB"; + public static final String RANK = "c_key_data"; + public static final String RANK_type = "INTEGER"; +} diff --git a/src/org/thialfihar/android/apg/provider/PublicKeys.java b/src/org/thialfihar/android/apg/provider/PublicKeys.java deleted file mode 100644 index f15841fa5..000000000 --- a/src/org/thialfihar/android/apg/provider/PublicKeys.java +++ /dev/null @@ -1,22 +0,0 @@ -/*
- * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.thialfihar.android.apg.provider;
-
-public class PublicKeys extends PublicKeys1 {
- private PublicKeys() {
- }
-}
\ No newline at end of file diff --git a/src/org/thialfihar/android/apg/provider/PublicKeys1.java b/src/org/thialfihar/android/apg/provider/PublicKeys1.java deleted file mode 100644 index d12a67a17..000000000 --- a/src/org/thialfihar/android/apg/provider/PublicKeys1.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -import android.net.Uri; -import android.provider.BaseColumns; - -class PublicKeys1 implements BaseColumns { - public static final String TABLE_NAME = "public_keys"; - - public static final String _ID_type = "INTEGER PRIMARY KEY"; - public static final String KEY_ID = "c_key_id"; - public static final String KEY_ID_type = "INT64"; - public static final String KEY_DATA = "c_key_data"; - public static final String KEY_DATA_type = "BLOB"; - public static final String WHO_ID = "c_who_id"; - public static final String WHO_ID_type = "INTEGER"; - - public static final Uri CONTENT_URI = - Uri.parse("content://" + DataProvider.AUTHORITY + "/public_keys"); - public static final Uri CONTENT_URI_BY_KEY_ID = - Uri.parse("content://" + DataProvider.AUTHORITY + "/public_keys/key_id"); - public static final String CONTENT_TYPE = - "vnd.android.cursor.dir/vnd.thialfihar.apg.public_key"; - public static final String CONTENT_ITEM_TYPE = - "vnd.android.cursor.item/vnd.thialfihar.apg.public_key"; - public static final String DEFAULT_SORT_ORDER = _ID + " DESC"; -}
\ No newline at end of file diff --git a/src/org/thialfihar/android/apg/provider/SecretKeys1.java b/src/org/thialfihar/android/apg/provider/SecretKeys1.java deleted file mode 100644 index 3ca405f70..000000000 --- a/src/org/thialfihar/android/apg/provider/SecretKeys1.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -import android.net.Uri; -import android.provider.BaseColumns; - -class SecretKeys1 implements BaseColumns { - public static final String TABLE_NAME = "secret_keys"; - - public static final String _ID_type = "INTEGER PRIMARY KEY"; - public static final String KEY_ID = "c_key_id"; - public static final String KEY_ID_type = "INT64"; - public static final String KEY_DATA = "c_key_data"; - public static final String KEY_DATA_type = "BLOB"; - public static final String WHO_ID = "c_who_id"; - public static final String WHO_ID_type = "INTEGER"; - - public static final Uri CONTENT_URI = - Uri.parse("content://" + DataProvider.AUTHORITY + "/secret_keys"); - public static final Uri CONTENT_URI_BY_KEY_ID = - Uri.parse("content://" + DataProvider.AUTHORITY + "/secret_keys/key_id"); - public static final String CONTENT_TYPE = - "vnd.android.cursor.dir/vnd.thialfihar.apg.secret_key"; - public static final String CONTENT_ITEM_TYPE = - "vnd.android.cursor.item/vnd.thialfihar.apg.secret_key"; - public static final String DEFAULT_SORT_ORDER = _ID + " DESC"; -}
\ No newline at end of file diff --git a/src/org/thialfihar/android/apg/provider/SecretKeys.java b/src/org/thialfihar/android/apg/provider/UserIds.java index d31f306ae..2b1162beb 100644 --- a/src/org/thialfihar/android/apg/provider/SecretKeys.java +++ b/src/org/thialfihar/android/apg/provider/UserIds.java @@ -16,7 +16,16 @@ package org.thialfihar.android.apg.provider;
-public class SecretKeys extends SecretKeys1 {
- private SecretKeys() {
- }
-}
\ No newline at end of file +import android.provider.BaseColumns;
+
+public class UserIds implements BaseColumns {
+ public static final String TABLE_NAME = "user_ids";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String KEY_ID = "c_key_id";
+ public static final String KEY_ID_type = "INTEGER";
+ public static final String USER_ID = "c_user_id";
+ public static final String USER_ID_type = "TEXT";
+ public static final String RANK = "c_rank";
+ public static final String RANK_type = "INTEGER";
+}
diff --git a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java index bc38fba4c..044da2db2 100644 --- a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java +++ b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java @@ -233,7 +233,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { }
public GregorianCalendar getExpiryDate() {
- return mExpiryDate;
+ return mExpiryDate;
}
public int getUsage() {
diff --git a/src/org/thialfihar/android/apg/ui/widget/SectionView.java b/src/org/thialfihar/android/apg/ui/widget/SectionView.java index cc1410c26..e5dcd4aca 100644 --- a/src/org/thialfihar/android/apg/ui/widget/SectionView.java +++ b/src/org/thialfihar/android/apg/ui/widget/SectionView.java @@ -76,7 +76,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor }
}
- String error = data.getString("error");
+ String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) {
Toast.makeText(getContext(),
getContext().getString(R.string.errorMessage, error),
@@ -310,24 +310,24 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor mNewKeySize, passPhrase,
masterKey);
} catch (NoSuchProviderException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (NoSuchAlgorithmException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (PGPException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (InvalidParameterException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (InvalidAlgorithmParameterException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (Apg.GeneralException e) {
- error = e.getMessage();
+ error = "" + e;
}
Message message = new Message();
Bundle data = new Bundle();
data.putBoolean("closeProgressDialog", true);
if (error != null) {
- data.putString("error", error);
+ data.putString(Apg.EXTRA_ERROR, error);
} else {
data.putBoolean("gotNewKey", true);
}
|