aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/layout/filter_info.xml76
-rw-r--r--res/layout/key_list_child_item_master_key.xml160
-rw-r--r--res/layout/key_list_child_item_user_id.xml66
-rw-r--r--res/values-da/strings.xml516
-rw-r--r--src/org/thialfihar/android/apg/Apg.java3794
-rw-r--r--src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java224
-rw-r--r--src/org/thialfihar/android/apg/BaseActivity.java762
-rw-r--r--src/org/thialfihar/android/apg/CachedPassPhrase.java90
-rw-r--r--src/org/thialfihar/android/apg/Constants.java70
-rw-r--r--src/org/thialfihar/android/apg/DataDestination.java158
-rw-r--r--src/org/thialfihar/android/apg/DataSource.java176
-rw-r--r--src/org/thialfihar/android/apg/FileDialog.java236
-rw-r--r--src/org/thialfihar/android/apg/GeneralActivity.java354
-rw-r--r--src/org/thialfihar/android/apg/Id.java310
-rw-r--r--src/org/thialfihar/android/apg/InputData.java50
-rw-r--r--src/org/thialfihar/android/apg/IntegerListPreference.java190
-rw-r--r--src/org/thialfihar/android/apg/KeyListActivity.java1524
-rw-r--r--src/org/thialfihar/android/apg/MailListActivity.java436
-rw-r--r--src/org/thialfihar/android/apg/PositionAwareInputStream.java134
-rw-r--r--src/org/thialfihar/android/apg/PreferencesActivity.java360
-rw-r--r--src/org/thialfihar/android/apg/Primes.java370
-rw-r--r--src/org/thialfihar/android/apg/ProgressDialogUpdater.java46
-rw-r--r--src/org/thialfihar/android/apg/PublicKeyListActivity.java124
-rw-r--r--src/org/thialfihar/android/apg/SecretKeyListActivity.java360
-rw-r--r--src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java450
-rw-r--r--src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java348
-rw-r--r--src/org/thialfihar/android/apg/Service.java156
-rw-r--r--src/org/thialfihar/android/apg/provider/Accounts.java54
-rw-r--r--src/org/thialfihar/android/apg/provider/Database.java1210
-rw-r--r--src/org/thialfihar/android/apg/provider/KeyRings.java66
-rw-r--r--src/org/thialfihar/android/apg/provider/UserIds.java62
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/Editor.java50
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/KeyEditor.java484
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/SectionView.java674
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java388
-rw-r--r--src/org/thialfihar/android/apg/utils/Choice.java88
-rw-r--r--src/org/thialfihar/android/apg/utils/IterableIterator.java62
37 files changed, 7339 insertions, 7339 deletions
diff --git a/res/layout/filter_info.xml b/res/layout/filter_info.xml
index 2270682f5..9fef6548e 100644
--- a/res/layout/filter_info.xml
+++ b/res/layout/filter_info.xml
@@ -1,38 +1,38 @@
-<?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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="5dip"
- android:orientation="horizontal">
-
- <TextView
- android:id="@+id/filterInfo"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:ellipsize="end"/>
-
- <Button
- android:id="@+id/btn_clear"
- android:text="@string/btn_clearFilter"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
-</LinearLayout>
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="5dip"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/filterInfo"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="end"/>
+
+ <Button
+ android:id="@+id/btn_clear"
+ android:text="@string/btn_clearFilter"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
diff --git a/res/layout/key_list_child_item_master_key.xml b/res/layout/key_list_child_item_master_key.xml
index 790fd42f4..998ba256f 100644
--- a/res/layout/key_list_child_item_master_key.xml
+++ b/res/layout/key_list_child_item_master_key.xml
@@ -1,80 +1,80 @@
-<?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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:singleLine="true"
- android:paddingLeft="10dip"
- android:layout_marginRight="?android:attr/scrollbarSize"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:layout_width="fill_parent">
-
- <LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:orientation="horizontal"
- android:paddingRight="3dip">
-
- <ImageView
- android:id="@+id/ic_masterKey"
- android:src="@drawable/key_small"
- android:paddingRight="6dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"/>
-
- <TextView
- android:id="@+id/keyId"
- android:text="Key ID"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="5dip"
- android:typeface="monospace"/>
-
- <TextView
- android:id="@+id/keyDetails"
- android:text="(RSA, 1024bit)"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <LinearLayout
- android:gravity="right"
- android:orientation="horizontal"
- android:paddingBottom="2dip"
- android:paddingTop="2dip"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_gravity="center_vertical">
-
- <ImageView
- android:id="@+id/ic_encryptKey"
- android:src="@drawable/encrypted_small"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <ImageView
- android:id="@+id/ic_signKey"
- android:src="@drawable/signed_small"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- </LinearLayout>
-
- </LinearLayout>
-
-</LinearLayout>
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:singleLine="true"
+ android:paddingLeft="10dip"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:layout_width="fill_parent">
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:orientation="horizontal"
+ android:paddingRight="3dip">
+
+ <ImageView
+ android:id="@+id/ic_masterKey"
+ android:src="@drawable/key_small"
+ android:paddingRight="6dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ <TextView
+ android:id="@+id/keyId"
+ android:text="Key ID"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:typeface="monospace"/>
+
+ <TextView
+ android:id="@+id/keyDetails"
+ android:text="(RSA, 1024bit)"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <LinearLayout
+ android:gravity="right"
+ android:orientation="horizontal"
+ android:paddingBottom="2dip"
+ android:paddingTop="2dip"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical">
+
+ <ImageView
+ android:id="@+id/ic_encryptKey"
+ android:src="@drawable/encrypted_small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <ImageView
+ android:id="@+id/ic_signKey"
+ android:src="@drawable/signed_small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/key_list_child_item_user_id.xml b/res/layout/key_list_child_item_user_id.xml
index 3226dd58a..598dcef22 100644
--- a/res/layout/key_list_child_item_user_id.xml
+++ b/res/layout/key_list_child_item_user_id.xml
@@ -1,33 +1,33 @@
-<?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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingLeft="40dip"
- android:layout_marginRight="?android:attr/scrollbarSize"
- android:singleLine="true"
- android:layout_width="fill_parent"
- android:layout_height="?android:attr/listPreferredItemHeight">
-
- <TextView
- android:id="@+id/userId"
- android:text="User ID"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="3dip"/>
-
-</LinearLayout>
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingLeft="40dip"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:singleLine="true"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight">
+
+ <TextView
+ android:id="@+id/userId"
+ android:text="User ID"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="3dip"/>
+
+</LinearLayout>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index dee065a50..0fa617ebc 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -1,258 +1,258 @@
-<?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">Mail Inbox</string>
- <string name="title_managePublicKeys">Administrér Offentlige Nøgler</string>
- <string name="title_manageSecretKeys">Administrér Private Nøgler</string>
- <string name="title_selectRecipients">Vælg Modtagere</string>
- <string name="title_selectSignature">Vælg Signatur</string>
- <string name="title_encrypt">Kryptér</string>
- <string name="title_decrypt">Afkryptér</string>
- <string name="title_authentification">Autentificering</string>
- <string name="title_createKey">Opret Nøgle</string>
- <string name="title_editKey">Redigér Nøgle</string>
- <string name="title_preferences">Indstillinger</string>
- <string name="title_changePassPhrase">Skift Kodeord</string>
- <string name="title_setPassPhrase">Sæt Kodeord</string>
- <string name="title_sendEmail">"Send Mail..."</string>
- <string name="title_encryptToFile">Kryptér Til Fil</string>
- <string name="title_decryptToFile">Afkryptér Til Fil</string>
- <string name="title_addAccount">Tilføj Konto</string>
- <string name="title_importKeys">Importér Nøgle</string>
- <string name="title_exportKey">Eksportér Nøgle</string>
- <string name="title_exportKeys">Eksportér Nøgler</string>
- <string name="title_keyNotFound">Ingen Nøgle Matchede</string>
-
- <!-- section_lowerCase: capitalized words, no punctuation -->
- <string name="section_userIds">Bruger ID</string>
- <string name="section_keys">Nøgler</string>
- <string name="section_general">Generelt</string>
- <string name="section_defaults">Standard Indstillinger</string>
-
- <!-- btn_lowerCase: capitalized words, no punctuation -->
- <string name="btn_encryptToClipboard">Kryptér Til Clipboard</string>
- <string name="btn_send">Kryptér Og Mail</string>
- <string name="btn_encrypt">Kryptér</string>
- <string name="btn_decrypt">Afkryptér</string>
- <string name="btn_verify">Bekræft</string>
- <string name="btn_selectEncryptKeys">Vælg Modtagere</string>
- <string name="btn_reply">Svar</string>
- <string name="btn_encryptMessage">Kryptér Besked</string>
- <string name="btn_decryptMessage">Afkryptér Besked</string>
- <string name="btn_encryptFile">Kryptér Fil</string>
- <string name="btn_decryptFile">Afkryptér Fil</string>
- <string name="btn_save">Gem</string>
- <string name="btn_doNotSave">Fortryd</string>
- <string name="btn_delete">Slet</string>
- <string name="btn_noDate">Ingen</string>
- <string name="btn_clearFilter">Ryd Filter</string>
-
- <!-- menu_lowerCase: capitalized words, no punctuation -->
- <string name="menu_about">Om APG</string>
- <string name="menu_addAccount">Tilføj GMail Konto</string>
- <string name="menu_deleteAccount">Slet Konto</string>
- <string name="menu_managePublicKeys">Administrér Offentlige Nøgler</string>
- <string name="menu_manageSecretKeys">Administrér Private Nøgler</string>
- <string name="menu_preferences">Indstillinger</string>
- <string name="menu_changePassPhrase">Skift Kodeord</string>
- <string name="menu_setPassPhrase">Sæt Kodeord</string>
- <string name="menu_importKeys">Importér Nøgler</string>
- <string name="menu_exportKeys">Eksportér Nøgler</string>
- <string name="menu_exportKey">Eksportér Nøgle</string>
- <string name="menu_deleteKey">Slet Nøgle</string>
- <string name="menu_createKey">Opret Nøgle</string>
- <string name="menu_editKey">Redigér Nøgle</string>
- <string name="menu_search">Søg</string>
-
- <!-- label_lowerCase: capitalized words, no punctuation -->
- <string name="label_sign">Signér</string>
- <string name="label_message">Besked</string>
- <string name="label_file">Fil</string>
- <string name="label_passPhrase">Kodeord</string>
- <string name="label_passPhraseAgain">Igen</string>
- <string name="label_algorithm">Algoritme</string>
- <string name="label_asciiArmour">ASCII Rustning</string>
- <string name="label_selectPublicKeys">Offentlig(e) Nøgle(r)</string>
- <string name="label_deleteAfterEncryption">Slet Efter Kryptering</string>
- <string name="label_deleteAfterDecryption">Slet Efter Afkryptering</string>
- <string name="label_encryptionAlgorithm">Krypteringsalgoritme</string>
- <string name="label_hashAlgorithm">Hash-algoritme</string>
- <string name="label_asymmetric">Offentlig Nøgle</string>
- <string name="label_symmetric">Kodeord</string>
- <string name="label_passPhraseCacheTtl">Kodeords Cache</string>
- <string name="label_messageCompression">Besked Komprimering</string>
- <string name="label_fileCompression">Fil Komprimering</string>
-
- <string name="noKeysSelected">Vælg</string>
- <string name="oneKeySelected">1 Valgt</string>
- <string name="nKeysSelected">Valgt</string>
- <string name="unknownUserId">&lt;ukendt&gt;</string>
- <string name="none">&lt;ingen&gt;</string>
- <string name="noKey">&lt;ingen nøgle&gt;</string>
- <string name="noDate">-</string>
- <string name="noExpiry">&lt;udløber ikke&gt;</string>
- <string name="unknownStatus"></string>
- <string name="canEncrypt">kan kryptere</string>
- <string name="canSign">kan signere</string>
- <string name="expired">udløbet</string>
- <string name="notValid">ugyldig</string>
-
- <!-- choice_lowerCase: capitalized firwst word, no punctuation -->
- <string name="choice_none">Ingen</string>
- <string name="choice_signOnly">Kun signering</string>
- <string name="choice_encryptOnly">Kun kryptering</string>
- <string name="choice_signAndEncrypt">Signér og kryptér</string>
- <string name="choice_15secs">15 secs</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">Indtil slut</string>
-
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
-
- <string name="filemanager_titleOpen">Åbn...</string>
- <string name="filemanager_titleSave">Gem Som...</string>
- <string name="filemanager_titleEncrypt">Vælg Fil Som Skal Krypteres...</string>
- <string name="filemanager_titleDecrypt">Vælg Fil Som Skal Afkrypteres...</string>
- <string name="filemanager_btnOpen">Åbn</string>
- <string name="filemanager_btnSave">Gem</string>
-
- <string name="warning">Advarsel</string>
- <string name="error">Fejl</string>
- <string name="warningMessage">Advarsel: %s</string>
- <string name="errorMessage">Fejl: %s</string>
-
- <!-- sentences -->
- <string name="wrongPassPhrase">Forkert kodeord.</string>
- <string name="usingClipboardContent">Bruger clipboardets indhold.</string>
- <string name="keySaved">Nøgle gemt.</string>
- <string name="setAPassPhrase">Sæt et kodeord via Indstillinger først.</string>
- <string name="noFilemanagerInstalled">Der er ikke installeret en kompatibel fil håndtering.</string>
- <string name="passPhrasesDoNotMatch">Kodeordet matchede ikke.</string>
- <string name="passPhraseMustNotBeEmpty">Et tomt kodeord er ikke tilladt.</string>
- <string name="passPhraseForSymmetricEncryption">Kodeord til symmetrisk kryptering:</string>
- <string name="passPhraseFor">Kodeord til %s:</string>
- <string name="fileDeleteConfirmation">Er du sikker på at du vil slette \n%s?</string>
- <string name="fileDeleteSuccessful">Filen er slettet.</string>
- <string name="noFileSelected">Vælg en fil først.</string>
- <string name="decryptionSuccessful">Afkryptering lykkedes.</string>
- <string name="encryptionSuccessful">Kryptering lykkedes.</string>
- <string name="encryptionToClipboardSuccessful">Kryptering til clipboard lykkedes.</string>
- <string name="enterPassPhraseTwice">Tast kodeordet to gange.</string>
- <string name="selectEncryptionKey">Vælg mindst én krypteringsnøgle.</string>
- <string name="selectEncryptionOrSignatureKey">Vælg mindst én krypteringsnøgle eller signaturnøgle.</string>
- <string name="specifyFileToEncryptTo">Angiv navnet på filen der skal krypteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.</string>
- <string name="specifyFileToDecryptTo">Angiv navnet på filen der skal afkrypteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.</string>
- <string name="specifyGoogleMailAccount">Angiv den Google Mail konto du ønsker at tilføje.</string>
- <string name="specifyFileToImportFrom">Angiv hvilken fil du ønsker at importere nøgler fra. (.asc eller .gpg)</string>
- <string name="specifyFileToExportTo">Angiv hvilken fil der skal eksporteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.</string>
- <string name="specifyFileToExportSecretKeysTo">Angiv hvilken fil der skal eksporteres til.\nADVARSEL! Du er ved at eksportere PRIVATE nøgler.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.</string>
- <string name="keyDeletionConfirmation">Ønsker du at slette \'%s\'-nøglen?\nDu kan ikke fortryde denne handling!</string>
- <string name="secretKeyDeletionConfirmation">Ønsker du at slette den PRIVATE nøgle, \'%s\'?\nDu kan ikke fortryde denne handling!</string>
- <string name="keysAddedAndUpdated">Tilføjelse af %s nøgle(r) og opdatering af %s nøgle(r) lykkedes."</string>
- <string name="keysAdded">Tilføjelse af %s nøgle(r) lykkedes.</string>
- <string name="keysUpdated">Opdatring af %s nøgle(r) lykkedes.</string>
- <string name="noKeysAddedOrUpdated">Ingen nøgler blev tilføjet eller opdateret.</string>
- <string name="keyExported">Eksport af 1 nøgle lykkedes.</string>
- <string name="keysExported">Eksport af %s nøgler lykkedes.</string>
- <string name="noKeysExported">Ingen nøgler blev eksporteret.</string>
- <string name="keyCreationElGamalInfo">NB: Kun undernøgler understøtter ElGamal, og for ELGamal vil den nærmeste nøglestørrelse - 1536, 2048, 3072, 4096, eller 8192 - blive brugt.</string>
- <string name="keyNotFound">Kunne ikke finde %08X nøglen.</string>
-
- <!-- error_lowerCase: phrases, no punctuation, all lowercase,
- they will be put after "errorMessage", e.g. "Error: file not found" -->
- <string name="error_fileDeleteFailed">\'%s\' kunne ikke slettes</string>
- <string name="error_fileNotFound">filen findes ikke</string>
- <string name="error_noSecretKeyFound">ingen egnet privat nøgle fundet</string>
- <string name="error_noKnownEncryptionFound">ingen kendt krypterings måde fundet</string>
- <string name="error_externalStorageNotReady">ekstern storage er ikke parat</string>
- <string name="error_accountNotFound">\'%s\'-kontoen blev ikke fundet</string>
- <string name="error_addingAccountFailed">\'%s\'-kontoen kunne ikke tilføjes</string>
- <string name="error_invalidEmail">ugyldig email \'%s\'</string>
- <string name="error_keySizeMinimum512bit">nøgle størrelsen skal være på mindst 512bit</string>
- <string name="error_masterKeyMustNotBeElGamal">hoved nøglen kan ikke være en ElGamal nøgle</string>
- <string name="error_unknownAlgorithmChoice">ukendt algoritme valg</string>
- <string name="error_userIdNeedsAName">angiv et navn</string>
- <string name="error_userIdNeedsAnEmailAddress">angiv en email adresse</string>
- <string name="error_keyNeedsAUserId">der skal bruges mindst ét bruger-id</string>
- <string name="error_mainUserIdMustNotBeEmpty">hovedbruger-id kan ikke være tomt</string>
- <string name="error_keyNeedsMasterKey">der skal mindst bruges en hovednøgle</string>
- <string name="error_expiryMustComeAfterCreation">udløbsdatoen skal være senere end oprettelsesdatoen</string>
- <string name="error_noEncryptionKeysOrPassPhrase">hverken krypteringsnøgler eller kodeord modtaget</string>
- <string name="error_signatureFailed">signering mislykkedes</string>
- <string name="error_noSignaturePassPhrase">intet kodeord modtaget</string>
- <string name="error_noSignatureKey">ingen signatur nøgle modtaget</string>
- <string name="error_invalidData">ikke gyldig krypteringsdata</string>
- <string name="error_corruptData">beskadiget data</string>
- <string name="error_noSymmetricEncryptionPacket">kunne ikke finde en pakke med symmetrisk kryptering</string>
- <string name="error_wrongPassPhrase">forkert kodeord</string>
- <string name="error_savingKeys">der opstod en fejl da nøglen eller nøglerne skulle gemmes</string>
-
- <!-- progress_lowerCase: lowercase, phrases, usually ending in '...' -->
- <string name="progress_done">færdig.</string>
- <string name="progress_initializing">starter op...</string>
- <string name="progress_saving">gemmer...</string>
- <string name="progress_importing">importerer...</string>
- <string name="progress_exporting">eksporterer...</string>
- <string name="progress_generating">genererer nøgle, dette kan tage et stykke tid...</string>
- <string name="progress_buildingKey">opbygger nøgle...</string>
- <string name="progress_preparingMasterKey">forbereder hovednøgle...</string>
- <string name="progress_certifyingMasterKey">bekræfter hovednøgle...</string>
- <string name="progress_buildingMasterKeyRing">bygger hovednøglering...</string>
- <string name="progress_addingSubKeys">tilføjer undernøgler...</string>
- <string name="progress_savingKeyRing">gemmer nøglering...</string>
- <string name="progress_importingSecretKeys">importerer private nøgler...</string>
- <string name="progress_importingPublicKeys">importerer offentlige nøgler...</string>
- <string name="progress_reloadingKeys">genloader nøgler...</string>
- <string name="progress_exportingKey">eksporterer nøgle...</string>
- <string name="progress_exportingKeys">eksporterer nøgler...</string>
- <string name="progress_extractingSignatureKey">henter signatur nøgle...</string>
- <string name="progress_extractingKey">henter nøgle...</string>
- <string name="progress_preparingStreams">forbereder streams...</string>
- <string name="progress_encrypting">krypterer data...</string>
- <string name="progress_decrypting">afkrypterer data...</string>
- <string name="progress_preparingSignature">forbereder signatur...</string>
- <string name="progress_generatingSignature">skaber signatur...</string>
- <string name="progress_processingSignature">behandler signatur...</string>
- <string name="progress_verifyingSignature">bekræfter signatur...</string>
- <string name="progress_signing">signerer...</string>
- <string name="progress_readingData">læser data...</string>
- <string name="progress_findingKey">søger nøgle...</string>
- <string name="progress_decompressingData">afkomprimerer data...</string>
- <string name="progress_verifyingIntegrity">bekræfter integritet...</string>
-
- <!-- permission strings -->
- <string name="permission_read_key_details_label">Læs nøgle detaljer fra APG.</string>
- <string name="permission_read_key_details_description">Læs om offentlige og private nøgler gemt i APG, f. eks. nøgle ID og bruger ID. Selve nøglerne kan IKKE læses.</string>
-
- <!-- action strings -->
- <string name="action_encrypt">Kryptér</string>
- <string name="action_decrypt">Afkryptér</string>
- <string name="action_importPublic">Importér Offentlige Nøgler</string>
- <string name="action_importSecret">Importér Private Nøgler</string>
-
- <string name="hint_publicKeys">Søg I Offentlige Nøgler</string>
- <string name="hint_secretKeys">Søg I Private Nøgler</string>
- <string name="filterInfo">Filter: \"%s\"</string>
-
-</resources>
+<?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">Mail Inbox</string>
+ <string name="title_managePublicKeys">Administrér Offentlige Nøgler</string>
+ <string name="title_manageSecretKeys">Administrér Private Nøgler</string>
+ <string name="title_selectRecipients">Vælg Modtagere</string>
+ <string name="title_selectSignature">Vælg Signatur</string>
+ <string name="title_encrypt">Kryptér</string>
+ <string name="title_decrypt">Afkryptér</string>
+ <string name="title_authentification">Autentificering</string>
+ <string name="title_createKey">Opret Nøgle</string>
+ <string name="title_editKey">Redigér Nøgle</string>
+ <string name="title_preferences">Indstillinger</string>
+ <string name="title_changePassPhrase">Skift Kodeord</string>
+ <string name="title_setPassPhrase">Sæt Kodeord</string>
+ <string name="title_sendEmail">"Send Mail..."</string>
+ <string name="title_encryptToFile">Kryptér Til Fil</string>
+ <string name="title_decryptToFile">Afkryptér Til Fil</string>
+ <string name="title_addAccount">Tilføj Konto</string>
+ <string name="title_importKeys">Importér Nøgle</string>
+ <string name="title_exportKey">Eksportér Nøgle</string>
+ <string name="title_exportKeys">Eksportér Nøgler</string>
+ <string name="title_keyNotFound">Ingen Nøgle Matchede</string>
+
+ <!-- section_lowerCase: capitalized words, no punctuation -->
+ <string name="section_userIds">Bruger ID</string>
+ <string name="section_keys">Nøgler</string>
+ <string name="section_general">Generelt</string>
+ <string name="section_defaults">Standard Indstillinger</string>
+
+ <!-- btn_lowerCase: capitalized words, no punctuation -->
+ <string name="btn_encryptToClipboard">Kryptér Til Clipboard</string>
+ <string name="btn_send">Kryptér Og Mail</string>
+ <string name="btn_encrypt">Kryptér</string>
+ <string name="btn_decrypt">Afkryptér</string>
+ <string name="btn_verify">Bekræft</string>
+ <string name="btn_selectEncryptKeys">Vælg Modtagere</string>
+ <string name="btn_reply">Svar</string>
+ <string name="btn_encryptMessage">Kryptér Besked</string>
+ <string name="btn_decryptMessage">Afkryptér Besked</string>
+ <string name="btn_encryptFile">Kryptér Fil</string>
+ <string name="btn_decryptFile">Afkryptér Fil</string>
+ <string name="btn_save">Gem</string>
+ <string name="btn_doNotSave">Fortryd</string>
+ <string name="btn_delete">Slet</string>
+ <string name="btn_noDate">Ingen</string>
+ <string name="btn_clearFilter">Ryd Filter</string>
+
+ <!-- menu_lowerCase: capitalized words, no punctuation -->
+ <string name="menu_about">Om APG</string>
+ <string name="menu_addAccount">Tilføj GMail Konto</string>
+ <string name="menu_deleteAccount">Slet Konto</string>
+ <string name="menu_managePublicKeys">Administrér Offentlige Nøgler</string>
+ <string name="menu_manageSecretKeys">Administrér Private Nøgler</string>
+ <string name="menu_preferences">Indstillinger</string>
+ <string name="menu_changePassPhrase">Skift Kodeord</string>
+ <string name="menu_setPassPhrase">Sæt Kodeord</string>
+ <string name="menu_importKeys">Importér Nøgler</string>
+ <string name="menu_exportKeys">Eksportér Nøgler</string>
+ <string name="menu_exportKey">Eksportér Nøgle</string>
+ <string name="menu_deleteKey">Slet Nøgle</string>
+ <string name="menu_createKey">Opret Nøgle</string>
+ <string name="menu_editKey">Redigér Nøgle</string>
+ <string name="menu_search">Søg</string>
+
+ <!-- label_lowerCase: capitalized words, no punctuation -->
+ <string name="label_sign">Signér</string>
+ <string name="label_message">Besked</string>
+ <string name="label_file">Fil</string>
+ <string name="label_passPhrase">Kodeord</string>
+ <string name="label_passPhraseAgain">Igen</string>
+ <string name="label_algorithm">Algoritme</string>
+ <string name="label_asciiArmour">ASCII Rustning</string>
+ <string name="label_selectPublicKeys">Offentlig(e) Nøgle(r)</string>
+ <string name="label_deleteAfterEncryption">Slet Efter Kryptering</string>
+ <string name="label_deleteAfterDecryption">Slet Efter Afkryptering</string>
+ <string name="label_encryptionAlgorithm">Krypteringsalgoritme</string>
+ <string name="label_hashAlgorithm">Hash-algoritme</string>
+ <string name="label_asymmetric">Offentlig Nøgle</string>
+ <string name="label_symmetric">Kodeord</string>
+ <string name="label_passPhraseCacheTtl">Kodeords Cache</string>
+ <string name="label_messageCompression">Besked Komprimering</string>
+ <string name="label_fileCompression">Fil Komprimering</string>
+
+ <string name="noKeysSelected">Vælg</string>
+ <string name="oneKeySelected">1 Valgt</string>
+ <string name="nKeysSelected">Valgt</string>
+ <string name="unknownUserId">&lt;ukendt&gt;</string>
+ <string name="none">&lt;ingen&gt;</string>
+ <string name="noKey">&lt;ingen nøgle&gt;</string>
+ <string name="noDate">-</string>
+ <string name="noExpiry">&lt;udløber ikke&gt;</string>
+ <string name="unknownStatus"></string>
+ <string name="canEncrypt">kan kryptere</string>
+ <string name="canSign">kan signere</string>
+ <string name="expired">udløbet</string>
+ <string name="notValid">ugyldig</string>
+
+ <!-- choice_lowerCase: capitalized firwst word, no punctuation -->
+ <string name="choice_none">Ingen</string>
+ <string name="choice_signOnly">Kun signering</string>
+ <string name="choice_encryptOnly">Kun kryptering</string>
+ <string name="choice_signAndEncrypt">Signér og kryptér</string>
+ <string name="choice_15secs">15 secs</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">Indtil slut</string>
+
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+
+ <string name="filemanager_titleOpen">Åbn...</string>
+ <string name="filemanager_titleSave">Gem Som...</string>
+ <string name="filemanager_titleEncrypt">Vælg Fil Som Skal Krypteres...</string>
+ <string name="filemanager_titleDecrypt">Vælg Fil Som Skal Afkrypteres...</string>
+ <string name="filemanager_btnOpen">Åbn</string>
+ <string name="filemanager_btnSave">Gem</string>
+
+ <string name="warning">Advarsel</string>
+ <string name="error">Fejl</string>
+ <string name="warningMessage">Advarsel: %s</string>
+ <string name="errorMessage">Fejl: %s</string>
+
+ <!-- sentences -->
+ <string name="wrongPassPhrase">Forkert kodeord.</string>
+ <string name="usingClipboardContent">Bruger clipboardets indhold.</string>
+ <string name="keySaved">Nøgle gemt.</string>
+ <string name="setAPassPhrase">Sæt et kodeord via Indstillinger først.</string>
+ <string name="noFilemanagerInstalled">Der er ikke installeret en kompatibel fil håndtering.</string>
+ <string name="passPhrasesDoNotMatch">Kodeordet matchede ikke.</string>
+ <string name="passPhraseMustNotBeEmpty">Et tomt kodeord er ikke tilladt.</string>
+ <string name="passPhraseForSymmetricEncryption">Kodeord til symmetrisk kryptering:</string>
+ <string name="passPhraseFor">Kodeord til %s:</string>
+ <string name="fileDeleteConfirmation">Er du sikker på at du vil slette \n%s?</string>
+ <string name="fileDeleteSuccessful">Filen er slettet.</string>
+ <string name="noFileSelected">Vælg en fil først.</string>
+ <string name="decryptionSuccessful">Afkryptering lykkedes.</string>
+ <string name="encryptionSuccessful">Kryptering lykkedes.</string>
+ <string name="encryptionToClipboardSuccessful">Kryptering til clipboard lykkedes.</string>
+ <string name="enterPassPhraseTwice">Tast kodeordet to gange.</string>
+ <string name="selectEncryptionKey">Vælg mindst én krypteringsnøgle.</string>
+ <string name="selectEncryptionOrSignatureKey">Vælg mindst én krypteringsnøgle eller signaturnøgle.</string>
+ <string name="specifyFileToEncryptTo">Angiv navnet på filen der skal krypteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.</string>
+ <string name="specifyFileToDecryptTo">Angiv navnet på filen der skal afkrypteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.</string>
+ <string name="specifyGoogleMailAccount">Angiv den Google Mail konto du ønsker at tilføje.</string>
+ <string name="specifyFileToImportFrom">Angiv hvilken fil du ønsker at importere nøgler fra. (.asc eller .gpg)</string>
+ <string name="specifyFileToExportTo">Angiv hvilken fil der skal eksporteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.</string>
+ <string name="specifyFileToExportSecretKeysTo">Angiv hvilken fil der skal eksporteres til.\nADVARSEL! Du er ved at eksportere PRIVATE nøgler.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.</string>
+ <string name="keyDeletionConfirmation">Ønsker du at slette \'%s\'-nøglen?\nDu kan ikke fortryde denne handling!</string>
+ <string name="secretKeyDeletionConfirmation">Ønsker du at slette den PRIVATE nøgle, \'%s\'?\nDu kan ikke fortryde denne handling!</string>
+ <string name="keysAddedAndUpdated">Tilføjelse af %s nøgle(r) og opdatering af %s nøgle(r) lykkedes."</string>
+ <string name="keysAdded">Tilføjelse af %s nøgle(r) lykkedes.</string>
+ <string name="keysUpdated">Opdatring af %s nøgle(r) lykkedes.</string>
+ <string name="noKeysAddedOrUpdated">Ingen nøgler blev tilføjet eller opdateret.</string>
+ <string name="keyExported">Eksport af 1 nøgle lykkedes.</string>
+ <string name="keysExported">Eksport af %s nøgler lykkedes.</string>
+ <string name="noKeysExported">Ingen nøgler blev eksporteret.</string>
+ <string name="keyCreationElGamalInfo">NB: Kun undernøgler understøtter ElGamal, og for ELGamal vil den nærmeste nøglestørrelse - 1536, 2048, 3072, 4096, eller 8192 - blive brugt.</string>
+ <string name="keyNotFound">Kunne ikke finde %08X nøglen.</string>
+
+ <!-- error_lowerCase: phrases, no punctuation, all lowercase,
+ they will be put after "errorMessage", e.g. "Error: file not found" -->
+ <string name="error_fileDeleteFailed">\'%s\' kunne ikke slettes</string>
+ <string name="error_fileNotFound">filen findes ikke</string>
+ <string name="error_noSecretKeyFound">ingen egnet privat nøgle fundet</string>
+ <string name="error_noKnownEncryptionFound">ingen kendt krypterings måde fundet</string>
+ <string name="error_externalStorageNotReady">ekstern storage er ikke parat</string>
+ <string name="error_accountNotFound">\'%s\'-kontoen blev ikke fundet</string>
+ <string name="error_addingAccountFailed">\'%s\'-kontoen kunne ikke tilføjes</string>
+ <string name="error_invalidEmail">ugyldig email \'%s\'</string>
+ <string name="error_keySizeMinimum512bit">nøgle størrelsen skal være på mindst 512bit</string>
+ <string name="error_masterKeyMustNotBeElGamal">hoved nøglen kan ikke være en ElGamal nøgle</string>
+ <string name="error_unknownAlgorithmChoice">ukendt algoritme valg</string>
+ <string name="error_userIdNeedsAName">angiv et navn</string>
+ <string name="error_userIdNeedsAnEmailAddress">angiv en email adresse</string>
+ <string name="error_keyNeedsAUserId">der skal bruges mindst ét bruger-id</string>
+ <string name="error_mainUserIdMustNotBeEmpty">hovedbruger-id kan ikke være tomt</string>
+ <string name="error_keyNeedsMasterKey">der skal mindst bruges en hovednøgle</string>
+ <string name="error_expiryMustComeAfterCreation">udløbsdatoen skal være senere end oprettelsesdatoen</string>
+ <string name="error_noEncryptionKeysOrPassPhrase">hverken krypteringsnøgler eller kodeord modtaget</string>
+ <string name="error_signatureFailed">signering mislykkedes</string>
+ <string name="error_noSignaturePassPhrase">intet kodeord modtaget</string>
+ <string name="error_noSignatureKey">ingen signatur nøgle modtaget</string>
+ <string name="error_invalidData">ikke gyldig krypteringsdata</string>
+ <string name="error_corruptData">beskadiget data</string>
+ <string name="error_noSymmetricEncryptionPacket">kunne ikke finde en pakke med symmetrisk kryptering</string>
+ <string name="error_wrongPassPhrase">forkert kodeord</string>
+ <string name="error_savingKeys">der opstod en fejl da nøglen eller nøglerne skulle gemmes</string>
+
+ <!-- progress_lowerCase: lowercase, phrases, usually ending in '...' -->
+ <string name="progress_done">færdig.</string>
+ <string name="progress_initializing">starter op...</string>
+ <string name="progress_saving">gemmer...</string>
+ <string name="progress_importing">importerer...</string>
+ <string name="progress_exporting">eksporterer...</string>
+ <string name="progress_generating">genererer nøgle, dette kan tage et stykke tid...</string>
+ <string name="progress_buildingKey">opbygger nøgle...</string>
+ <string name="progress_preparingMasterKey">forbereder hovednøgle...</string>
+ <string name="progress_certifyingMasterKey">bekræfter hovednøgle...</string>
+ <string name="progress_buildingMasterKeyRing">bygger hovednøglering...</string>
+ <string name="progress_addingSubKeys">tilføjer undernøgler...</string>
+ <string name="progress_savingKeyRing">gemmer nøglering...</string>
+ <string name="progress_importingSecretKeys">importerer private nøgler...</string>
+ <string name="progress_importingPublicKeys">importerer offentlige nøgler...</string>
+ <string name="progress_reloadingKeys">genloader nøgler...</string>
+ <string name="progress_exportingKey">eksporterer nøgle...</string>
+ <string name="progress_exportingKeys">eksporterer nøgler...</string>
+ <string name="progress_extractingSignatureKey">henter signatur nøgle...</string>
+ <string name="progress_extractingKey">henter nøgle...</string>
+ <string name="progress_preparingStreams">forbereder streams...</string>
+ <string name="progress_encrypting">krypterer data...</string>
+ <string name="progress_decrypting">afkrypterer data...</string>
+ <string name="progress_preparingSignature">forbereder signatur...</string>
+ <string name="progress_generatingSignature">skaber signatur...</string>
+ <string name="progress_processingSignature">behandler signatur...</string>
+ <string name="progress_verifyingSignature">bekræfter signatur...</string>
+ <string name="progress_signing">signerer...</string>
+ <string name="progress_readingData">læser data...</string>
+ <string name="progress_findingKey">søger nøgle...</string>
+ <string name="progress_decompressingData">afkomprimerer data...</string>
+ <string name="progress_verifyingIntegrity">bekræfter integritet...</string>
+
+ <!-- permission strings -->
+ <string name="permission_read_key_details_label">Læs nøgle detaljer fra APG.</string>
+ <string name="permission_read_key_details_description">Læs om offentlige og private nøgler gemt i APG, f. eks. nøgle ID og bruger ID. Selve nøglerne kan IKKE læses.</string>
+
+ <!-- action strings -->
+ <string name="action_encrypt">Kryptér</string>
+ <string name="action_decrypt">Afkryptér</string>
+ <string name="action_importPublic">Importér Offentlige Nøgler</string>
+ <string name="action_importSecret">Importér Private Nøgler</string>
+
+ <string name="hint_publicKeys">Søg I Offentlige Nøgler</string>
+ <string name="hint_secretKeys">Søg I Private Nøgler</string>
+ <string name="filterInfo">Filter: \"%s\"</string>
+
+</resources>
diff --git a/src/org/thialfihar/android/apg/Apg.java b/src/org/thialfihar/android/apg/Apg.java
index e2c6df5b9..58639cee0 100644
--- a/src/org/thialfihar/android/apg/Apg.java
+++ b/src/org/thialfihar/android/apg/Apg.java
@@ -1,1897 +1,1897 @@
-/*
- * 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.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.SignatureException;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Vector;
-import java.util.regex.Pattern;
-
-import org.bouncycastle2.bcpg.ArmoredInputStream;
-import org.bouncycastle2.bcpg.ArmoredOutputStream;
-import org.bouncycastle2.bcpg.BCPGOutputStream;
-import org.bouncycastle2.bcpg.CompressionAlgorithmTags;
-import org.bouncycastle2.bcpg.HashAlgorithmTags;
-import org.bouncycastle2.bcpg.SymmetricKeyAlgorithmTags;
-import org.bouncycastle2.bcpg.sig.KeyFlags;
-import org.bouncycastle2.jce.provider.BouncyCastleProvider;
-import org.bouncycastle2.jce.spec.ElGamalParameterSpec;
-import org.bouncycastle2.openpgp.PGPCompressedData;
-import org.bouncycastle2.openpgp.PGPCompressedDataGenerator;
-import org.bouncycastle2.openpgp.PGPEncryptedData;
-import org.bouncycastle2.openpgp.PGPEncryptedDataGenerator;
-import org.bouncycastle2.openpgp.PGPEncryptedDataList;
-import org.bouncycastle2.openpgp.PGPException;
-import org.bouncycastle2.openpgp.PGPKeyPair;
-import org.bouncycastle2.openpgp.PGPKeyRingGenerator;
-import org.bouncycastle2.openpgp.PGPLiteralData;
-import org.bouncycastle2.openpgp.PGPLiteralDataGenerator;
-import org.bouncycastle2.openpgp.PGPObjectFactory;
-import org.bouncycastle2.openpgp.PGPOnePassSignature;
-import org.bouncycastle2.openpgp.PGPOnePassSignatureList;
-import org.bouncycastle2.openpgp.PGPPBEEncryptedData;
-import org.bouncycastle2.openpgp.PGPPrivateKey;
-import org.bouncycastle2.openpgp.PGPPublicKey;
-import org.bouncycastle2.openpgp.PGPPublicKeyEncryptedData;
-import org.bouncycastle2.openpgp.PGPPublicKeyRing;
-import org.bouncycastle2.openpgp.PGPSecretKey;
-import org.bouncycastle2.openpgp.PGPSecretKeyRing;
-import org.bouncycastle2.openpgp.PGPSignature;
-import org.bouncycastle2.openpgp.PGPSignatureGenerator;
-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.DataProvider;
-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.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.view.ViewGroup;
-
-public class Apg {
- private static final String mApgPackageName = "org.thialfihar.android.apg";
-
- public static class Intent {
- public static final String DECRYPT = "org.thialfihar.android.apg.intent.DECRYPT";
- 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 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 IMPORT = "org.thialfihar.android.apg.intent.IMPORT";
- }
-
- public static final String EXTRA_TEXT = "text";
- 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_DECRYPTED_DATA = "decryptedData";
- public static final String EXTRA_ENCRYPTED_MESSAGE = "encryptedMessage";
- public static final String EXTRA_ENCRYPTED_DATA = "encryptedData";
- public static final String EXTRA_RESULT_URI = "resultUri";
- 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 final String EXTRA_ASCII_ARMOUR = "asciiArmour";
- public static final String EXTRA_BINARY = "binary";
-
- public static final String AUTHORITY = DataProvider.AUTHORITY;
-
- public static final Uri CONTENT_URI_SECRET_KEY_RINGS =
- Uri.parse("content://" + AUTHORITY + "/key_rings/secret/");
- public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID =
- Uri.parse("content://" + AUTHORITY + "/key_rings/secret/key_id/");
- public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_EMAILS =
- Uri.parse("content://" + AUTHORITY + "/key_rings/secret/emails/");
-
- public static final Uri CONTENT_URI_PUBLIC_KEY_RINGS =
- Uri.parse("content://" + AUTHORITY + "/key_rings/public/");
- public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_KEY_ID =
- Uri.parse("content://" + AUTHORITY + "/key_rings/public/key_id/");
- public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS =
- Uri.parse("content://" + AUTHORITY + "/key_rings/public/emails/");
-
- private static String VERSION = null;
-
- private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS =
- new int[] {
- SymmetricKeyAlgorithmTags.AES_256,
- SymmetricKeyAlgorithmTags.AES_192,
- SymmetricKeyAlgorithmTags.AES_128,
- SymmetricKeyAlgorithmTags.CAST5,
- SymmetricKeyAlgorithmTags.TRIPLE_DES };
- private static final int[] PREFERRED_HASH_ALGORITHMS =
- new int[] {
- HashAlgorithmTags.SHA1,
- HashAlgorithmTags.SHA256,
- HashAlgorithmTags.RIPEMD160 };
- private static final int[] PREFERRED_COMPRESSION_ALGORITHMS =
- new int[] {
- CompressionAlgorithmTags.ZLIB,
- CompressionAlgorithmTags.BZIP2,
- CompressionAlgorithmTags.ZIP };
-
- 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);
-
- 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;
-
- public GeneralException(String message) {
- super(message);
- }
- }
-
- public static class NoAsymmetricEncryptionException extends Exception {
- static final long serialVersionUID = 0xf812773343L;
-
- public NoAsymmetricEncryptionException() {
- super();
- }
- }
-
- public static void initialize(Context context) {
- if (mDatabase == null) {
- mDatabase = new Database(context);
- }
- }
-
- public static Database getDatabase() {
- return mDatabase;
- }
-
- public static void setEditPassPhrase(String passPhrase) {
- mEditPassPhrase = passPhrase;
- }
-
- public static String getEditPassPhrase() {
- return mEditPassPhrase;
- }
-
- public static void setCachedPassPhrase(long keyId, String passPhrase) {
- mPassPhraseCache.put(keyId, new CachedPassPhrase(new Date().getTime(), passPhrase));
- }
-
- public static String getCachedPassPhrase(long keyId) {
- long realId = keyId;
- if (realId != Id.key.symmetric) {
- PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
- if (keyRing == null) {
- return null;
- }
- PGPSecretKey masterKey = getMasterKey(keyRing);
- if (masterKey == null) {
- return null;
- }
- realId = masterKey.getKeyID();
- }
- CachedPassPhrase cpp = mPassPhraseCache.get(realId);
- if (cpp == null) {
- return null;
- }
- // set it again to reset the cache life cycle
- setCachedPassPhrase(realId, cpp.passPhrase);
- return cpp.passPhrase;
- }
-
- 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()) {
- 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,
- int algorithmChoice, int keySize, String passPhrase,
- PGPSecretKey masterKey)
- throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
- GeneralException, InvalidAlgorithmParameterException {
-
- if (keySize < 512) {
- throw new GeneralException(context.getString(R.string.error_keySizeMinimum512bit));
- }
-
- Security.addProvider(new BouncyCastleProvider());
-
- if (passPhrase == null) {
- passPhrase = "";
- }
-
- int algorithm = 0;
- KeyPairGenerator keyGen = null;
-
- switch (algorithmChoice) {
- case Id.choice.algorithm.dsa: {
- keyGen = KeyPairGenerator.getInstance("DSA", new BouncyCastleProvider());
- keyGen.initialize(keySize, new SecureRandom());
- algorithm = PGPPublicKey.DSA;
- break;
- }
-
- case Id.choice.algorithm.elgamal: {
- if (masterKey == null) {
- throw new GeneralException(context.getString(R.string.error_masterKeyMustNotBeElGamal));
- }
- keyGen = KeyPairGenerator.getInstance("ELGAMAL", new BouncyCastleProvider());
- BigInteger p = Primes.getBestPrime(keySize);
- BigInteger g = new BigInteger("2");
-
- ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
-
- keyGen.initialize(elParams);
- algorithm = PGPPublicKey.ELGAMAL_ENCRYPT;
- break;
- }
-
- case Id.choice.algorithm.rsa: {
- keyGen = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
- keyGen.initialize(keySize, new SecureRandom());
-
- algorithm = PGPPublicKey.RSA_GENERAL;
- break;
- }
-
- default: {
- throw new GeneralException(context.getString(R.string.error_unknownAlgorithmChoice));
- }
- }
-
- PGPKeyPair keyPair = new PGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date());
-
- PGPSecretKey secretKey = null;
- if (masterKey == null) {
- // enough for now, as we assemble the key again later anyway
- secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, keyPair, "",
- PGPEncryptedData.CAST5, passPhrase.toCharArray(),
- null, null,
- new SecureRandom(), new BouncyCastleProvider().getName());
-
- } else {
- PGPPublicKey tmpKey = masterKey.getPublicKey();
- PGPPublicKey masterPublicKey =
- new PGPPublicKey(tmpKey.getAlgorithm(),
- tmpKey.getKey(new BouncyCastleProvider()),
- tmpKey.getCreationTime());
- PGPPrivateKey masterPrivateKey =
- masterKey.extractPrivateKey(passPhrase.toCharArray(),
- new BouncyCastleProvider());
-
- PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
- PGPKeyRingGenerator ringGen =
- new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
- masterKeyPair, "",
- PGPEncryptedData.CAST5, passPhrase.toCharArray(),
- null, null,
- new SecureRandom(), new BouncyCastleProvider().getName());
- ringGen.addSubKey(keyPair);
- PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing();
- Iterator it = secKeyRing.getSecretKeys();
- // first one is the master key
- it.next();
- secretKey = (PGPSecretKey) it.next();
- }
-
- return secretKey;
- }
-
- private static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) {
- GregorianCalendar tmp = new GregorianCalendar();
- tmp.setTime(first.getTime());
- long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
- tmp.add(Calendar.DAY_OF_MONTH, (int)numDays);
- while (tmp.before(second)) {
- tmp.add(Calendar.DAY_OF_MONTH, 1);
- ++numDays;
- }
- return numDays;
- }
-
- public static void buildSecretKey(Activity context,
- SectionView userIdsView, SectionView keysView,
- String oldPassPhrase, String newPassPhrase,
- ProgressDialogUpdater progress)
- throws Apg.GeneralException, NoSuchProviderException, PGPException,
- NoSuchAlgorithmException, SignatureException, IOException, Database.GeneralException {
-
- progress.setProgress(R.string.progress_buildingKey, 0, 100);
-
- Security.addProvider(new BouncyCastleProvider());
-
- if (oldPassPhrase == null || oldPassPhrase.equals("")) {
- oldPassPhrase = "";
- }
-
- if (newPassPhrase == null || newPassPhrase.equals("")) {
- newPassPhrase = "";
- }
-
- Vector<String> userIds = new Vector<String>();
- Vector<PGPSecretKey> keys = new Vector<PGPSecretKey>();
-
- ViewGroup userIdEditors = userIdsView.getEditors();
- ViewGroup keyEditors = keysView.getEditors();
-
- boolean gotMainUserId = false;
- for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
- UserIdEditor editor = (UserIdEditor)userIdEditors.getChildAt(i);
- String userId = null;
- try {
- userId = editor.getValue();
- } catch (UserIdEditor.NoNameException e) {
- throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAName));
- } catch (UserIdEditor.NoEmailException e) {
- throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress));
- } catch (UserIdEditor.InvalidEmailException e) {
- throw new Apg.GeneralException("" + e);
- }
-
- if (userId.equals("")) {
- continue;
- }
-
- if (editor.isMainUserId()) {
- userIds.insertElementAt(userId, 0);
- gotMainUserId = true;
- } else {
- userIds.add(userId);
- }
- }
-
- if (userIds.size() == 0) {
- throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsAUserId));
- }
-
- if (!gotMainUserId) {
- throw new Apg.GeneralException(context.getString(R.string.error_mainUserIdMustNotBeEmpty));
- }
-
- if (keyEditors.getChildCount() == 0) {
- throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsMasterKey));
- }
-
- for (int i = 0; i < keyEditors.getChildCount(); ++i) {
- KeyEditor editor = (KeyEditor)keyEditors.getChildAt(i);
- keys.add(editor.getValue());
- }
-
- progress.setProgress(R.string.progress_preparingMasterKey, 10, 100);
- KeyEditor keyEditor = (KeyEditor) keyEditors.getChildAt(0);
- int usageId = keyEditor.getUsage();
- boolean canSign = (usageId == Id.choice.usage.sign_only ||
- usageId == Id.choice.usage.sign_and_encrypt);
- boolean canEncrypt = (usageId == Id.choice.usage.encrypt_only ||
- usageId == Id.choice.usage.sign_and_encrypt);
-
- String mainUserId = userIds.get(0);
-
- PGPSecretKey masterKey = keys.get(0);
- PGPPublicKey tmpKey = masterKey.getPublicKey();
- PGPPublicKey masterPublicKey =
- new PGPPublicKey(tmpKey.getAlgorithm(),
- tmpKey.getKey(new BouncyCastleProvider()),
- tmpKey.getCreationTime());
- PGPPrivateKey masterPrivateKey =
- masterKey.extractPrivateKey(oldPassPhrase.toCharArray(),
- new BouncyCastleProvider());
-
- progress.setProgress(R.string.progress_certifyingMasterKey, 20, 100);
- for (int i = 0; i < userIds.size(); ++i) {
- String userId = userIds.get(i);
-
- PGPSignatureGenerator sGen =
- new PGPSignatureGenerator(masterPublicKey.getAlgorithm(),
- HashAlgorithmTags.SHA1, new BouncyCastleProvider());
-
- sGen.initSign(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
-
- PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
-
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
- }
-
- // TODO: cross-certify the master key with every sub key
-
- PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
-
- PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
-
- int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA;
- if (canEncrypt) {
- keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
- }
- hashedPacketsGen.setKeyFlags(true, keyFlags);
-
- hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
- 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 = getNumDaysBetween(creationDate, expiryDate);
- if (numDays <= 0) {
- throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
- }
- hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
- }
-
- progress.setProgress(R.string.progress_buildingMasterKeyRing, 30, 100);
- PGPKeyRingGenerator keyGen =
- new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
- masterKeyPair, mainUserId,
- PGPEncryptedData.CAST5, newPassPhrase.toCharArray(),
- hashedPacketsGen.generate(), unhashedPacketsGen.generate(),
- new SecureRandom(), new BouncyCastleProvider().getName());
-
- progress.setProgress(R.string.progress_addingSubKeys, 40, 100);
- for (int i = 1; i < keys.size(); ++i) {
- progress.setProgress(40 + 50 * (i - 1)/ (keys.size() - 1), 100);
- PGPSecretKey subKey = keys.get(i);
- keyEditor = (KeyEditor) keyEditors.getChildAt(i);
- PGPPublicKey subPublicKey = subKey.getPublicKey();
- PGPPrivateKey subPrivateKey =
- subKey.extractPrivateKey(oldPassPhrase.toCharArray(),
- new BouncyCastleProvider());
- PGPKeyPair subKeyPair =
- new PGPKeyPair(subPublicKey.getAlgorithm(),
- subPublicKey.getKey(new BouncyCastleProvider()),
- subPrivateKey.getKey(),
- subPublicKey.getCreationTime());
-
- hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
-
- keyFlags = 0;
- usageId = keyEditor.getUsage();
- canSign = (usageId == Id.choice.usage.sign_only ||
- usageId == Id.choice.usage.sign_and_encrypt);
- canEncrypt = (usageId == Id.choice.usage.encrypt_only ||
- usageId == Id.choice.usage.sign_and_encrypt);
- if (canSign) {
- keyFlags |= KeyFlags.SIGN_DATA;
- }
- if (canEncrypt) {
- keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
- }
- 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 = getNumDaysBetween(creationDate, expiryDate);
- if (numDays <= 0) {
- throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
- }
- hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
- }
-
- keyGen.addSubKey(subKeyPair,
- hashedPacketsGen.generate(), unhashedPacketsGen.generate());
- }
-
- PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
- PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
-
- progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
- mDatabase.saveKeyRing(secretKeyRing);
- mDatabase.saveKeyRing(publicKeyRing);
-
- progress.setProgress(R.string.progress_done, 100, 100);
- }
-
- public static Bundle importKeyRings(Activity context, int type,
- InputData data,
- ProgressDialogUpdater progress)
- throws GeneralException, FileNotFoundException, PGPException, IOException {
- Bundle returnData = new Bundle();
-
- if (type == Id.type.secret_key) {
- progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
- } else {
- progress.setProgress(R.string.progress_importingPublicKeys, 0, 100);
- }
-
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
- }
-
- PositionAwareInputStream progressIn = new PositionAwareInputStream(data.getInputStream());
- // 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;
- 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;
- }
- 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;
-
- 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;
- }
- progress.setProgress((int)(100 * progressIn.position() / data.getSize()), 100);
- obj = objectFactory.nextObject();
- }
- }
- } catch (EOFException e) {
- // nothing to do, we are done
- }
-
- returnData.putInt("added", newKeys);
- returnData.putInt("updated", oldKeys);
-
- progress.setProgress(R.string.progress_done, 100, 100);
-
- return returnData;
- }
-
- public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds,
- OutputStream outStream,
- ProgressDialogUpdater progress)
- throws GeneralException, FileNotFoundException, PGPException, IOException {
- Bundle returnData = new Bundle();
-
- if (keyRingIds.size() == 1) {
- progress.setProgress(R.string.progress_exportingKey, 0, 100);
- } else {
- progress.setProgress(R.string.progress_exportingKeys, 0, 100);
- }
-
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
- }
- ArmoredOutputStream out = new ArmoredOutputStream(outStream);
-
- int numKeys = 0;
- 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;
-
- if (obj instanceof PGPSecretKeyRing) {
- secretKeyRing = (PGPSecretKeyRing) obj;
- secretKeyRing.encode(out);
- } else if (obj instanceof PGPPublicKeyRing) {
- publicKeyRing = (PGPPublicKeyRing) obj;
- publicKeyRing.encode(out);
- } else {
- continue;
- }
- ++numKeys;
- }
- out.close();
- returnData.putInt("exported", numKeys);
-
- progress.setProgress(R.string.progress_done, 100, 100);
-
- return returnData;
- }
-
- public static Date getCreationDate(PGPPublicKey key) {
- return key.getCreationTime();
- }
-
- public static Date getCreationDate(PGPSecretKey key) {
- return key.getPublicKey().getCreationTime();
- }
-
- public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
- if (keyRing == null) {
- return null;
- }
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- if (key.isMasterKey()) {
- return key;
- }
- }
-
- return null;
- }
-
- public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
- if (keyRing == null) {
- return null;
- }
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (key.isMasterKey()) {
- return key;
- }
- }
-
- return null;
- }
-
- public static Vector<PGPPublicKey> getEncryptKeys(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> encryptKeys = new Vector<PGPPublicKey>();
-
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- if (isEncryptionKey(key)) {
- encryptKeys.add(key);
- }
- }
-
- return encryptKeys;
- }
-
- public static Vector<PGPSecretKey> getSigningKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
-
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (isSigningKey(key)) {
- signingKeys.add(key);
- }
- }
-
- return signingKeys;
- }
-
- public static Vector<PGPPublicKey> getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> usableKeys = new Vector<PGPPublicKey>();
- Vector<PGPPublicKey> encryptKeys = getEncryptKeys(keyRing);
- PGPPublicKey masterKey = null;
- for (int i = 0; i < encryptKeys.size(); ++i) {
- PGPPublicKey key = encryptKeys.get(i);
- if (!isExpired(key)) {
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
- public static boolean isExpired(PGPPublicKey key) {
- Date creationDate = getCreationDate(key);
- Date expiryDate = getExpiryDate(key);
- Date now = new Date();
- if (now.compareTo(creationDate) >= 0 &&
- (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
- return false;
- }
- return true;
- }
-
- public static boolean isExpired(PGPSecretKey key) {
- return isExpired(key.getPublicKey());
- }
-
- public static Vector<PGPSecretKey> getUsableSigningKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
- Vector<PGPSecretKey> signingKeys = getSigningKeys(keyRing);
- PGPSecretKey masterKey = null;
- for (int i = 0; i < signingKeys.size(); ++i) {
- PGPSecretKey key = signingKeys.get(i);
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
- public static Date getExpiryDate(PGPPublicKey key) {
- Date creationDate = getCreationDate(key);
- if (key.getValidDays() == 0) {
- // no expiry
- return null;
- }
- Calendar calendar = GregorianCalendar.getInstance();
- calendar.setTime(creationDate);
- calendar.add(Calendar.DATE, key.getValidDays());
- Date expiryDate = calendar.getTime();
-
- return expiryDate;
- }
-
- public static Date getExpiryDate(PGPSecretKey key) {
- return getExpiryDate(key.getPublicKey());
- }
-
- public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
- PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
- if (keyRing == null) {
- return null;
- }
- Vector<PGPPublicKey> encryptKeys = getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- return null;
- }
- return encryptKeys.get(0);
- }
-
- public static PGPSecretKey getSigningKey(long masterKeyId) {
- PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
- if (keyRing == null) {
- return null;
- }
- Vector<PGPSecretKey> signingKeys = getUsableSigningKeys(keyRing);
- if (signingKeys.size() == 0) {
- return null;
- }
- return signingKeys.get(0);
- }
-
- public static String getMainUserId(PGPPublicKey key) {
- for (String userId : new IterableIterator<String>(key.getUserIDs())) {
- return userId;
- }
- return null;
- }
-
- public static String getMainUserId(PGPSecretKey key) {
- for (String userId : new IterableIterator<String>(key.getUserIDs())) {
- return userId;
- }
- return null;
- }
-
- public static String getMainUserIdSafe(Context context, PGPPublicKey key) {
- String userId = getMainUserId(key);
- if (userId == null) {
- userId = context.getResources().getString(R.string.unknownUserId);
- }
- return userId;
- }
-
- public static String getMainUserIdSafe(Context context, PGPSecretKey key) {
- String userId = getMainUserId(key);
- if (userId == null) {
- userId = context.getResources().getString(R.string.unknownUserId);
- }
- return userId;
- }
-
- public static boolean isEncryptionKey(PGPPublicKey key) {
- if (!key.isEncryptionKey()) {
- return false;
- }
-
- if (key.getVersion() <= 3) {
- // this must be true now
- return key.isEncryptionKey();
- }
-
- // special cases
- if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
- return true;
- }
-
- if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
- return true;
- }
-
- for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
- if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
- continue;
- }
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
-
- if (hashed != null &&(hashed.getKeyFlags() &
- (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
- return true;
- }
-
- PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
-
- if (unhashed != null &&(unhashed.getKeyFlags() &
- (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
- return true;
- }
- }
- return false;
- }
-
- public static boolean isEncryptionKey(PGPSecretKey key) {
- return isEncryptionKey(key.getPublicKey());
- }
-
- public static boolean isSigningKey(PGPPublicKey key) {
- if (key.getVersion() <= 3) {
- return true;
- }
-
- // special case
- if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
- return true;
- }
-
- for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
- if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
- continue;
- }
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
-
- if (hashed != null && (hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
- return true;
- }
-
- PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
-
- if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
- return true;
- }
- }
-
- return false;
- }
-
- public static boolean isSigningKey(PGPSecretKey key) {
- return isSigningKey(key.getPublicKey());
- }
-
- 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 (algorithm) {
- case PGPPublicKey.RSA_ENCRYPT:
- case PGPPublicKey.RSA_GENERAL:
- case PGPPublicKey.RSA_SIGN: {
- algorithmStr = "RSA";
- break;
- }
-
- case PGPPublicKey.DSA: {
- algorithmStr = "DSA";
- break;
- }
-
- case PGPPublicKey.ELGAMAL_ENCRYPT:
- case PGPPublicKey.ELGAMAL_GENERAL: {
- algorithmStr = "ElGamal";
- break;
- }
-
- default: {
- algorithmStr = "???";
- break;
- }
- }
- return algorithmStr + ", " + keySize + "bit";
- }
-
- public static void deleteKey(int keyRingId) {
- mDatabase.deleteKeyRing(keyRingId);
- }
-
- public static Object getKeyRing(int keyRingId) {
- return mDatabase.getKeyRing(keyRingId);
- }
-
- 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 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 PGPSecretKey getSecretKey(long keyId) {
- PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
- if (keyRing == null) {
- return null;
- }
- return keyRing.getSecretKey(keyId);
- }
-
- 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;
- }
- }
-
- 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());
- }
-
- if (c != null) {
- c.close();
- }
-
- return userId;
- }
-
- public static void encrypt(Context context,
- InputData data, OutputStream outStream,
- boolean armored,
- long encryptionKeyIds[], long signatureKeyId,
- String signaturePassPhrase,
- ProgressDialogUpdater progress,
- int symmetricAlgorithm, int hashAlgorithm, int compression,
- String passPhrase)
- throws IOException, GeneralException, PGPException, NoSuchProviderException,
- NoSuchAlgorithmException, SignatureException {
- Security.addProvider(new BouncyCastleProvider());
-
- if (encryptionKeyIds == null) {
- encryptionKeyIds = new long[0];
- }
-
- ArmoredOutputStream armorOut = null;
- OutputStream out = null;
- OutputStream encryptOut = null;
- if (armored) {
- armorOut = new ArmoredOutputStream(outStream);
- armorOut.setHeader("Version", getFullVersion(context));
- out = armorOut;
- } else {
- out = outStream;
- }
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
-
- if (encryptionKeyIds.length == 0 && passPhrase == null) {
- throw new GeneralException(context.getString(R.string.error_noEncryptionKeysOrPassPhrase));
- }
-
- if (signatureKeyId != 0) {
- signingKeyRing = getSecretKeyRing(signatureKeyId);
- signingKey = getSigningKey(signatureKeyId);
- if (signingKey == null) {
- throw new GeneralException(context.getString(R.string.error_signatureFailed));
- }
-
- if (signaturePassPhrase == null) {
- throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
- }
- progress.setProgress(R.string.progress_extractingSignatureKey, 0, 100);
- signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
- new BouncyCastleProvider());
- }
-
- PGPSignatureGenerator signatureGenerator = null;
- progress.setProgress(R.string.progress_preparingStreams, 5, 100);
- // encrypt and compress input file content
- PGPEncryptedDataGenerator cPk =
- new PGPEncryptedDataGenerator(symmetricAlgorithm, true, new SecureRandom(),
- new BouncyCastleProvider());
-
- if (encryptionKeyIds.length == 0) {
- // symmetric encryption
- cPk.addMethod(passPhrase.toCharArray());
- }
- for (int i = 0; i < encryptionKeyIds.length; ++i) {
- PGPPublicKey key = getEncryptPublicKey(encryptionKeyIds[i]);
- if (key != null) {
- cPk.addMethod(key);
- }
- }
- encryptOut = cPk.open(out, new byte[1 << 16]);
-
- if (signatureKeyId != 0) {
- progress.setProgress(R.string.progress_preparingSignature, 10, 100);
- signatureGenerator =
- new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
- hashAlgorithm,
- new BouncyCastleProvider());
- signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
- String userId = getMainUserId(getMasterKey(signingKeyRing));
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, userId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
- }
-
- PGPCompressedDataGenerator compressGen = null;
- BCPGOutputStream bcpgOut = null;
- if (compression == Id.choice.compression.none) {
- bcpgOut = new BCPGOutputStream(encryptOut);
- } else {
- compressGen = new PGPCompressedDataGenerator(compression);
- bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
- }
- if (signatureKeyId != 0) {
- signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
- }
-
- PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
- // file name not needed, so empty string
- OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "",
- new Date(), new byte[1 << 16]);
-
- progress.setProgress(R.string.progress_encrypting, 20, 100);
- long done = 0;
- int n = 0;
- byte[] buffer = new byte[1 << 16];
- InputStream in = data.getInputStream();
- while ((n = in.read(buffer)) > 0) {
- pOut.write(buffer, 0, n);
- if (signatureKeyId != 0) {
- signatureGenerator.update(buffer, 0, n);
- }
- done += n;
- if (data.getSize() != 0) {
- progress.setProgress((int) (20 + (95 - 20) * done / data.getSize()), 100);
- }
- }
-
- literalGen.close();
-
- if (signatureKeyId != 0) {
- progress.setProgress(R.string.progress_generatingSignature, 95, 100);
- signatureGenerator.generate().encode(pOut);
- }
- if (compressGen != null) {
- compressGen.close();
- }
- encryptOut.close();
- if (armored) {
- armorOut.close();
- }
-
- progress.setProgress(R.string.progress_done, 100, 100);
- }
-
- public static void signText(Context context,
- InputData data, OutputStream outStream,
- long signatureKeyId, String signaturePassPhrase,
- int hashAlgorithm,
- ProgressDialogUpdater progress)
- throws GeneralException, PGPException, IOException, NoSuchAlgorithmException,
- SignatureException {
- Security.addProvider(new BouncyCastleProvider());
-
- ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
- armorOut.setHeader("Version", getFullVersion(context));
-
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
-
- if (signatureKeyId == 0) {
- throw new GeneralException(context.getString(R.string.error_noSignatureKey));
- }
-
- signingKeyRing = getSecretKeyRing(signatureKeyId);
- signingKey = getSigningKey(signatureKeyId);
- if (signingKey == null) {
- throw new GeneralException(context.getString(R.string.error_signatureFailed));
- }
-
- if (signaturePassPhrase == null) {
- throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
- }
- signaturePrivateKey =
- signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
- new BouncyCastleProvider());
-
- PGPSignatureGenerator signatureGenerator = null;
- progress.setProgress(R.string.progress_preparingStreams, 0, 100);
-
- progress.setProgress(R.string.progress_preparingSignature, 30, 100);
- signatureGenerator =
- new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
- hashAlgorithm,
- new BouncyCastleProvider());
- signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
- String userId = getMainUserId(getMasterKey(signingKeyRing));
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, userId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
-
- progress.setProgress(R.string.progress_signing, 40, 100);
-
- armorOut.beginClearText(hashAlgorithm);
-
- ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
- InputStream inStream = data.getInputStream();
- int lookAhead = readInputLine(lineOut, inStream);
-
- processLine(armorOut, signatureGenerator, lineOut.toByteArray());
-
- if (lookAhead != -1) {
- do {
- lookAhead = readInputLine(lineOut, lookAhead, inStream);
-
- signatureGenerator.update((byte)'\r');
- signatureGenerator.update((byte)'\n');
-
- processLine(armorOut, signatureGenerator, lineOut.toByteArray());
- }
- while (lookAhead != -1);
- }
-
- armorOut.endClearText();
-
- BCPGOutputStream bOut = new BCPGOutputStream(armorOut);
- signatureGenerator.generate().encode(bOut);
- armorOut.close();
-
- progress.setProgress(R.string.progress_done, 100, 100);
- }
-
- public static long getDecryptionKeyId(Context context, InputData data)
- throws GeneralException, NoAsymmetricEncryptionException, IOException {
- InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- PGPEncryptedDataList enc;
- Object o = pgpF.nextObject();
-
- // the first object might be a PGP marker packet.
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpF.nextObject();
- }
-
- if (enc == null) {
- throw new GeneralException(context.getString(R.string.error_invalidData));
- }
-
- // TODO: currently we always only look at the first known key
- // find the secret key
- PGPSecretKey secretKey = null;
- Iterator it = enc.getEncryptedDataObjects();
- boolean gotAsymmetricEncryption = false;
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPublicKeyEncryptedData) {
- gotAsymmetricEncryption = true;
- PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
- secretKey = getSecretKey(pbe.getKeyID());
- if (secretKey != null) {
- break;
- }
- }
- }
-
- if (!gotAsymmetricEncryption) {
- throw new NoAsymmetricEncryptionException();
- }
-
- if (secretKey == null) {
- return Id.key.none;
- }
-
- return secretKey.getKeyID();
- }
-
- public static boolean hasSymmetricEncryption(Context context, InputData data)
- throws GeneralException, IOException {
- InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- PGPEncryptedDataList enc;
- Object o = pgpF.nextObject();
-
- // the first object might be a PGP marker packet.
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpF.nextObject();
- }
-
- if (enc == null) {
- throw new GeneralException(context.getString(R.string.error_invalidData));
- }
-
- Iterator it = enc.getEncryptedDataObjects();
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPBEEncryptedData) {
- return true;
- }
- }
-
- return false;
- }
-
- public static Bundle decrypt(Context context,
- InputData data, OutputStream outStream,
- String passPhrase, ProgressDialogUpdater progress,
- boolean assumeSymmetric)
- throws IOException, GeneralException, PGPException, SignatureException {
- if (passPhrase == null) {
- passPhrase = "";
- }
- Bundle returnData = new Bundle();
- InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- PGPEncryptedDataList enc;
- Object o = pgpF.nextObject();
- long signatureKeyId = 0;
-
- int currentProgress = 0;
- progress.setProgress(R.string.progress_readingData, currentProgress, 100);
-
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpF.nextObject();
- }
-
- if (enc == null) {
- throw new GeneralException(context.getString(R.string.error_invalidData));
- }
-
- InputStream clear = null;
- PGPEncryptedData encryptedData = null;
-
- currentProgress += 5;
-
- // TODO: currently we always only look at the first known key or symmetric encryption,
- // there might be more...
- if (assumeSymmetric) {
- PGPPBEEncryptedData pbe = null;
- Iterator it = enc.getEncryptedDataObjects();
- // find secret key
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPBEEncryptedData) {
- pbe = (PGPPBEEncryptedData) obj;
- break;
- }
- }
-
- if (pbe == null) {
- throw new GeneralException(context.getString(R.string.error_noSymmetricEncryptionPacket));
- }
-
- progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
- clear = pbe.getDataStream(passPhrase.toCharArray(), new BouncyCastleProvider());
- encryptedData = pbe;
- currentProgress += 5;
- } else {
- progress.setProgress(R.string.progress_findingKey, currentProgress, 100);
- PGPPublicKeyEncryptedData pbe = null;
- PGPSecretKey secretKey = null;
- Iterator it = enc.getEncryptedDataObjects();
- // find secret key
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPublicKeyEncryptedData) {
- PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- secretKey = getSecretKey(encData.getKeyID());
- if (secretKey != null) {
- pbe = encData;
- break;
- }
- }
- }
-
- if (secretKey == null) {
- throw new GeneralException(context.getString(R.string.error_noSecretKeyFound));
- }
-
- currentProgress += 5;
- progress.setProgress(R.string.progress_extractingKey, currentProgress, 100);
- PGPPrivateKey privateKey = null;
- try {
- privateKey = secretKey.extractPrivateKey(passPhrase.toCharArray(),
- new BouncyCastleProvider());
- } catch (PGPException e) {
- throw new PGPException(context.getString(R.string.error_wrongPassPhrase));
- }
- currentProgress += 5;
- progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
- clear = pbe.getDataStream(privateKey, new BouncyCastleProvider());
- encryptedData = pbe;
- currentProgress += 5;
- }
-
- PGPObjectFactory plainFact = new PGPObjectFactory(clear);
- Object dataChunk = plainFact.nextObject();
- PGPOnePassSignature signature = null;
- PGPPublicKey signatureKey = null;
- int signatureIndex = -1;
-
- if (dataChunk instanceof PGPCompressedData) {
- progress.setProgress(R.string.progress_decompressingData, currentProgress, 100);
- PGPObjectFactory fact =
- new PGPObjectFactory(((PGPCompressedData) dataChunk).getDataStream());
- dataChunk = fact.nextObject();
- plainFact = fact;
- currentProgress += 10;
- }
-
- if (dataChunk instanceof PGPOnePassSignatureList) {
- progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
- returnData.putBoolean(EXTRA_SIGNATURE, true);
- PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
- for (int i = 0; i < sigList.size(); ++i) {
- signature = sigList.get(i);
- signatureKey = getPublicKey(signature.getKeyID());
- if (signatureKeyId == 0) {
- signatureKeyId = signature.getKeyID();
- }
- if (signatureKey == null) {
- signature = null;
- } else {
- signatureIndex = i;
- signatureKeyId = signature.getKeyID();
- String userId = null;
- PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
- if (sigKeyRing != null) {
- userId = getMainUserId(getMasterKey(sigKeyRing));
- }
- returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
- break;
- }
- }
-
- returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
-
- if (signature != null) {
- signature.initVerify(signatureKey, new BouncyCastleProvider());
- } else {
- returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
- }
-
- dataChunk = plainFact.nextObject();
- currentProgress += 10;
- }
-
- if (dataChunk instanceof PGPLiteralData) {
- progress.setProgress(R.string.progress_decrypting, currentProgress, 100);
- PGPLiteralData literalData = (PGPLiteralData) dataChunk;
- OutputStream out = outStream;
-
- byte[] buffer = new byte[1 << 16];
- InputStream dataIn = literalData.getInputStream();
-
- int startProgress = currentProgress;
- int endProgress = 100;
- if (signature != null) {
- endProgress = 90;
- } else if (encryptedData.isIntegrityProtected()) {
- endProgress = 95;
- }
- int n = 0;
- int done = 0;
- long startPos = data.getStreamPosition();
- while ((n = dataIn.read(buffer)) > 0) {
- out.write(buffer, 0, n);
- done += n;
- if (signature != null) {
- try {
- signature.update(buffer, 0, n);
- } catch (SignatureException e) {
- returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
- signature = null;
- }
- }
- // unknown size, but try to at least have a moving, slowing down progress bar
- currentProgress = startProgress + (endProgress - startProgress) * done / (done + 100000);
- if (data.getSize() - startPos == 0) {
- currentProgress = endProgress;
- } else {
- currentProgress = (int)(startProgress + (endProgress - startProgress) *
- (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
- }
- progress.setProgress(currentProgress, 100);
- }
-
- if (signature != null) {
- progress.setProgress(R.string.progress_verifyingSignature, 90, 100);
- PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
- PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex);
- if (signature.verify(messageSignature)) {
- returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, true);
- } else {
- returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
- }
- }
- }
-
- // TODO: add integrity somewhere
- if (encryptedData.isIntegrityProtected()) {
- progress.setProgress(R.string.progress_verifyingIntegrity, 95, 100);
- if (encryptedData.verify()) {
- // passed
- } else {
- // failed
- }
- } else {
- // no integrity check
- }
-
- progress.setProgress(R.string.progress_done, 100, 100);
- return returnData;
- }
-
- public static Bundle verifyText(Context context,
- InputData data, OutputStream outStream,
- ProgressDialogUpdater progress)
- throws IOException, GeneralException, PGPException, SignatureException {
- Bundle returnData = new Bundle();
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream());
-
- progress.setProgress(R.string.progress_done, 0, 100);
-
- // mostly taken from ClearSignedFileProcessor
- ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
- int lookAhead = readInputLine(lineOut, aIn);
- byte[] lineSep = getLineSeparator();
-
- byte[] line = lineOut.toByteArray();
- out.write(line, 0, getLengthWithoutSeparator(line));
- out.write(lineSep);
-
- while (lookAhead != -1 && aIn.isClearText()) {
- lookAhead = readInputLine(lineOut, lookAhead, aIn);
- line = lineOut.toByteArray();
- out.write(line, 0, getLengthWithoutSeparator(line));
- out.write(lineSep);
- }
-
- out.close();
-
- byte[] clearText = out.toByteArray();
- outStream.write(clearText);
-
- returnData.putBoolean(EXTRA_SIGNATURE, true);
-
- progress.setProgress(R.string.progress_processingSignature, 60, 100);
- PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
-
- PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
- if (sigList == null) {
- throw new GeneralException(context.getString(R.string.error_corruptData));
- }
- PGPSignature signature = null;
- long signatureKeyId = 0;
- PGPPublicKey signatureKey = null;
- for (int i = 0; i < sigList.size(); ++i) {
- signature = sigList.get(i);
- signatureKey = getPublicKey(signature.getKeyID());
- if (signatureKeyId == 0) {
- signatureKeyId = signature.getKeyID();
- }
- if (signatureKey == null) {
- signature = null;
- } else {
- signatureKeyId = signature.getKeyID();
- String userId = null;
- PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
- if (sigKeyRing != null) {
- userId = getMainUserId(getMasterKey(sigKeyRing));
- }
- returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
- break;
- }
- }
-
- returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
-
- if (signature == null) {
- returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
- progress.setProgress(R.string.progress_done, 100, 100);
- return returnData;
- }
-
- signature.initVerify(signatureKey, new BouncyCastleProvider());
-
- InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText));
-
- lookAhead = readInputLine(lineOut, sigIn);
-
- processLine(signature, lineOut.toByteArray());
-
- if (lookAhead != -1) {
- do {
- lookAhead = readInputLine(lineOut, lookAhead, sigIn);
-
- signature.update((byte)'\r');
- signature.update((byte)'\n');
-
- processLine(signature, lineOut.toByteArray());
- }
- while (lookAhead != -1);
- }
-
- returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, signature.verify());
-
- progress.setProgress(R.string.progress_done, 100, 100);
- return returnData;
- }
-
- public static int getStreamContent(Context context, InputStream inStream)
- throws IOException {
- InputStream in = PGPUtil.getDecoderStream(inStream);
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- Object object = pgpF.nextObject();
- while (object != null) {
- if (object instanceof PGPPublicKeyRing ||
- object instanceof PGPSecretKeyRing) {
- return Id.content.keys;
- } else if (object instanceof PGPEncryptedDataList) {
- return Id.content.encrypted_data;
- }
- object = pgpF.nextObject();
- }
-
- return Id.content.unknown;
- }
-
- // taken from ClearSignedFileProcessor in BC
- private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
- throws IOException {
- bOut.reset();
-
- int lookAhead = -1;
- int ch;
-
- while ((ch = fIn.read()) >= 0) {
- bOut.write(ch);
- if (ch == '\r' || ch == '\n') {
- lookAhead = readPassedEOL(bOut, ch, fIn);
- break;
- }
- }
-
- return lookAhead;
- }
-
- private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn)
- throws IOException {
- bOut.reset();
-
- int ch = lookAhead;
-
- do {
- bOut.write(ch);
- if (ch == '\r' || ch == '\n') {
- lookAhead = readPassedEOL(bOut, ch, fIn);
- break;
- }
- }
- while ((ch = fIn.read()) >= 0);
-
- if (ch < 0) {
- lookAhead = -1;
- }
-
- return lookAhead;
- }
-
- private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
- throws IOException {
- int lookAhead = fIn.read();
-
- if (lastCh == '\r' && lookAhead == '\n') {
- bOut.write(lookAhead);
- lookAhead = fIn.read();
- }
-
- return lookAhead;
- }
-
- private static void processLine(PGPSignature sig, byte[] line)
- throws SignatureException, IOException {
- int length = getLengthWithoutWhiteSpace(line);
- if (length > 0) {
- sig.update(line, 0, length);
- }
- }
-
- private static void processLine(OutputStream aOut, PGPSignatureGenerator sGen, byte[] line)
- throws SignatureException, IOException {
- int length = getLengthWithoutWhiteSpace(line);
- if (length > 0) {
- sGen.update(line, 0, length);
- }
-
- aOut.write(line, 0, line.length);
- }
-
- private static int getLengthWithoutSeparator(byte[] line) {
- int end = line.length - 1;
-
- while (end >= 0 && isLineEnding(line[end])) {
- end--;
- }
-
- return end + 1;
- }
-
- private static boolean isLineEnding(byte b) {
- return b == '\r' || b == '\n';
- }
-
- private static int getLengthWithoutWhiteSpace(byte[] line) {
- int end = line.length - 1;
-
- while (end >= 0 && isWhiteSpace(line[end])) {
- end--;
- }
-
- return end + 1;
- }
-
- private static boolean isWhiteSpace(byte b) {
- return b == '\r' || b == '\n' || b == '\t' || b == ' ';
- }
-
- private static byte[] getLineSeparator() {
- String nl = System.getProperty("line.separator");
- byte[] nlBytes = new byte[nl.length()];
-
- for (int i = 0; i != nlBytes.length; i++) {
- nlBytes[i] = (byte)nl.charAt(i);
- }
-
- return nlBytes;
- }
-
- public static String getVersion(Context context) {
- if (VERSION != null) {
- return VERSION;
- }
- try {
- PackageInfo pi = context.getPackageManager().getPackageInfo(mApgPackageName, 0);
- VERSION = pi.versionName;
- return VERSION;
- } catch (NameNotFoundException e) {
- // unpossible!
- return "0.0.0";
- }
- }
-
- public static String getFullVersion(Context context) {
- return "APG v" + getVersion(context);
- }
-
- public static String generateRandomString(int length) {
- SecureRandom random = new SecureRandom();
- /*
- try {
- random = SecureRandom.getInstance("SHA1PRNG", new BouncyCastleProvider());
- } catch (NoSuchAlgorithmException e) {
- // TODO: need to handle this case somehow
- return null;
- }*/
- byte bytes[] = new byte[length];
- random.nextBytes(bytes);
- String result = "";
- for (int i = 0; i < length; ++i) {
- int v = ((int)bytes[i] + 256) % 64;
- if (v < 10) {
- result += (char)((int)'0' + v);
- } else if (v < 36) {
- result += (char)((int)'A' + v - 10);
- } else if (v < 62) {
- result += (char)((int)'a' + v - 36);
- } else if (v == 62) {
- result += '_';
- } else if (v == 63) {
- result += '.';
- }
- }
- return result;
- }
-
- static long getLengthOfStream(InputStream in) throws IOException {
- long size = 0;
- long n = 0;
- byte dummy[] = new byte[0x10000];
- while ((n = in.read(dummy)) > 0) {
- size += n;
- }
- return size;
- }
-}
+/*
+ * 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.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.SignatureException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+import java.util.regex.Pattern;
+
+import org.bouncycastle2.bcpg.ArmoredInputStream;
+import org.bouncycastle2.bcpg.ArmoredOutputStream;
+import org.bouncycastle2.bcpg.BCPGOutputStream;
+import org.bouncycastle2.bcpg.CompressionAlgorithmTags;
+import org.bouncycastle2.bcpg.HashAlgorithmTags;
+import org.bouncycastle2.bcpg.SymmetricKeyAlgorithmTags;
+import org.bouncycastle2.bcpg.sig.KeyFlags;
+import org.bouncycastle2.jce.provider.BouncyCastleProvider;
+import org.bouncycastle2.jce.spec.ElGamalParameterSpec;
+import org.bouncycastle2.openpgp.PGPCompressedData;
+import org.bouncycastle2.openpgp.PGPCompressedDataGenerator;
+import org.bouncycastle2.openpgp.PGPEncryptedData;
+import org.bouncycastle2.openpgp.PGPEncryptedDataGenerator;
+import org.bouncycastle2.openpgp.PGPEncryptedDataList;
+import org.bouncycastle2.openpgp.PGPException;
+import org.bouncycastle2.openpgp.PGPKeyPair;
+import org.bouncycastle2.openpgp.PGPKeyRingGenerator;
+import org.bouncycastle2.openpgp.PGPLiteralData;
+import org.bouncycastle2.openpgp.PGPLiteralDataGenerator;
+import org.bouncycastle2.openpgp.PGPObjectFactory;
+import org.bouncycastle2.openpgp.PGPOnePassSignature;
+import org.bouncycastle2.openpgp.PGPOnePassSignatureList;
+import org.bouncycastle2.openpgp.PGPPBEEncryptedData;
+import org.bouncycastle2.openpgp.PGPPrivateKey;
+import org.bouncycastle2.openpgp.PGPPublicKey;
+import org.bouncycastle2.openpgp.PGPPublicKeyEncryptedData;
+import org.bouncycastle2.openpgp.PGPPublicKeyRing;
+import org.bouncycastle2.openpgp.PGPSecretKey;
+import org.bouncycastle2.openpgp.PGPSecretKeyRing;
+import org.bouncycastle2.openpgp.PGPSignature;
+import org.bouncycastle2.openpgp.PGPSignatureGenerator;
+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.DataProvider;
+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.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.ViewGroup;
+
+public class Apg {
+ private static final String mApgPackageName = "org.thialfihar.android.apg";
+
+ public static class Intent {
+ public static final String DECRYPT = "org.thialfihar.android.apg.intent.DECRYPT";
+ 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 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 IMPORT = "org.thialfihar.android.apg.intent.IMPORT";
+ }
+
+ public static final String EXTRA_TEXT = "text";
+ 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_DECRYPTED_DATA = "decryptedData";
+ public static final String EXTRA_ENCRYPTED_MESSAGE = "encryptedMessage";
+ public static final String EXTRA_ENCRYPTED_DATA = "encryptedData";
+ public static final String EXTRA_RESULT_URI = "resultUri";
+ 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 final String EXTRA_ASCII_ARMOUR = "asciiArmour";
+ public static final String EXTRA_BINARY = "binary";
+
+ public static final String AUTHORITY = DataProvider.AUTHORITY;
+
+ public static final Uri CONTENT_URI_SECRET_KEY_RINGS =
+ Uri.parse("content://" + AUTHORITY + "/key_rings/secret/");
+ public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID =
+ Uri.parse("content://" + AUTHORITY + "/key_rings/secret/key_id/");
+ public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_EMAILS =
+ Uri.parse("content://" + AUTHORITY + "/key_rings/secret/emails/");
+
+ public static final Uri CONTENT_URI_PUBLIC_KEY_RINGS =
+ Uri.parse("content://" + AUTHORITY + "/key_rings/public/");
+ public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_KEY_ID =
+ Uri.parse("content://" + AUTHORITY + "/key_rings/public/key_id/");
+ public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS =
+ Uri.parse("content://" + AUTHORITY + "/key_rings/public/emails/");
+
+ private static String VERSION = null;
+
+ private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS =
+ new int[] {
+ SymmetricKeyAlgorithmTags.AES_256,
+ SymmetricKeyAlgorithmTags.AES_192,
+ SymmetricKeyAlgorithmTags.AES_128,
+ SymmetricKeyAlgorithmTags.CAST5,
+ SymmetricKeyAlgorithmTags.TRIPLE_DES };
+ private static final int[] PREFERRED_HASH_ALGORITHMS =
+ new int[] {
+ HashAlgorithmTags.SHA1,
+ HashAlgorithmTags.SHA256,
+ HashAlgorithmTags.RIPEMD160 };
+ private static final int[] PREFERRED_COMPRESSION_ALGORITHMS =
+ new int[] {
+ CompressionAlgorithmTags.ZLIB,
+ CompressionAlgorithmTags.BZIP2,
+ CompressionAlgorithmTags.ZIP };
+
+ 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);
+
+ 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;
+
+ public GeneralException(String message) {
+ super(message);
+ }
+ }
+
+ public static class NoAsymmetricEncryptionException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ public NoAsymmetricEncryptionException() {
+ super();
+ }
+ }
+
+ public static void initialize(Context context) {
+ if (mDatabase == null) {
+ mDatabase = new Database(context);
+ }
+ }
+
+ public static Database getDatabase() {
+ return mDatabase;
+ }
+
+ public static void setEditPassPhrase(String passPhrase) {
+ mEditPassPhrase = passPhrase;
+ }
+
+ public static String getEditPassPhrase() {
+ return mEditPassPhrase;
+ }
+
+ public static void setCachedPassPhrase(long keyId, String passPhrase) {
+ mPassPhraseCache.put(keyId, new CachedPassPhrase(new Date().getTime(), passPhrase));
+ }
+
+ public static String getCachedPassPhrase(long keyId) {
+ long realId = keyId;
+ if (realId != Id.key.symmetric) {
+ PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
+ if (keyRing == null) {
+ return null;
+ }
+ PGPSecretKey masterKey = getMasterKey(keyRing);
+ if (masterKey == null) {
+ return null;
+ }
+ realId = masterKey.getKeyID();
+ }
+ CachedPassPhrase cpp = mPassPhraseCache.get(realId);
+ if (cpp == null) {
+ return null;
+ }
+ // set it again to reset the cache life cycle
+ setCachedPassPhrase(realId, cpp.passPhrase);
+ return cpp.passPhrase;
+ }
+
+ 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()) {
+ 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,
+ int algorithmChoice, int keySize, String passPhrase,
+ PGPSecretKey masterKey)
+ throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
+ GeneralException, InvalidAlgorithmParameterException {
+
+ if (keySize < 512) {
+ throw new GeneralException(context.getString(R.string.error_keySizeMinimum512bit));
+ }
+
+ Security.addProvider(new BouncyCastleProvider());
+
+ if (passPhrase == null) {
+ passPhrase = "";
+ }
+
+ int algorithm = 0;
+ KeyPairGenerator keyGen = null;
+
+ switch (algorithmChoice) {
+ case Id.choice.algorithm.dsa: {
+ keyGen = KeyPairGenerator.getInstance("DSA", new BouncyCastleProvider());
+ keyGen.initialize(keySize, new SecureRandom());
+ algorithm = PGPPublicKey.DSA;
+ break;
+ }
+
+ case Id.choice.algorithm.elgamal: {
+ if (masterKey == null) {
+ throw new GeneralException(context.getString(R.string.error_masterKeyMustNotBeElGamal));
+ }
+ keyGen = KeyPairGenerator.getInstance("ELGAMAL", new BouncyCastleProvider());
+ BigInteger p = Primes.getBestPrime(keySize);
+ BigInteger g = new BigInteger("2");
+
+ ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
+
+ keyGen.initialize(elParams);
+ algorithm = PGPPublicKey.ELGAMAL_ENCRYPT;
+ break;
+ }
+
+ case Id.choice.algorithm.rsa: {
+ keyGen = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
+ keyGen.initialize(keySize, new SecureRandom());
+
+ algorithm = PGPPublicKey.RSA_GENERAL;
+ break;
+ }
+
+ default: {
+ throw new GeneralException(context.getString(R.string.error_unknownAlgorithmChoice));
+ }
+ }
+
+ PGPKeyPair keyPair = new PGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date());
+
+ PGPSecretKey secretKey = null;
+ if (masterKey == null) {
+ // enough for now, as we assemble the key again later anyway
+ secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, keyPair, "",
+ PGPEncryptedData.CAST5, passPhrase.toCharArray(),
+ null, null,
+ new SecureRandom(), new BouncyCastleProvider().getName());
+
+ } else {
+ PGPPublicKey tmpKey = masterKey.getPublicKey();
+ PGPPublicKey masterPublicKey =
+ new PGPPublicKey(tmpKey.getAlgorithm(),
+ tmpKey.getKey(new BouncyCastleProvider()),
+ tmpKey.getCreationTime());
+ PGPPrivateKey masterPrivateKey =
+ masterKey.extractPrivateKey(passPhrase.toCharArray(),
+ new BouncyCastleProvider());
+
+ PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
+ PGPKeyRingGenerator ringGen =
+ new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
+ masterKeyPair, "",
+ PGPEncryptedData.CAST5, passPhrase.toCharArray(),
+ null, null,
+ new SecureRandom(), new BouncyCastleProvider().getName());
+ ringGen.addSubKey(keyPair);
+ PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing();
+ Iterator it = secKeyRing.getSecretKeys();
+ // first one is the master key
+ it.next();
+ secretKey = (PGPSecretKey) it.next();
+ }
+
+ return secretKey;
+ }
+
+ private static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) {
+ GregorianCalendar tmp = new GregorianCalendar();
+ tmp.setTime(first.getTime());
+ long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
+ tmp.add(Calendar.DAY_OF_MONTH, (int)numDays);
+ while (tmp.before(second)) {
+ tmp.add(Calendar.DAY_OF_MONTH, 1);
+ ++numDays;
+ }
+ return numDays;
+ }
+
+ public static void buildSecretKey(Activity context,
+ SectionView userIdsView, SectionView keysView,
+ String oldPassPhrase, String newPassPhrase,
+ ProgressDialogUpdater progress)
+ throws Apg.GeneralException, NoSuchProviderException, PGPException,
+ NoSuchAlgorithmException, SignatureException, IOException, Database.GeneralException {
+
+ progress.setProgress(R.string.progress_buildingKey, 0, 100);
+
+ Security.addProvider(new BouncyCastleProvider());
+
+ if (oldPassPhrase == null || oldPassPhrase.equals("")) {
+ oldPassPhrase = "";
+ }
+
+ if (newPassPhrase == null || newPassPhrase.equals("")) {
+ newPassPhrase = "";
+ }
+
+ Vector<String> userIds = new Vector<String>();
+ Vector<PGPSecretKey> keys = new Vector<PGPSecretKey>();
+
+ ViewGroup userIdEditors = userIdsView.getEditors();
+ ViewGroup keyEditors = keysView.getEditors();
+
+ boolean gotMainUserId = false;
+ for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
+ UserIdEditor editor = (UserIdEditor)userIdEditors.getChildAt(i);
+ String userId = null;
+ try {
+ userId = editor.getValue();
+ } catch (UserIdEditor.NoNameException e) {
+ throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAName));
+ } catch (UserIdEditor.NoEmailException e) {
+ throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress));
+ } catch (UserIdEditor.InvalidEmailException e) {
+ throw new Apg.GeneralException("" + e);
+ }
+
+ if (userId.equals("")) {
+ continue;
+ }
+
+ if (editor.isMainUserId()) {
+ userIds.insertElementAt(userId, 0);
+ gotMainUserId = true;
+ } else {
+ userIds.add(userId);
+ }
+ }
+
+ if (userIds.size() == 0) {
+ throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsAUserId));
+ }
+
+ if (!gotMainUserId) {
+ throw new Apg.GeneralException(context.getString(R.string.error_mainUserIdMustNotBeEmpty));
+ }
+
+ if (keyEditors.getChildCount() == 0) {
+ throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsMasterKey));
+ }
+
+ for (int i = 0; i < keyEditors.getChildCount(); ++i) {
+ KeyEditor editor = (KeyEditor)keyEditors.getChildAt(i);
+ keys.add(editor.getValue());
+ }
+
+ progress.setProgress(R.string.progress_preparingMasterKey, 10, 100);
+ KeyEditor keyEditor = (KeyEditor) keyEditors.getChildAt(0);
+ int usageId = keyEditor.getUsage();
+ boolean canSign = (usageId == Id.choice.usage.sign_only ||
+ usageId == Id.choice.usage.sign_and_encrypt);
+ boolean canEncrypt = (usageId == Id.choice.usage.encrypt_only ||
+ usageId == Id.choice.usage.sign_and_encrypt);
+
+ String mainUserId = userIds.get(0);
+
+ PGPSecretKey masterKey = keys.get(0);
+ PGPPublicKey tmpKey = masterKey.getPublicKey();
+ PGPPublicKey masterPublicKey =
+ new PGPPublicKey(tmpKey.getAlgorithm(),
+ tmpKey.getKey(new BouncyCastleProvider()),
+ tmpKey.getCreationTime());
+ PGPPrivateKey masterPrivateKey =
+ masterKey.extractPrivateKey(oldPassPhrase.toCharArray(),
+ new BouncyCastleProvider());
+
+ progress.setProgress(R.string.progress_certifyingMasterKey, 20, 100);
+ for (int i = 0; i < userIds.size(); ++i) {
+ String userId = userIds.get(i);
+
+ PGPSignatureGenerator sGen =
+ new PGPSignatureGenerator(masterPublicKey.getAlgorithm(),
+ HashAlgorithmTags.SHA1, new BouncyCastleProvider());
+
+ sGen.initSign(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+
+ PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
+ }
+
+ // TODO: cross-certify the master key with every sub key
+
+ PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA;
+ if (canEncrypt) {
+ keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
+ }
+ hashedPacketsGen.setKeyFlags(true, keyFlags);
+
+ hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
+ 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 = getNumDaysBetween(creationDate, expiryDate);
+ if (numDays <= 0) {
+ throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
+ }
+ hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
+ }
+
+ progress.setProgress(R.string.progress_buildingMasterKeyRing, 30, 100);
+ PGPKeyRingGenerator keyGen =
+ new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
+ masterKeyPair, mainUserId,
+ PGPEncryptedData.CAST5, newPassPhrase.toCharArray(),
+ hashedPacketsGen.generate(), unhashedPacketsGen.generate(),
+ new SecureRandom(), new BouncyCastleProvider().getName());
+
+ progress.setProgress(R.string.progress_addingSubKeys, 40, 100);
+ for (int i = 1; i < keys.size(); ++i) {
+ progress.setProgress(40 + 50 * (i - 1)/ (keys.size() - 1), 100);
+ PGPSecretKey subKey = keys.get(i);
+ keyEditor = (KeyEditor) keyEditors.getChildAt(i);
+ PGPPublicKey subPublicKey = subKey.getPublicKey();
+ PGPPrivateKey subPrivateKey =
+ subKey.extractPrivateKey(oldPassPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ PGPKeyPair subKeyPair =
+ new PGPKeyPair(subPublicKey.getAlgorithm(),
+ subPublicKey.getKey(new BouncyCastleProvider()),
+ subPrivateKey.getKey(),
+ subPublicKey.getCreationTime());
+
+ hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ keyFlags = 0;
+ usageId = keyEditor.getUsage();
+ canSign = (usageId == Id.choice.usage.sign_only ||
+ usageId == Id.choice.usage.sign_and_encrypt);
+ canEncrypt = (usageId == Id.choice.usage.encrypt_only ||
+ usageId == Id.choice.usage.sign_and_encrypt);
+ if (canSign) {
+ keyFlags |= KeyFlags.SIGN_DATA;
+ }
+ if (canEncrypt) {
+ keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
+ }
+ 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 = getNumDaysBetween(creationDate, expiryDate);
+ if (numDays <= 0) {
+ throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
+ }
+ hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
+ }
+
+ keyGen.addSubKey(subKeyPair,
+ hashedPacketsGen.generate(), unhashedPacketsGen.generate());
+ }
+
+ PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
+ PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
+
+ progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
+ mDatabase.saveKeyRing(secretKeyRing);
+ mDatabase.saveKeyRing(publicKeyRing);
+
+ progress.setProgress(R.string.progress_done, 100, 100);
+ }
+
+ public static Bundle importKeyRings(Activity context, int type,
+ InputData data,
+ ProgressDialogUpdater progress)
+ throws GeneralException, FileNotFoundException, PGPException, IOException {
+ Bundle returnData = new Bundle();
+
+ if (type == Id.type.secret_key) {
+ progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
+ } else {
+ progress.setProgress(R.string.progress_importingPublicKeys, 0, 100);
+ }
+
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
+ }
+
+ PositionAwareInputStream progressIn = new PositionAwareInputStream(data.getInputStream());
+ // 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;
+ 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;
+ }
+ 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;
+
+ 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;
+ }
+ progress.setProgress((int)(100 * progressIn.position() / data.getSize()), 100);
+ obj = objectFactory.nextObject();
+ }
+ }
+ } catch (EOFException e) {
+ // nothing to do, we are done
+ }
+
+ returnData.putInt("added", newKeys);
+ returnData.putInt("updated", oldKeys);
+
+ progress.setProgress(R.string.progress_done, 100, 100);
+
+ return returnData;
+ }
+
+ public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds,
+ OutputStream outStream,
+ ProgressDialogUpdater progress)
+ throws GeneralException, FileNotFoundException, PGPException, IOException {
+ Bundle returnData = new Bundle();
+
+ if (keyRingIds.size() == 1) {
+ progress.setProgress(R.string.progress_exportingKey, 0, 100);
+ } else {
+ progress.setProgress(R.string.progress_exportingKeys, 0, 100);
+ }
+
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
+ }
+ ArmoredOutputStream out = new ArmoredOutputStream(outStream);
+
+ int numKeys = 0;
+ 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;
+
+ if (obj instanceof PGPSecretKeyRing) {
+ secretKeyRing = (PGPSecretKeyRing) obj;
+ secretKeyRing.encode(out);
+ } else if (obj instanceof PGPPublicKeyRing) {
+ publicKeyRing = (PGPPublicKeyRing) obj;
+ publicKeyRing.encode(out);
+ } else {
+ continue;
+ }
+ ++numKeys;
+ }
+ out.close();
+ returnData.putInt("exported", numKeys);
+
+ progress.setProgress(R.string.progress_done, 100, 100);
+
+ return returnData;
+ }
+
+ public static Date getCreationDate(PGPPublicKey key) {
+ return key.getCreationTime();
+ }
+
+ public static Date getCreationDate(PGPSecretKey key) {
+ return key.getPublicKey().getCreationTime();
+ }
+
+ public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
+ if (keyRing == null) {
+ return null;
+ }
+ for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
+ if (key.isMasterKey()) {
+ return key;
+ }
+ }
+
+ return null;
+ }
+
+ public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
+ if (keyRing == null) {
+ return null;
+ }
+ for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
+ if (key.isMasterKey()) {
+ return key;
+ }
+ }
+
+ return null;
+ }
+
+ public static Vector<PGPPublicKey> getEncryptKeys(PGPPublicKeyRing keyRing) {
+ Vector<PGPPublicKey> encryptKeys = new Vector<PGPPublicKey>();
+
+ for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
+ if (isEncryptionKey(key)) {
+ encryptKeys.add(key);
+ }
+ }
+
+ return encryptKeys;
+ }
+
+ public static Vector<PGPSecretKey> getSigningKeys(PGPSecretKeyRing keyRing) {
+ Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
+
+ for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
+ if (isSigningKey(key)) {
+ signingKeys.add(key);
+ }
+ }
+
+ return signingKeys;
+ }
+
+ public static Vector<PGPPublicKey> getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
+ Vector<PGPPublicKey> usableKeys = new Vector<PGPPublicKey>();
+ Vector<PGPPublicKey> encryptKeys = getEncryptKeys(keyRing);
+ PGPPublicKey masterKey = null;
+ for (int i = 0; i < encryptKeys.size(); ++i) {
+ PGPPublicKey key = encryptKeys.get(i);
+ if (!isExpired(key)) {
+ if (key.isMasterKey()) {
+ masterKey = key;
+ } else {
+ usableKeys.add(key);
+ }
+ }
+ }
+ if (masterKey != null) {
+ usableKeys.add(masterKey);
+ }
+ return usableKeys;
+ }
+
+ public static boolean isExpired(PGPPublicKey key) {
+ Date creationDate = getCreationDate(key);
+ Date expiryDate = getExpiryDate(key);
+ Date now = new Date();
+ if (now.compareTo(creationDate) >= 0 &&
+ (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean isExpired(PGPSecretKey key) {
+ return isExpired(key.getPublicKey());
+ }
+
+ public static Vector<PGPSecretKey> getUsableSigningKeys(PGPSecretKeyRing keyRing) {
+ Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
+ Vector<PGPSecretKey> signingKeys = getSigningKeys(keyRing);
+ PGPSecretKey masterKey = null;
+ for (int i = 0; i < signingKeys.size(); ++i) {
+ PGPSecretKey key = signingKeys.get(i);
+ if (key.isMasterKey()) {
+ masterKey = key;
+ } else {
+ usableKeys.add(key);
+ }
+ }
+ if (masterKey != null) {
+ usableKeys.add(masterKey);
+ }
+ return usableKeys;
+ }
+
+ public static Date getExpiryDate(PGPPublicKey key) {
+ Date creationDate = getCreationDate(key);
+ if (key.getValidDays() == 0) {
+ // no expiry
+ return null;
+ }
+ Calendar calendar = GregorianCalendar.getInstance();
+ calendar.setTime(creationDate);
+ calendar.add(Calendar.DATE, key.getValidDays());
+ Date expiryDate = calendar.getTime();
+
+ return expiryDate;
+ }
+
+ public static Date getExpiryDate(PGPSecretKey key) {
+ return getExpiryDate(key.getPublicKey());
+ }
+
+ public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
+ PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
+ if (keyRing == null) {
+ return null;
+ }
+ Vector<PGPPublicKey> encryptKeys = getUsableEncryptKeys(keyRing);
+ if (encryptKeys.size() == 0) {
+ return null;
+ }
+ return encryptKeys.get(0);
+ }
+
+ public static PGPSecretKey getSigningKey(long masterKeyId) {
+ PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
+ if (keyRing == null) {
+ return null;
+ }
+ Vector<PGPSecretKey> signingKeys = getUsableSigningKeys(keyRing);
+ if (signingKeys.size() == 0) {
+ return null;
+ }
+ return signingKeys.get(0);
+ }
+
+ public static String getMainUserId(PGPPublicKey key) {
+ for (String userId : new IterableIterator<String>(key.getUserIDs())) {
+ return userId;
+ }
+ return null;
+ }
+
+ public static String getMainUserId(PGPSecretKey key) {
+ for (String userId : new IterableIterator<String>(key.getUserIDs())) {
+ return userId;
+ }
+ return null;
+ }
+
+ public static String getMainUserIdSafe(Context context, PGPPublicKey key) {
+ String userId = getMainUserId(key);
+ if (userId == null) {
+ userId = context.getResources().getString(R.string.unknownUserId);
+ }
+ return userId;
+ }
+
+ public static String getMainUserIdSafe(Context context, PGPSecretKey key) {
+ String userId = getMainUserId(key);
+ if (userId == null) {
+ userId = context.getResources().getString(R.string.unknownUserId);
+ }
+ return userId;
+ }
+
+ public static boolean isEncryptionKey(PGPPublicKey key) {
+ if (!key.isEncryptionKey()) {
+ return false;
+ }
+
+ if (key.getVersion() <= 3) {
+ // this must be true now
+ return key.isEncryptionKey();
+ }
+
+ // special cases
+ if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
+ return true;
+ }
+
+ if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
+ return true;
+ }
+
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+
+ if (hashed != null &&(hashed.getKeyFlags() &
+ (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
+ return true;
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+
+ if (unhashed != null &&(unhashed.getKeyFlags() &
+ (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isEncryptionKey(PGPSecretKey key) {
+ return isEncryptionKey(key.getPublicKey());
+ }
+
+ public static boolean isSigningKey(PGPPublicKey key) {
+ if (key.getVersion() <= 3) {
+ return true;
+ }
+
+ // special case
+ if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
+ return true;
+ }
+
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+
+ if (hashed != null && (hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
+ return true;
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+
+ if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean isSigningKey(PGPSecretKey key) {
+ return isSigningKey(key.getPublicKey());
+ }
+
+ 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 (algorithm) {
+ case PGPPublicKey.RSA_ENCRYPT:
+ case PGPPublicKey.RSA_GENERAL:
+ case PGPPublicKey.RSA_SIGN: {
+ algorithmStr = "RSA";
+ break;
+ }
+
+ case PGPPublicKey.DSA: {
+ algorithmStr = "DSA";
+ break;
+ }
+
+ case PGPPublicKey.ELGAMAL_ENCRYPT:
+ case PGPPublicKey.ELGAMAL_GENERAL: {
+ algorithmStr = "ElGamal";
+ break;
+ }
+
+ default: {
+ algorithmStr = "???";
+ break;
+ }
+ }
+ return algorithmStr + ", " + keySize + "bit";
+ }
+
+ public static void deleteKey(int keyRingId) {
+ mDatabase.deleteKeyRing(keyRingId);
+ }
+
+ public static Object getKeyRing(int keyRingId) {
+ return mDatabase.getKeyRing(keyRingId);
+ }
+
+ 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 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 PGPSecretKey getSecretKey(long keyId) {
+ PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
+ if (keyRing == null) {
+ return null;
+ }
+ return keyRing.getSecretKey(keyId);
+ }
+
+ 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;
+ }
+ }
+
+ 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());
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return userId;
+ }
+
+ public static void encrypt(Context context,
+ InputData data, OutputStream outStream,
+ boolean armored,
+ long encryptionKeyIds[], long signatureKeyId,
+ String signaturePassPhrase,
+ ProgressDialogUpdater progress,
+ int symmetricAlgorithm, int hashAlgorithm, int compression,
+ String passPhrase)
+ throws IOException, GeneralException, PGPException, NoSuchProviderException,
+ NoSuchAlgorithmException, SignatureException {
+ Security.addProvider(new BouncyCastleProvider());
+
+ if (encryptionKeyIds == null) {
+ encryptionKeyIds = new long[0];
+ }
+
+ ArmoredOutputStream armorOut = null;
+ OutputStream out = null;
+ OutputStream encryptOut = null;
+ if (armored) {
+ armorOut = new ArmoredOutputStream(outStream);
+ armorOut.setHeader("Version", getFullVersion(context));
+ out = armorOut;
+ } else {
+ out = outStream;
+ }
+ PGPSecretKey signingKey = null;
+ PGPSecretKeyRing signingKeyRing = null;
+ PGPPrivateKey signaturePrivateKey = null;
+
+ if (encryptionKeyIds.length == 0 && passPhrase == null) {
+ throw new GeneralException(context.getString(R.string.error_noEncryptionKeysOrPassPhrase));
+ }
+
+ if (signatureKeyId != 0) {
+ signingKeyRing = getSecretKeyRing(signatureKeyId);
+ signingKey = getSigningKey(signatureKeyId);
+ if (signingKey == null) {
+ throw new GeneralException(context.getString(R.string.error_signatureFailed));
+ }
+
+ if (signaturePassPhrase == null) {
+ throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
+ }
+ progress.setProgress(R.string.progress_extractingSignatureKey, 0, 100);
+ signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ }
+
+ PGPSignatureGenerator signatureGenerator = null;
+ progress.setProgress(R.string.progress_preparingStreams, 5, 100);
+ // encrypt and compress input file content
+ PGPEncryptedDataGenerator cPk =
+ new PGPEncryptedDataGenerator(symmetricAlgorithm, true, new SecureRandom(),
+ new BouncyCastleProvider());
+
+ if (encryptionKeyIds.length == 0) {
+ // symmetric encryption
+ cPk.addMethod(passPhrase.toCharArray());
+ }
+ for (int i = 0; i < encryptionKeyIds.length; ++i) {
+ PGPPublicKey key = getEncryptPublicKey(encryptionKeyIds[i]);
+ if (key != null) {
+ cPk.addMethod(key);
+ }
+ }
+ encryptOut = cPk.open(out, new byte[1 << 16]);
+
+ if (signatureKeyId != 0) {
+ progress.setProgress(R.string.progress_preparingSignature, 10, 100);
+ signatureGenerator =
+ new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
+ hashAlgorithm,
+ new BouncyCastleProvider());
+ signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
+ String userId = getMainUserId(getMasterKey(signingKeyRing));
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ spGen.setSignerUserID(false, userId);
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+ }
+
+ PGPCompressedDataGenerator compressGen = null;
+ BCPGOutputStream bcpgOut = null;
+ if (compression == Id.choice.compression.none) {
+ bcpgOut = new BCPGOutputStream(encryptOut);
+ } else {
+ compressGen = new PGPCompressedDataGenerator(compression);
+ bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
+ }
+ if (signatureKeyId != 0) {
+ signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
+ }
+
+ PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
+ // file name not needed, so empty string
+ OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "",
+ new Date(), new byte[1 << 16]);
+
+ progress.setProgress(R.string.progress_encrypting, 20, 100);
+ long done = 0;
+ int n = 0;
+ byte[] buffer = new byte[1 << 16];
+ InputStream in = data.getInputStream();
+ while ((n = in.read(buffer)) > 0) {
+ pOut.write(buffer, 0, n);
+ if (signatureKeyId != 0) {
+ signatureGenerator.update(buffer, 0, n);
+ }
+ done += n;
+ if (data.getSize() != 0) {
+ progress.setProgress((int) (20 + (95 - 20) * done / data.getSize()), 100);
+ }
+ }
+
+ literalGen.close();
+
+ if (signatureKeyId != 0) {
+ progress.setProgress(R.string.progress_generatingSignature, 95, 100);
+ signatureGenerator.generate().encode(pOut);
+ }
+ if (compressGen != null) {
+ compressGen.close();
+ }
+ encryptOut.close();
+ if (armored) {
+ armorOut.close();
+ }
+
+ progress.setProgress(R.string.progress_done, 100, 100);
+ }
+
+ public static void signText(Context context,
+ InputData data, OutputStream outStream,
+ long signatureKeyId, String signaturePassPhrase,
+ int hashAlgorithm,
+ ProgressDialogUpdater progress)
+ throws GeneralException, PGPException, IOException, NoSuchAlgorithmException,
+ SignatureException {
+ Security.addProvider(new BouncyCastleProvider());
+
+ ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
+ armorOut.setHeader("Version", getFullVersion(context));
+
+ PGPSecretKey signingKey = null;
+ PGPSecretKeyRing signingKeyRing = null;
+ PGPPrivateKey signaturePrivateKey = null;
+
+ if (signatureKeyId == 0) {
+ throw new GeneralException(context.getString(R.string.error_noSignatureKey));
+ }
+
+ signingKeyRing = getSecretKeyRing(signatureKeyId);
+ signingKey = getSigningKey(signatureKeyId);
+ if (signingKey == null) {
+ throw new GeneralException(context.getString(R.string.error_signatureFailed));
+ }
+
+ if (signaturePassPhrase == null) {
+ throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
+ }
+ signaturePrivateKey =
+ signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
+ new BouncyCastleProvider());
+
+ PGPSignatureGenerator signatureGenerator = null;
+ progress.setProgress(R.string.progress_preparingStreams, 0, 100);
+
+ progress.setProgress(R.string.progress_preparingSignature, 30, 100);
+ signatureGenerator =
+ new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
+ hashAlgorithm,
+ new BouncyCastleProvider());
+ signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
+ String userId = getMainUserId(getMasterKey(signingKeyRing));
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ spGen.setSignerUserID(false, userId);
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+
+ progress.setProgress(R.string.progress_signing, 40, 100);
+
+ armorOut.beginClearText(hashAlgorithm);
+
+ ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
+ InputStream inStream = data.getInputStream();
+ int lookAhead = readInputLine(lineOut, inStream);
+
+ processLine(armorOut, signatureGenerator, lineOut.toByteArray());
+
+ if (lookAhead != -1) {
+ do {
+ lookAhead = readInputLine(lineOut, lookAhead, inStream);
+
+ signatureGenerator.update((byte)'\r');
+ signatureGenerator.update((byte)'\n');
+
+ processLine(armorOut, signatureGenerator, lineOut.toByteArray());
+ }
+ while (lookAhead != -1);
+ }
+
+ armorOut.endClearText();
+
+ BCPGOutputStream bOut = new BCPGOutputStream(armorOut);
+ signatureGenerator.generate().encode(bOut);
+ armorOut.close();
+
+ progress.setProgress(R.string.progress_done, 100, 100);
+ }
+
+ public static long getDecryptionKeyId(Context context, InputData data)
+ throws GeneralException, NoAsymmetricEncryptionException, IOException {
+ InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
+ PGPObjectFactory pgpF = new PGPObjectFactory(in);
+ PGPEncryptedDataList enc;
+ Object o = pgpF.nextObject();
+
+ // the first object might be a PGP marker packet.
+ if (o instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList) o;
+ } else {
+ enc = (PGPEncryptedDataList) pgpF.nextObject();
+ }
+
+ if (enc == null) {
+ throw new GeneralException(context.getString(R.string.error_invalidData));
+ }
+
+ // TODO: currently we always only look at the first known key
+ // find the secret key
+ PGPSecretKey secretKey = null;
+ Iterator it = enc.getEncryptedDataObjects();
+ boolean gotAsymmetricEncryption = false;
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPublicKeyEncryptedData) {
+ gotAsymmetricEncryption = true;
+ PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
+ secretKey = getSecretKey(pbe.getKeyID());
+ if (secretKey != null) {
+ break;
+ }
+ }
+ }
+
+ if (!gotAsymmetricEncryption) {
+ throw new NoAsymmetricEncryptionException();
+ }
+
+ if (secretKey == null) {
+ return Id.key.none;
+ }
+
+ return secretKey.getKeyID();
+ }
+
+ public static boolean hasSymmetricEncryption(Context context, InputData data)
+ throws GeneralException, IOException {
+ InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
+ PGPObjectFactory pgpF = new PGPObjectFactory(in);
+ PGPEncryptedDataList enc;
+ Object o = pgpF.nextObject();
+
+ // the first object might be a PGP marker packet.
+ if (o instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList) o;
+ } else {
+ enc = (PGPEncryptedDataList) pgpF.nextObject();
+ }
+
+ if (enc == null) {
+ throw new GeneralException(context.getString(R.string.error_invalidData));
+ }
+
+ Iterator it = enc.getEncryptedDataObjects();
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPBEEncryptedData) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static Bundle decrypt(Context context,
+ InputData data, OutputStream outStream,
+ String passPhrase, ProgressDialogUpdater progress,
+ boolean assumeSymmetric)
+ throws IOException, GeneralException, PGPException, SignatureException {
+ if (passPhrase == null) {
+ passPhrase = "";
+ }
+ Bundle returnData = new Bundle();
+ InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
+ PGPObjectFactory pgpF = new PGPObjectFactory(in);
+ PGPEncryptedDataList enc;
+ Object o = pgpF.nextObject();
+ long signatureKeyId = 0;
+
+ int currentProgress = 0;
+ progress.setProgress(R.string.progress_readingData, currentProgress, 100);
+
+ if (o instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList) o;
+ } else {
+ enc = (PGPEncryptedDataList) pgpF.nextObject();
+ }
+
+ if (enc == null) {
+ throw new GeneralException(context.getString(R.string.error_invalidData));
+ }
+
+ InputStream clear = null;
+ PGPEncryptedData encryptedData = null;
+
+ currentProgress += 5;
+
+ // TODO: currently we always only look at the first known key or symmetric encryption,
+ // there might be more...
+ if (assumeSymmetric) {
+ PGPPBEEncryptedData pbe = null;
+ Iterator it = enc.getEncryptedDataObjects();
+ // find secret key
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPBEEncryptedData) {
+ pbe = (PGPPBEEncryptedData) obj;
+ break;
+ }
+ }
+
+ if (pbe == null) {
+ throw new GeneralException(context.getString(R.string.error_noSymmetricEncryptionPacket));
+ }
+
+ progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
+ clear = pbe.getDataStream(passPhrase.toCharArray(), new BouncyCastleProvider());
+ encryptedData = pbe;
+ currentProgress += 5;
+ } else {
+ progress.setProgress(R.string.progress_findingKey, currentProgress, 100);
+ PGPPublicKeyEncryptedData pbe = null;
+ PGPSecretKey secretKey = null;
+ Iterator it = enc.getEncryptedDataObjects();
+ // find secret key
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPublicKeyEncryptedData) {
+ PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
+ secretKey = getSecretKey(encData.getKeyID());
+ if (secretKey != null) {
+ pbe = encData;
+ break;
+ }
+ }
+ }
+
+ if (secretKey == null) {
+ throw new GeneralException(context.getString(R.string.error_noSecretKeyFound));
+ }
+
+ currentProgress += 5;
+ progress.setProgress(R.string.progress_extractingKey, currentProgress, 100);
+ PGPPrivateKey privateKey = null;
+ try {
+ privateKey = secretKey.extractPrivateKey(passPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ } catch (PGPException e) {
+ throw new PGPException(context.getString(R.string.error_wrongPassPhrase));
+ }
+ currentProgress += 5;
+ progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
+ clear = pbe.getDataStream(privateKey, new BouncyCastleProvider());
+ encryptedData = pbe;
+ currentProgress += 5;
+ }
+
+ PGPObjectFactory plainFact = new PGPObjectFactory(clear);
+ Object dataChunk = plainFact.nextObject();
+ PGPOnePassSignature signature = null;
+ PGPPublicKey signatureKey = null;
+ int signatureIndex = -1;
+
+ if (dataChunk instanceof PGPCompressedData) {
+ progress.setProgress(R.string.progress_decompressingData, currentProgress, 100);
+ PGPObjectFactory fact =
+ new PGPObjectFactory(((PGPCompressedData) dataChunk).getDataStream());
+ dataChunk = fact.nextObject();
+ plainFact = fact;
+ currentProgress += 10;
+ }
+
+ if (dataChunk instanceof PGPOnePassSignatureList) {
+ progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
+ returnData.putBoolean(EXTRA_SIGNATURE, true);
+ PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
+ for (int i = 0; i < sigList.size(); ++i) {
+ signature = sigList.get(i);
+ signatureKey = getPublicKey(signature.getKeyID());
+ if (signatureKeyId == 0) {
+ signatureKeyId = signature.getKeyID();
+ }
+ if (signatureKey == null) {
+ signature = null;
+ } else {
+ signatureIndex = i;
+ signatureKeyId = signature.getKeyID();
+ String userId = null;
+ PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
+ if (sigKeyRing != null) {
+ userId = getMainUserId(getMasterKey(sigKeyRing));
+ }
+ returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
+ break;
+ }
+ }
+
+ returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
+
+ if (signature != null) {
+ signature.initVerify(signatureKey, new BouncyCastleProvider());
+ } else {
+ returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
+ }
+
+ dataChunk = plainFact.nextObject();
+ currentProgress += 10;
+ }
+
+ if (dataChunk instanceof PGPLiteralData) {
+ progress.setProgress(R.string.progress_decrypting, currentProgress, 100);
+ PGPLiteralData literalData = (PGPLiteralData) dataChunk;
+ OutputStream out = outStream;
+
+ byte[] buffer = new byte[1 << 16];
+ InputStream dataIn = literalData.getInputStream();
+
+ int startProgress = currentProgress;
+ int endProgress = 100;
+ if (signature != null) {
+ endProgress = 90;
+ } else if (encryptedData.isIntegrityProtected()) {
+ endProgress = 95;
+ }
+ int n = 0;
+ int done = 0;
+ long startPos = data.getStreamPosition();
+ while ((n = dataIn.read(buffer)) > 0) {
+ out.write(buffer, 0, n);
+ done += n;
+ if (signature != null) {
+ try {
+ signature.update(buffer, 0, n);
+ } catch (SignatureException e) {
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
+ signature = null;
+ }
+ }
+ // unknown size, but try to at least have a moving, slowing down progress bar
+ currentProgress = startProgress + (endProgress - startProgress) * done / (done + 100000);
+ if (data.getSize() - startPos == 0) {
+ currentProgress = endProgress;
+ } else {
+ currentProgress = (int)(startProgress + (endProgress - startProgress) *
+ (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
+ }
+ progress.setProgress(currentProgress, 100);
+ }
+
+ if (signature != null) {
+ progress.setProgress(R.string.progress_verifyingSignature, 90, 100);
+ PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
+ PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex);
+ if (signature.verify(messageSignature)) {
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, true);
+ } else {
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
+ }
+ }
+ }
+
+ // TODO: add integrity somewhere
+ if (encryptedData.isIntegrityProtected()) {
+ progress.setProgress(R.string.progress_verifyingIntegrity, 95, 100);
+ if (encryptedData.verify()) {
+ // passed
+ } else {
+ // failed
+ }
+ } else {
+ // no integrity check
+ }
+
+ progress.setProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ public static Bundle verifyText(Context context,
+ InputData data, OutputStream outStream,
+ ProgressDialogUpdater progress)
+ throws IOException, GeneralException, PGPException, SignatureException {
+ Bundle returnData = new Bundle();
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream());
+
+ progress.setProgress(R.string.progress_done, 0, 100);
+
+ // mostly taken from ClearSignedFileProcessor
+ ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
+ int lookAhead = readInputLine(lineOut, aIn);
+ byte[] lineSep = getLineSeparator();
+
+ byte[] line = lineOut.toByteArray();
+ out.write(line, 0, getLengthWithoutSeparator(line));
+ out.write(lineSep);
+
+ while (lookAhead != -1 && aIn.isClearText()) {
+ lookAhead = readInputLine(lineOut, lookAhead, aIn);
+ line = lineOut.toByteArray();
+ out.write(line, 0, getLengthWithoutSeparator(line));
+ out.write(lineSep);
+ }
+
+ out.close();
+
+ byte[] clearText = out.toByteArray();
+ outStream.write(clearText);
+
+ returnData.putBoolean(EXTRA_SIGNATURE, true);
+
+ progress.setProgress(R.string.progress_processingSignature, 60, 100);
+ PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
+
+ PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
+ if (sigList == null) {
+ throw new GeneralException(context.getString(R.string.error_corruptData));
+ }
+ PGPSignature signature = null;
+ long signatureKeyId = 0;
+ PGPPublicKey signatureKey = null;
+ for (int i = 0; i < sigList.size(); ++i) {
+ signature = sigList.get(i);
+ signatureKey = getPublicKey(signature.getKeyID());
+ if (signatureKeyId == 0) {
+ signatureKeyId = signature.getKeyID();
+ }
+ if (signatureKey == null) {
+ signature = null;
+ } else {
+ signatureKeyId = signature.getKeyID();
+ String userId = null;
+ PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
+ if (sigKeyRing != null) {
+ userId = getMainUserId(getMasterKey(sigKeyRing));
+ }
+ returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
+ break;
+ }
+ }
+
+ returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
+
+ if (signature == null) {
+ returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
+ progress.setProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ signature.initVerify(signatureKey, new BouncyCastleProvider());
+
+ InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText));
+
+ lookAhead = readInputLine(lineOut, sigIn);
+
+ processLine(signature, lineOut.toByteArray());
+
+ if (lookAhead != -1) {
+ do {
+ lookAhead = readInputLine(lineOut, lookAhead, sigIn);
+
+ signature.update((byte)'\r');
+ signature.update((byte)'\n');
+
+ processLine(signature, lineOut.toByteArray());
+ }
+ while (lookAhead != -1);
+ }
+
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, signature.verify());
+
+ progress.setProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ public static int getStreamContent(Context context, InputStream inStream)
+ throws IOException {
+ InputStream in = PGPUtil.getDecoderStream(inStream);
+ PGPObjectFactory pgpF = new PGPObjectFactory(in);
+ Object object = pgpF.nextObject();
+ while (object != null) {
+ if (object instanceof PGPPublicKeyRing ||
+ object instanceof PGPSecretKeyRing) {
+ return Id.content.keys;
+ } else if (object instanceof PGPEncryptedDataList) {
+ return Id.content.encrypted_data;
+ }
+ object = pgpF.nextObject();
+ }
+
+ return Id.content.unknown;
+ }
+
+ // taken from ClearSignedFileProcessor in BC
+ private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
+ throws IOException {
+ bOut.reset();
+
+ int lookAhead = -1;
+ int ch;
+
+ while ((ch = fIn.read()) >= 0) {
+ bOut.write(ch);
+ if (ch == '\r' || ch == '\n') {
+ lookAhead = readPassedEOL(bOut, ch, fIn);
+ break;
+ }
+ }
+
+ return lookAhead;
+ }
+
+ private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn)
+ throws IOException {
+ bOut.reset();
+
+ int ch = lookAhead;
+
+ do {
+ bOut.write(ch);
+ if (ch == '\r' || ch == '\n') {
+ lookAhead = readPassedEOL(bOut, ch, fIn);
+ break;
+ }
+ }
+ while ((ch = fIn.read()) >= 0);
+
+ if (ch < 0) {
+ lookAhead = -1;
+ }
+
+ return lookAhead;
+ }
+
+ private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
+ throws IOException {
+ int lookAhead = fIn.read();
+
+ if (lastCh == '\r' && lookAhead == '\n') {
+ bOut.write(lookAhead);
+ lookAhead = fIn.read();
+ }
+
+ return lookAhead;
+ }
+
+ private static void processLine(PGPSignature sig, byte[] line)
+ throws SignatureException, IOException {
+ int length = getLengthWithoutWhiteSpace(line);
+ if (length > 0) {
+ sig.update(line, 0, length);
+ }
+ }
+
+ private static void processLine(OutputStream aOut, PGPSignatureGenerator sGen, byte[] line)
+ throws SignatureException, IOException {
+ int length = getLengthWithoutWhiteSpace(line);
+ if (length > 0) {
+ sGen.update(line, 0, length);
+ }
+
+ aOut.write(line, 0, line.length);
+ }
+
+ private static int getLengthWithoutSeparator(byte[] line) {
+ int end = line.length - 1;
+
+ while (end >= 0 && isLineEnding(line[end])) {
+ end--;
+ }
+
+ return end + 1;
+ }
+
+ private static boolean isLineEnding(byte b) {
+ return b == '\r' || b == '\n';
+ }
+
+ private static int getLengthWithoutWhiteSpace(byte[] line) {
+ int end = line.length - 1;
+
+ while (end >= 0 && isWhiteSpace(line[end])) {
+ end--;
+ }
+
+ return end + 1;
+ }
+
+ private static boolean isWhiteSpace(byte b) {
+ return b == '\r' || b == '\n' || b == '\t' || b == ' ';
+ }
+
+ private static byte[] getLineSeparator() {
+ String nl = System.getProperty("line.separator");
+ byte[] nlBytes = new byte[nl.length()];
+
+ for (int i = 0; i != nlBytes.length; i++) {
+ nlBytes[i] = (byte)nl.charAt(i);
+ }
+
+ return nlBytes;
+ }
+
+ public static String getVersion(Context context) {
+ if (VERSION != null) {
+ return VERSION;
+ }
+ try {
+ PackageInfo pi = context.getPackageManager().getPackageInfo(mApgPackageName, 0);
+ VERSION = pi.versionName;
+ return VERSION;
+ } catch (NameNotFoundException e) {
+ // unpossible!
+ return "0.0.0";
+ }
+ }
+
+ public static String getFullVersion(Context context) {
+ return "APG v" + getVersion(context);
+ }
+
+ public static String generateRandomString(int length) {
+ SecureRandom random = new SecureRandom();
+ /*
+ try {
+ random = SecureRandom.getInstance("SHA1PRNG", new BouncyCastleProvider());
+ } catch (NoSuchAlgorithmException e) {
+ // TODO: need to handle this case somehow
+ return null;
+ }*/
+ byte bytes[] = new byte[length];
+ random.nextBytes(bytes);
+ String result = "";
+ for (int i = 0; i < length; ++i) {
+ int v = ((int)bytes[i] + 256) % 64;
+ if (v < 10) {
+ result += (char)((int)'0' + v);
+ } else if (v < 36) {
+ result += (char)((int)'A' + v - 10);
+ } else if (v < 62) {
+ result += (char)((int)'a' + v - 36);
+ } else if (v == 62) {
+ result += '_';
+ } else if (v == 63) {
+ result += '.';
+ }
+ }
+ return result;
+ }
+
+ static long getLengthOfStream(InputStream in) throws IOException {
+ long size = 0;
+ long n = 0;
+ byte dummy[] = new byte[0x10000];
+ while ((n = in.read(dummy)) > 0) {
+ size += n;
+ }
+ return size;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java
index 01cd2de25..7c582ef0c 100644
--- a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java
+++ b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java
@@ -1,112 +1,112 @@
-/*
- * 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 org.bouncycastle2.jce.provider.BouncyCastleProvider;
-import org.bouncycastle2.openpgp.PGPException;
-import org.bouncycastle2.openpgp.PGPSecretKey;
-
-import android.app.Activity;
-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;
-import android.widget.Toast;
-
-public class AskForSecretKeyPassPhrase {
- public static interface PassPhraseCallbackInterface {
- void passPhraseCallback(long keyId, String passPhrase);
- }
-
- public static Dialog createDialog(Activity context, long secretKeyId,
- PassPhraseCallbackInterface callback) {
- AlertDialog.Builder alert = new AlertDialog.Builder(context);
-
- 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.getSecretKeyRing(secretKeyId));
- if (secretKey == 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));
- }
-
- LayoutInflater inflater =
- (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.pass_phrase, null);
- final EditText input = (EditText) view.findViewById(R.id.passPhrase);
- final EditText inputNotUsed = (EditText) view.findViewById(R.id.passPhraseAgain);
- inputNotUsed.setVisibility(View.GONE);
-
- alert.setView(view);
-
- final PassPhraseCallbackInterface cb = callback;
- 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);
- }
- });
-
- alert.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- activity.removeDialog(Id.dialog.pass_phrase);
- }
- });
-
- return alert.create();
- }
-}
+/*
+ * 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 org.bouncycastle2.jce.provider.BouncyCastleProvider;
+import org.bouncycastle2.openpgp.PGPException;
+import org.bouncycastle2.openpgp.PGPSecretKey;
+
+import android.app.Activity;
+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;
+import android.widget.Toast;
+
+public class AskForSecretKeyPassPhrase {
+ public static interface PassPhraseCallbackInterface {
+ void passPhraseCallback(long keyId, String passPhrase);
+ }
+
+ public static Dialog createDialog(Activity context, long secretKeyId,
+ PassPhraseCallbackInterface callback) {
+ AlertDialog.Builder alert = new AlertDialog.Builder(context);
+
+ 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.getSecretKeyRing(secretKeyId));
+ if (secretKey == 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));
+ }
+
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(R.layout.pass_phrase, null);
+ final EditText input = (EditText) view.findViewById(R.id.passPhrase);
+ final EditText inputNotUsed = (EditText) view.findViewById(R.id.passPhraseAgain);
+ inputNotUsed.setVisibility(View.GONE);
+
+ alert.setView(view);
+
+ final PassPhraseCallbackInterface cb = callback;
+ 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);
+ }
+ });
+
+ alert.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ activity.removeDialog(Id.dialog.pass_phrase);
+ }
+ });
+
+ return alert.create();
+ }
+}
diff --git a/src/org/thialfihar/android/apg/BaseActivity.java b/src/org/thialfihar/android/apg/BaseActivity.java
index 7fd8752b7..17fe3b0c8 100644
--- a/src/org/thialfihar/android/apg/BaseActivity.java
+++ b/src/org/thialfihar/android/apg/BaseActivity.java
@@ -1,381 +1,381 @@
-/*
- * 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.File;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Message;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class BaseActivity extends Activity
- implements Runnable, ProgressDialogUpdater,
- AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
-
- private ProgressDialog mProgressDialog = null;
- private Thread mRunningThread = null;
-
- private long mSecretKeyId = 0;
- private String mDeleteFile = null;
-
- protected Preferences mPreferences;
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- handlerCallback(msg);
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mPreferences = Preferences.getPreferences(this);
-
- Apg.initialize(this);
-
- 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
- }
- }
-
- startCacheService(this, mPreferences);
- }
-
- public static void startCacheService(Activity activity, Preferences preferences) {
- Intent intent = new Intent(activity, Service.class);
- intent.putExtra(Service.EXTRA_TTL, preferences.getPassPhraseCacheTtl());
- activity.startService(intent);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences)
- .setIcon(android.R.drawable.ic_menu_preferences);
- menu.add(0, Id.menu.option.about, 1, R.string.menu_about)
- .setIcon(android.R.drawable.ic_menu_info_details);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case Id.menu.option.about: {
- showDialog(Id.dialog.about);
- return true;
- }
-
- case Id.menu.option.preferences: {
- startActivity(new Intent(this, PreferencesActivity.class));
- return true;
- }
-
- case Id.menu.option.search: {
- startSearch("", false, null, false);
- return true;
- }
-
- default: {
- break;
- }
- }
- return false;
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- // in case it is a progress dialog
- mProgressDialog = new ProgressDialog(this);
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setCancelable(false);
- switch (id) {
- case Id.dialog.encrypting: {
- mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
- return mProgressDialog;
- }
-
- case Id.dialog.decrypting: {
- mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
- return mProgressDialog;
- }
-
- case Id.dialog.saving: {
- mProgressDialog.setMessage(this.getString(R.string.progress_saving));
- return mProgressDialog;
- }
-
- case Id.dialog.importing: {
- mProgressDialog.setMessage(this.getString(R.string.progress_importing));
- return mProgressDialog;
- }
-
- case Id.dialog.exporting: {
- mProgressDialog.setMessage(this.getString(R.string.progress_exporting));
- return mProgressDialog;
- }
-
- default: {
- break;
- }
- }
- mProgressDialog = null;
-
- switch (id) {
- case Id.dialog.about: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setTitle("About " + Apg.getFullVersion(this));
-
- LayoutInflater inflater =
- (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View layout = inflater.inflate(R.layout.info, null);
- TextView message = (TextView) layout.findViewById(R.id.message);
- message.setText("This is an attempt to bring OpenPGP to Android. " +
- "It is far from complete, but more features are planned (see website).\n\n" +
- "Feel free to send bug reports, suggestions, feature requests, feedback, " +
- "photographs.\n\n" +
- "mail: thi@thialfihar.org\n" +
- "site: http://apg.thialfihar.org\n\n" +
- "This software is provided \"as is\", without warranty of any kind.");
- alert.setView(layout);
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- BaseActivity.this.removeDialog(Id.dialog.about);
- }
- });
-
- return alert.create();
- }
-
- case Id.dialog.pass_phrase: {
- return AskForSecretKeyPassPhrase.createDialog(this, getSecretKeyId(), this);
- }
-
- case Id.dialog.pass_phrases_do_not_match: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setIcon(android.R.drawable.ic_dialog_alert);
- alert.setTitle(R.string.error);
- alert.setMessage(R.string.passPhrasesDoNotMatch);
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(Id.dialog.pass_phrases_do_not_match);
- }
- });
- alert.setCancelable(false);
-
- return alert.create();
- }
-
- case Id.dialog.no_pass_phrase: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setIcon(android.R.drawable.ic_dialog_alert);
- alert.setTitle(R.string.error);
- alert.setMessage(R.string.passPhraseMustNotBeEmpty);
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(Id.dialog.no_pass_phrase);
- }
- });
- alert.setCancelable(false);
-
- return alert.create();
- }
-
- case Id.dialog.delete_file: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setIcon(android.R.drawable.ic_dialog_alert);
- alert.setTitle(R.string.warning);
- alert.setMessage(this.getString(R.string.fileDeleteConfirmation, getDeleteFile()));
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(Id.dialog.delete_file);
- File file = new File(getDeleteFile());
- String msg = "";
- if (file.delete()) {
- msg = BaseActivity.this.getString(
- R.string.fileDeleteSuccessful);
- } else {
- msg = BaseActivity.this.getString(
- R.string.errorMessage,
- BaseActivity.this.getString(
- R.string.error_fileDeleteFailed, file));
- }
- Toast.makeText(BaseActivity.this,
- msg, Toast.LENGTH_SHORT).show();
- }
- });
- alert.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(Id.dialog.delete_file);
- }
- });
- alert.setCancelable(true);
-
- return alert.create();
- }
-
- default: {
- break;
- }
- }
-
- return super.onCreateDialog(id);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.secret_keys: {
- if (resultCode == RESULT_OK) {
- Bundle bundle = data.getExtras();
- setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
- } else {
- setSecretKeyId(Id.key.none);
- }
- break;
- }
-
- default: {
- break;
- }
- }
-
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- public void setProgress(int resourceId, int progress, int max) {
- setProgress(getString(resourceId), progress, max);
- }
-
- public void setProgress(int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- 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);
- }
-
- public void setProgress(String message, int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- 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);
- }
-
- public void handlerCallback(Message msg) {
- Bundle data = msg.getData();
- if (data == null) {
- return;
- }
-
- int type = data.getInt(Apg.EXTRA_STATUS);
- switch (type) {
- case Id.message.progress_update: {
- String message = data.getString(Apg.EXTRA_MESSAGE);
- if (mProgressDialog != null) {
- if (message != null) {
- mProgressDialog.setMessage(message);
- }
- mProgressDialog.setMax(data.getInt(Apg.EXTRA_MAX));
- mProgressDialog.setProgress(data.getInt(Apg.EXTRA_PROGRESS));
- }
- break;
- }
-
- case Id.message.import_done: // intentionall no break
- case Id.message.export_done: // intentionall no break
- case Id.message.done: {
- mProgressDialog = null;
- doneCallback(msg);
- break;
- }
- }
- }
-
- public void doneCallback(Message msg) {
-
- }
-
- public void passPhraseCallback(long keyId, String passPhrase) {
- Apg.setCachedPassPhrase(keyId, passPhrase);
- }
-
- public void sendMessage(Message msg) {
- mHandler.sendMessage(msg);
- }
-
- public void startThread() {
- mRunningThread = new Thread(this);
- mRunningThread.start();
- }
-
- public void run() {
-
- }
-
- public void setSecretKeyId(long id) {
- mSecretKeyId = id;
- }
-
- public long getSecretKeyId() {
- return mSecretKeyId;
- }
-
- protected void setDeleteFile(String deleteFile) {
- mDeleteFile = deleteFile;
- }
-
- protected String getDeleteFile() {
- return mDeleteFile;
- }
-}
+/*
+ * 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.File;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class BaseActivity extends Activity
+ implements Runnable, ProgressDialogUpdater,
+ AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
+
+ private ProgressDialog mProgressDialog = null;
+ private Thread mRunningThread = null;
+
+ private long mSecretKeyId = 0;
+ private String mDeleteFile = null;
+
+ protected Preferences mPreferences;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ handlerCallback(msg);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mPreferences = Preferences.getPreferences(this);
+
+ Apg.initialize(this);
+
+ 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
+ }
+ }
+
+ startCacheService(this, mPreferences);
+ }
+
+ public static void startCacheService(Activity activity, Preferences preferences) {
+ Intent intent = new Intent(activity, Service.class);
+ intent.putExtra(Service.EXTRA_TTL, preferences.getPassPhraseCacheTtl());
+ activity.startService(intent);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences)
+ .setIcon(android.R.drawable.ic_menu_preferences);
+ menu.add(0, Id.menu.option.about, 1, R.string.menu_about)
+ .setIcon(android.R.drawable.ic_menu_info_details);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case Id.menu.option.about: {
+ showDialog(Id.dialog.about);
+ return true;
+ }
+
+ case Id.menu.option.preferences: {
+ startActivity(new Intent(this, PreferencesActivity.class));
+ return true;
+ }
+
+ case Id.menu.option.search: {
+ startSearch("", false, null, false);
+ return true;
+ }
+
+ default: {
+ break;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ // in case it is a progress dialog
+ mProgressDialog = new ProgressDialog(this);
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ mProgressDialog.setCancelable(false);
+ switch (id) {
+ case Id.dialog.encrypting: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
+ return mProgressDialog;
+ }
+
+ case Id.dialog.decrypting: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
+ return mProgressDialog;
+ }
+
+ case Id.dialog.saving: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_saving));
+ return mProgressDialog;
+ }
+
+ case Id.dialog.importing: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_importing));
+ return mProgressDialog;
+ }
+
+ case Id.dialog.exporting: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_exporting));
+ return mProgressDialog;
+ }
+
+ default: {
+ break;
+ }
+ }
+ mProgressDialog = null;
+
+ switch (id) {
+ case Id.dialog.about: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setTitle("About " + Apg.getFullVersion(this));
+
+ LayoutInflater inflater =
+ (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View layout = inflater.inflate(R.layout.info, null);
+ TextView message = (TextView) layout.findViewById(R.id.message);
+ message.setText("This is an attempt to bring OpenPGP to Android. " +
+ "It is far from complete, but more features are planned (see website).\n\n" +
+ "Feel free to send bug reports, suggestions, feature requests, feedback, " +
+ "photographs.\n\n" +
+ "mail: thi@thialfihar.org\n" +
+ "site: http://apg.thialfihar.org\n\n" +
+ "This software is provided \"as is\", without warranty of any kind.");
+ alert.setView(layout);
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ BaseActivity.this.removeDialog(Id.dialog.about);
+ }
+ });
+
+ return alert.create();
+ }
+
+ case Id.dialog.pass_phrase: {
+ return AskForSecretKeyPassPhrase.createDialog(this, getSecretKeyId(), this);
+ }
+
+ case Id.dialog.pass_phrases_do_not_match: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.error);
+ alert.setMessage(R.string.passPhrasesDoNotMatch);
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.pass_phrases_do_not_match);
+ }
+ });
+ alert.setCancelable(false);
+
+ return alert.create();
+ }
+
+ case Id.dialog.no_pass_phrase: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.error);
+ alert.setMessage(R.string.passPhraseMustNotBeEmpty);
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.no_pass_phrase);
+ }
+ });
+ alert.setCancelable(false);
+
+ return alert.create();
+ }
+
+ case Id.dialog.delete_file: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.warning);
+ alert.setMessage(this.getString(R.string.fileDeleteConfirmation, getDeleteFile()));
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.delete_file);
+ File file = new File(getDeleteFile());
+ String msg = "";
+ if (file.delete()) {
+ msg = BaseActivity.this.getString(
+ R.string.fileDeleteSuccessful);
+ } else {
+ msg = BaseActivity.this.getString(
+ R.string.errorMessage,
+ BaseActivity.this.getString(
+ R.string.error_fileDeleteFailed, file));
+ }
+ Toast.makeText(BaseActivity.this,
+ msg, Toast.LENGTH_SHORT).show();
+ }
+ });
+ alert.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.delete_file);
+ }
+ });
+ alert.setCancelable(true);
+
+ return alert.create();
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ return super.onCreateDialog(id);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.request.secret_keys: {
+ if (resultCode == RESULT_OK) {
+ Bundle bundle = data.getExtras();
+ setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
+ } else {
+ setSecretKeyId(Id.key.none);
+ }
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ public void setProgress(int resourceId, int progress, int max) {
+ setProgress(getString(resourceId), progress, max);
+ }
+
+ public void setProgress(int progress, int max) {
+ Message msg = new Message();
+ Bundle data = new Bundle();
+ 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);
+ }
+
+ public void setProgress(String message, int progress, int max) {
+ Message msg = new Message();
+ Bundle data = new Bundle();
+ 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);
+ }
+
+ public void handlerCallback(Message msg) {
+ Bundle data = msg.getData();
+ if (data == null) {
+ return;
+ }
+
+ int type = data.getInt(Apg.EXTRA_STATUS);
+ switch (type) {
+ case Id.message.progress_update: {
+ String message = data.getString(Apg.EXTRA_MESSAGE);
+ if (mProgressDialog != null) {
+ if (message != null) {
+ mProgressDialog.setMessage(message);
+ }
+ mProgressDialog.setMax(data.getInt(Apg.EXTRA_MAX));
+ mProgressDialog.setProgress(data.getInt(Apg.EXTRA_PROGRESS));
+ }
+ break;
+ }
+
+ case Id.message.import_done: // intentionall no break
+ case Id.message.export_done: // intentionall no break
+ case Id.message.done: {
+ mProgressDialog = null;
+ doneCallback(msg);
+ break;
+ }
+ }
+ }
+
+ public void doneCallback(Message msg) {
+
+ }
+
+ public void passPhraseCallback(long keyId, String passPhrase) {
+ Apg.setCachedPassPhrase(keyId, passPhrase);
+ }
+
+ public void sendMessage(Message msg) {
+ mHandler.sendMessage(msg);
+ }
+
+ public void startThread() {
+ mRunningThread = new Thread(this);
+ mRunningThread.start();
+ }
+
+ public void run() {
+
+ }
+
+ public void setSecretKeyId(long id) {
+ mSecretKeyId = id;
+ }
+
+ public long getSecretKeyId() {
+ return mSecretKeyId;
+ }
+
+ protected void setDeleteFile(String deleteFile) {
+ mDeleteFile = deleteFile;
+ }
+
+ protected String getDeleteFile() {
+ return mDeleteFile;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/CachedPassPhrase.java b/src/org/thialfihar/android/apg/CachedPassPhrase.java
index 74248aee3..92ef708c2 100644
--- a/src/org/thialfihar/android/apg/CachedPassPhrase.java
+++ b/src/org/thialfihar/android/apg/CachedPassPhrase.java
@@ -1,45 +1,45 @@
-package org.thialfihar.android.apg;
-
-public class CachedPassPhrase {
- public final long timestamp;
- public final String passPhrase;
-
- public CachedPassPhrase(long timestamp, String passPhrase) {
- super();
- this.timestamp = timestamp;
- 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;
- }
-
- CachedPassPhrase o = (CachedPassPhrase) other;
- if (timestamp != o.timestamp) {
- return false;
- }
-
- if (passPhrase != o.passPhrase) {
- if (passPhrase == null || o.passPhrase == null) {
- return false;
- }
-
- if (!passPhrase.equals(o.passPhrase)) {
- return false;
- }
- }
-
- return true;
- }
-
- public String toString() {
- return "(" + timestamp + ", *******)";
- }
-}
+package org.thialfihar.android.apg;
+
+public class CachedPassPhrase {
+ public final long timestamp;
+ public final String passPhrase;
+
+ public CachedPassPhrase(long timestamp, String passPhrase) {
+ super();
+ this.timestamp = timestamp;
+ 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;
+ }
+
+ CachedPassPhrase o = (CachedPassPhrase) other;
+ if (timestamp != o.timestamp) {
+ return false;
+ }
+
+ if (passPhrase != o.passPhrase) {
+ if (passPhrase == null || o.passPhrase == null) {
+ return false;
+ }
+
+ if (!passPhrase.equals(o.passPhrase)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public String toString() {
+ return "(" + timestamp + ", *******)";
+ }
+}
diff --git a/src/org/thialfihar/android/apg/Constants.java b/src/org/thialfihar/android/apg/Constants.java
index d4b86029d..4e818b0b9 100644
--- a/src/org/thialfihar/android/apg/Constants.java
+++ b/src/org/thialfihar/android/apg/Constants.java
@@ -1,35 +1,35 @@
-/*
- * 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 android.os.Environment;
-
-public final class Constants {
- public static final class path {
- public static final String app_dir = Environment.getExternalStorageDirectory() + "/APG";
- }
-
- public static final class pref {
- public static final String has_seen_change_log = "seenChangeLogDialog";
- public static final String default_encryption_algorithm = "defaultEncryptionAlgorithm";
- public static final String default_hash_algorithm = "defaultHashAlgorithm";
- public static final String default_ascii_armour = "defaultAsciiArmour";
- public static final String default_message_compression = "defaultMessageCompression";
- public static final String default_file_compression = "defaultFileCompression";
- public static final String pass_phrase_cache_ttl = "passPhraseCacheTtl";
- }
-}
+/*
+ * 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 android.os.Environment;
+
+public final class Constants {
+ public static final class path {
+ public static final String app_dir = Environment.getExternalStorageDirectory() + "/APG";
+ }
+
+ public static final class pref {
+ public static final String has_seen_change_log = "seenChangeLogDialog";
+ public static final String default_encryption_algorithm = "defaultEncryptionAlgorithm";
+ public static final String default_hash_algorithm = "defaultHashAlgorithm";
+ public static final String default_ascii_armour = "defaultAsciiArmour";
+ public static final String default_message_compression = "defaultMessageCompression";
+ public static final String default_file_compression = "defaultFileCompression";
+ public static final String pass_phrase_cache_ttl = "passPhraseCacheTtl";
+ }
+}
diff --git a/src/org/thialfihar/android/apg/DataDestination.java b/src/org/thialfihar/android/apg/DataDestination.java
index 28cacd7ae..a09154c2d 100644
--- a/src/org/thialfihar/android/apg/DataDestination.java
+++ b/src/org/thialfihar/android/apg/DataDestination.java
@@ -1,79 +1,79 @@
-package org.thialfihar.android.apg;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.thialfihar.android.apg.Apg.GeneralException;
-
-import android.content.Context;
-import android.os.Environment;
-
-public class DataDestination {
- private String mStreamFilename;
- private String mFilename;
- private int mMode = Id.mode.undefined;
-
- public DataDestination() {
-
- }
-
- public void setMode(int mode) {
- mMode = mode;
- }
-
- public void setFilename(String filename) {
- mFilename = filename;
- }
-
- public String getStreamFilename() {
- return mStreamFilename;
- }
-
- protected OutputStream getOutputStream(Context context)
- throws Apg.GeneralException, FileNotFoundException, IOException {
- OutputStream out = null;
- mStreamFilename = null;
-
- switch (mMode) {
- case Id.mode.stream: {
- try {
- while (true) {
- mStreamFilename = Apg.generateRandomString(32);
- if (mStreamFilename == null) {
- throw new Apg.GeneralException("couldn't generate random file name");
- }
- context.openFileInput(mStreamFilename).close();
- }
- } catch (FileNotFoundException e) {
- // found a name that isn't used yet
- }
- out = context.openFileOutput(mStreamFilename, Context.MODE_PRIVATE);
- break;
- }
-
- case Id.mode.byte_array: {
- out = new ByteArrayOutputStream();
- break;
- }
-
- case Id.mode.file: {
- if (mFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
- }
- }
- out = new FileOutputStream(mFilename);
- break;
- }
-
- default: {
- break;
- }
- }
-
- return out;
- }
-}
+package org.thialfihar.android.apg;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.thialfihar.android.apg.Apg.GeneralException;
+
+import android.content.Context;
+import android.os.Environment;
+
+public class DataDestination {
+ private String mStreamFilename;
+ private String mFilename;
+ private int mMode = Id.mode.undefined;
+
+ public DataDestination() {
+
+ }
+
+ public void setMode(int mode) {
+ mMode = mode;
+ }
+
+ public void setFilename(String filename) {
+ mFilename = filename;
+ }
+
+ public String getStreamFilename() {
+ return mStreamFilename;
+ }
+
+ protected OutputStream getOutputStream(Context context)
+ throws Apg.GeneralException, FileNotFoundException, IOException {
+ OutputStream out = null;
+ mStreamFilename = null;
+
+ switch (mMode) {
+ case Id.mode.stream: {
+ try {
+ while (true) {
+ mStreamFilename = Apg.generateRandomString(32);
+ if (mStreamFilename == null) {
+ throw new Apg.GeneralException("couldn't generate random file name");
+ }
+ context.openFileInput(mStreamFilename).close();
+ }
+ } catch (FileNotFoundException e) {
+ // found a name that isn't used yet
+ }
+ out = context.openFileOutput(mStreamFilename, Context.MODE_PRIVATE);
+ break;
+ }
+
+ case Id.mode.byte_array: {
+ out = new ByteArrayOutputStream();
+ break;
+ }
+
+ case Id.mode.file: {
+ if (mFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
+ }
+ }
+ out = new FileOutputStream(mFilename);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ return out;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/DataSource.java b/src/org/thialfihar/android/apg/DataSource.java
index 159cdb349..34583ab59 100644
--- a/src/org/thialfihar/android/apg/DataSource.java
+++ b/src/org/thialfihar/android/apg/DataSource.java
@@ -1,88 +1,88 @@
-package org.thialfihar.android.apg;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.thialfihar.android.apg.Apg.GeneralException;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.Environment;
-
-public class DataSource {
- private Uri mContentUri = null;
- private String mText = null;
- private byte[] mData = null;
-
- public DataSource() {
-
- }
-
- public void setUri(Uri uri) {
- mContentUri = uri;
- }
-
- public void setUri(String uri) {
- if (uri.startsWith("/")) {
- setUri(Uri.parse("file://" + uri));
- } else {
- setUri(Uri.parse(uri));
- }
- }
-
- public void setText(String text) {
- mText = text;
- }
-
- public void setData(byte[] data) {
- mData = data;
- }
-
- public InputData getInputData(Context context, boolean withSize)
- throws GeneralException, FileNotFoundException, IOException {
- InputStream in = null;
- long size = 0;
-
- if (mContentUri != null) {
- if (mContentUri.getScheme().equals("file")) {
- // get the rest after "file://"
- String path = mContentUri.toString().substring(6);
- if (path.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
- }
- }
- in = new FileInputStream(path);
- File file = new File(path);
- if (withSize) {
- size = file.length();
- }
- } else {
- in = context.getContentResolver().openInputStream(mContentUri);
- if (withSize) {
- InputStream tmp = context.getContentResolver().openInputStream(mContentUri);
- size = Apg.getLengthOfStream(tmp);
- tmp.close();
- }
- }
- } else if (mText != null || mData != null) {
- byte[] bytes = null;
- if (mData != null) {
- bytes = mData;
- } else {
- bytes = mText.getBytes();
- }
- in = new ByteArrayInputStream(bytes);
- if (withSize) {
- size = bytes.length;
- }
- }
-
- return new InputData(in, size);
- }
-
-}
+package org.thialfihar.android.apg;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.thialfihar.android.apg.Apg.GeneralException;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Environment;
+
+public class DataSource {
+ private Uri mContentUri = null;
+ private String mText = null;
+ private byte[] mData = null;
+
+ public DataSource() {
+
+ }
+
+ public void setUri(Uri uri) {
+ mContentUri = uri;
+ }
+
+ public void setUri(String uri) {
+ if (uri.startsWith("/")) {
+ setUri(Uri.parse("file://" + uri));
+ } else {
+ setUri(Uri.parse(uri));
+ }
+ }
+
+ public void setText(String text) {
+ mText = text;
+ }
+
+ public void setData(byte[] data) {
+ mData = data;
+ }
+
+ public InputData getInputData(Context context, boolean withSize)
+ throws GeneralException, FileNotFoundException, IOException {
+ InputStream in = null;
+ long size = 0;
+
+ if (mContentUri != null) {
+ if (mContentUri.getScheme().equals("file")) {
+ // get the rest after "file://"
+ String path = mContentUri.toString().substring(6);
+ if (path.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
+ }
+ }
+ in = new FileInputStream(path);
+ File file = new File(path);
+ if (withSize) {
+ size = file.length();
+ }
+ } else {
+ in = context.getContentResolver().openInputStream(mContentUri);
+ if (withSize) {
+ InputStream tmp = context.getContentResolver().openInputStream(mContentUri);
+ size = Apg.getLengthOfStream(tmp);
+ tmp.close();
+ }
+ }
+ } else if (mText != null || mData != null) {
+ byte[] bytes = null;
+ if (mData != null) {
+ bytes = mData;
+ } else {
+ bytes = mText.getBytes();
+ }
+ in = new ByteArrayInputStream(bytes);
+ if (withSize) {
+ size = bytes.length;
+ }
+ }
+
+ return new InputData(in, size);
+ }
+
+}
diff --git a/src/org/thialfihar/android/apg/FileDialog.java b/src/org/thialfihar/android/apg/FileDialog.java
index b6bbbf3f1..d717bb0bb 100644
--- a/src/org/thialfihar/android/apg/FileDialog.java
+++ b/src/org/thialfihar/android/apg/FileDialog.java
@@ -1,118 +1,118 @@
-/*
- * 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 android.app.Activity;
-import android.app.AlertDialog;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.Uri;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.Toast;
-
-public class FileDialog {
- private static EditText mFilename;
- private static ImageButton mBrowse;
- private static Activity mActivity;
- private static String mFileManagerTitle;
- private static String mFileManagerButton;
- private static int mRequestCode;
-
- public static interface OnClickListener {
- public void onCancelClick();
- public void onOkClick(String filename);
- }
-
- public static AlertDialog build(Activity activity, String title, String message,
- 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);
-
- alert.setTitle(title);
- alert.setMessage(message);
-
- View view = (View) inflater.inflate(R.layout.file_dialog, null);
-
- mActivity = activity;
- mFilename = (EditText) view.findViewById(R.id.input);
- mFilename.setText(defaultFile);
- mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
- mBrowse.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- openFile();
- }
- });
- mFileManagerTitle = fileManagerTitle;
- mFileManagerButton = fileManagerButton;
- mRequestCode = requestCode;
-
- alert.setView(view);
-
- final OnClickListener clickListener = onClickListener;
-
- alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- clickListener.onOkClick(mFilename.getText().toString());
- }
- });
-
- alert.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- clickListener.onCancelClick();
- }
- });
- return alert.create();
- }
-
- public static void setFilename(String filename) {
- if (mFilename != null) {
- mFilename.setText(filename);
- }
- }
-
- /**
- * Opens the file manager to select a file to open.
- */
- private static void openFile() {
- String filename = mFilename.getText().toString();
-
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
-
- intent.setData(Uri.parse("file://" + filename));
- intent.setType("*/*");
-
- try {
- mActivity.startActivityForResult(intent, mRequestCode);
- } catch (ActivityNotFoundException e) {
- // No compatible file manager was found.
- Toast.makeText(mActivity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
- }
- }
-}
+/*
+ * 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 android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.Toast;
+
+public class FileDialog {
+ private static EditText mFilename;
+ private static ImageButton mBrowse;
+ private static Activity mActivity;
+ private static String mFileManagerTitle;
+ private static String mFileManagerButton;
+ private static int mRequestCode;
+
+ public static interface OnClickListener {
+ public void onCancelClick();
+ public void onOkClick(String filename);
+ }
+
+ public static AlertDialog build(Activity activity, String title, String message,
+ 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);
+
+ alert.setTitle(title);
+ alert.setMessage(message);
+
+ View view = (View) inflater.inflate(R.layout.file_dialog, null);
+
+ mActivity = activity;
+ mFilename = (EditText) view.findViewById(R.id.input);
+ mFilename.setText(defaultFile);
+ mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
+ mBrowse.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ openFile();
+ }
+ });
+ mFileManagerTitle = fileManagerTitle;
+ mFileManagerButton = fileManagerButton;
+ mRequestCode = requestCode;
+
+ alert.setView(view);
+
+ final OnClickListener clickListener = onClickListener;
+
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ clickListener.onOkClick(mFilename.getText().toString());
+ }
+ });
+
+ alert.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ clickListener.onCancelClick();
+ }
+ });
+ return alert.create();
+ }
+
+ public static void setFilename(String filename) {
+ if (mFilename != null) {
+ mFilename.setText(filename);
+ }
+ }
+
+ /**
+ * Opens the file manager to select a file to open.
+ */
+ private static void openFile() {
+ String filename = mFilename.getText().toString();
+
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+ intent.setData(Uri.parse("file://" + filename));
+ intent.setType("*/*");
+
+ try {
+ mActivity.startActivityForResult(intent, mRequestCode);
+ } catch (ActivityNotFoundException e) {
+ // No compatible file manager was found.
+ Toast.makeText(mActivity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
+ }
+ }
+}
diff --git a/src/org/thialfihar/android/apg/GeneralActivity.java b/src/org/thialfihar/android/apg/GeneralActivity.java
index 49cbdcf2a..01bd93f92 100644
--- a/src/org/thialfihar/android/apg/GeneralActivity.java
+++ b/src/org/thialfihar/android/apg/GeneralActivity.java
@@ -1,177 +1,177 @@
-package org.thialfihar.android.apg;
-
-import java.io.ByteArrayInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Vector;
-
-import org.thialfihar.android.apg.utils.Choice;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.Toast;
-
-public class GeneralActivity extends BaseActivity {
- private Intent mIntent;
- private ArrayAdapter<Choice> mAdapter;
- private ListView mList;
- private Button mCancelButton;
- private String mDataString;
- private Uri mDataUri;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.general);
-
- mIntent = getIntent();
-
- InputStream inStream = null;
- {
- String data = mIntent.getStringExtra(Intent.EXTRA_TEXT);
- if (data != null) {
- mDataString = data;
- inStream = new ByteArrayInputStream(data.getBytes());
- }
- }
-
- if (inStream == null) {
- Uri data = mIntent.getData();
- if (data != null) {
- mDataUri = data;
- try {
- inStream = getContentResolver().openInputStream(data);
- } catch (FileNotFoundException e) {
- // didn't work
- Toast.makeText(this, "failed to open stream", Toast.LENGTH_SHORT).show();
- }
- }
- }
-
- if (inStream == null) {
- Toast.makeText(this, "no data found", Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
-
- int contentType = Id.content.unknown;
- try {
- contentType = Apg.getStreamContent(this, inStream);
- inStream.close();
- } catch (IOException e) {
- // just means that there's no PGP data in there
- }
-
- mList = (ListView) findViewById(R.id.options);
- Vector<Choice> choices = new Vector<Choice>();
-
- if (contentType == Id.content.keys) {
- choices.add(new Choice(Id.choice.action.import_public,
- getString(R.string.action_importPublic)));
- choices.add(new Choice(Id.choice.action.import_secret,
- getString(R.string.action_importSecret)));
- }
-
- if (contentType == Id.content.encrypted_data) {
- choices.add(new Choice(Id.choice.action.decrypt, getString(R.string.action_decrypt)));
- }
-
- if (contentType == Id.content.unknown) {
- choices.add(new Choice(Id.choice.action.encrypt, getString(R.string.action_encrypt)));
- }
-
- mAdapter = new ArrayAdapter<Choice>(this, android.R.layout.simple_list_item_1, choices);
- mList.setAdapter(mAdapter);
-
- mList.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
- clicked(mAdapter.getItem(arg2).getId());
- }
- });
-
- mCancelButton = (Button) findViewById(R.id.btn_cancel);
- mCancelButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- GeneralActivity.this.finish();
- }
- });
-
- if (choices.size() == 1) {
- clicked(choices.get(0).getId());
- }
- }
-
- private void clicked(int id) {
- Intent intent = new Intent();
- switch (id) {
- case Id.choice.action.encrypt: {
- intent.setClass(this, EncryptActivity.class);
- if (mDataString != null) {
- intent.setAction(Apg.Intent.ENCRYPT);
- intent.putExtra(Apg.EXTRA_TEXT, mDataString);
- } else if (mDataUri != null) {
- intent.setAction(Apg.Intent.ENCRYPT_FILE);
- intent.setDataAndType(mDataUri, mIntent.getType());
- }
-
- break;
- }
-
- case Id.choice.action.decrypt: {
- intent.setClass(this, DecryptActivity.class);
- if (mDataString != null) {
- intent.setAction(Apg.Intent.DECRYPT);
- intent.putExtra(Apg.EXTRA_TEXT, mDataString);
- } else if (mDataUri != null) {
- intent.setAction(Apg.Intent.DECRYPT_FILE);
- intent.setDataAndType(mDataUri, mIntent.getType());
- }
-
- break;
- }
-
- case Id.choice.action.import_public: {
- intent.setClass(this, PublicKeyListActivity.class);
- intent.setAction(Apg.Intent.IMPORT);
- if (mDataString != null) {
- intent.putExtra(Apg.EXTRA_TEXT, mDataString);
- } else if (mDataUri != null) {
- intent.setDataAndType(mDataUri, mIntent.getType());
- }
- break;
- }
-
- case Id.choice.action.import_secret: {
- intent.setClass(this, SecretKeyListActivity.class);
- intent.setAction(Apg.Intent.IMPORT);
- if (mDataString != null) {
- intent.putExtra(Apg.EXTRA_TEXT, mDataString);
- } else if (mDataUri != null) {
- intent.setDataAndType(mDataUri, mIntent.getType());
- }
- break;
- }
-
- default: {
- // shouldn't happen
- return;
- }
- }
-
- startActivity(intent);
- finish();
- }
-}
+package org.thialfihar.android.apg;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+import org.thialfihar.android.apg.utils.Choice;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+
+public class GeneralActivity extends BaseActivity {
+ private Intent mIntent;
+ private ArrayAdapter<Choice> mAdapter;
+ private ListView mList;
+ private Button mCancelButton;
+ private String mDataString;
+ private Uri mDataUri;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.general);
+
+ mIntent = getIntent();
+
+ InputStream inStream = null;
+ {
+ String data = mIntent.getStringExtra(Intent.EXTRA_TEXT);
+ if (data != null) {
+ mDataString = data;
+ inStream = new ByteArrayInputStream(data.getBytes());
+ }
+ }
+
+ if (inStream == null) {
+ Uri data = mIntent.getData();
+ if (data != null) {
+ mDataUri = data;
+ try {
+ inStream = getContentResolver().openInputStream(data);
+ } catch (FileNotFoundException e) {
+ // didn't work
+ Toast.makeText(this, "failed to open stream", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ if (inStream == null) {
+ Toast.makeText(this, "no data found", Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+
+ int contentType = Id.content.unknown;
+ try {
+ contentType = Apg.getStreamContent(this, inStream);
+ inStream.close();
+ } catch (IOException e) {
+ // just means that there's no PGP data in there
+ }
+
+ mList = (ListView) findViewById(R.id.options);
+ Vector<Choice> choices = new Vector<Choice>();
+
+ if (contentType == Id.content.keys) {
+ choices.add(new Choice(Id.choice.action.import_public,
+ getString(R.string.action_importPublic)));
+ choices.add(new Choice(Id.choice.action.import_secret,
+ getString(R.string.action_importSecret)));
+ }
+
+ if (contentType == Id.content.encrypted_data) {
+ choices.add(new Choice(Id.choice.action.decrypt, getString(R.string.action_decrypt)));
+ }
+
+ if (contentType == Id.content.unknown) {
+ choices.add(new Choice(Id.choice.action.encrypt, getString(R.string.action_encrypt)));
+ }
+
+ mAdapter = new ArrayAdapter<Choice>(this, android.R.layout.simple_list_item_1, choices);
+ mList.setAdapter(mAdapter);
+
+ mList.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
+ clicked(mAdapter.getItem(arg2).getId());
+ }
+ });
+
+ mCancelButton = (Button) findViewById(R.id.btn_cancel);
+ mCancelButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ GeneralActivity.this.finish();
+ }
+ });
+
+ if (choices.size() == 1) {
+ clicked(choices.get(0).getId());
+ }
+ }
+
+ private void clicked(int id) {
+ Intent intent = new Intent();
+ switch (id) {
+ case Id.choice.action.encrypt: {
+ intent.setClass(this, EncryptActivity.class);
+ if (mDataString != null) {
+ intent.setAction(Apg.Intent.ENCRYPT);
+ intent.putExtra(Apg.EXTRA_TEXT, mDataString);
+ } else if (mDataUri != null) {
+ intent.setAction(Apg.Intent.ENCRYPT_FILE);
+ intent.setDataAndType(mDataUri, mIntent.getType());
+ }
+
+ break;
+ }
+
+ case Id.choice.action.decrypt: {
+ intent.setClass(this, DecryptActivity.class);
+ if (mDataString != null) {
+ intent.setAction(Apg.Intent.DECRYPT);
+ intent.putExtra(Apg.EXTRA_TEXT, mDataString);
+ } else if (mDataUri != null) {
+ intent.setAction(Apg.Intent.DECRYPT_FILE);
+ intent.setDataAndType(mDataUri, mIntent.getType());
+ }
+
+ break;
+ }
+
+ case Id.choice.action.import_public: {
+ intent.setClass(this, PublicKeyListActivity.class);
+ intent.setAction(Apg.Intent.IMPORT);
+ if (mDataString != null) {
+ intent.putExtra(Apg.EXTRA_TEXT, mDataString);
+ } else if (mDataUri != null) {
+ intent.setDataAndType(mDataUri, mIntent.getType());
+ }
+ break;
+ }
+
+ case Id.choice.action.import_secret: {
+ intent.setClass(this, SecretKeyListActivity.class);
+ intent.setAction(Apg.Intent.IMPORT);
+ if (mDataString != null) {
+ intent.putExtra(Apg.EXTRA_TEXT, mDataString);
+ } else if (mDataUri != null) {
+ intent.setDataAndType(mDataUri, mIntent.getType());
+ }
+ break;
+ }
+
+ default: {
+ // shouldn't happen
+ return;
+ }
+ }
+
+ startActivity(intent);
+ finish();
+ }
+}
diff --git a/src/org/thialfihar/android/apg/Id.java b/src/org/thialfihar/android/apg/Id.java
index bc8b7e443..9ff484271 100644
--- a/src/org/thialfihar/android/apg/Id.java
+++ b/src/org/thialfihar/android/apg/Id.java
@@ -1,155 +1,155 @@
-/*
- * 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 org.bouncycastle2.bcpg.CompressionAlgorithmTags;
-
-public final class Id {
- public static final class menu {
- public static final int export = 0x21070001;
- public static final int delete = 0x21070002;
- public static final int edit = 0x21070003;
-
- public static final class option {
- public static final int new_pass_phrase = 0x21070001;
- public static final int create = 0x21070002;
- public static final int about = 0x21070003;
- public static final int manage_public_keys = 0x21070004;
- public static final int manage_secret_keys = 0x21070005;
- public static final int import_keys = 0x21070006;
- public static final int export_keys = 0x21070007;
- public static final int preferences = 0x21070008;
- public static final int search = 0x21070009;
- }
- }
-
- public static final class message {
- public static final int progress_update = 0x21070001;
- public static final int done = 0x21070002;
- public static final int import_keys = 0x21070003;
- public static final int export_keys = 0x21070004;
- public static final int import_done = 0x21070005;
- public static final int export_done = 0x21070006;
- public static final int create_key = 0x21070007;
- public static final int edit_key = 0x21070008;
- }
-
- public static final class request {
- public static final int public_keys = 0x21070001;
- public static final int secret_keys = 0x21070002;
- public static final int filename = 0x21070003;
- public static final int output_filename = 0x21070004;
- }
-
- public static final class dialog {
- public static final int pass_phrase = 0x21070001;
- public static final int encrypting = 0x21070002;
- public static final int decrypting = 0x21070003;
- public static final int new_pass_phrase = 0x21070004;
- public static final int pass_phrases_do_not_match = 0x21070005;
- public static final int no_pass_phrase = 0x21070006;
- public static final int saving = 0x21070007;
- public static final int delete_key = 0x21070008;
- public static final int import_keys = 0x21070009;
- public static final int importing = 0x2107000a;
- public static final int export_key = 0x2107000b;
- public static final int export_keys = 0x2107000c;
- public static final int exporting = 0x2107000d;
- public static final int new_account = 0x2107000e;
- public static final int about = 0x2107000f;
- public static final int change_log = 0x21070010;
- public static final int output_filename = 0x21070011;
- public static final int delete_file = 0x21070012;
- }
-
- public static final class task {
- public static final int import_keys = 0x21070001;
- 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;
- public static final int user_id = 0x21070003;
- public static final int key = 0x21070004;
- }
-
- public static final class choice {
- public static final class algorithm {
- public static final int dsa = 0x21070001;
- public static final int elgamal = 0x21070002;
- public static final int rsa = 0x21070003;
- }
-
- public static final class compression {
- public static final int none = 0x21070001;
- public static final int zlib = CompressionAlgorithmTags.ZLIB;
- public static final int bzip2 = CompressionAlgorithmTags.BZIP2;
- public static final int zip = CompressionAlgorithmTags.ZIP;
- }
-
- public static final class usage {
- public static final int sign_only = 0x21070001;
- public static final int encrypt_only = 0x21070002;
- public static final int sign_and_encrypt = 0x21070003;
- }
-
- public static final class action {
- public static final int encrypt = 0x21070001;
- public static final int decrypt = 0x21070002;
- public static final int import_public = 0x21070003;
- public static final int import_secret = 0x21070004;
- }
- }
-
- public static final class return_value {
- public static final int ok = 0;
- public static final int error = -1;
- public static final int no_master_key = -2;
- public static final int updated = 1;
- }
-
- public static final class target {
- public static final int clipboard = 0x21070001;
- public static final int email = 0x21070002;
- public static final int file = 0x21070003;
- public static final int message = 0x21070004;
- }
-
- public static final class mode {
- public static final int undefined = 0x21070001;
- public static final int byte_array = 0x21070002;
- public static final int file = 0x21070003;
- public static final int stream = 0x21070004;
- }
-
- public static final class key {
- public static final int none = 0;
- public static final int symmetric = -1;
- }
-
- public static final class content {
- public static final int unknown = 0;
- public static final int encrypted_data = 1;
- public static final int keys = 2;
- }
-}
+/*
+ * 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 org.bouncycastle2.bcpg.CompressionAlgorithmTags;
+
+public final class Id {
+ public static final class menu {
+ public static final int export = 0x21070001;
+ public static final int delete = 0x21070002;
+ public static final int edit = 0x21070003;
+
+ public static final class option {
+ public static final int new_pass_phrase = 0x21070001;
+ public static final int create = 0x21070002;
+ public static final int about = 0x21070003;
+ public static final int manage_public_keys = 0x21070004;
+ public static final int manage_secret_keys = 0x21070005;
+ public static final int import_keys = 0x21070006;
+ public static final int export_keys = 0x21070007;
+ public static final int preferences = 0x21070008;
+ public static final int search = 0x21070009;
+ }
+ }
+
+ public static final class message {
+ public static final int progress_update = 0x21070001;
+ public static final int done = 0x21070002;
+ public static final int import_keys = 0x21070003;
+ public static final int export_keys = 0x21070004;
+ public static final int import_done = 0x21070005;
+ public static final int export_done = 0x21070006;
+ public static final int create_key = 0x21070007;
+ public static final int edit_key = 0x21070008;
+ }
+
+ public static final class request {
+ public static final int public_keys = 0x21070001;
+ public static final int secret_keys = 0x21070002;
+ public static final int filename = 0x21070003;
+ public static final int output_filename = 0x21070004;
+ }
+
+ public static final class dialog {
+ public static final int pass_phrase = 0x21070001;
+ public static final int encrypting = 0x21070002;
+ public static final int decrypting = 0x21070003;
+ public static final int new_pass_phrase = 0x21070004;
+ public static final int pass_phrases_do_not_match = 0x21070005;
+ public static final int no_pass_phrase = 0x21070006;
+ public static final int saving = 0x21070007;
+ public static final int delete_key = 0x21070008;
+ public static final int import_keys = 0x21070009;
+ public static final int importing = 0x2107000a;
+ public static final int export_key = 0x2107000b;
+ public static final int export_keys = 0x2107000c;
+ public static final int exporting = 0x2107000d;
+ public static final int new_account = 0x2107000e;
+ public static final int about = 0x2107000f;
+ public static final int change_log = 0x21070010;
+ public static final int output_filename = 0x21070011;
+ public static final int delete_file = 0x21070012;
+ }
+
+ public static final class task {
+ public static final int import_keys = 0x21070001;
+ 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;
+ public static final int user_id = 0x21070003;
+ public static final int key = 0x21070004;
+ }
+
+ public static final class choice {
+ public static final class algorithm {
+ public static final int dsa = 0x21070001;
+ public static final int elgamal = 0x21070002;
+ public static final int rsa = 0x21070003;
+ }
+
+ public static final class compression {
+ public static final int none = 0x21070001;
+ public static final int zlib = CompressionAlgorithmTags.ZLIB;
+ public static final int bzip2 = CompressionAlgorithmTags.BZIP2;
+ public static final int zip = CompressionAlgorithmTags.ZIP;
+ }
+
+ public static final class usage {
+ public static final int sign_only = 0x21070001;
+ public static final int encrypt_only = 0x21070002;
+ public static final int sign_and_encrypt = 0x21070003;
+ }
+
+ public static final class action {
+ public static final int encrypt = 0x21070001;
+ public static final int decrypt = 0x21070002;
+ public static final int import_public = 0x21070003;
+ public static final int import_secret = 0x21070004;
+ }
+ }
+
+ public static final class return_value {
+ public static final int ok = 0;
+ public static final int error = -1;
+ public static final int no_master_key = -2;
+ public static final int updated = 1;
+ }
+
+ public static final class target {
+ public static final int clipboard = 0x21070001;
+ public static final int email = 0x21070002;
+ public static final int file = 0x21070003;
+ public static final int message = 0x21070004;
+ }
+
+ public static final class mode {
+ public static final int undefined = 0x21070001;
+ public static final int byte_array = 0x21070002;
+ public static final int file = 0x21070003;
+ public static final int stream = 0x21070004;
+ }
+
+ public static final class key {
+ public static final int none = 0;
+ public static final int symmetric = -1;
+ }
+
+ public static final class content {
+ public static final int unknown = 0;
+ public static final int encrypted_data = 1;
+ public static final int keys = 2;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/InputData.java b/src/org/thialfihar/android/apg/InputData.java
index cf49f1c33..49f66d5b6 100644
--- a/src/org/thialfihar/android/apg/InputData.java
+++ b/src/org/thialfihar/android/apg/InputData.java
@@ -1,25 +1,25 @@
-package org.thialfihar.android.apg;
-
-import java.io.InputStream;
-
-public class InputData {
- private PositionAwareInputStream mInputStream;
- private long mSize;
-
- InputData(InputStream inputStream, long size) {
- mInputStream = new PositionAwareInputStream(inputStream);
- mSize = size;
- }
-
- public InputStream getInputStream() {
- return mInputStream;
- }
-
- public long getSize() {
- return mSize;
- }
-
- public long getStreamPosition() {
- return mInputStream.position();
- }
-}
+package org.thialfihar.android.apg;
+
+import java.io.InputStream;
+
+public class InputData {
+ private PositionAwareInputStream mInputStream;
+ private long mSize;
+
+ InputData(InputStream inputStream, long size) {
+ mInputStream = new PositionAwareInputStream(inputStream);
+ mSize = size;
+ }
+
+ public InputStream getInputStream() {
+ return mInputStream;
+ }
+
+ public long getSize() {
+ return mSize;
+ }
+
+ public long getStreamPosition() {
+ return mInputStream.position();
+ }
+}
diff --git a/src/org/thialfihar/android/apg/IntegerListPreference.java b/src/org/thialfihar/android/apg/IntegerListPreference.java
index 26a58afd5..0c5a643ef 100644
--- a/src/org/thialfihar/android/apg/IntegerListPreference.java
+++ b/src/org/thialfihar/android/apg/IntegerListPreference.java
@@ -1,95 +1,95 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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 android.content.Context;
-import android.preference.ListPreference;
-import android.util.AttributeSet;
-
-/**
- * A list preference which persists its values as integers instead of strings.
- * Code reading the values should use
- * {@link android.content.SharedPreferences#getInt}.
- * When using XML-declared arrays for entry values, the arrays should be regular
- * string arrays containing valid integer values.
- *
- * @author Rodrigo Damazio
- */
-public class IntegerListPreference extends ListPreference {
-
- public IntegerListPreference(Context context) {
- super(context);
-
- verifyEntryValues(null);
- }
-
- public IntegerListPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- verifyEntryValues(null);
- }
-
- @Override
- public void setEntryValues(CharSequence[] entryValues) {
- CharSequence[] oldValues = getEntryValues();
- super.setEntryValues(entryValues);
- verifyEntryValues(oldValues);
- }
-
- @Override
- public void setEntryValues(int entryValuesResId) {
- CharSequence[] oldValues = getEntryValues();
- super.setEntryValues(entryValuesResId);
- verifyEntryValues(oldValues);
- }
-
- @Override
- protected String getPersistedString(String defaultReturnValue) {
- // During initial load, there's no known default value
- int defaultIntegerValue = Integer.MIN_VALUE;
- if (defaultReturnValue != null) {
- defaultIntegerValue = Integer.parseInt(defaultReturnValue);
- }
-
- // When the list preference asks us to read a string, instead read an
- // integer.
- int value = getPersistedInt(defaultIntegerValue);
- return Integer.toString(value);
- }
-
- @Override
- protected boolean persistString(String value) {
- // When asked to save a string, instead save an integer
- return persistInt(Integer.parseInt(value));
- }
-
- private void verifyEntryValues(CharSequence[] oldValues) {
- CharSequence[] entryValues = getEntryValues();
- if (entryValues == null) {
- return;
- }
-
- for (CharSequence entryValue : entryValues) {
- try {
- Integer.parseInt(entryValue.toString());
- } catch (NumberFormatException nfe) {
- super.setEntryValues(oldValues);
- throw nfe;
- }
- }
- }
-}
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * 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 android.content.Context;
+import android.preference.ListPreference;
+import android.util.AttributeSet;
+
+/**
+ * A list preference which persists its values as integers instead of strings.
+ * Code reading the values should use
+ * {@link android.content.SharedPreferences#getInt}.
+ * When using XML-declared arrays for entry values, the arrays should be regular
+ * string arrays containing valid integer values.
+ *
+ * @author Rodrigo Damazio
+ */
+public class IntegerListPreference extends ListPreference {
+
+ public IntegerListPreference(Context context) {
+ super(context);
+
+ verifyEntryValues(null);
+ }
+
+ public IntegerListPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ verifyEntryValues(null);
+ }
+
+ @Override
+ public void setEntryValues(CharSequence[] entryValues) {
+ CharSequence[] oldValues = getEntryValues();
+ super.setEntryValues(entryValues);
+ verifyEntryValues(oldValues);
+ }
+
+ @Override
+ public void setEntryValues(int entryValuesResId) {
+ CharSequence[] oldValues = getEntryValues();
+ super.setEntryValues(entryValuesResId);
+ verifyEntryValues(oldValues);
+ }
+
+ @Override
+ protected String getPersistedString(String defaultReturnValue) {
+ // During initial load, there's no known default value
+ int defaultIntegerValue = Integer.MIN_VALUE;
+ if (defaultReturnValue != null) {
+ defaultIntegerValue = Integer.parseInt(defaultReturnValue);
+ }
+
+ // When the list preference asks us to read a string, instead read an
+ // integer.
+ int value = getPersistedInt(defaultIntegerValue);
+ return Integer.toString(value);
+ }
+
+ @Override
+ protected boolean persistString(String value) {
+ // When asked to save a string, instead save an integer
+ return persistInt(Integer.parseInt(value));
+ }
+
+ private void verifyEntryValues(CharSequence[] oldValues) {
+ CharSequence[] entryValues = getEntryValues();
+ if (entryValues == null) {
+ return;
+ }
+
+ for (CharSequence entryValue : entryValues) {
+ try {
+ Integer.parseInt(entryValue.toString());
+ } catch (NumberFormatException nfe) {
+ super.setEntryValues(oldValues);
+ throw nfe;
+ }
+ }
+ }
+}
diff --git a/src/org/thialfihar/android/apg/KeyListActivity.java b/src/org/thialfihar/android/apg/KeyListActivity.java
index 26c744f33..fc074e75d 100644
--- a/src/org/thialfihar/android/apg/KeyListActivity.java
+++ b/src/org/thialfihar/android/apg/KeyListActivity.java
@@ -1,762 +1,762 @@
-/*
- * 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.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-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.app.SearchManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-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.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.Button;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class KeyListActivity extends BaseActivity {
- protected ExpandableListView mList;
- protected KeyListAdapter mListAdapter;
- protected View mFilterLayout;
- protected Button mClearFilterButton;
- protected TextView mFilterInfo;
-
- protected int mSelectedItem = -1;
- protected int mTask = 0;
-
- protected String mImportFilename = Constants.path.app_dir + "/";
- protected String mExportFilename = Constants.path.app_dir + "/";
-
- protected String mImportData;
-
- protected int mKeyType = Id.type.public_key;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.key_list);
-
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
-
- mList = (ExpandableListView) findViewById(R.id.list);
- registerForContextMenu(mList);
-
- mFilterLayout = (View) findViewById(R.id.layout_filter);
- mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
- mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
-
- mClearFilterButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- handleIntent(new Intent());
- }
- });
-
- handleIntent(getIntent());
-
- Intent intent = getIntent();
- if (Apg.Intent.IMPORT.equals(intent.getAction())) {
- if ("file".equals(intent.getScheme()) && intent.getDataString() != null) {
- mImportFilename = intent.getDataString().replace("file://", "");
- } else {
- mImportData = intent.getStringExtra(Apg.EXTRA_TEXT);
- }
- importKeys();
- }
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- String searchString = null;
- if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
- searchString = intent.getStringExtra(SearchManager.QUERY);
- if (searchString != null && searchString.trim().length() == 0) {
- searchString = null;
- }
- }
-
- if (searchString == null) {
- mFilterLayout.setVisibility(View.GONE);
- } else {
- mFilterLayout.setVisibility(View.VISIBLE);
- mFilterInfo.setText(getString(R.string.filterInfo, searchString));
- }
-
- if (mListAdapter != null) {
- mListAdapter.cleanup();
- }
- mListAdapter = new KeyListAdapter(this, searchString);
- mList.setAdapter(mListAdapter);
- }
-
- @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();
-
- try {
- InputStream importInputStream = null;
- OutputStream exportOutputStream = null;
- long size = 0;
- if (mTask == Id.task.import_keys) {
- if (mImportData != null) {
- byte[] bytes = mImportData.getBytes();
- size = bytes.length;
- importInputStream = new ByteArrayInputStream(bytes);
- } else {
- File file = new File(mImportFilename);
- size = file.length();
- importInputStream = new FileInputStream(file);
- }
- } else {
- exportOutputStream = new FileOutputStream(mExportFilename);
- }
-
- if (mTask == Id.task.import_keys) {
- data = Apg.importKeyRings(this, mKeyType, new InputData(importInputStream, size), 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, exportOutputStream, 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;
- }
-
- mImportData = null;
-
- 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 String mSearchString;
-
- 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, String searchString) {
- mSearchString = searchString;
-
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mDatabase = Apg.getDatabase().db();
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- 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')");
-
- if (searchString != null && searchString.trim().length() > 0) {
- String[] chunks = searchString.trim().split(" +");
- qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
- UserIds.TABLE_NAME + " AS tmp WHERE " +
- "tmp." + UserIds.KEY_ID + " = " +
- Keys.TABLE_NAME + "." + Keys._ID);
- for (int i = 0; i < chunks.length; ++i) {
- qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
- qb.appendWhereEscapeString("%" + chunks[i] + "%");
- }
- qb.appendWhere(")");
- }
-
- mCursor = qb.query(mDatabase,
- 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");
-
- // content provider way for reference, might have to go back to it sometime:
- /*Uri contentUri = null;
- if (mKeyType == Id.type.secret_key) {
- contentUri = Apg.CONTENT_URI_SECRET_KEY_RINGS;
- } else {
- contentUri = Apg.CONTENT_URI_PUBLIC_KEY_RINGS;
- }
- mCursor = getContentResolver().query(
- contentUri,
- new String[] {
- DataProvider._ID, // 0
- DataProvider.MASTER_KEY_ID, // 1
- DataProvider.USER_ID, // 2
- },
- null, null, null);*/
-
- startManagingCursor(mCursor);
- rebuild(false);
- }
-
- public void cleanup() {
- if (mCursor != null) {
- stopManagingCursor(mCursor);
- mCursor.close();
- }
- }
-
- 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);
- }
-}
+/*
+ * 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.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+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.app.SearchManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+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.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.Button;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class KeyListActivity extends BaseActivity {
+ protected ExpandableListView mList;
+ protected KeyListAdapter mListAdapter;
+ protected View mFilterLayout;
+ protected Button mClearFilterButton;
+ protected TextView mFilterInfo;
+
+ protected int mSelectedItem = -1;
+ protected int mTask = 0;
+
+ protected String mImportFilename = Constants.path.app_dir + "/";
+ protected String mExportFilename = Constants.path.app_dir + "/";
+
+ protected String mImportData;
+
+ protected int mKeyType = Id.type.public_key;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.key_list);
+
+ setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+
+ mList = (ExpandableListView) findViewById(R.id.list);
+ registerForContextMenu(mList);
+
+ mFilterLayout = (View) findViewById(R.id.layout_filter);
+ mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
+ mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
+
+ mClearFilterButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleIntent(new Intent());
+ }
+ });
+
+ handleIntent(getIntent());
+
+ Intent intent = getIntent();
+ if (Apg.Intent.IMPORT.equals(intent.getAction())) {
+ if ("file".equals(intent.getScheme()) && intent.getDataString() != null) {
+ mImportFilename = intent.getDataString().replace("file://", "");
+ } else {
+ mImportData = intent.getStringExtra(Apg.EXTRA_TEXT);
+ }
+ importKeys();
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ handleIntent(intent);
+ }
+
+ private void handleIntent(Intent intent) {
+ String searchString = null;
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ searchString = intent.getStringExtra(SearchManager.QUERY);
+ if (searchString != null && searchString.trim().length() == 0) {
+ searchString = null;
+ }
+ }
+
+ if (searchString == null) {
+ mFilterLayout.setVisibility(View.GONE);
+ } else {
+ mFilterLayout.setVisibility(View.VISIBLE);
+ mFilterInfo.setText(getString(R.string.filterInfo, searchString));
+ }
+
+ if (mListAdapter != null) {
+ mListAdapter.cleanup();
+ }
+ mListAdapter = new KeyListAdapter(this, searchString);
+ mList.setAdapter(mListAdapter);
+ }
+
+ @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();
+
+ try {
+ InputStream importInputStream = null;
+ OutputStream exportOutputStream = null;
+ long size = 0;
+ if (mTask == Id.task.import_keys) {
+ if (mImportData != null) {
+ byte[] bytes = mImportData.getBytes();
+ size = bytes.length;
+ importInputStream = new ByteArrayInputStream(bytes);
+ } else {
+ File file = new File(mImportFilename);
+ size = file.length();
+ importInputStream = new FileInputStream(file);
+ }
+ } else {
+ exportOutputStream = new FileOutputStream(mExportFilename);
+ }
+
+ if (mTask == Id.task.import_keys) {
+ data = Apg.importKeyRings(this, mKeyType, new InputData(importInputStream, size), 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, exportOutputStream, 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;
+ }
+
+ mImportData = null;
+
+ 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 String mSearchString;
+
+ 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, String searchString) {
+ mSearchString = searchString;
+
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mDatabase = Apg.getDatabase().db();
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ 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')");
+
+ if (searchString != null && searchString.trim().length() > 0) {
+ String[] chunks = searchString.trim().split(" +");
+ qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
+ UserIds.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + UserIds.KEY_ID + " = " +
+ Keys.TABLE_NAME + "." + Keys._ID);
+ for (int i = 0; i < chunks.length; ++i) {
+ qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
+ qb.appendWhereEscapeString("%" + chunks[i] + "%");
+ }
+ qb.appendWhere(")");
+ }
+
+ mCursor = qb.query(mDatabase,
+ 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");
+
+ // content provider way for reference, might have to go back to it sometime:
+ /*Uri contentUri = null;
+ if (mKeyType == Id.type.secret_key) {
+ contentUri = Apg.CONTENT_URI_SECRET_KEY_RINGS;
+ } else {
+ contentUri = Apg.CONTENT_URI_PUBLIC_KEY_RINGS;
+ }
+ mCursor = getContentResolver().query(
+ contentUri,
+ new String[] {
+ DataProvider._ID, // 0
+ DataProvider.MASTER_KEY_ID, // 1
+ DataProvider.USER_ID, // 2
+ },
+ null, null, null);*/
+
+ startManagingCursor(mCursor);
+ rebuild(false);
+ }
+
+ public void cleanup() {
+ if (mCursor != null) {
+ stopManagingCursor(mCursor);
+ mCursor.close();
+ }
+ }
+
+ 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 d78dd47e1..dd856fc90 100644
--- a/src/org/thialfihar/android/apg/MailListActivity.java
+++ b/src/org/thialfihar/android/apg/MailListActivity.java
@@ -1,218 +1,218 @@
-/*
- * 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.util.Vector;
-import java.util.regex.Matcher;
-
-import android.app.ListActivity;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.Html;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-public class MailListActivity extends ListActivity {
- LayoutInflater mInflater = null;
-
- private static class Conversation {
- public long id;
- public String subject;
- public Vector<Message> messages;
-
- public Conversation(long id, String subject) {
- this.id = id;
- this.subject = subject;
- }
- }
-
- private static class Message {
- public Conversation parent;
- public long id;
- public String subject;
- public String fromAddress;
- public String data;
- public String replyTo;
- public boolean signedOnly;
-
- public Message(Conversation parent, long id, String subject,
- String fromAddress, String replyTo,
- String data, boolean signedOnly) {
- this.parent = parent;
- this.id = id;
- this.subject = subject;
- this.fromAddress = fromAddress;
- this.replyTo = replyTo;
- this.data = data;
- if (this.replyTo == null || this.replyTo.equals("")) {
- this.replyTo = this.fromAddress;
- }
- this.signedOnly = signedOnly;
- }
- }
-
- private Vector<Conversation> mconversations;
- private Vector<Message> mmessages;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- mconversations = new Vector<Conversation>();
- mmessages = new Vector<Message>();
-
- 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 =
- managedQuery(uri, new String[] { "conversation_id", "subject" }, null, null, null);
- for (int i = 0; i < cursor.getCount(); ++i) {
- cursor.moveToPosition(i);
-
- int idIndex = cursor.getColumnIndex("conversation_id");
- int subjectIndex = cursor.getColumnIndex("subject");
- long conversationId = cursor.getLong(idIndex);
- Conversation conversation =
- new Conversation(conversationId, cursor.getString(subjectIndex));
- Uri messageUri = Uri.withAppendedPath(uri, "" + conversationId + "/messages");
- Cursor messageCursor =
- managedQuery(messageUri, new String[] {
- "messageId",
- "subject",
- "fromAddress",
- "replyToAddresses",
- "body" }, null, null, null);
- Vector<Message> messages = new Vector<Message>();
- for (int j = 0; j < messageCursor.getCount(); ++j) {
- messageCursor.moveToPosition(j);
- idIndex = messageCursor.getColumnIndex("messageId");
- subjectIndex = messageCursor.getColumnIndex("subject");
- int fromAddressIndex = messageCursor.getColumnIndex("fromAddress");
- int replyToIndex = messageCursor.getColumnIndex("replyToAddresses");
- int bodyIndex = messageCursor.getColumnIndex("body");
- String data = messageCursor.getString(bodyIndex);
- data = Html.fromHtml(data).toString();
- boolean signedOnly = false;
- Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
- if (matcher.matches()) {
- data = matcher.group(1);
- } else {
- matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data);
- if (matcher.matches()) {
- data = matcher.group(1);
- signedOnly = true;
- } else {
- data = null;
- }
- }
- Message message =
- new Message(conversation,
- messageCursor.getLong(idIndex),
- messageCursor.getString(subjectIndex),
- messageCursor.getString(fromAddressIndex),
- messageCursor.getString(replyToIndex),
- data, signedOnly);
-
- messages.add(message);
- mmessages.add(message);
- }
- conversation.messages = messages;
- mconversations.add(conversation);
- }
-
- setListAdapter(new MailboxAdapter());
- getListView().setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> arg0, View v, int position, long id) {
- Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
- intent.setAction(Apg.Intent.DECRYPT);
- Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
- intent.putExtra(Apg.EXTRA_TEXT, message.data);
- intent.putExtra(Apg.EXTRA_SUBJECT, message.subject);
- intent.putExtra(Apg.EXTRA_REPLY_TO, message.replyTo);
- startActivity(intent);
- }
- });
- }
-
- private class MailboxAdapter extends BaseAdapter implements ListAdapter {
-
- @Override
- public boolean isEnabled(int position) {
- Message message = (Message) getItem(position);
- return message.data != null;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public int getCount() {
- return mmessages.size();
- }
-
- @Override
- public Object getItem(int position) {
- return mmessages.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return mmessages.get(position).id;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View view = mInflater.inflate(R.layout.mailbox_message_item, null);
-
- Message message = (Message) getItem(position);
-
- TextView subject = (TextView) view.findViewById(R.id.subject);
- TextView email = (TextView) view.findViewById(R.id.emailAddress);
- ImageView status = (ImageView) view.findViewById(R.id.ic_status);
-
- subject.setText(message.subject);
- email.setText(message.fromAddress);
- if (message.data != null) {
- if (message.signedOnly) {
- status.setImageResource(R.drawable.signed);
- } else {
- status.setImageResource(R.drawable.encrypted);
- }
- status.setVisibility(View.VISIBLE);
- } else {
- status.setVisibility(View.INVISIBLE);
- }
-
- return view;
- }
- }
-}
+/*
+ * 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.util.Vector;
+import java.util.regex.Matcher;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.Html;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+public class MailListActivity extends ListActivity {
+ LayoutInflater mInflater = null;
+
+ private static class Conversation {
+ public long id;
+ public String subject;
+ public Vector<Message> messages;
+
+ public Conversation(long id, String subject) {
+ this.id = id;
+ this.subject = subject;
+ }
+ }
+
+ private static class Message {
+ public Conversation parent;
+ public long id;
+ public String subject;
+ public String fromAddress;
+ public String data;
+ public String replyTo;
+ public boolean signedOnly;
+
+ public Message(Conversation parent, long id, String subject,
+ String fromAddress, String replyTo,
+ String data, boolean signedOnly) {
+ this.parent = parent;
+ this.id = id;
+ this.subject = subject;
+ this.fromAddress = fromAddress;
+ this.replyTo = replyTo;
+ this.data = data;
+ if (this.replyTo == null || this.replyTo.equals("")) {
+ this.replyTo = this.fromAddress;
+ }
+ this.signedOnly = signedOnly;
+ }
+ }
+
+ private Vector<Conversation> mconversations;
+ private Vector<Message> mmessages;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ mconversations = new Vector<Conversation>();
+ mmessages = new Vector<Message>();
+
+ 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 =
+ managedQuery(uri, new String[] { "conversation_id", "subject" }, null, null, null);
+ for (int i = 0; i < cursor.getCount(); ++i) {
+ cursor.moveToPosition(i);
+
+ int idIndex = cursor.getColumnIndex("conversation_id");
+ int subjectIndex = cursor.getColumnIndex("subject");
+ long conversationId = cursor.getLong(idIndex);
+ Conversation conversation =
+ new Conversation(conversationId, cursor.getString(subjectIndex));
+ Uri messageUri = Uri.withAppendedPath(uri, "" + conversationId + "/messages");
+ Cursor messageCursor =
+ managedQuery(messageUri, new String[] {
+ "messageId",
+ "subject",
+ "fromAddress",
+ "replyToAddresses",
+ "body" }, null, null, null);
+ Vector<Message> messages = new Vector<Message>();
+ for (int j = 0; j < messageCursor.getCount(); ++j) {
+ messageCursor.moveToPosition(j);
+ idIndex = messageCursor.getColumnIndex("messageId");
+ subjectIndex = messageCursor.getColumnIndex("subject");
+ int fromAddressIndex = messageCursor.getColumnIndex("fromAddress");
+ int replyToIndex = messageCursor.getColumnIndex("replyToAddresses");
+ int bodyIndex = messageCursor.getColumnIndex("body");
+ String data = messageCursor.getString(bodyIndex);
+ data = Html.fromHtml(data).toString();
+ boolean signedOnly = false;
+ Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
+ if (matcher.matches()) {
+ data = matcher.group(1);
+ } else {
+ matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data);
+ if (matcher.matches()) {
+ data = matcher.group(1);
+ signedOnly = true;
+ } else {
+ data = null;
+ }
+ }
+ Message message =
+ new Message(conversation,
+ messageCursor.getLong(idIndex),
+ messageCursor.getString(subjectIndex),
+ messageCursor.getString(fromAddressIndex),
+ messageCursor.getString(replyToIndex),
+ data, signedOnly);
+
+ messages.add(message);
+ mmessages.add(message);
+ }
+ conversation.messages = messages;
+ mconversations.add(conversation);
+ }
+
+ setListAdapter(new MailboxAdapter());
+ getListView().setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View v, int position, long id) {
+ Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
+ intent.setAction(Apg.Intent.DECRYPT);
+ Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
+ intent.putExtra(Apg.EXTRA_TEXT, message.data);
+ intent.putExtra(Apg.EXTRA_SUBJECT, message.subject);
+ intent.putExtra(Apg.EXTRA_REPLY_TO, message.replyTo);
+ startActivity(intent);
+ }
+ });
+ }
+
+ private class MailboxAdapter extends BaseAdapter implements ListAdapter {
+
+ @Override
+ public boolean isEnabled(int position) {
+ Message message = (Message) getItem(position);
+ return message.data != null;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public int getCount() {
+ return mmessages.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mmessages.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mmessages.get(position).id;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = mInflater.inflate(R.layout.mailbox_message_item, null);
+
+ Message message = (Message) getItem(position);
+
+ TextView subject = (TextView) view.findViewById(R.id.subject);
+ TextView email = (TextView) view.findViewById(R.id.emailAddress);
+ ImageView status = (ImageView) view.findViewById(R.id.ic_status);
+
+ subject.setText(message.subject);
+ email.setText(message.fromAddress);
+ if (message.data != null) {
+ if (message.signedOnly) {
+ status.setImageResource(R.drawable.signed);
+ } else {
+ status.setImageResource(R.drawable.encrypted);
+ }
+ status.setVisibility(View.VISIBLE);
+ } else {
+ status.setVisibility(View.INVISIBLE);
+ }
+
+ return view;
+ }
+ }
+}
diff --git a/src/org/thialfihar/android/apg/PositionAwareInputStream.java b/src/org/thialfihar/android/apg/PositionAwareInputStream.java
index 661e053f2..3b0fdc1f9 100644
--- a/src/org/thialfihar/android/apg/PositionAwareInputStream.java
+++ b/src/org/thialfihar/android/apg/PositionAwareInputStream.java
@@ -1,67 +1,67 @@
-package org.thialfihar.android.apg;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public class PositionAwareInputStream extends InputStream {
- private InputStream mStream;
- private long mPosition;
-
- public PositionAwareInputStream(InputStream in) {
- mStream = in;
- mPosition = 0;
- }
-
- @Override
- public int read() throws IOException {
- int ch = mStream.read();
- ++mPosition;
- return ch;
- }
-
- @Override
- public int available() throws IOException {
- return mStream.available();
- }
-
- @Override
- public void close() throws IOException {
- mStream.close();
- }
-
- @Override
- public boolean markSupported() {
- return false;
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- int result = mStream.read(b);
- mPosition += result;
- return result;
- }
-
- @Override
- public int read(byte[] b, int offset, int length) throws IOException {
- int result = mStream.read(b, offset, length);
- mPosition += result;
- return result;
- }
-
- @Override
- public synchronized void reset() throws IOException {
- mStream.reset();
- mPosition = 0;
- }
-
- @Override
- public long skip(long n) throws IOException {
- long result = mStream.skip(n);
- mPosition += result;
- return result;
- }
-
- public long position() {
- return mPosition;
- }
-}
+package org.thialfihar.android.apg;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class PositionAwareInputStream extends InputStream {
+ private InputStream mStream;
+ private long mPosition;
+
+ public PositionAwareInputStream(InputStream in) {
+ mStream = in;
+ mPosition = 0;
+ }
+
+ @Override
+ public int read() throws IOException {
+ int ch = mStream.read();
+ ++mPosition;
+ return ch;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return mStream.available();
+ }
+
+ @Override
+ public void close() throws IOException {
+ mStream.close();
+ }
+
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ int result = mStream.read(b);
+ mPosition += result;
+ return result;
+ }
+
+ @Override
+ public int read(byte[] b, int offset, int length) throws IOException {
+ int result = mStream.read(b, offset, length);
+ mPosition += result;
+ return result;
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ mStream.reset();
+ mPosition = 0;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ long result = mStream.skip(n);
+ mPosition += result;
+ return result;
+ }
+
+ public long position() {
+ return mPosition;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/PreferencesActivity.java b/src/org/thialfihar/android/apg/PreferencesActivity.java
index 7e584a7a6..c8778c780 100644
--- a/src/org/thialfihar/android/apg/PreferencesActivity.java
+++ b/src/org/thialfihar/android/apg/PreferencesActivity.java
@@ -1,180 +1,180 @@
-/*
- * 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 org.bouncycastle2.bcpg.HashAlgorithmTags;
-import org.bouncycastle2.openpgp.PGPEncryptedData;
-
-import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.PreferenceActivity;
-
-public class PreferencesActivity extends PreferenceActivity {
- private IntegerListPreference mPassPhraseCacheTtl = null;
- private IntegerListPreference mEncryptionAlgorithm = null;
- private IntegerListPreference mHashAlgorithm = null;
- private IntegerListPreference mMessageCompression = null;
- private IntegerListPreference mFileCompression = null;
- private CheckBoxPreference mAsciiArmour = null;
- private Preferences mPreferences;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mPreferences = Preferences.getPreferences(this);
-
- addPreferencesFromResource(R.xml.apg_preferences);
-
- mPassPhraseCacheTtl = (IntegerListPreference) findPreference(Constants.pref.pass_phrase_cache_ttl);
- mPassPhraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl());
- mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
- mPassPhraseCacheTtl.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mPassPhraseCacheTtl.setValue(newValue.toString());
- mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
- mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
- BaseActivity.startCacheService(PreferencesActivity.this, mPreferences);
- return false;
- }
- });
-
- mEncryptionAlgorithm = (IntegerListPreference) findPreference(Constants.pref.default_encryption_algorithm);
- int valueIds[] = {
- PGPEncryptedData.AES_128, PGPEncryptedData.AES_192, PGPEncryptedData.AES_256,
- PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH, PGPEncryptedData.CAST5,
- PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES, PGPEncryptedData.IDEA,
- };
- String entries[] = {
- "AES-128", "AES-192", "AES-256",
- "Blowfish", "Twofish", "CAST5",
- "DES", "Triple DES", "IDEA",
- };
- String values[] = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
- mEncryptionAlgorithm.setEntries(entries);
- mEncryptionAlgorithm.setEntryValues(values);
- mEncryptionAlgorithm.setValue("" + mPreferences.getDefaultEncryptionAlgorithm());
- mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
- mEncryptionAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mEncryptionAlgorithm.setValue(newValue.toString());
- mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
- mPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue.toString()));
- return false;
- }
- });
-
- mHashAlgorithm = (IntegerListPreference) findPreference(Constants.pref.default_hash_algorithm);
- valueIds = new int[] {
- HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160, HashAlgorithmTags.SHA1,
- HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256, HashAlgorithmTags.SHA384,
- HashAlgorithmTags.SHA512,
- };
- entries = new String[] {
- "MD5", "RIPEMD-160", "SHA-1",
- "SHA-224", "SHA-256", "SHA-384",
- "SHA-512",
- };
- values = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
- mHashAlgorithm.setEntries(entries);
- mHashAlgorithm.setEntryValues(values);
- mHashAlgorithm.setValue("" + mPreferences.getDefaultHashAlgorithm());
- mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
- mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mHashAlgorithm.setValue(newValue.toString());
- mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
- mPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString()));
- return false;
- }
- });
-
- mMessageCompression = (IntegerListPreference) findPreference(Constants.pref.default_message_compression);
- valueIds = new int[] {
- Id.choice.compression.none,
- Id.choice.compression.zip,
- Id.choice.compression.zlib,
- Id.choice.compression.bzip2,
- };
- entries = new String[] {
- getString(R.string.choice_none) + " (" + getString(R.string.fast) + ")",
- "ZIP (" + getString(R.string.fast) + ")",
- "ZLIB (" + getString(R.string.fast) + ")",
- "BZIP2 (" + getString(R.string.very_slow) + ")",
- };
- values = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
- mMessageCompression.setEntries(entries);
- mMessageCompression.setEntryValues(values);
- mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
- mMessageCompression.setSummary(mMessageCompression.getEntry());
- mMessageCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mMessageCompression.setValue(newValue.toString());
- mMessageCompression.setSummary(mMessageCompression.getEntry());
- mPreferences.setDefaultMessageCompression(Integer.parseInt(newValue.toString()));
- return false;
- }
- });
-
- mFileCompression = (IntegerListPreference) findPreference(Constants.pref.default_file_compression);
- mFileCompression.setEntries(entries);
- mFileCompression.setEntryValues(values);
- mFileCompression.setValue("" + mPreferences.getDefaultFileCompression());
- mFileCompression.setSummary(mFileCompression.getEntry());
- mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mFileCompression.setValue(newValue.toString());
- mFileCompression.setSummary(mFileCompression.getEntry());
- mPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString()));
- return false;
- }
- });
-
- mAsciiArmour = (CheckBoxPreference) findPreference(Constants.pref.default_ascii_armour);
- mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour());
- mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mAsciiArmour.setChecked((Boolean)newValue);
- mPreferences.setDefaultAsciiArmour((Boolean)newValue);
- return false;
- }
- });
- }
-}
-
+/*
+ * 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 org.bouncycastle2.bcpg.HashAlgorithmTags;
+import org.bouncycastle2.openpgp.PGPEncryptedData;
+
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+
+public class PreferencesActivity extends PreferenceActivity {
+ private IntegerListPreference mPassPhraseCacheTtl = null;
+ private IntegerListPreference mEncryptionAlgorithm = null;
+ private IntegerListPreference mHashAlgorithm = null;
+ private IntegerListPreference mMessageCompression = null;
+ private IntegerListPreference mFileCompression = null;
+ private CheckBoxPreference mAsciiArmour = null;
+ private Preferences mPreferences;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mPreferences = Preferences.getPreferences(this);
+
+ addPreferencesFromResource(R.xml.apg_preferences);
+
+ mPassPhraseCacheTtl = (IntegerListPreference) findPreference(Constants.pref.pass_phrase_cache_ttl);
+ mPassPhraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl());
+ mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
+ mPassPhraseCacheTtl.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mPassPhraseCacheTtl.setValue(newValue.toString());
+ mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
+ mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
+ BaseActivity.startCacheService(PreferencesActivity.this, mPreferences);
+ return false;
+ }
+ });
+
+ mEncryptionAlgorithm = (IntegerListPreference) findPreference(Constants.pref.default_encryption_algorithm);
+ int valueIds[] = {
+ PGPEncryptedData.AES_128, PGPEncryptedData.AES_192, PGPEncryptedData.AES_256,
+ PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH, PGPEncryptedData.CAST5,
+ PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES, PGPEncryptedData.IDEA,
+ };
+ String entries[] = {
+ "AES-128", "AES-192", "AES-256",
+ "Blowfish", "Twofish", "CAST5",
+ "DES", "Triple DES", "IDEA",
+ };
+ String values[] = new String[valueIds.length];
+ for (int i = 0; i < values.length; ++i) {
+ values[i] = "" + valueIds[i];
+ }
+ mEncryptionAlgorithm.setEntries(entries);
+ mEncryptionAlgorithm.setEntryValues(values);
+ mEncryptionAlgorithm.setValue("" + mPreferences.getDefaultEncryptionAlgorithm());
+ mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
+ mEncryptionAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mEncryptionAlgorithm.setValue(newValue.toString());
+ mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
+ mPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue.toString()));
+ return false;
+ }
+ });
+
+ mHashAlgorithm = (IntegerListPreference) findPreference(Constants.pref.default_hash_algorithm);
+ valueIds = new int[] {
+ HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160, HashAlgorithmTags.SHA1,
+ HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256, HashAlgorithmTags.SHA384,
+ HashAlgorithmTags.SHA512,
+ };
+ entries = new String[] {
+ "MD5", "RIPEMD-160", "SHA-1",
+ "SHA-224", "SHA-256", "SHA-384",
+ "SHA-512",
+ };
+ values = new String[valueIds.length];
+ for (int i = 0; i < values.length; ++i) {
+ values[i] = "" + valueIds[i];
+ }
+ mHashAlgorithm.setEntries(entries);
+ mHashAlgorithm.setEntryValues(values);
+ mHashAlgorithm.setValue("" + mPreferences.getDefaultHashAlgorithm());
+ mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
+ mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mHashAlgorithm.setValue(newValue.toString());
+ mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
+ mPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString()));
+ return false;
+ }
+ });
+
+ mMessageCompression = (IntegerListPreference) findPreference(Constants.pref.default_message_compression);
+ valueIds = new int[] {
+ Id.choice.compression.none,
+ Id.choice.compression.zip,
+ Id.choice.compression.zlib,
+ Id.choice.compression.bzip2,
+ };
+ entries = new String[] {
+ getString(R.string.choice_none) + " (" + getString(R.string.fast) + ")",
+ "ZIP (" + getString(R.string.fast) + ")",
+ "ZLIB (" + getString(R.string.fast) + ")",
+ "BZIP2 (" + getString(R.string.very_slow) + ")",
+ };
+ values = new String[valueIds.length];
+ for (int i = 0; i < values.length; ++i) {
+ values[i] = "" + valueIds[i];
+ }
+ mMessageCompression.setEntries(entries);
+ mMessageCompression.setEntryValues(values);
+ mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
+ mMessageCompression.setSummary(mMessageCompression.getEntry());
+ mMessageCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mMessageCompression.setValue(newValue.toString());
+ mMessageCompression.setSummary(mMessageCompression.getEntry());
+ mPreferences.setDefaultMessageCompression(Integer.parseInt(newValue.toString()));
+ return false;
+ }
+ });
+
+ mFileCompression = (IntegerListPreference) findPreference(Constants.pref.default_file_compression);
+ mFileCompression.setEntries(entries);
+ mFileCompression.setEntryValues(values);
+ mFileCompression.setValue("" + mPreferences.getDefaultFileCompression());
+ mFileCompression.setSummary(mFileCompression.getEntry());
+ mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mFileCompression.setValue(newValue.toString());
+ mFileCompression.setSummary(mFileCompression.getEntry());
+ mPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString()));
+ return false;
+ }
+ });
+
+ mAsciiArmour = (CheckBoxPreference) findPreference(Constants.pref.default_ascii_armour);
+ mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour());
+ mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mAsciiArmour.setChecked((Boolean)newValue);
+ mPreferences.setDefaultAsciiArmour((Boolean)newValue);
+ return false;
+ }
+ });
+ }
+}
+
diff --git a/src/org/thialfihar/android/apg/Primes.java b/src/org/thialfihar/android/apg/Primes.java
index e97a6c6c5..f0f391291 100644
--- a/src/org/thialfihar/android/apg/Primes.java
+++ b/src/org/thialfihar/android/apg/Primes.java
@@ -1,185 +1,185 @@
-/*
- * 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.math.BigInteger;
-
-public final class Primes {
- // taken from http://www.ietf.org/rfc/rfc3526.txt
- public static final String P1536 =
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF";
-
- public static final String P2048 =
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AACAA68 FFFFFFFF FFFFFFFF";
-
- public static final String P3072 =
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
- "43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF";
-
- public static final String P4096 =
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
- "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
- "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
- "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
- "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
- "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
- "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199" +
- "FFFFFFFF FFFFFFFF";
-
- public static final String P6144 =
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
- "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
- "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
- "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
- "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
- "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
- "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
- "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
- "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
- "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
- "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
- "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
- "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
- "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
- "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
- "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
- "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
- "12BF2D5B 0B7474D6 E694F91E 6DCC4024 FFFFFFFF FFFFFFFF";
-
- public static final String P8192 =
- "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
- "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
- "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
- "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
- "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
- "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
- "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
- "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
- "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
- "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
- "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
- "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
- "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
- "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
- "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
- "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
- "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
- "12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4" +
- "38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300" +
- "741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568" +
- "3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9" +
- "22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B" +
- "4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A" +
- "062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36" +
- "4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1" +
- "B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92" +
- "4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47" +
- "9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71" +
- "60C980DD 98EDD3DF FFFFFFFF FFFFFFFF";
-
- public static BigInteger getBestPrime(int keySize) {
- String primeString;
- if (keySize >= (8192 + 6144) / 2) {
- primeString = P8192;
- } else if (keySize >= (6144 + 4096) / 2) {
- primeString = P6144;
- } else if (keySize >= (4096 + 3072) / 2) {
- primeString = P4096;
- } else if (keySize >= (3072 + 2048) / 2) {
- primeString = P3072;
- } else if (keySize >= (2048 + 1536) / 2) {
- primeString = P2048;
- } else {
- primeString = P1536;
- }
-
- return new BigInteger(primeString.replaceAll(" ", ""), 16);
- }
-}
+/*
+ * 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.math.BigInteger;
+
+public final class Primes {
+ // taken from http://www.ietf.org/rfc/rfc3526.txt
+ public static final String P1536 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF";
+
+ public static final String P2048 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AACAA68 FFFFFFFF FFFFFFFF";
+
+ public static final String P3072 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF";
+
+ public static final String P4096 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
+ "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
+ "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
+ "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
+ "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
+ "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199" +
+ "FFFFFFFF FFFFFFFF";
+
+ public static final String P6144 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
+ "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
+ "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
+ "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
+ "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
+ "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
+ "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
+ "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
+ "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
+ "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
+ "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
+ "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
+ "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
+ "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
+ "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
+ "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
+ "12BF2D5B 0B7474D6 E694F91E 6DCC4024 FFFFFFFF FFFFFFFF";
+
+ public static final String P8192 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
+ "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
+ "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
+ "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
+ "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
+ "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
+ "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
+ "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
+ "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
+ "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
+ "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
+ "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
+ "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
+ "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
+ "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
+ "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
+ "12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4" +
+ "38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300" +
+ "741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568" +
+ "3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9" +
+ "22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B" +
+ "4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A" +
+ "062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36" +
+ "4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1" +
+ "B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92" +
+ "4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47" +
+ "9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71" +
+ "60C980DD 98EDD3DF FFFFFFFF FFFFFFFF";
+
+ public static BigInteger getBestPrime(int keySize) {
+ String primeString;
+ if (keySize >= (8192 + 6144) / 2) {
+ primeString = P8192;
+ } else if (keySize >= (6144 + 4096) / 2) {
+ primeString = P6144;
+ } else if (keySize >= (4096 + 3072) / 2) {
+ primeString = P4096;
+ } else if (keySize >= (3072 + 2048) / 2) {
+ primeString = P3072;
+ } else if (keySize >= (2048 + 1536) / 2) {
+ primeString = P2048;
+ } else {
+ primeString = P1536;
+ }
+
+ return new BigInteger(primeString.replaceAll(" ", ""), 16);
+ }
+}
diff --git a/src/org/thialfihar/android/apg/ProgressDialogUpdater.java b/src/org/thialfihar/android/apg/ProgressDialogUpdater.java
index fe38c2a34..691a90353 100644
--- a/src/org/thialfihar/android/apg/ProgressDialogUpdater.java
+++ b/src/org/thialfihar/android/apg/ProgressDialogUpdater.java
@@ -1,23 +1,23 @@
-/*
- * 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;
-
-public interface ProgressDialogUpdater {
- void setProgress(String message, int current, int total);
- void setProgress(int resourceId, int current, int total);
- void setProgress(int current, int total);
-}
+/*
+ * 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;
+
+public interface ProgressDialogUpdater {
+ void setProgress(String message, int current, int total);
+ void setProgress(int resourceId, int current, int total);
+ void setProgress(int current, int total);
+}
diff --git a/src/org/thialfihar/android/apg/PublicKeyListActivity.java b/src/org/thialfihar/android/apg/PublicKeyListActivity.java
index 916d4e6c2..610e23683 100644
--- a/src/org/thialfihar/android/apg/PublicKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/PublicKeyListActivity.java
@@ -1,62 +1,62 @@
-/*
- * 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 android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.Menu;
-import android.view.View;
-import android.widget.ExpandableListView;
-
-public class PublicKeyListActivity extends KeyListActivity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- mExportFilename = Constants.path.app_dir + "/pubexport.asc";
- mKeyType = Id.type.public_key;
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys)
- .setIcon(android.R.drawable.ic_menu_add);
- menu.add(0, Id.menu.option.export_keys, 1, R.string.menu_exportKeys)
- .setIcon(android.R.drawable.ic_menu_save);
- menu.add(1, Id.menu.option.search, 2, R.string.menu_search)
- .setIcon(android.R.drawable.ic_menu_search);
- menu.add(1, Id.menu.option.preferences, 3, R.string.menu_preferences)
- .setIcon(android.R.drawable.ic_menu_preferences);
- menu.add(1, Id.menu.option.about, 4, R.string.menu_about)
- .setIcon(android.R.drawable.ic_menu_info_details);
- return true;
- }
-
- @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);
-
- if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- // 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);
- }
- }
-}
+/*
+ * 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 android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.View;
+import android.widget.ExpandableListView;
+
+public class PublicKeyListActivity extends KeyListActivity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mExportFilename = Constants.path.app_dir + "/pubexport.asc";
+ mKeyType = Id.type.public_key;
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys)
+ .setIcon(android.R.drawable.ic_menu_add);
+ menu.add(0, Id.menu.option.export_keys, 1, R.string.menu_exportKeys)
+ .setIcon(android.R.drawable.ic_menu_save);
+ menu.add(1, Id.menu.option.search, 2, R.string.menu_search)
+ .setIcon(android.R.drawable.ic_menu_search);
+ menu.add(1, Id.menu.option.preferences, 3, R.string.menu_preferences)
+ .setIcon(android.R.drawable.ic_menu_preferences);
+ menu.add(1, Id.menu.option.about, 4, R.string.menu_about)
+ .setIcon(android.R.drawable.ic_menu_info_details);
+ return true;
+ }
+
+ @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);
+
+ if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ // 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);
+ }
+ }
+}
diff --git a/src/org/thialfihar/android/apg/SecretKeyListActivity.java b/src/org/thialfihar/android/apg/SecretKeyListActivity.java
index 9ff7f0fa3..d3c89ddc9 100644
--- a/src/org/thialfihar/android/apg/SecretKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/SecretKeyListActivity.java
@@ -1,180 +1,180 @@
-/*
- * 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 android.app.Dialog;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
-import android.widget.ExpandableListView.OnChildClickListener;
-
-public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- mExportFilename = Constants.path.app_dir + "/secexport.asc";
- mKeyType = Id.type.secret_key;
- super.onCreate(savedInstanceState);
- mList.setOnChildClickListener(this);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys)
- .setIcon(android.R.drawable.ic_menu_add);
- menu.add(0, Id.menu.option.export_keys, 1, R.string.menu_exportKeys)
- .setIcon(android.R.drawable.ic_menu_save);
- menu.add(1, Id.menu.option.create, 2, R.string.menu_createKey)
- .setIcon(android.R.drawable.ic_menu_add);
- menu.add(3, Id.menu.option.search, 3, R.string.menu_search)
- .setIcon(android.R.drawable.ic_menu_search);
- menu.add(3, Id.menu.option.preferences, 4, R.string.menu_preferences)
- .setIcon(android.R.drawable.ic_menu_preferences);
- menu.add(3, Id.menu.option.about, 5, R.string.menu_about)
- .setIcon(android.R.drawable.ic_menu_info_details);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case Id.menu.option.create: {
- createKey();
- 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);
-
- if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- // 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);
- }
- }
-
- @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.edit: {
- mSelectedItem = groupPosition;
- checkPassPhraseAndEdit();
- return true;
- }
-
- default: {
- return super.onContextItemSelected(menuItem);
- }
- }
- }
-
- @Override
- public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
- int childPosition, long id) {
- mSelectedItem = groupPosition;
- checkPassPhraseAndEdit();
- return true;
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case Id.dialog.pass_phrase: {
- long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
- return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
- }
-
- default: {
- return super.onCreateDialog(id);
- }
- }
- }
-
- public void checkPassPhraseAndEdit() {
- long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
- String passPhrase = Apg.getCachedPassPhrase(keyId);
- if (passPhrase == null) {
- showDialog(Id.dialog.pass_phrase);
- } else {
- Apg.setEditPassPhrase(passPhrase);
- editKey();
- }
- }
-
- @Override
- public void passPhraseCallback(long keyId, String passPhrase) {
- super.passPhraseCallback(keyId, passPhrase);
- Apg.setEditPassPhrase(passPhrase);
- editKey();
- }
-
- private void createKey() {
- Apg.setEditPassPhrase("");
- Intent intent = new Intent(this, EditKeyActivity.class);
- startActivityForResult(intent, Id.message.create_key);
- }
-
- private void editKey() {
- long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
- Intent intent = new Intent(this, EditKeyActivity.class);
- intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
- startActivityForResult(intent, Id.message.edit_key);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.message.create_key: // intentionally no break
- case Id.message.edit_key: {
- if (resultCode == RESULT_OK) {
- refreshList();
- }
- break;
- }
-
- default: {
- break;
- }
- }
-
- super.onActivityResult(requestCode, resultCode, data);
- }
-}
+/*
+ * 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 android.app.Dialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.ExpandableListView.OnChildClickListener;
+
+public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mExportFilename = Constants.path.app_dir + "/secexport.asc";
+ mKeyType = Id.type.secret_key;
+ super.onCreate(savedInstanceState);
+ mList.setOnChildClickListener(this);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys)
+ .setIcon(android.R.drawable.ic_menu_add);
+ menu.add(0, Id.menu.option.export_keys, 1, R.string.menu_exportKeys)
+ .setIcon(android.R.drawable.ic_menu_save);
+ menu.add(1, Id.menu.option.create, 2, R.string.menu_createKey)
+ .setIcon(android.R.drawable.ic_menu_add);
+ menu.add(3, Id.menu.option.search, 3, R.string.menu_search)
+ .setIcon(android.R.drawable.ic_menu_search);
+ menu.add(3, Id.menu.option.preferences, 4, R.string.menu_preferences)
+ .setIcon(android.R.drawable.ic_menu_preferences);
+ menu.add(3, Id.menu.option.about, 5, R.string.menu_about)
+ .setIcon(android.R.drawable.ic_menu_info_details);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case Id.menu.option.create: {
+ createKey();
+ 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);
+
+ if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ // 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);
+ }
+ }
+
+ @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.edit: {
+ mSelectedItem = groupPosition;
+ checkPassPhraseAndEdit();
+ return true;
+ }
+
+ default: {
+ return super.onContextItemSelected(menuItem);
+ }
+ }
+ }
+
+ @Override
+ public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
+ int childPosition, long id) {
+ mSelectedItem = groupPosition;
+ checkPassPhraseAndEdit();
+ return true;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case Id.dialog.pass_phrase: {
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
+ return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
+ }
+
+ default: {
+ return super.onCreateDialog(id);
+ }
+ }
+ }
+
+ public void checkPassPhraseAndEdit() {
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
+ String passPhrase = Apg.getCachedPassPhrase(keyId);
+ if (passPhrase == null) {
+ showDialog(Id.dialog.pass_phrase);
+ } else {
+ Apg.setEditPassPhrase(passPhrase);
+ editKey();
+ }
+ }
+
+ @Override
+ public void passPhraseCallback(long keyId, String passPhrase) {
+ super.passPhraseCallback(keyId, passPhrase);
+ Apg.setEditPassPhrase(passPhrase);
+ editKey();
+ }
+
+ private void createKey() {
+ Apg.setEditPassPhrase("");
+ Intent intent = new Intent(this, EditKeyActivity.class);
+ startActivityForResult(intent, Id.message.create_key);
+ }
+
+ private void editKey() {
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
+ Intent intent = new Intent(this, EditKeyActivity.class);
+ intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
+ startActivityForResult(intent, Id.message.edit_key);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.message.create_key: // intentionally no break
+ case Id.message.edit_key: {
+ if (resultCode == RESULT_OK) {
+ refreshList();
+ }
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+}
diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
index 095772ec2..cbbb88371 100644
--- a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
+++ b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
@@ -1,225 +1,225 @@
-/*
- * 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.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.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.CheckBox;
-import android.widget.ListView;
-import android.widget.TextView;
-
-public class SelectPublicKeyListAdapter extends BaseAdapter {
- protected LayoutInflater mInflater;
- protected ListView mParent;
- protected SQLiteDatabase mDatabase;
- protected Cursor mCursor;
- protected String mSearchString;
- protected Activity mActivity;
-
- public SelectPublicKeyListAdapter(Activity activity, ListView parent,
- String searchString, long selectedKeyIds[]) {
- mSearchString = searchString;
-
- mActivity = activity;
- mParent = parent;
- mDatabase = Apg.getDatabase().db();
- mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- long now = new Date().getTime() / 1000;
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- 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') ");
-
- String inIdList = null;
-
- if (selectedKeyIds != null && selectedKeyIds.length > 0) {
- inIdList = KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " IN (";
- for (int i = 0; i < selectedKeyIds.length; ++i) {
- if (i != 0) {
- inIdList += ", ";
- }
- inIdList += DatabaseUtils.sqlEscapeString("" + selectedKeyIds[i]);
- }
- inIdList += ")";
- }
-
- if (searchString != null && searchString.trim().length() > 0) {
- String[] chunks = searchString.trim().split(" +");
- qb.appendWhere("(EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
- UserIds.TABLE_NAME + " AS tmp WHERE " +
- "tmp." + UserIds.KEY_ID + " = " +
- Keys.TABLE_NAME + "." + Keys._ID);
- for (int i = 0; i < chunks.length; ++i) {
- qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
- qb.appendWhereEscapeString("%" + chunks[i] + "%");
- }
- qb.appendWhere("))");
-
- if (inIdList != null) {
- qb.appendWhere(" OR (" + inIdList + ")");
- }
- }
-
- String orderBy = UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
- if (inIdList != null) {
- orderBy = inIdList + " DESC, " + orderBy;
- }
-
- mCursor = qb.query(mDatabase,
- 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, orderBy);
-
- activity.startManagingCursor(mCursor);
- }
-
- public void cleanup() {
- if (mCursor != null) {
- mActivity.stopManagingCursor(mCursor);
- mCursor.close();
- }
- }
-
- @Override
- public boolean isEnabled(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getInt(4) > 0; // valid CAN_ENCRYPT
- }
-
- @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_public_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.canEncrypt);
- } 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);
- }
- }
-
- status.setText(status.getText() + " ");
-
- CheckBox selected = (CheckBox) view.findViewById(R.id.selected);
-
- if (!enabled) {
- mParent.setItemChecked(position, false);
- }
-
- selected.setChecked(mParent.isItemChecked(position));
-
- view.setEnabled(enabled);
- mainUserId.setEnabled(enabled);
- mainUserIdRest.setEnabled(enabled);
- keyId.setEnabled(enabled);
- selected.setEnabled(enabled);
- status.setEnabled(enabled);
-
- return view;
- }
-}
+/*
+ * 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.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.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class SelectPublicKeyListAdapter extends BaseAdapter {
+ protected LayoutInflater mInflater;
+ protected ListView mParent;
+ protected SQLiteDatabase mDatabase;
+ protected Cursor mCursor;
+ protected String mSearchString;
+ protected Activity mActivity;
+
+ public SelectPublicKeyListAdapter(Activity activity, ListView parent,
+ String searchString, long selectedKeyIds[]) {
+ mSearchString = searchString;
+
+ mActivity = activity;
+ mParent = parent;
+ mDatabase = Apg.getDatabase().db();
+ mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ long now = new Date().getTime() / 1000;
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ 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') ");
+
+ String inIdList = null;
+
+ if (selectedKeyIds != null && selectedKeyIds.length > 0) {
+ inIdList = KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " IN (";
+ for (int i = 0; i < selectedKeyIds.length; ++i) {
+ if (i != 0) {
+ inIdList += ", ";
+ }
+ inIdList += DatabaseUtils.sqlEscapeString("" + selectedKeyIds[i]);
+ }
+ inIdList += ")";
+ }
+
+ if (searchString != null && searchString.trim().length() > 0) {
+ String[] chunks = searchString.trim().split(" +");
+ qb.appendWhere("(EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
+ UserIds.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + UserIds.KEY_ID + " = " +
+ Keys.TABLE_NAME + "." + Keys._ID);
+ for (int i = 0; i < chunks.length; ++i) {
+ qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
+ qb.appendWhereEscapeString("%" + chunks[i] + "%");
+ }
+ qb.appendWhere("))");
+
+ if (inIdList != null) {
+ qb.appendWhere(" OR (" + inIdList + ")");
+ }
+ }
+
+ String orderBy = UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
+ if (inIdList != null) {
+ orderBy = inIdList + " DESC, " + orderBy;
+ }
+
+ mCursor = qb.query(mDatabase,
+ 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, orderBy);
+
+ activity.startManagingCursor(mCursor);
+ }
+
+ public void cleanup() {
+ if (mCursor != null) {
+ mActivity.stopManagingCursor(mCursor);
+ mCursor.close();
+ }
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(4) > 0; // valid CAN_ENCRYPT
+ }
+
+ @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_public_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.canEncrypt);
+ } 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);
+ }
+ }
+
+ status.setText(status.getText() + " ");
+
+ CheckBox selected = (CheckBox) view.findViewById(R.id.selected);
+
+ if (!enabled) {
+ mParent.setItemChecked(position, false);
+ }
+
+ selected.setChecked(mParent.isItemChecked(position));
+
+ view.setEnabled(enabled);
+ mainUserId.setEnabled(enabled);
+ mainUserIdRest.setEnabled(enabled);
+ keyId.setEnabled(enabled);
+ selected.setEnabled(enabled);
+ status.setEnabled(enabled);
+
+ return view;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
index d83c2aca6..898a9e6da 100644
--- a/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
+++ b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
@@ -1,175 +1,175 @@
-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.database.sqlite.SQLiteQueryBuilder;
-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;
- protected String mSearchString;
- protected Activity mActivity;
-
- public SelectSecretKeyListAdapter(Activity activity, ListView parent, String searchString) {
- mSearchString = searchString;
-
- mActivity = activity;
- mParent = parent;
- mDatabase = Apg.getDatabase().db();
- mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- long now = new Date().getTime() / 1000;
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- 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') ");
-
- if (searchString != null && searchString.trim().length() > 0) {
- String[] chunks = searchString.trim().split(" +");
- qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
- UserIds.TABLE_NAME + " AS tmp WHERE " +
- "tmp." + UserIds.KEY_ID + " = " +
- Keys.TABLE_NAME + "." + Keys._ID);
- for (int i = 0; i < chunks.length; ++i) {
- qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
- qb.appendWhereEscapeString("%" + chunks[i] + "%");
- }
- qb.appendWhere(")");
- }
-
- mCursor = qb.query(mDatabase,
- 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);
- }
-
- public void cleanup() {
- if (mCursor != null) {
- mActivity.stopManagingCursor(mCursor);
- mCursor.close();
- }
- }
-
- @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;
- }
+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.database.sqlite.SQLiteQueryBuilder;
+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;
+ protected String mSearchString;
+ protected Activity mActivity;
+
+ public SelectSecretKeyListAdapter(Activity activity, ListView parent, String searchString) {
+ mSearchString = searchString;
+
+ mActivity = activity;
+ mParent = parent;
+ mDatabase = Apg.getDatabase().db();
+ mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ long now = new Date().getTime() / 1000;
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ 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') ");
+
+ if (searchString != null && searchString.trim().length() > 0) {
+ String[] chunks = searchString.trim().split(" +");
+ qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
+ UserIds.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + UserIds.KEY_ID + " = " +
+ Keys.TABLE_NAME + "." + Keys._ID);
+ for (int i = 0; i < chunks.length; ++i) {
+ qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
+ qb.appendWhereEscapeString("%" + chunks[i] + "%");
+ }
+ qb.appendWhere(")");
+ }
+
+ mCursor = qb.query(mDatabase,
+ 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);
+ }
+
+ public void cleanup() {
+ if (mCursor != null) {
+ mActivity.stopManagingCursor(mCursor);
+ mCursor.close();
+ }
+ }
+
+ @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
index 6105473db..70bc80a16 100644
--- a/src/org/thialfihar/android/apg/Service.java
+++ b/src/org/thialfihar/android/apg/Service.java
@@ -1,78 +1,78 @@
-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() {
- // 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;
- }
-}
+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() {
+ // 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 8162472ec..e1829ef4b 100644
--- a/src/org/thialfihar/android/apg/provider/Accounts.java
+++ b/src/org/thialfihar/android/apg/provider/Accounts.java
@@ -1,27 +1,27 @@
-/*
- * 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 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";
-}
+/*
+ * 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 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/Database.java b/src/org/thialfihar/android/apg/provider/Database.java
index 810ebebbf..05beb980d 100644
--- a/src/org/thialfihar/android/apg/provider/Database.java
+++ b/src/org/thialfihar/android/apg/provider/Database.java
@@ -1,605 +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;
- }
-}
+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/KeyRings.java b/src/org/thialfihar/android/apg/provider/KeyRings.java
index a4eae6695..58e95eba6 100644
--- a/src/org/thialfihar/android/apg/provider/KeyRings.java
+++ b/src/org/thialfihar/android/apg/provider/KeyRings.java
@@ -1,33 +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.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";
-}
+/*
+ * 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/UserIds.java b/src/org/thialfihar/android/apg/provider/UserIds.java
index 2b1162beb..2050ccf9c 100644
--- a/src/org/thialfihar/android/apg/provider/UserIds.java
+++ b/src/org/thialfihar/android/apg/provider/UserIds.java
@@ -1,31 +1,31 @@
-/*
- * 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 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";
-}
+/*
+ * 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 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/Editor.java b/src/org/thialfihar/android/apg/ui/widget/Editor.java
index ab29581a4..a7200fad0 100644
--- a/src/org/thialfihar/android/apg/ui/widget/Editor.java
+++ b/src/org/thialfihar/android/apg/ui/widget/Editor.java
@@ -1,25 +1,25 @@
-/*
- * 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.ui.widget;
-
-public interface Editor {
- public interface EditorListener {
- public void onDeleted(Editor editor);
- }
-
- public void setEditorListener(EditorListener listener);
-}
+/*
+ * 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.ui.widget;
+
+public interface Editor {
+ public interface EditorListener {
+ public void onDeleted(Editor editor);
+ }
+
+ public void setEditorListener(EditorListener listener);
+}
diff --git a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
index 1a9cfdbac..1bc7515f5 100644
--- a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
+++ b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
@@ -1,242 +1,242 @@
-/*
- * 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.ui.widget;
-
-import java.text.DateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Vector;
-
-import org.bouncycastle2.openpgp.PGPPublicKey;
-import org.bouncycastle2.openpgp.PGPSecretKey;
-import org.thialfihar.android.apg.Apg;
-import org.thialfihar.android.apg.Id;
-import org.thialfihar.android.apg.R;
-import org.thialfihar.android.apg.utils.Choice;
-
-import android.app.DatePickerDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.DatePicker;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
- private PGPSecretKey mKey;
-
- private EditorListener mEditorListener = null;
-
- private boolean mIsMasterKey;
- ImageButton mDeleteButton;
- TextView mAlgorithm;
- TextView mKeyId;
- Spinner mUsage;
- TextView mCreationDate;
- Button mExpiryDateButton;
- GregorianCalendar mExpiryDate;
-
- private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
- new DatePickerDialog.OnDateSetListener() {
- public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
- GregorianCalendar date = new GregorianCalendar(year, monthOfYear, dayOfMonth);
- setExpiryDate(date);
- }
- };
-
- public KeyEditor(Context context) {
- super(context);
- }
-
- public KeyEditor(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mAlgorithm = (TextView) findViewById(R.id.algorithm);
- mKeyId = (TextView) findViewById(R.id.keyId);
- mCreationDate = (TextView) findViewById(R.id.creation);
- mExpiryDateButton = (Button) findViewById(R.id.expiry);
- mUsage = (Spinner) findViewById(R.id.usage);
- Choice choices[] = {
- new Choice(Id.choice.usage.sign_only,
- getResources().getString(R.string.choice_signOnly)),
- new Choice(Id.choice.usage.encrypt_only,
- getResources().getString(R.string.choice_encryptOnly)),
- new Choice(Id.choice.usage.sign_and_encrypt,
- getResources().getString(R.string.choice_signAndEncrypt)),
- };
- ArrayAdapter<Choice> adapter =
- new ArrayAdapter<Choice>(getContext(),
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mUsage.setAdapter(adapter);
-
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(this);
-
- setExpiryDate(null);
-
- mExpiryDateButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- GregorianCalendar date = mExpiryDate;
- if (date == null) {
- date = new GregorianCalendar();
- }
-
- DatePickerDialog dialog =
- new DatePickerDialog(getContext(), mExpiryDateSetListener,
- date.get(Calendar.YEAR),
- date.get(Calendar.MONTH),
- date.get(Calendar.DAY_OF_MONTH));
- dialog.setCancelable(true);
- dialog.setButton(Dialog.BUTTON_NEGATIVE,
- getContext().getString(R.string.btn_noDate),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- setExpiryDate(null);
- }
- });
- dialog.show();
- }
- });
-
- super.onFinishInflate();
- }
-
- public void setValue(PGPSecretKey key, boolean isMasterKey) {
- mKey = key;
-
- mIsMasterKey = isMasterKey;
- if (mIsMasterKey) {
- mDeleteButton.setVisibility(View.INVISIBLE);
- }
-
- mAlgorithm.setText(Apg.getAlgorithmInfo(key));
- String keyId1Str = Long.toHexString((key.getKeyID() >> 32) & 0xffffffffL);
- while (keyId1Str.length() < 8) {
- keyId1Str = "0" + keyId1Str;
- }
- String keyId2Str = Long.toHexString(key.getKeyID() & 0xffffffffL);
- while (keyId2Str.length() < 8) {
- keyId2Str = "0" + keyId2Str;
- }
- mKeyId.setText(keyId1Str + " " + keyId2Str);
-
- Vector<Choice> choices = new Vector<Choice>();
- boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
- if (!isElGamalKey) {
- choices.add(new Choice(Id.choice.usage.sign_only,
- getResources().getString(R.string.choice_signOnly)));
- }
- if (!mIsMasterKey) {
- choices.add(new Choice(Id.choice.usage.encrypt_only,
- getResources().getString(R.string.choice_encryptOnly)));
- }
- if (!isElGamalKey) {
- choices.add(new Choice(Id.choice.usage.sign_and_encrypt,
- getResources().getString(R.string.choice_signAndEncrypt)));
- }
-
- ArrayAdapter<Choice> adapter =
- new ArrayAdapter<Choice>(getContext(),
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mUsage.setAdapter(adapter);
-
- int selectId = 0;
- if (Apg.isEncryptionKey(key)) {
- if (Apg.isSigningKey(key)) {
- selectId = Id.choice.usage.sign_and_encrypt;
- } else {
- selectId = Id.choice.usage.encrypt_only;
- }
- } else {
- selectId = Id.choice.usage.sign_only;
- }
-
- for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == selectId) {
- mUsage.setSelection(i);
- break;
- }
- }
-
- GregorianCalendar cal = new GregorianCalendar();
- cal.setTime(Apg.getCreationDate(key));
- mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime()));
- cal = new GregorianCalendar();
- Date date = Apg.getExpiryDate(key);
- if (date == null) {
- setExpiryDate(null);
- } else {
- cal.setTime(Apg.getExpiryDate(key));
- setExpiryDate(cal);
- }
- }
-
- public PGPSecretKey getValue() {
- return mKey;
- }
-
- @Override
- public void onClick(View v) {
- final ViewGroup parent = (ViewGroup)getParent();
- if (v == mDeleteButton) {
- parent.removeView(this);
- if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
- }
- }
- }
-
- @Override
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-
- private void setExpiryDate(GregorianCalendar date) {
- mExpiryDate = date;
- if (date == null) {
- mExpiryDateButton.setText(R.string.none);
- } else {
- mExpiryDateButton.setText(DateFormat.getDateInstance().format(date.getTime()));
- }
- }
-
- public GregorianCalendar getExpiryDate() {
- return mExpiryDate;
- }
-
- public int getUsage() {
- return ((Choice) mUsage.getSelectedItem()).getId();
- }
-}
+/*
+ * 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.ui.widget;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Vector;
+
+import org.bouncycastle2.openpgp.PGPPublicKey;
+import org.bouncycastle2.openpgp.PGPSecretKey;
+import org.thialfihar.android.apg.Apg;
+import org.thialfihar.android.apg.Id;
+import org.thialfihar.android.apg.R;
+import org.thialfihar.android.apg.utils.Choice;
+
+import android.app.DatePickerDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.DatePicker;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
+ private PGPSecretKey mKey;
+
+ private EditorListener mEditorListener = null;
+
+ private boolean mIsMasterKey;
+ ImageButton mDeleteButton;
+ TextView mAlgorithm;
+ TextView mKeyId;
+ Spinner mUsage;
+ TextView mCreationDate;
+ Button mExpiryDateButton;
+ GregorianCalendar mExpiryDate;
+
+ private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
+ new DatePickerDialog.OnDateSetListener() {
+ public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
+ GregorianCalendar date = new GregorianCalendar(year, monthOfYear, dayOfMonth);
+ setExpiryDate(date);
+ }
+ };
+
+ public KeyEditor(Context context) {
+ super(context);
+ }
+
+ public KeyEditor(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ setDrawingCacheEnabled(true);
+ setAlwaysDrawnWithCacheEnabled(true);
+
+ mAlgorithm = (TextView) findViewById(R.id.algorithm);
+ mKeyId = (TextView) findViewById(R.id.keyId);
+ mCreationDate = (TextView) findViewById(R.id.creation);
+ mExpiryDateButton = (Button) findViewById(R.id.expiry);
+ mUsage = (Spinner) findViewById(R.id.usage);
+ Choice choices[] = {
+ new Choice(Id.choice.usage.sign_only,
+ getResources().getString(R.string.choice_signOnly)),
+ new Choice(Id.choice.usage.encrypt_only,
+ getResources().getString(R.string.choice_encryptOnly)),
+ new Choice(Id.choice.usage.sign_and_encrypt,
+ getResources().getString(R.string.choice_signAndEncrypt)),
+ };
+ ArrayAdapter<Choice> adapter =
+ new ArrayAdapter<Choice>(getContext(),
+ android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mUsage.setAdapter(adapter);
+
+ mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton.setOnClickListener(this);
+
+ setExpiryDate(null);
+
+ mExpiryDateButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ GregorianCalendar date = mExpiryDate;
+ if (date == null) {
+ date = new GregorianCalendar();
+ }
+
+ DatePickerDialog dialog =
+ new DatePickerDialog(getContext(), mExpiryDateSetListener,
+ date.get(Calendar.YEAR),
+ date.get(Calendar.MONTH),
+ date.get(Calendar.DAY_OF_MONTH));
+ dialog.setCancelable(true);
+ dialog.setButton(Dialog.BUTTON_NEGATIVE,
+ getContext().getString(R.string.btn_noDate),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ setExpiryDate(null);
+ }
+ });
+ dialog.show();
+ }
+ });
+
+ super.onFinishInflate();
+ }
+
+ public void setValue(PGPSecretKey key, boolean isMasterKey) {
+ mKey = key;
+
+ mIsMasterKey = isMasterKey;
+ if (mIsMasterKey) {
+ mDeleteButton.setVisibility(View.INVISIBLE);
+ }
+
+ mAlgorithm.setText(Apg.getAlgorithmInfo(key));
+ String keyId1Str = Long.toHexString((key.getKeyID() >> 32) & 0xffffffffL);
+ while (keyId1Str.length() < 8) {
+ keyId1Str = "0" + keyId1Str;
+ }
+ String keyId2Str = Long.toHexString(key.getKeyID() & 0xffffffffL);
+ while (keyId2Str.length() < 8) {
+ keyId2Str = "0" + keyId2Str;
+ }
+ mKeyId.setText(keyId1Str + " " + keyId2Str);
+
+ Vector<Choice> choices = new Vector<Choice>();
+ boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
+ if (!isElGamalKey) {
+ choices.add(new Choice(Id.choice.usage.sign_only,
+ getResources().getString(R.string.choice_signOnly)));
+ }
+ if (!mIsMasterKey) {
+ choices.add(new Choice(Id.choice.usage.encrypt_only,
+ getResources().getString(R.string.choice_encryptOnly)));
+ }
+ if (!isElGamalKey) {
+ choices.add(new Choice(Id.choice.usage.sign_and_encrypt,
+ getResources().getString(R.string.choice_signAndEncrypt)));
+ }
+
+ ArrayAdapter<Choice> adapter =
+ new ArrayAdapter<Choice>(getContext(),
+ android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mUsage.setAdapter(adapter);
+
+ int selectId = 0;
+ if (Apg.isEncryptionKey(key)) {
+ if (Apg.isSigningKey(key)) {
+ selectId = Id.choice.usage.sign_and_encrypt;
+ } else {
+ selectId = Id.choice.usage.encrypt_only;
+ }
+ } else {
+ selectId = Id.choice.usage.sign_only;
+ }
+
+ for (int i = 0; i < choices.size(); ++i) {
+ if (choices.get(i).getId() == selectId) {
+ mUsage.setSelection(i);
+ break;
+ }
+ }
+
+ GregorianCalendar cal = new GregorianCalendar();
+ cal.setTime(Apg.getCreationDate(key));
+ mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime()));
+ cal = new GregorianCalendar();
+ Date date = Apg.getExpiryDate(key);
+ if (date == null) {
+ setExpiryDate(null);
+ } else {
+ cal.setTime(Apg.getExpiryDate(key));
+ setExpiryDate(cal);
+ }
+ }
+
+ public PGPSecretKey getValue() {
+ return mKey;
+ }
+
+ @Override
+ public void onClick(View v) {
+ final ViewGroup parent = (ViewGroup)getParent();
+ if (v == mDeleteButton) {
+ parent.removeView(this);
+ if (mEditorListener != null) {
+ mEditorListener.onDeleted(this);
+ }
+ }
+ }
+
+ @Override
+ public void setEditorListener(EditorListener listener) {
+ mEditorListener = listener;
+ }
+
+ private void setExpiryDate(GregorianCalendar date) {
+ mExpiryDate = date;
+ if (date == null) {
+ mExpiryDateButton.setText(R.string.none);
+ } else {
+ mExpiryDateButton.setText(DateFormat.getDateInstance().format(date.getTime()));
+ }
+ }
+
+ public GregorianCalendar getExpiryDate() {
+ return mExpiryDate;
+ }
+
+ public int getUsage() {
+ return ((Choice) mUsage.getSelectedItem()).getId();
+ }
+}
diff --git a/src/org/thialfihar/android/apg/ui/widget/SectionView.java b/src/org/thialfihar/android/apg/ui/widget/SectionView.java
index 3b7d801de..124c1c9da 100644
--- a/src/org/thialfihar/android/apg/ui/widget/SectionView.java
+++ b/src/org/thialfihar/android/apg/ui/widget/SectionView.java
@@ -1,337 +1,337 @@
-/*
- * 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.ui.widget;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidParameterException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.util.Vector;
-
-import org.bouncycastle2.openpgp.PGPException;
-import org.bouncycastle2.openpgp.PGPSecretKey;
-import org.thialfihar.android.apg.Apg;
-import org.thialfihar.android.apg.Id;
-import org.thialfihar.android.apg.R;
-import org.thialfihar.android.apg.ui.widget.Editor.EditorListener;
-import org.thialfihar.android.apg.utils.Choice;
-
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.Spinner;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Runnable {
- private LayoutInflater mInflater;
- private View mAdd;
- private ViewGroup mEditors;
- private TextView mTitle;
- private int mType = 0;
-
- private Choice mNewKeyAlgorithmChoice;
- private int mNewKeySize;
-
- volatile private PGPSecretKey mNewKey;
- private ProgressDialog mProgressDialog;
- private Thread mRunningThread = null;
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- Bundle data = msg.getData();
- if (data != null) {
- boolean closeProgressDialog = data.getBoolean("closeProgressDialog");
- if (closeProgressDialog) {
- if (mProgressDialog != null) {
- mProgressDialog.dismiss();
- mProgressDialog = null;
- }
- }
-
- String error = data.getString(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(getContext(),
- getContext().getString(R.string.errorMessage, error),
- Toast.LENGTH_SHORT).show();
- }
-
- boolean gotNewKey = data.getBoolean("gotNewKey");
- if (gotNewKey) {
- KeyEditor view =
- (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
- mEditors, false);
- view.setEditorListener(SectionView.this);
- boolean isMasterKey = (mEditors.getChildCount() == 0);
- view.setValue(mNewKey, isMasterKey);
- mEditors.addView(view);
- SectionView.this.updateEditorsVisible();
- }
- }
- }
- };
-
- public SectionView(Context context) {
- super(context);
- }
-
- public SectionView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ViewGroup getEditors() {
- return mEditors;
- }
-
- public void setType(int type) {
- mType = type;
- switch (type) {
- case Id.type.user_id: {
- mTitle.setText(R.string.section_userIds);
- break;
- }
-
- case Id.type.key: {
- mTitle.setText(R.string.section_keys);
- break;
- }
-
- default: {
- break;
- }
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mAdd = findViewById(R.id.header);
- mAdd.setOnClickListener(this);
-
- mEditors = (ViewGroup) findViewById(R.id.editors);
- mTitle = (TextView) findViewById(R.id.title);
-
- updateEditorsVisible();
- super.onFinishInflate();
- }
-
- /** {@inheritDoc} */
- public void onDeleted(Editor editor) {
- this.updateEditorsVisible();
- }
-
- protected void updateEditorsVisible() {
- final boolean hasChildren = mEditors.getChildCount() > 0;
- mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
- }
-
- /** {@inheritDoc} */
- public void onClick(View v) {
- switch (mType) {
- case Id.type.user_id: {
- UserIdEditor view =
- (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item,
- mEditors, false);
- view.setEditorListener(this);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
- mEditors.addView(view);
- break;
- }
-
- case Id.type.key: {
- AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
-
- View view = mInflater.inflate(R.layout.create_key, null);
- dialog.setView(view);
- dialog.setTitle(R.string.title_createKey);
- dialog.setMessage(R.string.keyCreationElGamalInfo);
-
- boolean wouldBeMasterKey = (mEditors.getChildCount() == 0);
-
- final Spinner algorithm = (Spinner) view.findViewById(R.id.algorithm);
- Vector<Choice> choices = new Vector<Choice>();
- choices.add(new Choice(Id.choice.algorithm.dsa,
- getResources().getString(R.string.dsa)));
- if (!wouldBeMasterKey) {
- choices.add(new Choice(Id.choice.algorithm.elgamal,
- getResources().getString(R.string.elgamal)));
- }
-
- choices.add(new Choice(Id.choice.algorithm.rsa,
- getResources().getString(R.string.rsa)));
-
- ArrayAdapter<Choice> adapter =
- new ArrayAdapter<Choice>(getContext(),
- android.R.layout.simple_spinner_item,
- choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- algorithm.setAdapter(adapter);
- // make RSA the default
- for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == Id.choice.algorithm.rsa) {
- algorithm.setSelection(i);
- break;
- }
- }
-
- final EditText keySize = (EditText) view.findViewById(R.id.size);
-
- dialog.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int id) {
- di.dismiss();
- try {
- mNewKeySize = Integer.parseInt("" + keySize.getText());
- } catch (NumberFormatException e) {
- mNewKeySize = 0;
- }
-
- mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem();
- createKey();
- }
- });
-
- dialog.setCancelable(true);
- dialog.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int id) {
- di.dismiss();
- }
- });
-
- dialog.create().show();
- break;
- }
-
- default: {
- break;
- }
- }
- this.updateEditorsVisible();
- }
-
- public void setUserIds(Vector<String> list) {
- if (mType != Id.type.user_id) {
- return;
- }
-
- mEditors.removeAllViews();
- for (String userId : list) {
- UserIdEditor view =
- (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, mEditors, false);
- view.setEditorListener(this);
- view.setValue(userId);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
- mEditors.addView(view);
- }
-
- this.updateEditorsVisible();
- }
-
- public void setKeys(Vector<PGPSecretKey> list) {
- if (mType != Id.type.key) {
- return;
- }
-
- mEditors.removeAllViews();
- for (PGPSecretKey key : list) {
- KeyEditor view =
- (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, false);
- view.setEditorListener(this);
- boolean isMasterKey = (mEditors.getChildCount() == 0);
- view.setValue(key, isMasterKey);
- mEditors.addView(view);
- }
-
- this.updateEditorsVisible();
- }
-
- private void createKey() {
- mProgressDialog = new ProgressDialog(getContext());
- mProgressDialog.setMessage(getContext().getString(R.string.progress_generating));
- mProgressDialog.setCancelable(false);
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
- mProgressDialog.show();
- mRunningThread = new Thread(this);
- mRunningThread.start();
- }
-
- public void run() {
- String error = null;
- try {
- PGPSecretKey masterKey = null;
- String passPhrase;
- if (mEditors.getChildCount() > 0) {
- masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
- passPhrase = Apg.getCachedPassPhrase(masterKey.getKeyID());
- } else {
- passPhrase = "";
- }
- mNewKey = Apg.createKey(getContext(),
- mNewKeyAlgorithmChoice.getId(),
- mNewKeySize, passPhrase,
- masterKey);
- } catch (NoSuchProviderException e) {
- error = "" + e;
- } catch (NoSuchAlgorithmException e) {
- error = "" + e;
- } catch (PGPException e) {
- error = "" + e;
- } catch (InvalidParameterException e) {
- error = "" + e;
- } catch (InvalidAlgorithmParameterException e) {
- error = "" + e;
- } catch (Apg.GeneralException e) {
- error = "" + e;
- }
-
- Message message = new Message();
- Bundle data = new Bundle();
- data.putBoolean("closeProgressDialog", true);
- if (error != null) {
- data.putString(Apg.EXTRA_ERROR, error);
- } else {
- data.putBoolean("gotNewKey", true);
- }
- message.setData(data);
- mHandler.sendMessage(message);
- }
-}
+/*
+ * 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.ui.widget;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.util.Vector;
+
+import org.bouncycastle2.openpgp.PGPException;
+import org.bouncycastle2.openpgp.PGPSecretKey;
+import org.thialfihar.android.apg.Apg;
+import org.thialfihar.android.apg.Id;
+import org.thialfihar.android.apg.R;
+import org.thialfihar.android.apg.ui.widget.Editor.EditorListener;
+import org.thialfihar.android.apg.utils.Choice;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Runnable {
+ private LayoutInflater mInflater;
+ private View mAdd;
+ private ViewGroup mEditors;
+ private TextView mTitle;
+ private int mType = 0;
+
+ private Choice mNewKeyAlgorithmChoice;
+ private int mNewKeySize;
+
+ volatile private PGPSecretKey mNewKey;
+ private ProgressDialog mProgressDialog;
+ private Thread mRunningThread = null;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ Bundle data = msg.getData();
+ if (data != null) {
+ boolean closeProgressDialog = data.getBoolean("closeProgressDialog");
+ if (closeProgressDialog) {
+ if (mProgressDialog != null) {
+ mProgressDialog.dismiss();
+ mProgressDialog = null;
+ }
+ }
+
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(getContext(),
+ getContext().getString(R.string.errorMessage, error),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ boolean gotNewKey = data.getBoolean("gotNewKey");
+ if (gotNewKey) {
+ KeyEditor view =
+ (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
+ mEditors, false);
+ view.setEditorListener(SectionView.this);
+ boolean isMasterKey = (mEditors.getChildCount() == 0);
+ view.setValue(mNewKey, isMasterKey);
+ mEditors.addView(view);
+ SectionView.this.updateEditorsVisible();
+ }
+ }
+ }
+ };
+
+ public SectionView(Context context) {
+ super(context);
+ }
+
+ public SectionView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ViewGroup getEditors() {
+ return mEditors;
+ }
+
+ public void setType(int type) {
+ mType = type;
+ switch (type) {
+ case Id.type.user_id: {
+ mTitle.setText(R.string.section_userIds);
+ break;
+ }
+
+ case Id.type.key: {
+ mTitle.setText(R.string.section_keys);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onFinishInflate() {
+ mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ setDrawingCacheEnabled(true);
+ setAlwaysDrawnWithCacheEnabled(true);
+
+ mAdd = findViewById(R.id.header);
+ mAdd.setOnClickListener(this);
+
+ mEditors = (ViewGroup) findViewById(R.id.editors);
+ mTitle = (TextView) findViewById(R.id.title);
+
+ updateEditorsVisible();
+ super.onFinishInflate();
+ }
+
+ /** {@inheritDoc} */
+ public void onDeleted(Editor editor) {
+ this.updateEditorsVisible();
+ }
+
+ protected void updateEditorsVisible() {
+ final boolean hasChildren = mEditors.getChildCount() > 0;
+ mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
+ }
+
+ /** {@inheritDoc} */
+ public void onClick(View v) {
+ switch (mType) {
+ case Id.type.user_id: {
+ UserIdEditor view =
+ (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item,
+ mEditors, false);
+ view.setEditorListener(this);
+ if (mEditors.getChildCount() == 0) {
+ view.setIsMainUserId(true);
+ }
+ mEditors.addView(view);
+ break;
+ }
+
+ case Id.type.key: {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
+
+ View view = mInflater.inflate(R.layout.create_key, null);
+ dialog.setView(view);
+ dialog.setTitle(R.string.title_createKey);
+ dialog.setMessage(R.string.keyCreationElGamalInfo);
+
+ boolean wouldBeMasterKey = (mEditors.getChildCount() == 0);
+
+ final Spinner algorithm = (Spinner) view.findViewById(R.id.algorithm);
+ Vector<Choice> choices = new Vector<Choice>();
+ choices.add(new Choice(Id.choice.algorithm.dsa,
+ getResources().getString(R.string.dsa)));
+ if (!wouldBeMasterKey) {
+ choices.add(new Choice(Id.choice.algorithm.elgamal,
+ getResources().getString(R.string.elgamal)));
+ }
+
+ choices.add(new Choice(Id.choice.algorithm.rsa,
+ getResources().getString(R.string.rsa)));
+
+ ArrayAdapter<Choice> adapter =
+ new ArrayAdapter<Choice>(getContext(),
+ android.R.layout.simple_spinner_item,
+ choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ algorithm.setAdapter(adapter);
+ // make RSA the default
+ for (int i = 0; i < choices.size(); ++i) {
+ if (choices.get(i).getId() == Id.choice.algorithm.rsa) {
+ algorithm.setSelection(i);
+ break;
+ }
+ }
+
+ final EditText keySize = (EditText) view.findViewById(R.id.size);
+
+ dialog.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface di, int id) {
+ di.dismiss();
+ try {
+ mNewKeySize = Integer.parseInt("" + keySize.getText());
+ } catch (NumberFormatException e) {
+ mNewKeySize = 0;
+ }
+
+ mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem();
+ createKey();
+ }
+ });
+
+ dialog.setCancelable(true);
+ dialog.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface di, int id) {
+ di.dismiss();
+ }
+ });
+
+ dialog.create().show();
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ this.updateEditorsVisible();
+ }
+
+ public void setUserIds(Vector<String> list) {
+ if (mType != Id.type.user_id) {
+ return;
+ }
+
+ mEditors.removeAllViews();
+ for (String userId : list) {
+ UserIdEditor view =
+ (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, mEditors, false);
+ view.setEditorListener(this);
+ view.setValue(userId);
+ if (mEditors.getChildCount() == 0) {
+ view.setIsMainUserId(true);
+ }
+ mEditors.addView(view);
+ }
+
+ this.updateEditorsVisible();
+ }
+
+ public void setKeys(Vector<PGPSecretKey> list) {
+ if (mType != Id.type.key) {
+ return;
+ }
+
+ mEditors.removeAllViews();
+ for (PGPSecretKey key : list) {
+ KeyEditor view =
+ (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, false);
+ view.setEditorListener(this);
+ boolean isMasterKey = (mEditors.getChildCount() == 0);
+ view.setValue(key, isMasterKey);
+ mEditors.addView(view);
+ }
+
+ this.updateEditorsVisible();
+ }
+
+ private void createKey() {
+ mProgressDialog = new ProgressDialog(getContext());
+ mProgressDialog.setMessage(getContext().getString(R.string.progress_generating));
+ mProgressDialog.setCancelable(false);
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ mProgressDialog.show();
+ mRunningThread = new Thread(this);
+ mRunningThread.start();
+ }
+
+ public void run() {
+ String error = null;
+ try {
+ PGPSecretKey masterKey = null;
+ String passPhrase;
+ if (mEditors.getChildCount() > 0) {
+ masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
+ passPhrase = Apg.getCachedPassPhrase(masterKey.getKeyID());
+ } else {
+ passPhrase = "";
+ }
+ mNewKey = Apg.createKey(getContext(),
+ mNewKeyAlgorithmChoice.getId(),
+ mNewKeySize, passPhrase,
+ masterKey);
+ } catch (NoSuchProviderException e) {
+ error = "" + e;
+ } catch (NoSuchAlgorithmException e) {
+ error = "" + e;
+ } catch (PGPException e) {
+ error = "" + e;
+ } catch (InvalidParameterException e) {
+ error = "" + e;
+ } catch (InvalidAlgorithmParameterException e) {
+ error = "" + e;
+ } catch (Apg.GeneralException e) {
+ error = "" + e;
+ }
+
+ Message message = new Message();
+ Bundle data = new Bundle();
+ data.putBoolean("closeProgressDialog", true);
+ if (error != null) {
+ data.putString(Apg.EXTRA_ERROR, error);
+ } else {
+ data.putBoolean("gotNewKey", true);
+ }
+ message.setData(data);
+ mHandler.sendMessage(message);
+ }
+}
diff --git a/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java b/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java
index 555a54375..c8f9f74e7 100644
--- a/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java
+++ b/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java
@@ -1,194 +1,194 @@
-/*
- * 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.ui.widget;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.thialfihar.android.apg.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-
-public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
- private EditorListener mEditorListener = null;
-
- private ImageButton mDeleteButton;
- private RadioButton mIsMainUserId;
- private EditText mName;
- private EditText mEmail;
- private EditText mComment;
-
- private static final Pattern EMAIL_PATTERN =
- Pattern.compile("^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+[.]([a-zA-Z])+([a-zA-Z])+",
- Pattern.CASE_INSENSITIVE);
-
- public static class NoNameException extends Exception {
- static final long serialVersionUID = 0xf812773343L;
-
- public NoNameException(String message) {
- super(message);
- }
- }
-
- public static class NoEmailException extends Exception {
- static final long serialVersionUID = 0xf812773344L;
-
- public NoEmailException(String message) {
- super(message);
- }
- }
-
- public static class InvalidEmailException extends Exception {
- static final long serialVersionUID = 0xf812773345L;
-
- public InvalidEmailException(String message) {
- super(message);
- }
- }
-
- public UserIdEditor(Context context) {
- super(context);
- }
-
- public UserIdEditor(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(this);
- mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId);
- mIsMainUserId.setOnClickListener(this);
-
- mName = (EditText) findViewById(R.id.name);
- mEmail = (EditText) findViewById(R.id.email);
- mComment = (EditText) findViewById(R.id.comment);
-
- super.onFinishInflate();
- }
-
- public void setValue(String userId) {
- mName.setText("");
- mComment.setText("");
- mEmail.setText("");
-
- Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
- Matcher matcher = withComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mComment.setText(matcher.group(2));
- mEmail.setText(matcher.group(3));
- return;
- }
-
- Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
- matcher = withoutComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mEmail.setText(matcher.group(2));
- return;
- }
- }
-
- public String getValue() throws NoNameException, NoEmailException, InvalidEmailException {
- String name = ("" + mName.getText()).trim();
- String email = ("" + mEmail.getText()).trim();
- String comment = ("" + mComment.getText()).trim();
-
- if (email.length() > 0) {
- Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
- if (!emailMatcher.matches()) {
- throw new InvalidEmailException(
- getContext().getString(R.string.error_invalidEmail, email));
- }
- }
-
- String userId = name;
- if (comment.length() > 0) {
- userId += " (" + comment + ")";
- }
- if (email.length() > 0) {
- userId += " <" + email + ">";
- }
-
- if (userId.equals("")) {
- // ok, empty one...
- return userId;
- }
-
- // otherwise make sure that name and email exist
- if (name.equals("")) {
- throw new NoNameException("need a name");
- }
-
- if (email.equals("")) {
- throw new NoEmailException("need an email");
- }
-
- return userId;
- }
-
- @Override
- public void onClick(View v) {
- final ViewGroup parent = (ViewGroup)getParent();
- if (v == mDeleteButton) {
- boolean wasMainUserId = mIsMainUserId.isChecked();
- parent.removeView(this);
- if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
- }
- if (wasMainUserId && parent.getChildCount() > 0) {
- UserIdEditor editor = (UserIdEditor) parent.getChildAt(0);
- editor.setIsMainUserId(true);
- }
- } else if (v == mIsMainUserId) {
- for (int i = 0; i < parent.getChildCount(); ++i) {
- UserIdEditor editor = (UserIdEditor) parent.getChildAt(i);
- if (editor == this) {
- editor.setIsMainUserId(true);
- } else {
- editor.setIsMainUserId(false);
- }
- }
- }
- }
-
- public void setIsMainUserId(boolean value) {
- mIsMainUserId.setChecked(value);
- }
-
- public boolean isMainUserId() {
- return mIsMainUserId.isChecked();
- }
-
- @Override
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-}
+/*
+ * 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.ui.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.thialfihar.android.apg.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+
+public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
+ private EditorListener mEditorListener = null;
+
+ private ImageButton mDeleteButton;
+ private RadioButton mIsMainUserId;
+ private EditText mName;
+ private EditText mEmail;
+ private EditText mComment;
+
+ private static final Pattern EMAIL_PATTERN =
+ Pattern.compile("^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+[.]([a-zA-Z])+([a-zA-Z])+",
+ Pattern.CASE_INSENSITIVE);
+
+ public static class NoNameException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ public NoNameException(String message) {
+ super(message);
+ }
+ }
+
+ public static class NoEmailException extends Exception {
+ static final long serialVersionUID = 0xf812773344L;
+
+ public NoEmailException(String message) {
+ super(message);
+ }
+ }
+
+ public static class InvalidEmailException extends Exception {
+ static final long serialVersionUID = 0xf812773345L;
+
+ public InvalidEmailException(String message) {
+ super(message);
+ }
+ }
+
+ public UserIdEditor(Context context) {
+ super(context);
+ }
+
+ public UserIdEditor(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ setDrawingCacheEnabled(true);
+ setAlwaysDrawnWithCacheEnabled(true);
+
+ mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton.setOnClickListener(this);
+ mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId);
+ mIsMainUserId.setOnClickListener(this);
+
+ mName = (EditText) findViewById(R.id.name);
+ mEmail = (EditText) findViewById(R.id.email);
+ mComment = (EditText) findViewById(R.id.comment);
+
+ super.onFinishInflate();
+ }
+
+ public void setValue(String userId) {
+ mName.setText("");
+ mComment.setText("");
+ mEmail.setText("");
+
+ Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
+ Matcher matcher = withComment.matcher(userId);
+ if (matcher.matches()) {
+ mName.setText(matcher.group(1));
+ mComment.setText(matcher.group(2));
+ mEmail.setText(matcher.group(3));
+ return;
+ }
+
+ Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
+ matcher = withoutComment.matcher(userId);
+ if (matcher.matches()) {
+ mName.setText(matcher.group(1));
+ mEmail.setText(matcher.group(2));
+ return;
+ }
+ }
+
+ public String getValue() throws NoNameException, NoEmailException, InvalidEmailException {
+ String name = ("" + mName.getText()).trim();
+ String email = ("" + mEmail.getText()).trim();
+ String comment = ("" + mComment.getText()).trim();
+
+ if (email.length() > 0) {
+ Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
+ if (!emailMatcher.matches()) {
+ throw new InvalidEmailException(
+ getContext().getString(R.string.error_invalidEmail, email));
+ }
+ }
+
+ String userId = name;
+ if (comment.length() > 0) {
+ userId += " (" + comment + ")";
+ }
+ if (email.length() > 0) {
+ userId += " <" + email + ">";
+ }
+
+ if (userId.equals("")) {
+ // ok, empty one...
+ return userId;
+ }
+
+ // otherwise make sure that name and email exist
+ if (name.equals("")) {
+ throw new NoNameException("need a name");
+ }
+
+ if (email.equals("")) {
+ throw new NoEmailException("need an email");
+ }
+
+ return userId;
+ }
+
+ @Override
+ public void onClick(View v) {
+ final ViewGroup parent = (ViewGroup)getParent();
+ if (v == mDeleteButton) {
+ boolean wasMainUserId = mIsMainUserId.isChecked();
+ parent.removeView(this);
+ if (mEditorListener != null) {
+ mEditorListener.onDeleted(this);
+ }
+ if (wasMainUserId && parent.getChildCount() > 0) {
+ UserIdEditor editor = (UserIdEditor) parent.getChildAt(0);
+ editor.setIsMainUserId(true);
+ }
+ } else if (v == mIsMainUserId) {
+ for (int i = 0; i < parent.getChildCount(); ++i) {
+ UserIdEditor editor = (UserIdEditor) parent.getChildAt(i);
+ if (editor == this) {
+ editor.setIsMainUserId(true);
+ } else {
+ editor.setIsMainUserId(false);
+ }
+ }
+ }
+ }
+
+ public void setIsMainUserId(boolean value) {
+ mIsMainUserId.setChecked(value);
+ }
+
+ public boolean isMainUserId() {
+ return mIsMainUserId.isChecked();
+ }
+
+ @Override
+ public void setEditorListener(EditorListener listener) {
+ mEditorListener = listener;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/utils/Choice.java b/src/org/thialfihar/android/apg/utils/Choice.java
index d094c1e1e..4014a1f4a 100644
--- a/src/org/thialfihar/android/apg/utils/Choice.java
+++ b/src/org/thialfihar/android/apg/utils/Choice.java
@@ -1,44 +1,44 @@
-/*
- * 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.utils;
-
-public class Choice {
- private String mName;
- private int mId;
-
- public Choice() {
- mId = -1;
- mName = "";
- }
-
- public Choice(int id, String name) {
- mId = id;
- mName = name;
- }
-
- public int getId() {
- return mId;
- }
-
- public String getName() {
- return mName;
- }
-
- public String toString() {
- return mName;
- }
-}
+/*
+ * 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.utils;
+
+public class Choice {
+ private String mName;
+ private int mId;
+
+ public Choice() {
+ mId = -1;
+ mName = "";
+ }
+
+ public Choice(int id, String name) {
+ mId = id;
+ mName = name;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String toString() {
+ return mName;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/utils/IterableIterator.java b/src/org/thialfihar/android/apg/utils/IterableIterator.java
index ff02c4194..1d9664e38 100644
--- a/src/org/thialfihar/android/apg/utils/IterableIterator.java
+++ b/src/org/thialfihar/android/apg/utils/IterableIterator.java
@@ -1,31 +1,31 @@
-/*
- * 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.utils;
-
-import java.util.Iterator;
-
-public class IterableIterator<T> implements Iterable<T> {
- private Iterator<T> mIter;
-
- public IterableIterator(Iterator<T> iter) {
- mIter = iter;
- }
-
- public Iterator<T> iterator() {
- return mIter;
- }
-}
+/*
+ * 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.utils;
+
+import java.util.Iterator;
+
+public class IterableIterator<T> implements Iterable<T> {
+ private Iterator<T> mIter;
+
+ public IterableIterator(Iterator<T> iter) {
+ mIter = iter;
+ }
+
+ public Iterator<T> iterator() {
+ return mIter;
+ }
+}