branching trunk out of latest 1.0.x to get a clean start for it

This commit is contained in:
Thialfihar 2010-06-05 00:33:33 +00:00
commit a85ae5e009
41 changed files with 2837 additions and 2592 deletions

View File

@ -17,7 +17,7 @@
<manifest <manifest
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
package="org.thialfihar.android.apg" package="org.thialfihar.android.apg"
android:versionName="0.9.5" android:versionCode="10"> android:versionCode="15" android:versionName="1.0.1">
<application <application
android:icon="@drawable/icon" android:icon="@drawable/icon"
@ -53,12 +53,28 @@
<activity <activity
android:name=".SelectPublicKeyListActivity" android:name=".SelectPublicKeyListActivity"
android:label="@string/title_selectRecipients" android:label="@string/title_selectRecipients"
android:configChanges="keyboardHidden|orientation|keyboard"/> android:configChanges="keyboardHidden|orientation|keyboard">
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/*"/>
</intent-filter>
</activity>
<activity <activity
android:name=".SelectSecretKeyListActivity" android:name=".SelectSecretKeyListActivity"
android:label="@string/title_selectSignature" android:label="@string/title_selectSignature"
android:configChanges="keyboardHidden|orientation|keyboard"/> android:configChanges="keyboardHidden|orientation|keyboard">
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.SELECT_SECRET_KEY" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/*"/>
</intent-filter>
</activity>
<activity <activity
android:name=".EncryptActivity" android:name=".EncryptActivity"
@ -68,6 +84,9 @@
<intent-filter> <intent-filter>
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT" /> <action android:name="org.thialfihar.android.apg.intent.ENCRYPT" />
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT_FILE" /> <action android:name="org.thialfihar.android.apg.intent.ENCRYPT_FILE" />
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/*"/>
</intent-filter> </intent-filter>
</activity> </activity>
@ -92,6 +111,9 @@
<intent-filter> <intent-filter>
<action android:name="org.thialfihar.android.apg.intent.DECRYPT" /> <action android:name="org.thialfihar.android.apg.intent.DECRYPT" />
<action android:name="org.thialfihar.android.apg.intent.DECRYPT_FILE" /> <action android:name="org.thialfihar.android.apg.intent.DECRYPT_FILE" />
<action android:name="org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/*"/>
</intent-filter> </intent-filter>
</activity> </activity>
@ -106,13 +128,22 @@
android:label="@string/title_preferences" android:label="@string/title_preferences"
android:configChanges="keyboardHidden|orientation|keyboard"/> android:configChanges="keyboardHidden|orientation|keyboard"/>
<service android:name=".Service" />
<provider <provider
android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"
android:name="org.thialfihar.android.apg.provider.DataProvider" android:name="org.thialfihar.android.apg.provider.DataProvider"
android:authorities="org.thialfihar.android.apg.provider"/> android:authorities="org.thialfihar.android.apg.provider"/>
</application> </application>
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="5" /> <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="5" />
<permission android:name="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"
android:protectionLevel="dangerous"
android:label="@string/permission_read_key_details_label"
android:description="@string/permission_read_key_details_description"/>
<uses-permission android:name="com.google.android.providers.gmail.permission.READ_GMAIL" /> <uses-permission android:name="com.google.android.providers.gmail.permission.READ_GMAIL" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest> </manifest>

View File

@ -77,6 +77,7 @@
<EditText <EditText
android:id="@+id/message" android:id="@+id/message"
android:inputType="text|textCapSentences|textMultiLine|textLongMessage" android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
android:scrollHorizontally="true"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:gravity="top"/> android:gravity="top"/>

View File

@ -69,20 +69,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="fill_parent"/> android:layout_height="fill_parent"/>
<TextView
android:id="@+id/creation"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="31.12.2009"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/expiry"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="31.12.2010"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView <TextView
android:id="@+id/status" android:id="@+id/status"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"

View File

@ -62,20 +62,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="fill_parent"/> android:layout_height="fill_parent"/>
<TextView
android:id="@+id/creation"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="31.12.2009"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/expiry"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="31.12.2010"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView <TextView
android:id="@+id/status" android:id="@+id/status"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"

View File

@ -38,10 +38,12 @@
<string name="title_importKeys">Import Keys</string> <string name="title_importKeys">Import Keys</string>
<string name="title_exportKey">Export Key</string> <string name="title_exportKey">Export Key</string>
<string name="title_exportKeys">Export Keys</string> <string name="title_exportKeys">Export Keys</string>
<string name="title_keyNotFound">Key Not Found</string>
<!-- section_lowerCase: capitalized words, no punctuation --> <!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string> <string name="section_userIds">User IDs</string>
<string name="section_keys">Keys</string> <string name="section_keys">Keys</string>
<string name="section_general">General</string>
<string name="section_defaults">Defaults</string> <string name="section_defaults">Defaults</string>
<!-- btn_lowerCase: capitalized words, no punctuation --> <!-- btn_lowerCase: capitalized words, no punctuation -->
@ -92,6 +94,9 @@
<string name="label_hashAlgorithm">Hash Algorithm</string> <string name="label_hashAlgorithm">Hash Algorithm</string>
<string name="label_asymmetric">Public Key</string> <string name="label_asymmetric">Public Key</string>
<string name="label_symmetric">Pass Phrase</string> <string name="label_symmetric">Pass Phrase</string>
<string name="label_passPhraseCacheTtl">Pass Phrase Cache</string>
<string name="label_messageCompression">Message Compression</string>
<string name="label_fileCompression">File Compression</string>
<string name="noKeysSelected">Select</string> <string name="noKeysSelected">Select</string>
<string name="oneKeySelected">1 Selected</string> <string name="oneKeySelected">1 Selected</string>
@ -108,9 +113,16 @@
<string name="notValid">not valid</string> <string name="notValid">not valid</string>
<!-- choice_lowerCase: capitalized firwst word, no punctuation --> <!-- choice_lowerCase: capitalized firwst word, no punctuation -->
<string name="choice_none">None</string>
<string name="choice_signOnly">Sign only</string> <string name="choice_signOnly">Sign only</string>
<string name="choice_encryptOnly">Encrypt only</string> <string name="choice_encryptOnly">Encrypt only</string>
<string name="choice_signAndEncrypt">Sign and Encrypt</string> <string name="choice_signAndEncrypt">Sign and Encrypt</string>
<string name="choice_15secs">15 secs</string>
<string name="choice_1min">1 min</string>
<string name="choice_3mins">3 mins</string>
<string name="choice_5mins">5 mins</string>
<string name="choice_10mins">10 mins</string>
<string name="choice_untilQuit">until quit</string>
<string name="dsa">DSA</string> <string name="dsa">DSA</string>
<string name="elgamal">ElGamal</string> <string name="elgamal">ElGamal</string>
@ -133,7 +145,7 @@
<string name="usingClipboardContent">Using clipboard content.</string> <string name="usingClipboardContent">Using clipboard content.</string>
<string name="keySaved">Key saved.</string> <string name="keySaved">Key saved.</string>
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string>
<string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> <string name="noFilemanagerInstalled">No compatible file manager installed.</string>
<string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string> <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string> <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string>
@ -150,19 +162,20 @@
<string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string>
<string name="specifyFileToImportFrom">Please specify which file to import from.</string> <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>
<string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
<string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string> <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>
<string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string>
<string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
<string name="keysAdded">Succssfully added %s keys.</string> <string name="keysAdded">Succssfully added %s key(s).</string>
<string name="keysUpdated">Succssfully updated %s keys.</string> <string name="keysUpdated">Succssfully updated %s key(s).</string>
<string name="noKeysAddedOrUpdated">No keys added or updated.</string> <string name="noKeysAddedOrUpdated">No keys added or updated.</string>
<string name="keyExported">Succssfully exported 1 key.</string> <string name="keyExported">Succssfully exported 1 key.</string>
<string name="keysExported">Succssfully exported %s keys.</string> <string name="keysExported">Succssfully exported %s keys.</string>
<string name="noKeysExported">No keys exported.</string> <string name="noKeysExported">No keys exported.</string>
<string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string>
<string name="keyNotFound">Couldn't find key %08X.</string>
<!-- error_lowerCase: phrases, no punctuation, all lowercase, <!-- error_lowerCase: phrases, no punctuation, all lowercase,
they will be put after "errorMessage", e.g. "Error: file not found" --> they will be put after "errorMessage", e.g. "Error: file not found" -->
@ -227,4 +240,3 @@
<string name="progress_verifyingIntegrity">verifying integrity...</string> <string name="progress_verifyingIntegrity">verifying integrity...</string>
</resources> </resources>

View File

@ -38,10 +38,12 @@
<string name="title_importKeys">Import Keys</string> <string name="title_importKeys">Import Keys</string>
<string name="title_exportKey">Export Key</string> <string name="title_exportKey">Export Key</string>
<string name="title_exportKeys">Export Keys</string> <string name="title_exportKeys">Export Keys</string>
<string name="title_keyNotFound">Key Not Found</string>
<!-- section_lowerCase: capitalized words, no punctuation --> <!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string> <string name="section_userIds">User IDs</string>
<string name="section_keys">Keys</string> <string name="section_keys">Keys</string>
<string name="section_general">General</string>
<string name="section_defaults">Defaults</string> <string name="section_defaults">Defaults</string>
<!-- btn_lowerCase: capitalized words, no punctuation --> <!-- btn_lowerCase: capitalized words, no punctuation -->
@ -92,6 +94,9 @@
<string name="label_hashAlgorithm">Hash Algorithm</string> <string name="label_hashAlgorithm">Hash Algorithm</string>
<string name="label_asymmetric">Public Key</string> <string name="label_asymmetric">Public Key</string>
<string name="label_symmetric">Pass Phrase</string> <string name="label_symmetric">Pass Phrase</string>
<string name="label_passPhraseCacheTtl">Pass Phrase Cache</string>
<string name="label_messageCompression">Message Compression</string>
<string name="label_fileCompression">File Compression</string>
<string name="noKeysSelected">Select</string> <string name="noKeysSelected">Select</string>
<string name="oneKeySelected">1 Selected</string> <string name="oneKeySelected">1 Selected</string>
@ -108,9 +113,16 @@
<string name="notValid">not valid</string> <string name="notValid">not valid</string>
<!-- choice_lowerCase: capitalized firwst word, no punctuation --> <!-- choice_lowerCase: capitalized firwst word, no punctuation -->
<string name="choice_none">None</string>
<string name="choice_signOnly">Sign only</string> <string name="choice_signOnly">Sign only</string>
<string name="choice_encryptOnly">Encrypt only</string> <string name="choice_encryptOnly">Encrypt only</string>
<string name="choice_signAndEncrypt">Sign and Encrypt</string> <string name="choice_signAndEncrypt">Sign and Encrypt</string>
<string name="choice_15secs">15 secs</string>
<string name="choice_1min">1 min</string>
<string name="choice_3mins">3 mins</string>
<string name="choice_5mins">5 mins</string>
<string name="choice_10mins">10 mins</string>
<string name="choice_untilQuit">until quit</string>
<string name="dsa">DSA</string> <string name="dsa">DSA</string>
<string name="elgamal">ElGamal</string> <string name="elgamal">ElGamal</string>
@ -133,7 +145,7 @@
<string name="usingClipboardContent">Using clipboard content.</string> <string name="usingClipboardContent">Using clipboard content.</string>
<string name="keySaved">Key saved.</string> <string name="keySaved">Key saved.</string>
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string>
<string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> <string name="noFilemanagerInstalled">No compatible file manager installed.</string>
<string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string> <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string> <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string>
@ -150,19 +162,20 @@
<string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string>
<string name="specifyFileToImportFrom">Please specify which file to import from.</string> <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>
<string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
<string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string> <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>
<string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string>
<string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
<string name="keysAdded">Succssfully added %s keys.</string> <string name="keysAdded">Succssfully added %s key(s).</string>
<string name="keysUpdated">Succssfully updated %s keys.</string> <string name="keysUpdated">Succssfully updated %s key(s).</string>
<string name="noKeysAddedOrUpdated">No keys added or updated.</string> <string name="noKeysAddedOrUpdated">No keys added or updated.</string>
<string name="keyExported">Succssfully exported 1 key.</string> <string name="keyExported">Succssfully exported 1 key.</string>
<string name="keysExported">Succssfully exported %s keys.</string> <string name="keysExported">Succssfully exported %s keys.</string>
<string name="noKeysExported">No keys exported.</string> <string name="noKeysExported">No keys exported.</string>
<string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string>
<string name="keyNotFound">Couldn't find key %08X.</string>
<!-- error_lowerCase: phrases, no punctuation, all lowercase, <!-- error_lowerCase: phrases, no punctuation, all lowercase,
they will be put after "errorMessage", e.g. "Error: file not found" --> they will be put after "errorMessage", e.g. "Error: file not found" -->
@ -227,4 +240,3 @@
<string name="progress_verifyingIntegrity">verifying integrity...</string> <string name="progress_verifyingIntegrity">verifying integrity...</string>
</resources> </resources>

View File

@ -38,10 +38,12 @@
<string name="title_importKeys">Import Keys</string> <string name="title_importKeys">Import Keys</string>
<string name="title_exportKey">Export Key</string> <string name="title_exportKey">Export Key</string>
<string name="title_exportKeys">Export Keys</string> <string name="title_exportKeys">Export Keys</string>
<string name="title_keyNotFound">Key Not Found</string>
<!-- section_lowerCase: capitalized words, no punctuation --> <!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string> <string name="section_userIds">User IDs</string>
<string name="section_keys">Keys</string> <string name="section_keys">Keys</string>
<string name="section_general">General</string>
<string name="section_defaults">Defaults</string> <string name="section_defaults">Defaults</string>
<!-- btn_lowerCase: capitalized words, no punctuation --> <!-- btn_lowerCase: capitalized words, no punctuation -->
@ -92,6 +94,9 @@
<string name="label_hashAlgorithm">Hash Algorithm</string> <string name="label_hashAlgorithm">Hash Algorithm</string>
<string name="label_asymmetric">Public Key</string> <string name="label_asymmetric">Public Key</string>
<string name="label_symmetric">Pass Phrase</string> <string name="label_symmetric">Pass Phrase</string>
<string name="label_passPhraseCacheTtl">Pass Phrase Cache</string>
<string name="label_messageCompression">Message Compression</string>
<string name="label_fileCompression">File Compression</string>
<string name="noKeysSelected">Select</string> <string name="noKeysSelected">Select</string>
<string name="oneKeySelected">1 Selected</string> <string name="oneKeySelected">1 Selected</string>
@ -108,9 +113,16 @@
<string name="notValid">not valid</string> <string name="notValid">not valid</string>
<!-- choice_lowerCase: capitalized firwst word, no punctuation --> <!-- choice_lowerCase: capitalized firwst word, no punctuation -->
<string name="choice_none">None</string>
<string name="choice_signOnly">Sign only</string> <string name="choice_signOnly">Sign only</string>
<string name="choice_encryptOnly">Encrypt only</string> <string name="choice_encryptOnly">Encrypt only</string>
<string name="choice_signAndEncrypt">Sign and Encrypt</string> <string name="choice_signAndEncrypt">Sign and Encrypt</string>
<string name="choice_15secs">15 secs</string>
<string name="choice_1min">1 min</string>
<string name="choice_3mins">3 mins</string>
<string name="choice_5mins">5 mins</string>
<string name="choice_10mins">10 mins</string>
<string name="choice_untilQuit">until quit</string>
<string name="dsa">DSA</string> <string name="dsa">DSA</string>
<string name="elgamal">ElGamal</string> <string name="elgamal">ElGamal</string>
@ -133,7 +145,7 @@
<string name="usingClipboardContent">Using clipboard content.</string> <string name="usingClipboardContent">Using clipboard content.</string>
<string name="keySaved">Key saved.</string> <string name="keySaved">Key saved.</string>
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string>
<string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> <string name="noFilemanagerInstalled">No compatible file manager installed.</string>
<string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string> <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string> <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string>
@ -150,19 +162,20 @@
<string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string>
<string name="specifyFileToImportFrom">Please specify which file to import from.</string> <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>
<string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
<string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string> <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>
<string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string>
<string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
<string name="keysAdded">Succssfully added %s keys.</string> <string name="keysAdded">Succssfully added %s key(s).</string>
<string name="keysUpdated">Succssfully updated %s keys.</string> <string name="keysUpdated">Succssfully updated %s key(s).</string>
<string name="noKeysAddedOrUpdated">No keys added or updated.</string> <string name="noKeysAddedOrUpdated">No keys added or updated.</string>
<string name="keyExported">Succssfully exported 1 key.</string> <string name="keyExported">Succssfully exported 1 key.</string>
<string name="keysExported">Succssfully exported %s keys.</string> <string name="keysExported">Succssfully exported %s keys.</string>
<string name="noKeysExported">No keys exported.</string> <string name="noKeysExported">No keys exported.</string>
<string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string>
<string name="keyNotFound">Couldn't find key %08X.</string>
<!-- error_lowerCase: phrases, no punctuation, all lowercase, <!-- error_lowerCase: phrases, no punctuation, all lowercase,
they will be put after "errorMessage", e.g. "Error: file not found" --> they will be put after "errorMessage", e.g. "Error: file not found" -->
@ -227,4 +240,3 @@
<string name="progress_verifyingIntegrity">verifying integrity...</string> <string name="progress_verifyingIntegrity">verifying integrity...</string>
</resources> </resources>

242
res/values-sl/strings.xml Normal file
View File

@ -0,0 +1,242 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="app_name">APG</string>
<!-- title_lowerCase: capitalized words, no punctuation -->
<string name="title_mailInbox">Poštni nabiralnik</string>
<string name="title_managePublicKeys">Upravljanje javnih ključev</string>
<string name="title_manageSecretKeys">Upravljanje zasebnih ključev</string>
<string name="title_selectRecipients">Izberi prejemnike</string>
<string name="title_selectSignature">Izberi podpis</string>
<string name="title_encrypt">Šifriraj</string>
<string name="title_decrypt">Dešifriraj</string>
<string name="title_authentification">Avtentikacija</string>
<string name="title_createKey">Ustvari ključ</string>
<string name="title_editKey">Uredi ključ</string>
<string name="title_preferences">Nastavitve</string>
<string name="title_changePassPhrase">Spremeni geslo</string>
<string name="title_setPassPhrase">Določi geslo</string>
<string name="title_sendEmail">"Pošlji e-pošto..."</string>
<string name="title_encryptToFile">Šifriraj v datoteko</string>
<string name="title_decryptToFile">Dešifriraj v datoteko</string>
<string name="title_addAccount">Dodaj račun</string>
<string name="title_importKeys">Uvozi ključe</string>
<string name="title_exportKey">Izvozi ključ</string>
<string name="title_exportKeys">Izvozi ključe</string>
<string name="title_keyNotFound">Ključ ni bil najden</string>
<!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">Uporabniške identitete</string>
<string name="section_keys">Ključi</string>
<string name="section_general">Splošno</string>
<string name="section_defaults">Privzete nastavitve</string>
<!-- btn_lowerCase: capitalized words, no punctuation -->
<string name="btn_encryptToClipboard">Šifriraj v odložišče</string>
<string name="btn_send">Šifriraj in pošlji</string>
<string name="btn_encrypt">Šifriraj</string>
<string name="btn_decrypt">Dešifriraj</string>
<string name="btn_verify">Overi</string>
<string name="btn_selectEncryptKeys">Izberi prejemnike</string>
<string name="btn_reply">Odgovori</string>
<string name="btn_encryptMessage">Šifriraj sporočilo</string>
<string name="btn_decryptMessage">Dešifriraj sporočilo</string>
<string name="btn_encryptFile">Šifriraj datoteko</string>
<string name="btn_decryptFile">Dešifriraj datoteko</string>
<string name="btn_save">Shrani</string>
<string name="btn_doNotSave">Prekliči</string>
<string name="btn_delete">Izbriši</string>
<string name="btn_noDate">Brez</string>
<!-- menu_lowerCase: capitalized words, no punctuation -->
<string name="menu_about">O programu</string>
<string name="menu_addAccount">Dodaj GMail račun</string>
<string name="menu_deleteAccount">Izbriši račun</string>
<string name="menu_managePublicKeys">Upravljanje javnih ključev</string>
<string name="menu_manageSecretKeys">Upravljanje zasebnih ključev</string>
<string name="menu_preferences">Nastavitve</string>
<string name="menu_changePassPhrase">Spremeni geslo</string>
<string name="menu_setPassPhrase">Določi geslo</string>
<string name="menu_importKeys">Uvozi ključe</string>
<string name="menu_exportKeys">Izvozi ključe</string>
<string name="menu_exportKey">Izvozi ključ</string>
<string name="menu_deleteKey">Izbriši ključ</string>
<string name="menu_createKey">Ustvari ključ</string>
<string name="menu_editKey">Uredi ključ</string>
<!-- label_lowerCase: capitalized words, no punctuation -->
<string name="label_sign">Podpiši</string>
<string name="label_message">Sporočilo</string>
<string name="label_file">Datoteka</string>
<string name="label_passPhrase">Geslo</string>
<string name="label_passPhraseAgain">Ponovi</string>
<string name="label_algorithm">Algoritem</string>
<string name="label_asciiArmour">ASCII Armour</string>
<string name="label_selectPublicKeys">Javni ključ(i)</string>
<string name="label_deleteAfterEncryption">Po šifriranju izbriši</string>
<string name="label_deleteAfterDecryption">Po dešifriranju izbriši</string>
<string name="label_encryptionAlgorithm">Šifrirni algoritem</string>
<string name="label_hashAlgorithm">Hash algoritem</string>
<string name="label_asymmetric">Javni ključ</string>
<string name="label_symmetric">Geslo</string>
<string name="label_passPhraseCacheTtl">Predpomnilnik gesel</string>
<string name="label_messageCompression">Zgoščevanje sporočil</string>
<string name="label_fileCompression">Zgoščevanje datotek</string>
<string name="noKeysSelected">Izberi</string>
<string name="oneKeySelected">1 izbran</string>
<string name="nKeysSelected">Izbrani</string>
<string name="unknownUserId">&lt;nepoznan&gt;</string>
<string name="none">&lt;brez&gt;</string>
<string name="noKey">&lt;brez ključa&gt;</string>
<string name="noDate">-</string>
<string name="noExpiry">&lt;nikoli&gt;</string>
<string name="unknownStatus"></string>
<string name="canEncrypt">lahko šifrira</string>
<string name="canSign">lahko podpiše</string>
<string name="expired">potečeno</string>
<string name="notValid">neveljavno</string>
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
<string name="choice_none">Brez</string>
<string name="choice_signOnly">Samo podpis</string>
<string name="choice_encryptOnly">Samo šifriranje</string>
<string name="choice_signAndEncrypt">Podpis in šifriranje</string>
<string name="choice_15secs">15 sek</string>
<string name="choice_1min">1 min</string>
<string name="choice_3mins">3 min</string>
<string name="choice_5mins">5 min</string>
<string name="choice_10mins">10 min</string>
<string name="choice_untilQuit">do izhoda</string>
<string name="dsa">DSA</string>
<string name="elgamal">ElGamal</string>
<string name="rsa">RSA</string>
<string name="filemanager_titleOpen">Odpri...</string>
<string name="filemanager_titleSave">Shrani kot...</string>
<string name="filemanager_titleEncrypt">Izberi datoteko za šifriranje...</string>
<string name="filemanager_titleDecrypt">Izberi datoteko za dešifriranje...</string>
<string name="filemanager_btnOpen">Odpri</string>
<string name="filemanager_btnSave">Shrani</string>
<string name="warning">Opozorilo</string>
<string name="error">Napaka</string>
<string name="warningMessage">Opozorilo: %s</string>
<string name="errorMessage">Napaka: %s</string>
<!-- sentences -->
<string name="wrongPassPhrase">Napačno geslo.</string>
<string name="usingClipboardContent">Uporabljam vsebino odložišča.</string>
<string name="keySaved">Ključ shranjen.</string>
<string name="setAPassPhrase">Najprej preko menija možnosti določite geslo.</string>
<string name="noFilemanagerInstalled">Nameščen ni noben združljiv upravitelj datotek.</string>
<string name="passPhrasesDoNotMatch">Gesli se ne ujemata.</string>
<string name="passPhraseMustNotBeEmpty">Prazna gesla niso dovoljena.</string>
<string name="passPhraseForSymmetricEncryption">Geslo za simetrično enkripcijo:</string>
<string name="passPhraseFor">Geslo za %s:</string>
<string name="fileDeleteConfirmation">Ali ste prepričani, da želite izbrisati\n%s?</string>
<string name="fileDeleteSuccessful">Uspešno izbrisano.</string>
<string name="noFileSelected">Najprej izberite datoteko.</string>
<string name="decryptionSuccessful">Uspešno dešifrirano.</string>
<string name="encryptionSuccessful">Uspešno šifrirano.</string>
<string name="encryptionToClipboardSuccessful">Uspešno šifrirano v odložišče.</string>
<string name="enterPassPhraseTwice">Vstavite geslo dvakrat.</string>
<string name="selectEncryptionKey">Izberite vsaj en šifrirni ključ.</string>
<string name="selectEncryptionOrSignatureKey">Izberite vsaj en šifrirni ključ ali ključ za podpis.</string>
<string name="specifyFileToEncryptTo">Določite datoteko v katero želite šifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string>
<string name="specifyFileToDecryptTo">Določite datoteko v katero želite dešifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string>
<string name="specifyGoogleMailAccount">Določite Google Mail račun, ki ga želite dodati.</string>
<string name="specifyFileToImportFrom">Določite iz katere datoteke želite uvoziti ključe. (.asc ali .gpg)</string>
<string name="specifyFileToExportTo">Določite v katero datoteko želite izvoziti.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string>
<string name="specifyFileToExportSecretKeysTo">Določite v katero datoteko želite izvoziti.\nPOZOR! Izvozili boste ZASEBNI ključ.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string>
<string name="keyDeletionConfirmation">Ali zares želite izbrisati ključ '%s'?\nTega ne boste mogli popraviti!</string>
<string name="secretKeyDeletionConfirmation">Ali zares želite izbrisati ZASEBNI ključ '%s'?\nTega ne boste mogli popraviti!</string>
<string name="keysAddedAndUpdated">Uspešno dodani ključi: %s. Uspešno posodobljeni ključi: %s."</string>
<string name="keysAdded">Uspešno dodani ključi: %s.</string>
<string name="keysUpdated">Uspešno posodobljeni ključi: %s.</string>
<string name="noKeysAddedOrUpdated">Noben ključ ni bil dodan ali posodobljen.</string>
<string name="keyExported">Uspešno izvožen 1 ključ.</string>
<string name="keysExported">Uspešno izvoženi ključi: %s</string>
<string name="noKeysExported">Noben ključ ni bil izvožen.</string>
<string name="keyCreationElGamalInfo">Opomba: le podključi podpirajo ElGamal. Za ElGamal bo uporabljena velikost najbližja 1536, 2048, 3072, 4096, ali 8192.</string>
<string name="keyNotFound">Ne najdem ključa %08X.</string>
<!-- error_lowerCase: phrases, no punctuation, all lowercase,
they will be put after "errorMessage", e.g. "Error: file not found" -->
<string name="error_fileDeleteFailed">izbris '%s' ni uspel</string>
<string name="error_fileNotFound">ne najdem datoteke</string>
<string name="error_noSecretKeyFound">najden ni bil noben ustrezen zasebni kluč</string>
<string name="error_noKnownEncryptionFound">najdena ni bila nobena poznana vrsta enkripcije</string>
<string name="error_externalStorageNotReady">zunanji pomnilnik ni pripravljen</string>
<string name="error_accountNotFound">račun '%s' ni najden</string>
<string name="error_addingAccountFailed">dodajanje računa '%s' ni uspelo</string>
<string name="error_invalidEmail">neveljaven e-naslov '%s'</string>
<string name="error_keySizeMinimum512bit">velikost ključa mora biti vsaj 512bit</string>
<string name="error_masterKeyMustNotBeElGamal">statični ključ ne more biti ključ ElGamal</string>
<string name="error_unknownAlgorithmChoice">neznana izbira algoritma</string>
<string name="error_userIdNeedsAName">določiti morate ime</string>
<string name="error_userIdNeedsAnEmailAddress">določiti morate naslov e-pošte</string>
<string name="error_keyNeedsAUserId">potrebujem vsaj eno uporabniško identiteto</string>
<string name="error_mainUserIdMustNotBeEmpty">glavna uporabniška identiteta ne more biti prazna</string>
<string name="error_keyNeedsMasterKey">potrebujem vsaj statični ključ</string>
<string name="error_expiryMustComeAfterCreation">datum poteka mora biti kasnejši od datuma nastanka</string>
<string name="error_noEncryptionKeysOrPassPhrase">dan ni bil noben šifrirni ključ ali geslo</string>
<string name="error_signatureFailed">podpis ni bil uspešen</string>
<string name="error_noSignaturePassPhrase">dano ni bilo nobeno geslo</string>
<string name="error_noSignatureKey">dan ni bil noben podpisni ključ</string>
<string name="error_invalidData">neveljavni šifrirni podatki</string>
<string name="error_corruptData">pokvarjeni podatki</string>
<string name="error_noSymmetricEncryptionPacket">ne najdem podatkov s simetrično enkripcijo</string>
<string name="error_wrongPassPhrase">napačno geslo</string>
<string name="error_savingKeys">napaka pri shranjevanju nakaterih ključev</string>
<!-- progress_lowerCase: lowercase, phrases, usually ending in '...' -->
<string name="progress_done">končano.</string>
<string name="progress_initializing">inicializiram...</string>
<string name="progress_saving">shranjujem...</string>
<string name="progress_importing">uvažam...</string>
<string name="progress_exporting">izvažam...</string>
<string name="progress_generating">generiram ključ, to lahko traja nekaj časa...</string>
<string name="progress_buildingKey">gradim ključ...</string>
<string name="progress_preparingMasterKey">pripravljam statični ključ...</string>
<string name="progress_certifyingMasterKey">potrjujem statični ključ...</string>
<string name="progress_buildingMasterKeyRing">gradim datoteko s statičnimi ključi...</string>
<string name="progress_addingSubKeys">dodajam podključe...</string>
<string name="progress_savingKeyRing">shranjujem datoteko s ključi...</string>
<string name="progress_importingSecretKeys">uvažam zasebne ključe...</string>
<string name="progress_importingPublicKeys">uvažam javne ključe...</string>
<string name="progress_reloadingKeys">reloading keys...</string>
<string name="progress_exportingKey">izvažam ključ...</string>
<string name="progress_exportingKeys">izvažam ključe...</string>
<string name="progress_extractingSignatureKey">izvlačim podpisni kluč...</string>
<string name="progress_extractingKey">izvlačim ključ...</string>
<string name="progress_preparingStreams">pripravljam tok...</string>
<string name="progress_encrypting">šifriram podatke...</string>
<string name="progress_decrypting">dešifriram podatke...</string>
<string name="progress_preparingSignature">pripravljam podpis...</string>
<string name="progress_generatingSignature">generiram podpis...</string>
<string name="progress_processingSignature">obdelujem podpis...</string>
<string name="progress_verifyingSignature">overovljam podpis...</string>
<string name="progress_signing">podpisujem...</string>
<string name="progress_readingData">berem podatke...</string>
<string name="progress_findingKey">iščem ključ...</string>
<string name="progress_decompressingData">raztezam podatke...</string>
<string name="progress_verifyingIntegrity">overovljam integriteto...</string>
</resources>

View File

@ -38,6 +38,7 @@
<string name="title_importKeys">Import Keys</string> <string name="title_importKeys">Import Keys</string>
<string name="title_exportKey">Export Key</string> <string name="title_exportKey">Export Key</string>
<string name="title_exportKeys">Export Keys</string> <string name="title_exportKeys">Export Keys</string>
<string name="title_keyNotFound">Key Not Found</string>
<!-- section_lowerCase: capitalized words, no punctuation --> <!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string> <string name="section_userIds">User IDs</string>
@ -144,7 +145,7 @@
<string name="usingClipboardContent">Using clipboard content.</string> <string name="usingClipboardContent">Using clipboard content.</string>
<string name="keySaved">Key saved.</string> <string name="keySaved">Key saved.</string>
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string>
<string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> <string name="noFilemanagerInstalled">No compatible file manager installed.</string>
<string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string> <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string> <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string>
@ -161,19 +162,20 @@
<string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string>
<string name="specifyFileToImportFrom">Please specify which file to import from.</string> <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>
<string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string> <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
<string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string> <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>
<string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string>
<string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
<string name="keysAdded">Succssfully added %s keys.</string> <string name="keysAdded">Succssfully added %s key(s).</string>
<string name="keysUpdated">Succssfully updated %s keys.</string> <string name="keysUpdated">Succssfully updated %s key(s).</string>
<string name="noKeysAddedOrUpdated">No keys added or updated.</string> <string name="noKeysAddedOrUpdated">No keys added or updated.</string>
<string name="keyExported">Succssfully exported 1 key.</string> <string name="keyExported">Succssfully exported 1 key.</string>
<string name="keysExported">Succssfully exported %s keys.</string> <string name="keysExported">Succssfully exported %s keys.</string>
<string name="noKeysExported">No keys exported.</string> <string name="noKeysExported">No keys exported.</string>
<string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string>
<string name="keyNotFound">Couldn't find key %08X.</string>
<!-- error_lowerCase: phrases, no punctuation, all lowercase, <!-- error_lowerCase: phrases, no punctuation, all lowercase,
they will be put after "errorMessage", e.g. "Error: file not found" --> they will be put after "errorMessage", e.g. "Error: file not found" -->
@ -237,5 +239,9 @@
<string name="progress_decompressingData">decompressing data...</string> <string name="progress_decompressingData">decompressing data...</string>
<string name="progress_verifyingIntegrity">verifying integrity...</string> <string name="progress_verifyingIntegrity">verifying integrity...</string>
<!-- permission strings -->
<string name="permission_read_key_details_label">Read key details from APG.</string>
<string name="permission_read_key_details_description">Read of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read.</string>
</resources> </resources>

View File

@ -1,78 +0,0 @@
/*
* Copyright (C) 2008 OpenIntents.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openintents.intents;
// Version Dec 9, 2008
/**
* Provides OpenIntents actions, extras, and categories used by providers.
* <p>
* These specifiers extend the standard Android specifiers.
* </p>
*/
public final class FileManager {
/**
* Activity Action: Pick a file through the file manager, or let user
* specify a custom file name. Data is the current file name or file name
* suggestion. Returns a new file name as file URI in data.
*
* <p>
* Constant Value: "org.openintents.action.PICK_FILE"
* </p>
*/
public static final String ACTION_PICK_FILE = "org.openintents.action.PICK_FILE";
/**
* Activity Action: Pick a directory through the file manager, or let user
* specify a custom file name. Data is the current directory name or
* directory name suggestion. Returns a new directory name as file URI in
* data.
*
* <p>
* Constant Value: "org.openintents.action.PICK_DIRECTORY"
* </p>
*/
public static final String ACTION_PICK_DIRECTORY = "org.openintents.action.PICK_DIRECTORY";
/**
* The title to display.
*
* <p>
* This is shown in the title bar of the file manager.
* </p>
*
* <p>
* Constant Value: "org.openintents.extra.TITLE"
* </p>
*/
public static final String EXTRA_TITLE = "org.openintents.extra.TITLE";
/**
* The text on the button to display.
*
* <p>
* Depending on the use, it makes sense to set this to "Open" or "Save".
* </p>
*
* <p>
* Constant Value: "org.openintents.extra.BUTTON_TEXT"
* </p>
*/
public static final String EXTRA_BUTTON_TEXT = "org.openintents.extra.BUTTON_TEXT";
}

View File

@ -19,6 +19,7 @@ package org.thialfihar.android.apg;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -35,8 +36,6 @@ import java.security.SecureRandom;
import java.security.Security; import java.security.Security;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.HashMap; import java.util.HashMap;
@ -80,18 +79,19 @@ import org.bouncycastle2.openpgp.PGPSignatureList;
import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator; import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector; import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle2.openpgp.PGPUtil; import org.bouncycastle2.openpgp.PGPUtil;
import org.thialfihar.android.apg.provider.PublicKeys; import org.thialfihar.android.apg.provider.Database;
import org.thialfihar.android.apg.provider.SecretKeys; 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.KeyEditor;
import org.thialfihar.android.apg.ui.widget.SectionView; import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.ui.widget.UserIdEditor; import org.thialfihar.android.apg.ui.widget.UserIdEditor;
import org.thialfihar.android.apg.utils.IterableIterator; import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.Activity; import android.app.Activity;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -102,9 +102,35 @@ public class Apg {
public static final String ENCRYPT = "org.thialfihar.android.apg.intent.ENCRYPT"; public static final String ENCRYPT = "org.thialfihar.android.apg.intent.ENCRYPT";
public static final String DECRYPT_FILE = "org.thialfihar.android.apg.intent.DECRYPT_FILE"; 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 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 String VERSION = "0.9.5"; public static final String EXTRA_DATA = "data";
public static final String EXTRA_STATUS = "status";
public static final String EXTRA_ERROR = "error";
public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage";
public static final String EXTRA_ENCRYPTED_MESSAGE = "decryptedMessage";
public static final String EXTRA_SIGNATURE = "signature";
public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId";
public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId";
public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess";
public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown";
public static final String EXTRA_USER_ID = "userId";
public static final String EXTRA_KEY_ID = "keyId";
public static final String EXTRA_REPLY_TO = "replyTo";
public static final String EXTRA_SEND_TO = "sendTo";
public static final String EXTRA_SUBJECT = "subject";
public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds";
public static final String EXTRA_SELECTION = "selection";
public static final String EXTRA_MESSAGE = "message";
public static final String EXTRA_PROGRESS = "progress";
public static final String EXTRA_MAX = "max";
public static final String EXTRA_ACCOUNT = "account";
public static String VERSION = "1.0.1";
public static String FULL_VERSION = "APG v" + VERSION; public static String FULL_VERSION = "APG v" + VERSION;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS =
@ -125,9 +151,6 @@ public class Apg {
CompressionAlgorithmTags.BZIP2, CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP }; CompressionAlgorithmTags.ZIP };
protected static Vector<PGPPublicKeyRing> mPublicKeyRings = new Vector<PGPPublicKeyRing>();
protected static Vector<PGPSecretKeyRing> mSecretKeyRings = new Vector<PGPSecretKeyRing>();
public static Pattern PGP_MESSAGE = public static Pattern PGP_MESSAGE =
Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
Pattern.DOTALL); Pattern.DOTALL);
@ -136,30 +159,12 @@ public class Apg {
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*", Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
Pattern.DOTALL); Pattern.DOTALL);
protected static boolean mInitialized = false;
protected static HashMap<Long, Integer> mSecretKeyIdToIdMap;
protected static HashMap<Long, PGPSecretKeyRing> mSecretKeyIdToKeyRingMap;
protected static HashMap<Long, Integer> mPublicKeyIdToIdMap;
protected static HashMap<Long, PGPPublicKeyRing> mPublicKeyIdToKeyRingMap;
public static final String PUBLIC_KEY_PROJECTION[] =
new String[] {
PublicKeys._ID,
PublicKeys.KEY_ID,
PublicKeys.KEY_DATA,
PublicKeys.WHO_ID, };
public static final String SECRET_KEY_PROJECTION[] =
new String[] {
PublicKeys._ID,
PublicKeys.KEY_ID,
PublicKeys.KEY_DATA,
PublicKeys.WHO_ID, };
private static HashMap<Long, CachedPassPhrase> mPassPhraseCache = private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
new HashMap<Long, CachedPassPhrase>(); new HashMap<Long, CachedPassPhrase>();
private static String mEditPassPhrase = null; private static String mEditPassPhrase = null;
private static Database mDatabase = null;
public static class GeneralException extends Exception { public static class GeneralException extends Exception {
static final long serialVersionUID = 0xf812773342L; static final long serialVersionUID = 0xf812773342L;
@ -176,102 +181,14 @@ public class Apg {
} }
} }
static { public static void initialize(Context context) {
mPublicKeyRings = new Vector<PGPPublicKeyRing>(); if (mDatabase == null) {
mSecretKeyRings = new Vector<PGPSecretKeyRing>(); mDatabase = new Database(context);
mSecretKeyIdToIdMap = new HashMap<Long, Integer>();
mSecretKeyIdToKeyRingMap = new HashMap<Long, PGPSecretKeyRing>();
mPublicKeyIdToIdMap = new HashMap<Long, Integer>();
mPublicKeyIdToKeyRingMap = new HashMap<Long, PGPPublicKeyRing>();
}
public static void initialize(Activity context) {
if (mInitialized) {
return;
}
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(Constants.path.app_dir);
if (!dir.exists() && !dir.mkdirs()) {
// ignore this for now, it's not crucial
// that the directory doesn't exist at this point
} }
} }
loadKeyRings(context, Id.type.public_key); public static Database getDatabase() {
loadKeyRings(context, Id.type.secret_key); return mDatabase;
mInitialized = true;
}
public static class PublicKeySorter implements Comparator<PGPPublicKeyRing> {
@Override
public int compare(PGPPublicKeyRing object1, PGPPublicKeyRing object2) {
PGPPublicKey key1 = getMasterKey(object1);
PGPPublicKey key2 = getMasterKey(object2);
if (key1 == null && key2 == null) {
return 0;
}
if (key1 == null) {
return -1;
}
if (key2 == null) {
return 1;
}
String uid1 = getMainUserId(key1);
String uid2 = getMainUserId(key2);
if (uid1 == null && uid2 == null) {
return 0;
}
if (uid1 == null) {
return -1;
}
if (uid2 == null) {
return 1;
}
return uid1.compareTo(uid2);
}
}
public static class SecretKeySorter implements Comparator<PGPSecretKeyRing> {
@Override
public int compare(PGPSecretKeyRing object1, PGPSecretKeyRing object2) {
PGPSecretKey key1 = getMasterKey(object1);
PGPSecretKey key2 = getMasterKey(object2);
if (key1 == null && key2 == null) {
return 0;
}
if (key1 == null) {
return -1;
}
if (key2 == null) {
return 1;
}
String uid1 = getMainUserId(key1);
String uid2 = getMainUserId(key2);
if (uid1 == null && uid2 == null) {
return 0;
}
if (uid1 == null) {
return -1;
}
if (uid2 == null) {
return 1;
}
return uid1.compareTo(uid2);
}
} }
public static void setEditPassPhrase(String passPhrase) { public static void setEditPassPhrase(String passPhrase) {
@ -289,7 +206,7 @@ public class Apg {
public static String getCachedPassPhrase(long keyId) { public static String getCachedPassPhrase(long keyId) {
long realId = keyId; long realId = keyId;
if (realId != Id.key.symmetric) { if (realId != Id.key.symmetric) {
PGPSecretKeyRing keyRing = findSecretKeyRing(keyId); PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
if (keyRing == null) { if (keyRing == null) {
return null; return null;
} }
@ -308,19 +225,30 @@ public class Apg {
return cpp.passPhrase; return cpp.passPhrase;
} }
public static void cleanUpCache(int ttl) { public static int cleanUpCache(int ttl, int initialDelay) {
int delay = initialDelay;
long realTtl = ttl * 1000;
long now = new Date().getTime(); long now = new Date().getTime();
Vector<Long> oldKeys = new Vector<Long>(); Vector<Long> oldKeys = new Vector<Long>();
for (Map.Entry<Long, CachedPassPhrase> pair : mPassPhraseCache.entrySet()) { for (Map.Entry<Long, CachedPassPhrase> pair : mPassPhraseCache.entrySet()) {
if ((now - pair.getValue().timestamp) >= 1000 * ttl) { long lived = now - pair.getValue().timestamp;
if (lived >= realTtl) {
oldKeys.add(pair.getKey()); 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) { for (long keyId : oldKeys) {
mPassPhraseCache.remove(keyId); mPassPhraseCache.remove(keyId);
} }
return delay;
} }
public static PGPSecretKey createKey(Context context, public static PGPSecretKey createKey(Context context,
@ -413,11 +341,10 @@ public class Apg {
secretKey = (PGPSecretKey) it.next(); secretKey = (PGPSecretKey) it.next();
} }
return secretKey; return secretKey;
} }
private static long getNumDatesBetween(GregorianCalendar first, GregorianCalendar second) { private static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) {
GregorianCalendar tmp = new GregorianCalendar(); GregorianCalendar tmp = new GregorianCalendar();
tmp.setTime(first.getTime()); tmp.setTime(first.getTime());
long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400; long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
@ -434,7 +361,7 @@ public class Apg {
String oldPassPhrase, String newPassPhrase, String oldPassPhrase, String newPassPhrase,
ProgressDialogUpdater progress) ProgressDialogUpdater progress)
throws Apg.GeneralException, NoSuchProviderException, PGPException, throws Apg.GeneralException, NoSuchProviderException, PGPException,
NoSuchAlgorithmException, SignatureException { NoSuchAlgorithmException, SignatureException, IOException, Database.GeneralException {
progress.setProgress(R.string.progress_buildingKey, 0, 100); progress.setProgress(R.string.progress_buildingKey, 0, 100);
@ -465,7 +392,7 @@ public class Apg {
} catch (UserIdEditor.NoEmailException e) { } catch (UserIdEditor.NoEmailException e) {
throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress)); throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress));
} catch (UserIdEditor.InvalidEmailException e) { } catch (UserIdEditor.InvalidEmailException e) {
throw new Apg.GeneralException(e.getMessage()); throw new Apg.GeneralException("" + e);
} }
if (userId.equals("")) { if (userId.equals("")) {
@ -549,11 +476,12 @@ public class Apg {
hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
// TODO: this doesn't work quite right yet
if (keyEditor.getExpiryDate() != null) { if (keyEditor.getExpiryDate() != null) {
GregorianCalendar creationDate = new GregorianCalendar(); GregorianCalendar creationDate = new GregorianCalendar();
creationDate.setTime(getCreationDate(masterKey)); creationDate.setTime(getCreationDate(masterKey));
GregorianCalendar expiryDate = keyEditor.getExpiryDate(); GregorianCalendar expiryDate = keyEditor.getExpiryDate();
long numDays = getNumDatesBetween(creationDate, expiryDate); long numDays = getNumDaysBetween(creationDate, expiryDate);
if (numDays <= 0) { if (numDays <= 0) {
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation)); throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
} }
@ -600,11 +528,12 @@ public class Apg {
} }
hashedPacketsGen.setKeyFlags(true, keyFlags); hashedPacketsGen.setKeyFlags(true, keyFlags);
// TODO: this doesn't work quite right yet
if (keyEditor.getExpiryDate() != null) { if (keyEditor.getExpiryDate() != null) {
GregorianCalendar creationDate = new GregorianCalendar(); GregorianCalendar creationDate = new GregorianCalendar();
creationDate.setTime(getCreationDate(masterKey)); creationDate.setTime(getCreationDate(masterKey));
GregorianCalendar expiryDate = keyEditor.getExpiryDate(); GregorianCalendar expiryDate = keyEditor.getExpiryDate();
long numDays = getNumDatesBetween(creationDate, expiryDate); long numDays = getNumDaysBetween(creationDate, expiryDate);
if (numDays <= 0) { if (numDays <= 0) {
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation)); throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
} }
@ -619,79 +548,16 @@ public class Apg {
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
progress.setProgress(R.string.progress_savingKeyRing, 90, 100); progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
saveKeyRing(context, secretKeyRing); mDatabase.saveKeyRing(secretKeyRing);
saveKeyRing(context, publicKeyRing); mDatabase.saveKeyRing(publicKeyRing);
loadKeyRings(context, Id.type.public_key);
loadKeyRings(context, Id.type.secret_key);
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
} }
private static int saveKeyRing(Activity context, PGPPublicKeyRing keyRing) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ContentValues values = new ContentValues();
PGPPublicKey masterKey = getMasterKey(keyRing);
if (masterKey == null) {
return Id.return_value.no_master_key;
}
try {
keyRing.encode(out);
out.close();
} catch (IOException e) {
return Id.return_value.error;
}
values.put(PublicKeys.KEY_ID, masterKey.getKeyID());
values.put(PublicKeys.KEY_DATA, out.toByteArray());
Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
Cursor cursor = context.managedQuery(uri, PUBLIC_KEY_PROJECTION, null, null, null);
if (cursor != null && cursor.getCount() > 0) {
context.getContentResolver().update(uri, values, null, null);
return Id.return_value.updated;
} else {
context.getContentResolver().insert(PublicKeys.CONTENT_URI, values);
return Id.return_value.ok;
}
}
private static int saveKeyRing(Activity context, PGPSecretKeyRing keyRing) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ContentValues values = new ContentValues();
PGPSecretKey masterKey = getMasterKey(keyRing);
if (masterKey == null) {
return Id.return_value.no_master_key;
}
try {
keyRing.encode(out);
out.close();
} catch (IOException e) {
return Id.return_value.error;
}
values.put(SecretKeys.KEY_ID, masterKey.getKeyID());
values.put(SecretKeys.KEY_DATA, out.toByteArray());
Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
Cursor cursor = context.managedQuery(uri, SECRET_KEY_PROJECTION, null, null, null);
if (cursor != null && cursor.getCount() > 0) {
context.getContentResolver().update(uri, values, null, null);
return Id.return_value.updated;
} else {
context.getContentResolver().insert(SecretKeys.CONTENT_URI, values);
return Id.return_value.ok;
}
}
public static Bundle importKeyRings(Activity context, int type, String filename, public static Bundle importKeyRings(Activity context, int type, String filename,
ProgressDialogUpdater progress) ProgressDialogUpdater progress)
throws GeneralException, FileNotFoundException, PGPException, IOException { throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
PGPObjectFactory objectFactory = null;
if (type == Id.type.secret_key) { if (type == Id.type.secret_key) {
progress.setProgress(R.string.progress_importingSecretKeys, 0, 100); progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
@ -704,37 +570,42 @@ public class Apg {
} }
FileInputStream fileIn = new FileInputStream(filename); FileInputStream fileIn = new FileInputStream(filename);
InputStream in = PGPUtil.getDecoderStream(fileIn); long fileSize = new File(filename).length();
objectFactory = new PGPObjectFactory(in); PositionAwareInputStream progressIn = new PositionAwareInputStream(fileIn);
// need to have access to the bufferedInput, so we can reuse it for the possible
Vector<Object> objects = new Vector<Object>(); // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
Object obj = objectFactory.nextObject(); // armour blocks
while (obj != null) { BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
objects.add(obj);
obj = objectFactory.nextObject();
}
int newKeys = 0; int newKeys = 0;
int oldKeys = 0; int oldKeys = 0;
for (int i = 0; i < objects.size(); ++i) { try {
progress.setProgress(i * 100 / objects.size(), 100); while (true) {
obj = objects.get(i); 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; PGPPublicKeyRing publicKeyRing;
PGPSecretKeyRing secretKeyRing; PGPSecretKeyRing secretKeyRing;
int retValue; // a return value that doesn't match any Id.return_value.* values, in case
// saveKeyRing is never called
int retValue = 2107;
if (type == Id.type.secret_key) { try {
if (!(obj instanceof PGPSecretKeyRing)) { if (type == Id.type.secret_key && obj instanceof PGPSecretKeyRing) {
continue;
}
secretKeyRing = (PGPSecretKeyRing) obj; secretKeyRing = (PGPSecretKeyRing) obj;
retValue = saveKeyRing(context, secretKeyRing); retValue = mDatabase.saveKeyRing(secretKeyRing);
} else { } else if (type == Id.type.public_key && obj instanceof PGPPublicKeyRing) {
if (!(obj instanceof PGPPublicKeyRing)) {
continue;
}
publicKeyRing = (PGPPublicKeyRing) obj; publicKeyRing = (PGPPublicKeyRing) obj;
retValue = saveKeyRing(context, publicKeyRing); 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) { if (retValue == Id.return_value.error) {
@ -746,10 +617,13 @@ public class Apg {
} else if (retValue == Id.return_value.ok) { } else if (retValue == Id.return_value.ok) {
++newKeys; ++newKeys;
} }
progress.setProgress((int)(100 * progressIn.position() / fileSize), 100);
obj = objectFactory.nextObject();
}
}
} catch (EOFException e) {
// nothing to do, we are done
} }
progress.setProgress(R.string.progress_reloadingKeys, 100, 100);
loadKeyRings(context, type);
returnData.putInt("added", newKeys); returnData.putInt("added", newKeys);
returnData.putInt("updated", oldKeys); returnData.putInt("updated", oldKeys);
@ -759,12 +633,13 @@ public class Apg {
return returnData; return returnData;
} }
public static Bundle exportKeyRings(Activity context, Vector<Object> keys, String filename, public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds,
String filename,
ProgressDialogUpdater progress) ProgressDialogUpdater progress)
throws GeneralException, FileNotFoundException, PGPException, IOException { throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
if (keys.size() == 1) { if (keyRingIds.size() == 1) {
progress.setProgress(R.string.progress_exportingKey, 0, 100); progress.setProgress(R.string.progress_exportingKey, 0, 100);
} else { } else {
progress.setProgress(R.string.progress_exportingKeys, 0, 100); progress.setProgress(R.string.progress_exportingKeys, 0, 100);
@ -777,9 +652,9 @@ public class Apg {
ArmoredOutputStream out = new ArmoredOutputStream(fileOut); ArmoredOutputStream out = new ArmoredOutputStream(fileOut);
int numKeys = 0; int numKeys = 0;
for (int i = 0; i < keys.size(); ++i) { for (int i = 0; i < keyRingIds.size(); ++i) {
progress.setProgress(i * 100 / keys.size(), 100); progress.setProgress(i * 100 / keyRingIds.size(), 100);
Object obj = keys.get(i); Object obj = mDatabase.getKeyRing(keyRingIds.get(i));
PGPPublicKeyRing publicKeyRing; PGPPublicKeyRing publicKeyRing;
PGPSecretKeyRing secretKeyRing; PGPSecretKeyRing secretKeyRing;
@ -803,61 +678,6 @@ public class Apg {
return returnData; return returnData;
} }
private static void loadKeyRings(Activity context, int type) {
Cursor cursor;
if (type == Id.type.secret_key) {
mSecretKeyRings.clear();
mSecretKeyIdToIdMap.clear();
mSecretKeyIdToKeyRingMap.clear();
cursor = context.managedQuery(SecretKeys.CONTENT_URI, SECRET_KEY_PROJECTION,
null, null, null);
} else {
mPublicKeyRings.clear();
mPublicKeyIdToIdMap.clear();
mPublicKeyIdToKeyRingMap.clear();
cursor = context.managedQuery(PublicKeys.CONTENT_URI, PUBLIC_KEY_PROJECTION,
null, null, null);
}
for (int i = 0; i < cursor.getCount(); ++i) {
cursor.moveToPosition(i);
String sharedIdColumn = PublicKeys._ID; // same in both
String sharedKeyIdColumn = PublicKeys.KEY_ID; // same in both
String sharedKeyDataColumn = PublicKeys.KEY_DATA; // same in both
int idIndex = cursor.getColumnIndex(sharedIdColumn);
int keyIdIndex = cursor.getColumnIndex(sharedKeyIdColumn);
int keyDataIndex = cursor.getColumnIndex(sharedKeyDataColumn);
byte keyData[] = cursor.getBlob(keyDataIndex);
int id = cursor.getInt(idIndex);
long keyId = cursor.getLong(keyIdIndex);
try {
if (type == Id.type.secret_key) {
PGPSecretKeyRing key = new PGPSecretKeyRing(keyData);
mSecretKeyRings.add(key);
mSecretKeyIdToIdMap.put(keyId, id);
mSecretKeyIdToKeyRingMap.put(keyId, key);
} else {
PGPPublicKeyRing key = new PGPPublicKeyRing(keyData);
mPublicKeyRings.add(key);
mPublicKeyIdToIdMap.put(keyId, id);
mPublicKeyIdToKeyRingMap.put(keyId, key);
}
} catch (IOException e) {
// TODO: some error handling
} catch (PGPException e) {
// TODO: some error handling
}
}
if (type == Id.type.secret_key) {
Collections.sort(mSecretKeyRings, new SecretKeySorter());
} else {
Collections.sort(mPublicKeyRings, new PublicKeySorter());
}
}
public static Date getCreationDate(PGPPublicKey key) { public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime(); return key.getCreationTime();
} }
@ -867,6 +687,9 @@ public class Apg {
} }
public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) { public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
if (keyRing == null) {
return null;
}
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) { for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
if (key.isMasterKey()) { if (key.isMasterKey()) {
return key; return key;
@ -877,6 +700,9 @@ public class Apg {
} }
public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) { public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
if (keyRing == null) {
return null;
}
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) { for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (key.isMasterKey()) { if (key.isMasterKey()) {
return key; return key;
@ -982,7 +808,7 @@ public class Apg {
} }
public static PGPPublicKey getEncryptPublicKey(long masterKeyId) { public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
PGPPublicKeyRing keyRing = mPublicKeyIdToKeyRingMap.get(masterKeyId); PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
if (keyRing == null) { if (keyRing == null) {
return null; return null;
} }
@ -994,7 +820,7 @@ public class Apg {
} }
public static PGPSecretKey getSigningKey(long masterKeyId) { public static PGPSecretKey getSigningKey(long masterKeyId) {
PGPSecretKeyRing keyRing = mSecretKeyIdToKeyRingMap.get(masterKeyId); PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
if (keyRing == null) { if (keyRing == null) {
return null; return null;
} }
@ -1035,14 +861,6 @@ public class Apg {
return userId; return userId;
} }
public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
return mPublicKeyIdToKeyRingMap.get(keyId);
}
public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
return mSecretKeyIdToKeyRingMap.get(keyId);
}
public static boolean isEncryptionKey(PGPPublicKey key) { public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) { if (!key.isEncryptionKey()) {
return false; return false;
@ -1122,9 +940,17 @@ public class Apg {
} }
public static String getAlgorithmInfo(PGPPublicKey key) { 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; String algorithmStr = null;
switch (key.getAlgorithm()) { switch (algorithm) {
case PGPPublicKey.RSA_ENCRYPT: case PGPPublicKey.RSA_ENCRYPT:
case PGPPublicKey.RSA_GENERAL: case PGPPublicKey.RSA_GENERAL:
case PGPPublicKey.RSA_SIGN: { case PGPPublicKey.RSA_SIGN: {
@ -1148,98 +974,122 @@ public class Apg {
break; break;
} }
} }
return algorithmStr + ", " + key.getBitStrength() + "bit"; return algorithmStr + ", " + keySize + "bit";
} }
public static String getAlgorithmInfo(PGPSecretKey key) { public static void deleteKey(int keyRingId) {
return getAlgorithmInfo(key.getPublicKey()); mDatabase.deleteKeyRing(keyRingId);
} }
public static void deleteKey(Activity context, PGPPublicKeyRing keyRing) { public static Object getKeyRing(int keyRingId) {
PGPPublicKey masterKey = getMasterKey(keyRing); return mDatabase.getKeyRing(keyRingId);
Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
context.getContentResolver().delete(uri, null, null);
loadKeyRings(context, Id.type.public_key);
} }
public static void deleteKey(Activity context, PGPSecretKeyRing keyRing) { public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
PGPSecretKey masterKey = getMasterKey(keyRing); byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_secret, keyId);
Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID()); if (data == null) {
context.getContentResolver().delete(uri, null, null); return null;
loadKeyRings(context, Id.type.secret_key);
} }
public static PGPPublicKey findPublicKey(long keyId) {
PGPPublicKey key = null;
for (int i = 0; i < mPublicKeyRings.size(); ++i) {
PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
try { try {
key = keyRing.getPublicKey(keyId); return new PGPSecretKeyRing(data);
if (key != null) { } catch (IOException e) {
return key; // no good way to handle this, return null
} // TODO: some info?
} catch (PGPException e) { } catch (PGPException e) {
// just not found, can ignore this // no good way to handle this, return null
} // TODO: some info?
} }
return null; return null;
} }
public static PGPSecretKey findSecretKey(long keyId) { public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
PGPSecretKey key = null; byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_public, keyId);
for (int i = 0; i < mSecretKeyRings.size(); ++i) { if (data == null) {
PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
key = keyRing.getSecretKey(keyId);
if (key != null) {
return key;
}
}
return null; return null;
} }
public static PGPSecretKeyRing findSecretKeyRing(long keyId) {
for (int i = 0; i < mSecretKeyRings.size(); ++i) {
PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
PGPSecretKey key = null;
key = keyRing.getSecretKey(keyId);
if (key != null) {
return keyRing;
}
}
return null;
}
public static PGPPublicKeyRing findPublicKeyRing(long keyId) {
for (int i = 0; i < mPublicKeyRings.size(); ++i) {
PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
PGPPublicKey key = null;
try { try {
key = keyRing.getPublicKey(keyId); return new PGPPublicKeyRing(data);
if (key != null) { } catch (IOException e) {
return keyRing; // no good way to handle this, return null
} // TODO: some info?
} catch (PGPException e) {
// key not found
}
} }
return null; return null;
} }
public static PGPPublicKey getPublicMasterKey(long keyId) { public static PGPSecretKey getSecretKey(long keyId) {
PGPPublicKey key = null; PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
for (int i = 0; i < mPublicKeyRings.size(); ++i) { if (keyRing == null) {
PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
try {
key = keyRing.getPublicKey(keyId);
if (key != null) {
return getMasterKey(keyRing);
}
} catch (PGPException e) {
// just not found, can ignore this
}
}
return 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, public static void encrypt(Context context,
InputStream inStream, OutputStream outStream, InputStream inStream, OutputStream outStream,
@ -1277,7 +1127,7 @@ public class Apg {
} }
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
signingKeyRing = findSecretKeyRing(signatureKeyId); signingKeyRing = getSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId); signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) { if (signingKey == null) {
throw new GeneralException(context.getString(R.string.error_signatureFailed)); throw new GeneralException(context.getString(R.string.error_signatureFailed));
@ -1329,7 +1179,7 @@ public class Apg {
if (compression == Id.choice.compression.none) { if (compression == Id.choice.compression.none) {
bcpgOut = new BCPGOutputStream(encryptOut); bcpgOut = new BCPGOutputStream(encryptOut);
} else { } else {
compressGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZLIB); compressGen = new PGPCompressedDataGenerator(compression);
bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut)); bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
} }
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
@ -1393,7 +1243,7 @@ public class Apg {
throw new GeneralException(context.getString(R.string.error_noSignatureKey)); throw new GeneralException(context.getString(R.string.error_noSignatureKey));
} }
signingKeyRing = findSecretKeyRing(signatureKeyId); signingKeyRing = getSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId); signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) { if (signingKey == null) {
throw new GeneralException(context.getString(R.string.error_signatureFailed)); throw new GeneralException(context.getString(R.string.error_signatureFailed));
@ -1479,7 +1329,7 @@ public class Apg {
if (obj instanceof PGPPublicKeyEncryptedData) { if (obj instanceof PGPPublicKeyEncryptedData) {
gotAsymmetricEncryption = true; gotAsymmetricEncryption = true;
PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj; PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
secretKey = findSecretKey(pbe.getKeyID()); secretKey = getSecretKey(pbe.getKeyID());
if (secretKey != null) { if (secretKey != null) {
break; break;
} }
@ -1532,6 +1382,9 @@ public class Apg {
String passPhrase, ProgressDialogUpdater progress, String passPhrase, ProgressDialogUpdater progress,
boolean assumeSymmetric) boolean assumeSymmetric)
throws IOException, GeneralException, PGPException, SignatureException { throws IOException, GeneralException, PGPException, SignatureException {
if (passPhrase == null) {
passPhrase = "";
}
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
InputStream in = PGPUtil.getDecoderStream(inStream); InputStream in = PGPUtil.getDecoderStream(inStream);
PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPObjectFactory pgpF = new PGPObjectFactory(in);
@ -1589,7 +1442,7 @@ public class Apg {
Object obj = it.next(); Object obj = it.next();
if (obj instanceof PGPPublicKeyEncryptedData) { if (obj instanceof PGPPublicKeyEncryptedData) {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
secretKey = findSecretKey(encData.getKeyID()); secretKey = getSecretKey(encData.getKeyID());
if (secretKey != null) { if (secretKey != null) {
pbe = encData; pbe = encData;
break; break;
@ -1634,11 +1487,11 @@ public class Apg {
if (dataChunk instanceof PGPOnePassSignatureList) { if (dataChunk instanceof PGPOnePassSignatureList) {
progress.setProgress(R.string.progress_processingSignature, currentProgress, 100); progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
returnData.putBoolean("signature", true); returnData.putBoolean(EXTRA_SIGNATURE, true);
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
for (int i = 0; i < sigList.size(); ++i) { for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i); signature = sigList.get(i);
signatureKey = findPublicKey(signature.getKeyID()); signatureKey = getPublicKey(signature.getKeyID());
if (signatureKeyId == 0) { if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID(); signatureKeyId = signature.getKeyID();
} }
@ -1648,21 +1501,21 @@ public class Apg {
signatureIndex = i; signatureIndex = i;
signatureKeyId = signature.getKeyID(); signatureKeyId = signature.getKeyID();
String userId = null; String userId = null;
PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId); PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
if (sigKeyRing != null) { if (sigKeyRing != null) {
userId = getMainUserId(getMasterKey(sigKeyRing)); userId = getMainUserId(getMasterKey(sigKeyRing));
} }
returnData.putString("signatureUserId", userId); returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
break; break;
} }
} }
returnData.putLong("signatureKeyId", signatureKeyId); returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
if (signature != null) { if (signature != null) {
signature.initVerify(signatureKey, new BouncyCastleProvider()); signature.initVerify(signatureKey, new BouncyCastleProvider());
} else { } else {
returnData.putBoolean("signatureUnknown", true); returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
} }
dataChunk = plainFact.nextObject(); dataChunk = plainFact.nextObject();
@ -1694,7 +1547,7 @@ public class Apg {
try { try {
signature.update(buffer, 0, n); signature.update(buffer, 0, n);
} catch (SignatureException e) { } catch (SignatureException e) {
returnData.putBoolean("signatureSuccess", false); returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
signature = null; signature = null;
} }
} }
@ -1714,9 +1567,9 @@ public class Apg {
PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex); PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex);
if (signature.verify(messageSignature)) { if (signature.verify(messageSignature)) {
returnData.putBoolean("signatureSuccess", true); returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, true);
} else { } else {
returnData.putBoolean("signatureSuccess", false); returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
} }
} }
} }
@ -1769,7 +1622,7 @@ public class Apg {
byte[] clearText = out.toByteArray(); byte[] clearText = out.toByteArray();
outStream.write(clearText); outStream.write(clearText);
returnData.putBoolean("signature", true); returnData.putBoolean(EXTRA_SIGNATURE, true);
progress.setProgress(R.string.progress_processingSignature, 60, 100); progress.setProgress(R.string.progress_processingSignature, 60, 100);
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
@ -1783,7 +1636,7 @@ public class Apg {
PGPPublicKey signatureKey = null; PGPPublicKey signatureKey = null;
for (int i = 0; i < sigList.size(); ++i) { for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i); signature = sigList.get(i);
signatureKey = findPublicKey(signature.getKeyID()); signatureKey = getPublicKey(signature.getKeyID());
if (signatureKeyId == 0) { if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID(); signatureKeyId = signature.getKeyID();
} }
@ -1792,19 +1645,19 @@ public class Apg {
} else { } else {
signatureKeyId = signature.getKeyID(); signatureKeyId = signature.getKeyID();
String userId = null; String userId = null;
PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId); PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
if (sigKeyRing != null) { if (sigKeyRing != null) {
userId = getMainUserId(getMasterKey(sigKeyRing)); userId = getMainUserId(getMasterKey(sigKeyRing));
} }
returnData.putString("signatureUserId", userId); returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
break; break;
} }
} }
returnData.putLong("signatureKeyId", signatureKeyId); returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
if (signature == null) { if (signature == null) {
returnData.putBoolean("signatureUnknown", true); returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
return returnData; return returnData;
} }
@ -1829,21 +1682,12 @@ public class Apg {
while (lookAhead != -1); while (lookAhead != -1);
} }
returnData.putBoolean("signatureSuccess", signature.verify()); returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, signature.verify());
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
return returnData; return returnData;
} }
public static Vector<PGPPublicKeyRing> getPublicKeyRings() {
return mPublicKeyRings;
}
public static Vector<PGPSecretKeyRing> getSecretKeyRings() {
return mSecretKeyRings;
}
// taken from ClearSignedFileProcessor in BC // taken from ClearSignedFileProcessor in BC
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
throws IOException { throws IOException {

View File

@ -25,6 +25,7 @@ import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.EditText; import android.widget.EditText;
@ -42,14 +43,24 @@ public class AskForSecretKeyPassPhrase {
alert.setTitle(R.string.title_authentification); alert.setTitle(R.string.title_authentification);
final PGPSecretKey secretKey; final PGPSecretKey secretKey;
final Activity activity = context;
if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) { if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) {
secretKey = null; secretKey = null;
alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption)); alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
} else { } else {
secretKey = Apg.getMasterKey(Apg.findSecretKeyRing(secretKeyId)); secretKey = Apg.getMasterKey(Apg.getSecretKeyRing(secretKeyId));
if (secretKey == null) { if (secretKey == null) {
return null; alert.setTitle(R.string.title_keyNotFound);
alert.setMessage(context.getString(R.string.keyNotFound, secretKeyId));
alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
activity.removeDialog(Id.dialog.pass_phrase);
}
});
alert.setCancelable(false);
return alert.create();
} }
String userId = Apg.getMainUserIdSafe(context, secretKey); String userId = Apg.getMainUserIdSafe(context, secretKey);
alert.setMessage(context.getString(R.string.passPhraseFor, userId)); alert.setMessage(context.getString(R.string.passPhraseFor, userId));
@ -65,7 +76,6 @@ public class AskForSecretKeyPassPhrase {
alert.setView(view); alert.setView(view);
final PassPhraseCallbackInterface cb = callback; final PassPhraseCallbackInterface cb = callback;
final Activity activity = context;
alert.setPositiveButton(android.R.string.ok, alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {

View File

@ -17,10 +17,7 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.File; import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import org.bouncycastle2.bcpg.CompressionAlgorithmTags;
import org.bouncycastle2.bcpg.HashAlgorithmTags; import org.bouncycastle2.bcpg.HashAlgorithmTags;
import org.bouncycastle2.openpgp.PGPEncryptedData; import org.bouncycastle2.openpgp.PGPEncryptedData;
@ -33,6 +30,7 @@ import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -53,8 +51,6 @@ public class BaseActivity extends Activity
private String mDeleteFile = null; private String mDeleteFile = null;
protected static SharedPreferences mPreferences = null; protected static SharedPreferences mPreferences = null;
private static Timer mCacheTimer = new Timer();
private Handler mHandler = new Handler() { private Handler mHandler = new Handler() {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
@ -66,33 +62,23 @@ public class BaseActivity extends Activity
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Apg.initialize(this);
if (mPreferences == null) { if (mPreferences == null) {
mPreferences = getPreferences(MODE_PRIVATE); mPreferences = getPreferences(MODE_PRIVATE);
} }
Apg.initialize(this);
if (mCacheTimer == null) { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
setPassPhraseCacheTimer(); 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
} }
} }
private void setPassPhraseCacheTimer() { Intent intent = new Intent(this, Service.class);
if (mCacheTimer != null) { intent.putExtra(Service.EXTRA_TTL, getPassPhraseCacheTtl());
mCacheTimer.cancel(); startService(intent);
mCacheTimer = null;
}
int ttl = getPassPhraseCacheTtl();
if (ttl == 0) {
// no timer needed
return;
}
// check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
// and makes sure the longest a pass phrase survives int the cache is 1.5 * ttl
mCacheTimer = new Timer();
mCacheTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
Apg.cleanUpCache(BaseActivity.this.getPassPhraseCacheTtl());
}
}, 0, ttl * 1000 / 2);
} }
@Override @Override
@ -282,7 +268,7 @@ public class BaseActivity extends Activity
case Id.request.secret_keys: { case Id.request.secret_keys: {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras(); Bundle bundle = data.getExtras();
setSecretKeyId(bundle.getLong("selectedKeyId")); setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
} else { } else {
setSecretKeyId(Id.key.none); setSecretKeyId(Id.key.none);
} }
@ -304,9 +290,9 @@ public class BaseActivity extends Activity
public void setProgress(int progress, int max) { public void setProgress(int progress, int max) {
Message msg = new Message(); Message msg = new Message();
Bundle data = new Bundle(); Bundle data = new Bundle();
data.putInt("type", Id.message.progress_update); data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
data.putInt("progress", progress); data.putInt(Apg.EXTRA_PROGRESS, progress);
data.putInt("max", max); data.putInt(Apg.EXTRA_MAX, max);
msg.setData(data); msg.setData(data);
mHandler.sendMessage(msg); mHandler.sendMessage(msg);
} }
@ -314,10 +300,10 @@ public class BaseActivity extends Activity
public void setProgress(String message, int progress, int max) { public void setProgress(String message, int progress, int max) {
Message msg = new Message(); Message msg = new Message();
Bundle data = new Bundle(); Bundle data = new Bundle();
data.putInt("type", Id.message.progress_update); data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
data.putString("message", message); data.putString(Apg.EXTRA_MESSAGE, message);
data.putInt("progress", progress); data.putInt(Apg.EXTRA_PROGRESS, progress);
data.putInt("max", max); data.putInt(Apg.EXTRA_MAX, max);
msg.setData(data); msg.setData(data);
mHandler.sendMessage(msg); mHandler.sendMessage(msg);
} }
@ -328,16 +314,16 @@ public class BaseActivity extends Activity
return; return;
} }
int type = data.getInt("type"); int type = data.getInt(Apg.EXTRA_STATUS);
switch (type) { switch (type) {
case Id.message.progress_update: { case Id.message.progress_update: {
String message = data.getString("message"); String message = data.getString(Apg.EXTRA_MESSAGE);
if (mProgressDialog != null) { if (mProgressDialog != null) {
if (message != null) { if (message != null) {
mProgressDialog.setMessage(message); mProgressDialog.setMessage(message);
} }
mProgressDialog.setMax(data.getInt("max")); mProgressDialog.setMax(data.getInt(Apg.EXTRA_MAX));
mProgressDialog.setProgress(data.getInt("progress")); mProgressDialog.setProgress(data.getInt(Apg.EXTRA_PROGRESS));
} }
break; break;
} }
@ -382,7 +368,14 @@ public class BaseActivity extends Activity
} }
public int getPassPhraseCacheTtl() { public int getPassPhraseCacheTtl() {
return mPreferences.getInt(Constants.pref.pass_phrase_cache_ttl, 300); int ttl = mPreferences.getInt(Constants.pref.pass_phrase_cache_ttl, 180);
// fix the value if it was set to "never" in previous versions, which currently is not
// supported
if (ttl == 0) {
ttl = 180;
setPassPhraseCacheTtl(ttl);
}
return ttl;
} }
public void setPassPhraseCacheTtl(int value) { public void setPassPhraseCacheTtl(int value) {
@ -390,7 +383,9 @@ public class BaseActivity extends Activity
editor.putInt(Constants.pref.pass_phrase_cache_ttl, value); editor.putInt(Constants.pref.pass_phrase_cache_ttl, value);
editor.commit(); editor.commit();
setPassPhraseCacheTimer(); Intent intent = new Intent(this, Service.class);
intent.putExtra(Service.EXTRA_TTL, value);
startService(intent);
} }
public int getDefaultEncryptionAlgorithm() { public int getDefaultEncryptionAlgorithm() {
@ -417,7 +412,7 @@ public class BaseActivity extends Activity
public int getDefaultMessageCompression() { public int getDefaultMessageCompression() {
return mPreferences.getInt(Constants.pref.default_message_compression, return mPreferences.getInt(Constants.pref.default_message_compression,
CompressionAlgorithmTags.ZLIB); Id.choice.compression.zlib);
} }
public void setDefaultMessageCompression(int value) { public void setDefaultMessageCompression(int value) {
@ -428,7 +423,7 @@ public class BaseActivity extends Activity
public int getDefaultFileCompression() { public int getDefaultFileCompression() {
return mPreferences.getInt(Constants.pref.default_file_compression, return mPreferences.getInt(Constants.pref.default_file_compression,
CompressionAlgorithmTags.ZLIB); Id.choice.compression.none);
} }
public void setDefaultFileCompression(int value) { public void setDefaultFileCompression(int value) {

View File

@ -10,6 +10,12 @@ public class CachedPassPhrase {
this.passPhrase = passPhrase; 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) { public boolean equals(Object other) {
if (!(other instanceof CachedPassPhrase)) { if (!(other instanceof CachedPassPhrase)) {
return false; return false;

View File

@ -31,8 +31,6 @@ import java.util.regex.Matcher;
import org.bouncycastle2.jce.provider.BouncyCastleProvider; import org.bouncycastle2.jce.provider.BouncyCastleProvider;
import org.bouncycastle2.openpgp.PGPException; import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.util.Strings;
import org.openintents.intents.FileManager;
import android.app.Dialog; import android.app.Dialog;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
@ -57,6 +55,9 @@ import android.widget.ViewFlipper;
public class DecryptActivity extends BaseActivity { public class DecryptActivity extends BaseActivity {
private long mSignatureKeyId = 0; private long mSignatureKeyId = 0;
private Intent mIntent;
private boolean mReturnResult = false;
private String mReplyTo = null; private String mReplyTo = null;
private String mSubject = null; private String mSubject = null;
private boolean mSignedOnly = false; private boolean mSignedOnly = false;
@ -158,9 +159,9 @@ public class DecryptActivity extends BaseActivity {
mSource.showNext(); mSource.showNext();
} }
Intent intent = getIntent(); mIntent = getIntent();
if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) { if (Intent.ACTION_VIEW.equals(mIntent.getAction())) {
Uri uri = intent.getData(); Uri uri = mIntent.getData();
try { try {
InputStream attachment = getContentResolver().openInputStream(uri); InputStream attachment = getContentResolver().openInputStream(uri);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
@ -170,15 +171,15 @@ public class DecryptActivity extends BaseActivity {
byteOut.write(bytes, 0, length); byteOut.write(bytes, 0, length);
} }
byteOut.close(); byteOut.close();
String data = Strings.fromUTF8ByteArray(byteOut.toByteArray()); String data = new String(byteOut.toByteArray());
mMessage.setText(data); mMessage.setText(data);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
// ignore, then // ignore, then
} catch (IOException e) { } catch (IOException e) {
// ignore, then // ignore, then
} }
} else if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SEND)) { } else if (Intent.ACTION_SEND.equals(mIntent.getAction())) {
Bundle extras = intent.getExtras(); Bundle extras = mIntent.getExtras();
if (extras == null) { if (extras == null) {
extras = new Bundle(); extras = new Bundle();
} }
@ -187,15 +188,15 @@ public class DecryptActivity extends BaseActivity {
mMessage.setText(data); mMessage.setText(data);
} }
mSubject = extras.getString(Intent.EXTRA_SUBJECT); mSubject = extras.getString(Intent.EXTRA_SUBJECT);
if (mSubject.startsWith("Fwd: ")) { if (mSubject != null && mSubject.startsWith("Fwd: ")) {
mSubject = mSubject.substring(5); mSubject = mSubject.substring(5);
} }
} else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT)) { } else if (Apg.Intent.DECRYPT.equals(mIntent.getAction())) {
Bundle extras = intent.getExtras(); Bundle extras = mIntent.getExtras();
if (extras == null) { if (extras == null) {
extras = new Bundle(); extras = new Bundle();
} }
String data = extras.getString("data"); String data = extras.getString(Apg.EXTRA_DATA);
if (data != null) { if (data != null) {
Matcher matcher = Apg.PGP_MESSAGE.matcher(data); Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
if (matcher.matches()) { if (matcher.matches()) {
@ -214,14 +215,39 @@ public class DecryptActivity extends BaseActivity {
} }
} }
} }
mReplyTo = extras.getString("replyTo"); mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO);
mSubject = extras.getString("subject"); mSubject = extras.getString(Apg.EXTRA_SUBJECT);
} else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT_FILE)) { } else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) {
mSource.setInAnimation(null); mSource.setInAnimation(null);
mSource.setOutAnimation(null); mSource.setOutAnimation(null);
while (mSource.getCurrentView().getId() != R.id.sourceFile) { while (mSource.getCurrentView().getId() != R.id.sourceFile) {
mSource.showNext(); mSource.showNext();
} }
} else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) {
Bundle extras = mIntent.getExtras();
if (extras == null) {
extras = new Bundle();
}
String data = extras.getString(Apg.EXTRA_DATA);
if (data != null) {
Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
if (matcher.matches()) {
data = matcher.group(1);
// replace non breakable spaces
data = data.replaceAll("\\xa0", " ");
mMessage.setText(data);
} else {
matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data);
if (matcher.matches()) {
data = matcher.group(1);
// replace non breakable spaces
data = data.replaceAll("\\xa0", " ");
mMessage.setText(data);
mDecryptButton.setText(R.string.btn_verify);
}
}
}
mReturnResult = true;
} }
if (mSource.getCurrentView().getId() == R.id.sourceMessage && if (mSource.getCurrentView().getId() == R.id.sourceMessage &&
@ -256,29 +282,41 @@ public class DecryptActivity extends BaseActivity {
}); });
mReplyButton.setVisibility(View.INVISIBLE); mReplyButton.setVisibility(View.INVISIBLE);
if (mReturnResult) {
mSourcePrevious.setClickable(false);
mSourcePrevious.setEnabled(false);
mSourcePrevious.setVisibility(View.INVISIBLE);
mSourceNext.setClickable(false);
mSourceNext.setEnabled(false);
mSourceNext.setVisibility(View.INVISIBLE);
mSourceLabel.setClickable(false);
mSourceLabel.setEnabled(false);
}
updateSource();
if (mSource.getCurrentView().getId() == R.id.sourceMessage && if (mSource.getCurrentView().getId() == R.id.sourceMessage &&
mMessage.getText().length() > 0) { mMessage.getText().length() > 0) {
mDecryptButton.performClick(); mDecryptButton.performClick();
} }
updateSource();
} }
private void openFile() { private void openFile() {
String filename = mFilename.getText().toString(); String filename = mFilename.getText().toString();
Intent intent = new Intent(FileManager.ACTION_PICK_FILE); Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setData(Uri.parse("file://" + filename)); intent.setData(Uri.parse("file://" + filename));
intent.setType("*/*");
intent.putExtra(FileManager.EXTRA_TITLE, getString(R.string.filemanager_titleDecrypt));
intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen);
try { try {
startActivityForResult(intent, Id.request.filename); startActivityForResult(intent, Id.request.filename);
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
// No compatible file manager was found. // No compatible file manager was found.
Toast.makeText(this, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
} }
} }
@ -373,9 +411,9 @@ public class DecryptActivity extends BaseActivity {
// look at the file/message again to check whether there's // look at the file/message again to check whether there's
// symmetric encryption data in there // symmetric encryption data in there
if (mDecryptTarget == Id.target.file) { if (mDecryptTarget == Id.target.file) {
((FileInputStream) in).reset(); in = new FileInputStream(mInputFilename);
} else { } else {
((ByteArrayInputStream) in).reset(); in = new ByteArrayInputStream(mMessage.getText().toString().getBytes());
} }
if (!Apg.hasSymmetricEncryption(this, in)) { if (!Apg.hasSymmetricEncryption(this, in)) {
throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound)); throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound));
@ -396,9 +434,9 @@ public class DecryptActivity extends BaseActivity {
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
error = getString(R.string.error_fileNotFound); error = getString(R.string.error_fileNotFound);
} catch (IOException e) { } catch (IOException e) {
error = e.getLocalizedMessage(); error = "" + e;
} catch (Apg.GeneralException e) { } catch (Apg.GeneralException e) {
error = e.getLocalizedMessage(); error = "" + e;
} }
if (error != null) { if (error != null) {
Toast.makeText(this, getString(R.string.errorMessage, error), Toast.makeText(this, getString(R.string.errorMessage, error),
@ -412,12 +450,11 @@ public class DecryptActivity extends BaseActivity {
String data = mMessage.getText().toString(); String data = mMessage.getText().toString();
data = data.replaceAll("(?m)^", "> "); data = data.replaceAll("(?m)^", "> ");
data = "\n\n" + data; data = "\n\n" + data;
intent.putExtra("data", data); intent.putExtra(Apg.EXTRA_DATA, data);
intent.putExtra("subject", "Re: " + mSubject); intent.putExtra(Apg.EXTRA_SUBJECT, "Re: " + mSubject);
intent.putExtra("sendTo", mReplyTo); intent.putExtra(Apg.EXTRA_SEND_TO, mReplyTo);
intent.putExtra("eyId", mSignatureKeyId); intent.putExtra(Apg.EXTRA_SIGNATURE_KEY_ID, getSecretKeyId());
intent.putExtra("signatureKeyId", getSecretKeyId()); intent.putExtra(Apg.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId });
intent.putExtra("encryptionKeyIds", new long[] { mSignatureKeyId });
startActivity(intent); startActivity(intent);
} }
@ -474,25 +511,23 @@ public class DecryptActivity extends BaseActivity {
out.close(); out.close();
if (mDecryptTarget == Id.target.message) { if (mDecryptTarget == Id.target.message) {
data.putString("decryptedMessage", data.putByteArray(Apg.EXTRA_DECRYPTED_MESSAGE,
Strings.fromUTF8ByteArray(((ByteArrayOutputStream) ((ByteArrayOutputStream) out).toByteArray());
out).toByteArray()));
} }
} catch (PGPException e) { } catch (PGPException e) {
error = e.getMessage(); error = "" + e;
} catch (IOException e) { } catch (IOException e) {
error = e.getMessage(); error = "" + e;
} catch (SignatureException e) { } catch (SignatureException e) {
error = e.getMessage(); error = "" + e;
e.printStackTrace();
} catch (Apg.GeneralException e) { } catch (Apg.GeneralException e) {
error = e.getMessage(); error = "" + e;
} }
data.putInt("type", Id.message.done); data.putInt(Apg.EXTRA_STATUS, Id.message.done);
if (error != null) { if (error != null) {
data.putString("error", error); data.putString(Apg.EXTRA_ERROR, error);
} }
msg.setData(data); msg.setData(data);
@ -509,20 +544,20 @@ public class DecryptActivity extends BaseActivity {
mSignatureLayout.setVisibility(View.GONE); mSignatureLayout.setVisibility(View.GONE);
mReplyButton.setVisibility(View.INVISIBLE); mReplyButton.setVisibility(View.INVISIBLE);
String error = data.getString("error"); String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) { if (error != null) {
Toast.makeText(DecryptActivity.this, Toast.makeText(DecryptActivity.this,
getString(R.string.errorMessage, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
data.getString("error")),
Toast.LENGTH_SHORT).show();
return; return;
} }
Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show();
switch (mDecryptTarget) { switch (mDecryptTarget) {
case Id.target.message: { case Id.target.message: {
String decryptedMessage = data.getString("decryptedMessage"); String decryptedMessage =
new String(data.getByteArray(Apg.EXTRA_DECRYPTED_MESSAGE));
mMessage.setText(decryptedMessage); mMessage.setText(decryptedMessage);
mMessage.setHorizontallyScrolling(false);
mReplyButton.setVisibility(View.VISIBLE); mReplyButton.setVisibility(View.VISIBLE);
break; break;
} }
@ -541,9 +576,9 @@ public class DecryptActivity extends BaseActivity {
} }
} }
if (data.getBoolean("signature")) { if (data.getBoolean(Apg.EXTRA_SIGNATURE)) {
String userId = data.getString("signatureUserId"); String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID);
mSignatureKeyId = data.getLong("signatureKeyId"); mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL)); mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL));
if (userId == null) { if (userId == null) {
userId = getResources().getString(R.string.unknownUserId); userId = getResources().getString(R.string.unknownUserId);
@ -555,15 +590,22 @@ public class DecryptActivity extends BaseActivity {
} }
mUserId.setText(userId); mUserId.setText(userId);
if (data.getBoolean("signatureSuccess")) { if (data.getBoolean(Apg.EXTRA_SIGNATURE_SUCCESS)) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
} else if (data.getBoolean("signatureUnknown")) { } else if (data.getBoolean(Apg.EXTRA_SIGNATURE_UNKNOWN)) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error); mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
} else { } else {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error); mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
} }
mSignatureLayout.setVisibility(View.VISIBLE); mSignatureLayout.setVisibility(View.VISIBLE);
} }
if (mReturnResult) {
Intent intent = new Intent();
intent.putExtras(data);
setResult(RESULT_OK, intent);
finish();
}
} }
@Override @Override

View File

@ -16,6 +16,7 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.IOException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SignatureException; import java.security.SignatureException;
@ -24,6 +25,7 @@ import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException; import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPSecretKey; import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing; import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.provider.Database;
import org.thialfihar.android.apg.ui.widget.KeyEditor; import org.thialfihar.android.apg.ui.widget.KeyEditor;
import org.thialfihar.android.apg.ui.widget.SectionView; import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.utils.IterableIterator; import org.thialfihar.android.apg.utils.IterableIterator;
@ -69,7 +71,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
Intent intent = getIntent(); Intent intent = getIntent();
long keyId = 0; long keyId = 0;
if (intent.getExtras() != null) { if (intent.getExtras() != null) {
keyId = intent.getExtras().getLong("keyId"); keyId = intent.getExtras().getLong(Apg.EXTRA_KEY_ID);
} }
if (keyId != 0) { if (keyId != 0) {
@ -115,7 +117,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
Toast.makeText(this, "Warning: Key editing is still kind of beta.", Toast.LENGTH_LONG).show(); Toast.makeText(this, "Warning: Key editing is still kind of beta.", Toast.LENGTH_LONG).show();
} }
public long getMasterKeyId() { private long getMasterKeyId() {
if (mKeys.getEditors().getChildCount() == 0) { if (mKeys.getEditors().getChildCount() == 0) {
return 0; return 0;
} }
@ -243,22 +245,27 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
newPassPhrase = oldPassPhrase; newPassPhrase = oldPassPhrase;
} }
Apg.buildSecretKey(this, mUserIds, mKeys, oldPassPhrase, newPassPhrase, this); Apg.buildSecretKey(this, mUserIds, mKeys, oldPassPhrase, newPassPhrase, this);
Apg.setCachedPassPhrase(getMasterKeyId(), newPassPhrase);
} catch (NoSuchProviderException e) { } catch (NoSuchProviderException e) {
error = e.getMessage(); error = "" + e;
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
error = e.getMessage(); error = "" + e;
} catch (PGPException e) { } catch (PGPException e) {
error = e.getMessage(); error = "" + e;
} catch (SignatureException e) { } catch (SignatureException e) {
error = e.getMessage(); error = "" + e;
} catch (Apg.GeneralException e) { } catch (Apg.GeneralException e) {
error = e.getMessage(); error = "" + e;
} catch (Database.GeneralException e) {
error = "" + e;
} catch (IOException e) {
error = "" + e;
} }
data.putInt("type", Id.message.done); data.putInt(Apg.EXTRA_STATUS, Id.message.done);
if (error != null) { if (error != null) {
data.putString("error", error); data.putString(Apg.EXTRA_ERROR, error);
} }
msg.setData(data); msg.setData(data);
@ -272,11 +279,10 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
Bundle data = msg.getData(); Bundle data = msg.getData();
removeDialog(Id.dialog.saving); removeDialog(Id.dialog.saving);
String error = data.getString("error"); String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) { if (error != null) {
Toast.makeText(EditKeyActivity.this, Toast.makeText(EditKeyActivity.this,
getString(R.string.errorMessage, data.getString("error")), getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
Toast.LENGTH_SHORT).show();
} else { } else {
Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show(); Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show();
setResult(RESULT_OK); setResult(RESULT_OK);

View File

@ -35,7 +35,6 @@ import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.bouncycastle2.openpgp.PGPSecretKey; import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing; import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.bouncycastle2.util.Strings; import org.bouncycastle2.util.Strings;
import org.openintents.intents.FileManager;
import org.thialfihar.android.apg.Apg.GeneralException; import org.thialfihar.android.apg.Apg.GeneralException;
import org.thialfihar.android.apg.utils.Choice; import org.thialfihar.android.apg.utils.Choice;
@ -62,11 +61,13 @@ import android.widget.Toast;
import android.widget.ViewFlipper; import android.widget.ViewFlipper;
public class EncryptActivity extends BaseActivity { public class EncryptActivity extends BaseActivity {
private Intent mIntent = null;
private String mSubject = null; private String mSubject = null;
private String mSendTo = null; private String mSendTo = null;
private long mEncryptionKeyIds[] = null; private long mEncryptionKeyIds[] = null;
private boolean mReturnResult = false;
private EditText mMessage = null; private EditText mMessage = null;
private Button mSelectKeysButton = null; private Button mSelectKeysButton = null;
private Button mEncryptButton = null; private Button mEncryptButton = null;
@ -265,21 +266,26 @@ public class EncryptActivity extends BaseActivity {
} }
}); });
Intent intent = getIntent(); mIntent = getIntent();
if (intent.getAction() != null && if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) ||
(intent.getAction().equals(Apg.Intent.ENCRYPT) || Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) ||
intent.getAction().equals(Apg.Intent.ENCRYPT_FILE))) { Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
Bundle extras = intent.getExtras(); Bundle extras = mIntent.getExtras();
if (extras == null) { if (extras == null) {
extras = new Bundle(); extras = new Bundle();
} }
String data = extras.getString("data");
mSendTo = extras.getString("sendTo"); if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
mSubject = extras.getString("subject"); mReturnResult = true;
long signatureKeyId = extras.getLong("signatureKeyId"); }
long encryptionKeyIds[] = extras.getLongArray("encryptionKeyIds");
String data = extras.getString(Apg.EXTRA_DATA);
mSendTo = extras.getString(Apg.EXTRA_SEND_TO);
mSubject = extras.getString(Apg.EXTRA_SUBJECT);
long signatureKeyId = extras.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
long encryptionKeyIds[] = extras.getLongArray(Apg.EXTRA_ENCRYPTION_KEY_IDS);
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId); PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(signatureKeyId);
PGPSecretKey masterKey = null; PGPSecretKey masterKey = null;
if (keyRing != null) { if (keyRing != null) {
masterKey = Apg.getMasterKey(keyRing); masterKey = Apg.getMasterKey(keyRing);
@ -295,7 +301,7 @@ public class EncryptActivity extends BaseActivity {
if (encryptionKeyIds != null) { if (encryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>(); Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < encryptionKeyIds.length; ++i) { for (int i = 0; i < encryptionKeyIds.length; ++i) {
PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]); PGPPublicKeyRing keyRing = Apg.getPublicKeyRing(encryptionKeyIds[i]);
PGPPublicKey masterKey = null; PGPPublicKey masterKey = null;
if (keyRing == null) { if (keyRing == null) {
continue; continue;
@ -318,7 +324,8 @@ public class EncryptActivity extends BaseActivity {
} }
} }
if (intent.getAction().equals(Apg.Intent.ENCRYPT)) { if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) ||
Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
if (data != null) { if (data != null) {
mMessage.setText(data); mMessage.setText(data);
} }
@ -327,7 +334,7 @@ public class EncryptActivity extends BaseActivity {
while (mSource.getCurrentView().getId() != R.id.sourceMessage) { while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
mSource.showNext(); mSource.showNext();
} }
} else if (intent.getAction().equals(Apg.Intent.ENCRYPT_FILE)) { } else if (Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())) {
mSource.setInAnimation(null); mSource.setInAnimation(null);
mSource.setOutAnimation(null); mSource.setOutAnimation(null);
while (mSource.getCurrentView().getId() != R.id.sourceFile) { while (mSource.getCurrentView().getId() != R.id.sourceFile) {
@ -339,23 +346,47 @@ public class EncryptActivity extends BaseActivity {
updateView(); updateView();
updateSource(); updateSource();
updateMode(); updateMode();
if (mReturnResult) {
mSourcePrevious.setClickable(false);
mSourcePrevious.setEnabled(false);
mSourcePrevious.setVisibility(View.INVISIBLE);
mSourceNext.setClickable(false);
mSourceNext.setEnabled(false);
mSourceNext.setVisibility(View.INVISIBLE);
mSourceLabel.setClickable(false);
mSourceLabel.setEnabled(false);
mEncryptToClipboardButton.setEnabled(false);
mEncryptToClipboardButton.setVisibility(View.INVISIBLE);
mEncryptButton.setText(R.string.btn_encrypt);
}
if (mReturnResult &&
mMessage.getText().length() > 0 &&
((mEncryptionKeyIds != null &&
mEncryptionKeyIds.length > 0) ||
getSecretKeyId() != 0)) {
encryptClicked();
}
} }
private void openFile() { private void openFile() {
String filename = mFilename.getText().toString(); String filename = mFilename.getText().toString();
Intent intent = new Intent(FileManager.ACTION_PICK_FILE); Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setData(Uri.parse("file://" + filename)); intent.setData(Uri.parse("file://" + filename));
intent.setType("*/*");
intent.putExtra(FileManager.EXTRA_TITLE, R.string.filemanager_titleEncrypt);
intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen);
try { try {
startActivityForResult(intent, Id.request.filename); startActivityForResult(intent, Id.request.filename);
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
// No compatible file manager was found. // No compatible file manager was found.
Toast.makeText(this, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
} }
} }
@ -457,7 +488,7 @@ public class EncryptActivity extends BaseActivity {
return; return;
} }
} else { } else {
boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0; boolean encryptIt = (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0);
// for now require at least one form of encryption for files // for now require at least one form of encryption for files
if (!encryptIt && mEncryptTarget == Id.target.file) { if (!encryptIt && mEncryptTarget == Id.target.file) {
Toast.makeText(this, R.string.selectEncryptionKey, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.selectEncryptionKey, Toast.LENGTH_SHORT).show();
@ -527,7 +558,7 @@ public class EncryptActivity extends BaseActivity {
} else { } else {
encryptionKeyIds = mEncryptionKeyIds; encryptionKeyIds = mEncryptionKeyIds;
signatureKeyId = getSecretKeyId(); signatureKeyId = getSecretKeyId();
signOnly = mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0; signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0);
} }
if (mEncryptTarget == Id.target.file) { if (mEncryptTarget == Id.target.file) {
@ -548,7 +579,7 @@ public class EncryptActivity extends BaseActivity {
} else { } else {
String message = mMessage.getText().toString(); String message = mMessage.getText().toString();
if (signOnly) { if (signOnly && mReturnResult) {
// fix the message a bit, trailing spaces and newlines break stuff, // fix the message a bit, trailing spaces and newlines break stuff,
// because GMail sends as HTML and such things fuck up the signature, // because GMail sends as HTML and such things fuck up the signature,
// TODO: things like "<" and ">" also fuck up the signature // TODO: things like "<" and ">" also fuck up the signature
@ -582,26 +613,27 @@ public class EncryptActivity extends BaseActivity {
out.close(); out.close();
if (mEncryptTarget != Id.target.file) { if (mEncryptTarget != Id.target.file) {
data.putString("message", new String(((ByteArrayOutputStream)out).toByteArray())); data.putByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE,
((ByteArrayOutputStream)out).toByteArray());
} }
} catch (IOException e) { } catch (IOException e) {
error = e.getMessage(); error = "" + e;
} catch (PGPException e) { } catch (PGPException e) {
error = e.getMessage(); error = "" + e;
} catch (NoSuchProviderException e) { } catch (NoSuchProviderException e) {
error = e.getMessage(); error = "" + e;
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
error = e.getMessage(); error = "" + e;
} catch (SignatureException e) { } catch (SignatureException e) {
error = e.getMessage(); error = "" + e;
} catch (Apg.GeneralException e) { } catch (Apg.GeneralException e) {
error = e.getMessage(); error = "" + e;
} }
data.putInt("type", Id.message.done); data.putInt(Apg.EXTRA_STATUS, Id.message.done);
if (error != null) { if (error != null) {
data.putString("error", error); data.putString(Apg.EXTRA_ERROR, error);
} }
msg.setData(data); msg.setData(data);
@ -645,7 +677,7 @@ public class EncryptActivity extends BaseActivity {
private void selectPublicKeys() { private void selectPublicKeys() {
Intent intent = new Intent(this, SelectPublicKeyListActivity.class); Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
intent.putExtra("selection", mEncryptionKeyIds); intent.putExtra(Apg.EXTRA_SELECTION, mEncryptionKeyIds);
startActivityForResult(intent, Id.request.public_keys); startActivityForResult(intent, Id.request.public_keys);
} }
@ -702,7 +734,7 @@ public class EncryptActivity extends BaseActivity {
case Id.request.public_keys: { case Id.request.public_keys: {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras(); Bundle bundle = data.getExtras();
mEncryptionKeyIds = bundle.getLongArray("selection"); mEncryptionKeyIds = bundle.getLongArray(Apg.EXTRA_SELECTION);
} }
updateView(); updateView();
break; break;
@ -723,16 +755,15 @@ public class EncryptActivity extends BaseActivity {
removeDialog(Id.dialog.encrypting); removeDialog(Id.dialog.encrypting);
Bundle data = msg.getData(); Bundle data = msg.getData();
String error = data.getString("error"); String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) { if (error != null) {
Toast.makeText(EncryptActivity.this, Toast.makeText(EncryptActivity.this,
getString(R.string.errorMessage, data.getString("error")), getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
Toast.LENGTH_SHORT).show();
return; return;
} else { }
String message = data.getString("message");
switch (mEncryptTarget) { switch (mEncryptTarget) {
case Id.target.clipboard: { case Id.target.clipboard: {
String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE));
ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clip.setText(message); clip.setText(message);
Toast.makeText(this, R.string.encryptionToClipboardSuccessful, Toast.makeText(this, R.string.encryptionToClipboardSuccessful,
@ -741,6 +772,15 @@ public class EncryptActivity extends BaseActivity {
} }
case Id.target.email: { case Id.target.email: {
if (mReturnResult) {
Intent intent = new Intent();
intent.putExtras(data);
setResult(RESULT_OK, intent);
finish();
return;
}
String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE));
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("text/plain; charset=utf-8"); emailIntent.setType("text/plain; charset=utf-8");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message); emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
@ -755,6 +795,7 @@ public class EncryptActivity extends BaseActivity {
EncryptActivity.this. EncryptActivity.this.
startActivity(Intent.createChooser(emailIntent, startActivity(Intent.createChooser(emailIntent,
getString(R.string.title_sendEmail))); getString(R.string.title_sendEmail)));
break;
} }
case Id.target.file: { case Id.target.file: {
@ -772,7 +813,6 @@ public class EncryptActivity extends BaseActivity {
} }
} }
} }
}
@Override @Override
protected Dialog onCreateDialog(int id) { protected Dialog onCreateDialog(int id) {

View File

@ -16,8 +16,6 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import org.openintents.intents.FileManager;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
@ -48,6 +46,8 @@ public class FileDialog {
String defaultFile, OnClickListener onClickListener, String defaultFile, OnClickListener onClickListener,
String fileManagerTitle, String fileManagerButton, String fileManagerTitle, String fileManagerButton,
int requestCode) { 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 inflater =
(LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
AlertDialog.Builder alert = new AlertDialog.Builder(activity); AlertDialog.Builder alert = new AlertDialog.Builder(activity);
@ -102,18 +102,17 @@ public class FileDialog {
private static void openFile() { private static void openFile() {
String filename = mFilename.getText().toString(); String filename = mFilename.getText().toString();
Intent intent = new Intent(FileManager.ACTION_PICK_FILE); Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setData(Uri.parse("file://" + filename)); intent.setData(Uri.parse("file://" + filename));
intent.setType("*/*");
intent.putExtra(FileManager.EXTRA_TITLE, mFileManagerTitle);
intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, mFileManagerButton);
try { try {
mActivity.startActivityForResult(intent, mRequestCode); mActivity.startActivityForResult(intent, mRequestCode);
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
// No compatible file manager was found. // No compatible file manager was found.
Toast.makeText(mActivity, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show(); Toast.makeText(mActivity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
} }
} }
} }

View File

@ -80,6 +80,11 @@ public final class Id {
public static final int export_keys = 0x21070002; 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 class type {
public static final int public_key = 0x21070001; public static final int public_key = 0x21070001;
public static final int secret_key = 0x21070002; public static final int secret_key = 0x21070002;

View File

@ -0,0 +1,642 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
public class KeyListActivity extends BaseActivity {
protected ExpandableListView mList;
protected KeyListAdapter mListAdapter;
protected int mSelectedItem = -1;
protected int mTask = 0;
protected String mImportFilename = Constants.path.app_dir + "/";
protected String mExportFilename = Constants.path.app_dir + "/";
protected int mKeyType = Id.type.public_key;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.key_list);
mList = (ExpandableListView) findViewById(R.id.list);
mListAdapter = new KeyListAdapter(this);
mList.setAdapter(mListAdapter);
registerForContextMenu(mList);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Id.menu.option.import_keys: {
showDialog(Id.dialog.import_keys);
return true;
}
case Id.menu.option.export_keys: {
showDialog(Id.dialog.export_keys);
return true;
}
default: {
return super.onOptionsItemSelected(item);
}
}
}
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
return super.onContextItemSelected(menuItem);
}
switch (menuItem.getItemId()) {
case Id.menu.export: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.export_key);
return true;
}
case Id.menu.delete: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.delete_key);
return true;
}
default: {
return super.onContextItemSelected(menuItem);
}
}
}
@Override
protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) {
case Id.dialog.delete_key: {
final int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
mSelectedItem = -1;
// TODO: better way to do this?
String userId = "<unknown>";
Object keyRing = Apg.getKeyRing(keyRingId);
if (keyRing != null) {
if (keyRing instanceof PGPPublicKeyRing) {
userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey((PGPPublicKeyRing) keyRing));
} else {
userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey((PGPSecretKeyRing) keyRing));
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.warning);
builder.setMessage(getString(mKeyType == Id.type.public_key ?
R.string.keyDeletionConfirmation :
R.string.secretKeyDeletionConfirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(R.string.btn_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(keyRingId);
removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
case Id.dialog.import_keys: {
return FileDialog.build(this, getString(R.string.title_importKeys),
getString(R.string.specifyFileToImportFrom),
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.import_keys);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.import_keys);
}
},
getString(R.string.filemanager_titleOpen),
getString(R.string.filemanager_btnOpen),
Id.request.filename);
}
case Id.dialog.export_key: {
singleKeyExport = true;
// break intentionally omitted, to use the Id.dialog.export_keys dialog
}
case Id.dialog.export_keys: {
String title = (singleKeyExport ?
getString(R.string.title_exportKey) :
getString(R.string.title_exportKeys));
final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
return FileDialog.build(this, title,
getString(mKeyType == Id.type.public_key ?
R.string.specifyFileToExportTo :
R.string.specifyFileToExportSecretKeysTo),
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_titleSave),
getString(R.string.filemanager_btnSave),
Id.request.filename);
}
default: {
return super.onCreateDialog(id);
}
}
}
public void importKeys() {
showDialog(Id.dialog.importing);
mTask = Id.task.import_keys;
startThread();
}
public void exportKeys() {
showDialog(Id.dialog.exporting);
mTask = Id.task.export_keys;
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == Id.task.import_keys) {
data = Apg.importKeyRings(this, mKeyType, filename, this);
} else {
Vector<Integer> keyRingIds = new Vector<Integer>();
if (mSelectedItem == -1) {
keyRingIds = Apg.getKeyRingIds(mKeyType == Id.type.public_key ?
Id.database.type_public :
Id.database.type_secret);
} else {
int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
keyRingIds.add(keyRingId);
mSelectedItem = -1;
}
data = Apg.exportKeyRings(this, keyRingIds, filename, this);
}
} catch (FileNotFoundException e) {
error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
error = "" + e;
} catch (PGPException e) {
error = "" + e;
} catch (Apg.GeneralException e) {
error = "" + e;
}
if (mTask == Id.task.import_keys) {
data.putInt(Apg.EXTRA_STATUS, Id.message.import_done);
} else {
data.putInt(Apg.EXTRA_STATUS, Id.message.export_done);
}
if (error != null) {
data.putString(Apg.EXTRA_ERROR, error);
}
msg.setData(data);
sendMessage(msg);
}
protected void deleteKey(int keyRingId) {
Apg.deleteKey(keyRingId);
refreshList();
}
protected void refreshList() {
mListAdapter.rebuild(true);
mListAdapter.notifyDataSetChanged();
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt(Apg.EXTRA_STATUS);
switch (type) {
case Id.message.import_done: {
removeDialog(Id.dialog.importing);
String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) {
Toast.makeText(KeyListActivity.this,
getString(R.string.errorMessage, error),
Toast.LENGTH_SHORT).show();
} else {
int added = data.getInt("added");
int updated = data.getInt("updated");
String message;
if (added > 0 && updated > 0) {
message = getString(R.string.keysAddedAndUpdated, added, updated);
} else if (added > 0) {
message = getString(R.string.keysAdded, added);
} else if (updated > 0) {
message = getString(R.string.keysUpdated, updated);
} else {
message = getString(R.string.noKeysAddedOrUpdated);
}
Toast.makeText(KeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
refreshList();
break;
}
case Id.message.export_done: {
removeDialog(Id.dialog.exporting);
String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) {
Toast.makeText(KeyListActivity.this,
getString(R.string.errorMessage, error),
Toast.LENGTH_SHORT).show();
} else {
int exported = data.getInt("exported");
String message;
if (exported == 1) {
message = getString(R.string.keyExported);
} else if (exported > 0) {
message = getString(R.string.keysExported, exported);
} else{
message = getString(R.string.noKeysExported);
}
Toast.makeText(KeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
}
protected class KeyListAdapter extends BaseExpandableListAdapter {
private LayoutInflater mInflater;
private Vector<Vector<KeyChild>> mChildren;
private SQLiteDatabase mDatabase;
private Cursor mCursor;
private class KeyChild {
public static final int KEY = 0;
public static final int USER_ID = 1;
public int type;
public String userId;
public long keyId;
public boolean isMasterKey;
public int algorithm;
public int keySize;
public boolean canSign;
public boolean canEncrypt;
public KeyChild(long keyId, boolean isMasterKey, int algorithm, int keySize,
boolean canSign, boolean canEncrypt) {
this.keyId = keyId;
this.isMasterKey = isMasterKey;
this.algorithm = algorithm;
this.keySize = keySize;
this.canSign = canSign;
this.canEncrypt = canEncrypt;
}
public KeyChild(String userId) {
type = USER_ID;
this.userId = userId;
}
}
public KeyListAdapter(Context context) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mDatabase = Apg.getDatabase().db();
mCursor = mDatabase.query(
KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
") " +
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
new String[] {
KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
},
KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
new String[] { "" + (mKeyType == Id.type.public_key ?
Id.database.type_public : Id.database.type_secret) },
null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
startManagingCursor(mCursor);
rebuild(false);
}
public void rebuild(boolean requery) {
if (requery) {
mCursor.requery();
}
mChildren = new Vector<Vector<KeyChild>>();
for (int i = 0; i < mCursor.getCount(); ++i) {
mChildren.add(null);
}
}
protected Vector<KeyChild> getChildrenOfGroup(int groupPosition) {
Vector<KeyChild> children = mChildren.get(groupPosition);
if (children != null) {
return children;
}
mCursor.moveToPosition(groupPosition);
children = new Vector<KeyChild>();
Cursor c = mDatabase.query(Keys.TABLE_NAME,
new String[] {
Keys._ID, // 0
Keys.KEY_ID, // 1
Keys.IS_MASTER_KEY, // 2
Keys.ALGORITHM, // 3
Keys.KEY_SIZE, // 4
Keys.CAN_SIGN, // 5
Keys.CAN_ENCRYPT, // 6
},
Keys.KEY_RING_ID + " = ?",
new String[] { mCursor.getString(0) },
null, null, Keys.RANK + " ASC");
long masterKeyId = -1;
for (int i = 0; i < c.getCount(); ++i) {
c.moveToPosition(i);
children.add(new KeyChild(c.getLong(1), c.getInt(2) == 1, c.getInt(3), c.getInt(4),
c.getInt(5) == 1, c.getInt(6) == 1));
if (i == 0) {
masterKeyId = c.getInt(0);
}
}
c.close();
if (masterKeyId != -1) {
c = mDatabase.query(UserIds.TABLE_NAME,
new String[] {
UserIds.USER_ID, // 0
},
UserIds.KEY_ID + " = ? AND " + UserIds.RANK + " > 0",
new String[] { "" + masterKeyId },
null, null, UserIds.RANK + " ASC");
for (int i = 0; i < c.getCount(); ++i) {
c.moveToPosition(i);
children.add(new KeyChild(c.getString(0)));
}
c.close();
}
mChildren.set(groupPosition, children);
return children;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public int getGroupCount() {
return mCursor.getCount();
}
public Object getChild(int groupPosition, int childPosition) {
return null;
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public int getChildrenCount(int groupPosition) {
return getChildrenOfGroup(groupPosition).size();
}
public Object getGroup(int position) {
return position;
}
public long getGroupId(int position) {
mCursor.moveToPosition(position);
return mCursor.getLong(1); // MASTER_KEY_ID
}
public int getKeyRingId(int position) {
mCursor.moveToPosition(position);
return mCursor.getInt(0); // _ID
}
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
mCursor.moveToPosition(groupPosition);
View view = mInflater.inflate(R.layout.key_list_group_item, null);
view.setBackgroundResource(android.R.drawable.list_selector_background);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText("");
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
String userId = mCursor.getString(2); // USER_ID
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
if (mainUserId.getText().length() == 0) {
mainUserId.setText(R.string.unknownUserId);
}
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
return view;
}
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView,
ViewGroup parent) {
mCursor.moveToPosition(groupPosition);
Vector<KeyChild> children = getChildrenOfGroup(groupPosition);
KeyChild child = children.get(childPosition);
View view = null;
switch (child.type) {
case KeyChild.KEY: {
if (child.isMasterKey) {
view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
} else {
view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
}
TextView keyId = (TextView) view.findViewById(R.id.keyId);
String keyIdStr = Long.toHexString(child.keyId & 0xffffffffL);
while (keyIdStr.length() < 8) {
keyIdStr = "0" + keyIdStr;
}
keyId.setText(keyIdStr);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);
keyDetails.setText("(" + algorithmStr + ")");
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
if (!child.canEncrypt) {
encryptIcon.setVisibility(View.GONE);
}
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
if (!child.canSign) {
signIcon.setVisibility(View.GONE);
}
break;
}
case KeyChild.USER_ID: {
view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
TextView userId = (TextView) view.findViewById(R.id.userId);
userId.setText(child.userId);
break;
}
}
return view;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {
// Get rid of URI prefix:
if (filename.startsWith("file://")) {
filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
FileDialog.setFilename(filename);
}
}
return;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}

View File

@ -87,7 +87,7 @@ public class MailListActivity extends ListActivity {
mconversations = new Vector<Conversation>(); mconversations = new Vector<Conversation>();
mmessages = new Vector<Message>(); mmessages = new Vector<Message>();
String account = getIntent().getExtras().getString("account"); String account = getIntent().getExtras().getString(Apg.EXTRA_ACCOUNT);
// TODO: what if account is null? // TODO: what if account is null?
Uri uri = Uri.parse("content://gmail-ls/conversations/" + account); Uri uri = Uri.parse("content://gmail-ls/conversations/" + account);
Cursor cursor = Cursor cursor =
@ -153,9 +153,9 @@ public class MailListActivity extends ListActivity {
Intent intent = new Intent(MailListActivity.this, DecryptActivity.class); Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
intent.setAction(Apg.Intent.DECRYPT); intent.setAction(Apg.Intent.DECRYPT);
Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position); Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
intent.putExtra("data", message.data); intent.putExtra(Apg.EXTRA_DATA, message.data);
intent.putExtra("subject", message.subject); intent.putExtra(Apg.EXTRA_SUBJECT, message.subject);
intent.putExtra("replyTo", message.replyTo); intent.putExtra(Apg.EXTRA_REPLY_TO, message.replyTo);
startActivity(intent); startActivity(intent);
} }
}); });

View File

@ -47,6 +47,8 @@ import android.widget.AdapterView.OnItemClickListener;
public class MainActivity extends BaseActivity { public class MainActivity extends BaseActivity {
private ListView mAccounts = null; private ListView mAccounts = null;
private AccountListAdapter mListAdapter = null;
private Cursor mAccountCursor;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -95,22 +97,22 @@ public class MainActivity extends BaseActivity {
} }
}); });
Cursor accountCursor = managedQuery(Accounts.CONTENT_URI, null, null, null, null); mAccountCursor =
Apg.getDatabase().db().query(Accounts.TABLE_NAME,
new String[] {
Accounts._ID,
Accounts.NAME,
}, null, null, null, null, Accounts.NAME + " ASC");
startManagingCursor(mAccountCursor);
mAccounts.setAdapter(new AccountListAdapter(this, accountCursor)); mListAdapter = new AccountListAdapter(this, mAccountCursor);
mAccounts.setAdapter(mListAdapter);
mAccounts.setOnItemClickListener(new OnItemClickListener() { mAccounts.setOnItemClickListener(new OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> arg0, View view, int index, long id) { public void onItemClick(AdapterView<?> arg0, View view, int index, long id) {
Cursor cursor = String accountName = (String) mAccounts.getItemAtPosition(index);
managedQuery(Uri.withAppendedPath(Accounts.CONTENT_URI, "" + id), null,
null, null, null);
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
int nameIndex = cursor.getColumnIndex(Accounts.NAME);
String accountName = cursor.getString(nameIndex);
startActivity(new Intent(MainActivity.this, MailListActivity.class) startActivity(new Intent(MainActivity.this, MailListActivity.class)
.putExtra("account", accountName)); .putExtra(Apg.EXTRA_ACCOUNT, accountName));
}
} }
}); });
registerForContextMenu(mAccounts); registerForContextMenu(mAccounts);
@ -154,9 +156,10 @@ public class MainActivity extends BaseActivity {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(Accounts.NAME, accountName); values.put(Accounts.NAME, accountName);
try { try {
MainActivity.this.getContentResolver() Apg.getDatabase().db().insert(Accounts.TABLE_NAME,
.insert(Accounts.CONTENT_URI, Accounts.NAME, values);
values); mAccountCursor.requery();
mListAdapter.notifyDataSetChanged();
} catch (SQLException e) { } catch (SQLException e) {
Toast.makeText(MainActivity.this, Toast.makeText(MainActivity.this,
getString(R.string.errorMessage, getString(R.string.errorMessage,
@ -188,6 +191,12 @@ public class MainActivity extends BaseActivity {
message.setText("Read the warnings!\n\n" + message.setText("Read the warnings!\n\n" +
"Changes:\n" + "Changes:\n" +
"* k9mail integration, k9mail beta build is available on the k9mail website\n" +
"* support of other file managers (e.g. ASTRO)\n" +
"* Slovenian translation (thanks, 359)\n" +
"* new database, much faster, less memory usage\n" +
"* defined Intents and content provider for other apps\n" +
"* bugfixes\n" +
"\n" + "\n" +
"WARNING: be careful editing your existing keys, as they " + "WARNING: be careful editing your existing keys, as they " +
"WILL be stripped of certificates right now.\n" + "WILL be stripped of certificates right now.\n" +
@ -277,8 +286,11 @@ public class MainActivity extends BaseActivity {
switch (menuItem.getItemId()) { switch (menuItem.getItemId()) {
case Id.menu.delete: { case Id.menu.delete: {
Uri uri = Uri.withAppendedPath(Accounts.CONTENT_URI, "" + info.id); Apg.getDatabase().db().delete(Accounts.TABLE_NAME,
this.getContentResolver().delete(uri, null, null); Accounts._ID + " = ?",
new String[] { "" + info.id });
mAccountCursor.requery();
mListAdapter.notifyDataSetChanged();
return true; return true;
} }
@ -297,6 +309,13 @@ public class MainActivity extends BaseActivity {
minflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); minflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} }
@Override
public Object getItem(int position) {
Cursor c = getCursor();
c.moveToPosition(position);
return c.getString(c.getColumnIndex(Accounts.NAME));
}
@Override @Override
public int getCount() { public int getCount() {
return super.getCount(); return super.getCount();

View File

@ -50,7 +50,6 @@ public class PreferencesActivity extends BaseActivity {
new Choice(180, getString(R.string.choice_3mins)), new Choice(180, getString(R.string.choice_3mins)),
new Choice(300, getString(R.string.choice_5mins)), new Choice(300, getString(R.string.choice_5mins)),
new Choice(600, getString(R.string.choice_10mins)), new Choice(600, getString(R.string.choice_10mins)),
new Choice(0, getString(R.string.choice_untilQuit)),
}; };
ArrayAdapter<Choice> adapter = ArrayAdapter<Choice> adapter =
new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices); new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);

View File

@ -16,54 +16,19 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
public class PublicKeyListActivity extends BaseActivity {
ExpandableListView mList;
protected int mSelectedItem = -1;
protected int mTask = 0;
private String mImportFilename = Constants.path.app_dir + "/pubring.gpg";
private String mExportFilename = Constants.path.app_dir + "/pubexport.asc";
public class PublicKeyListActivity extends KeyListActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
mExportFilename = Constants.path.app_dir + "/pubexport.asc";
mKeyType = Id.type.public_key;
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.key_list);
mList = (ExpandableListView) findViewById(R.id.list);
mList.setAdapter(new PublicKeyListAdapter(this));
registerForContextMenu(mList);
} }
@Override @Override
@ -79,507 +44,17 @@ public class PublicKeyListActivity extends BaseActivity {
return true; return true;
} }
@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 @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo); super.onCreateContextMenu(menu, v, menuInfo);
ExpandableListView.ExpandableListContextMenuInfo info = ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo; (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition); int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) { if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition); // TODO: user id? menu.setHeaderTitle("Key");
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
menu.add(0, Id.menu.export, 0, R.string.menu_exportKey); menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey); menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey);
} }
} }
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
return super.onContextItemSelected(menuItem);
}
switch (menuItem.getItemId()) {
case Id.menu.export: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.export_key);
return true;
}
case Id.menu.delete: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.delete_key);
return true;
}
default: {
return super.onContextItemSelected(menuItem);
}
}
}
@Override
protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) {
case Id.dialog.delete_key: {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(mSelectedItem);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.warning);
builder.setMessage(getString(R.string.keyDeletionConfirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(R.string.btn_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
case Id.dialog.import_keys: {
return FileDialog.build(this, getString(R.string.title_importKeys),
getString(R.string.specifyFileToImportFrom),
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.import_keys);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.import_keys);
}
},
getString(R.string.filemanager_titleOpen),
getString(R.string.filemanager_btnOpen),
Id.request.filename);
}
case Id.dialog.export_key: {
singleKeyExport = true;
// break intentionally omitted, to use the Id.dialog.export_keys dialog
}
case Id.dialog.export_keys: {
String title = (singleKeyExport ?
getString(R.string.title_exportKey) :
getString(R.string.title_exportKeys));
final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
return FileDialog.build(this, title,
getString(R.string.specifyFileToExportTo),
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_titleSave),
getString(R.string.filemanager_btnSave),
Id.request.filename);
}
default: {
return super.onCreateDialog(id);
}
}
}
public void importKeys() {
showDialog(Id.dialog.importing);
mTask = Id.task.import_keys;
startThread();
}
public void exportKeys() {
showDialog(Id.dialog.exporting);
mTask = Id.task.export_keys;
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == Id.task.import_keys) {
data = Apg.importKeyRings(this, Id.type.public_key, filename, this);
} else {
Vector<Object> keys = new Vector<Object>();
if (mSelectedItem == -1) {
for (PGPPublicKeyRing key : Apg.getPublicKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getPublicKeyRings().get(mSelectedItem));
}
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
error = e.getMessage();
} catch (PGPException e) {
error = e.getMessage();
} catch (Apg.GeneralException e) {
error = e.getMessage();
}
if (mTask == Id.task.import_keys) {
data.putInt("type", Id.message.import_done);
} else {
data.putInt("type", Id.message.export_done);
}
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
sendMessage(msg);
}
private void deleteKey(int index) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(index);
Apg.deleteKey(this, keyRing);
refreshList();
}
private void refreshList() {
((PublicKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case Id.message.import_done: {
removeDialog(Id.dialog.importing);
String error = data.getString("error");
if (error != null) {
Toast.makeText(PublicKeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int added = data.getInt("added");
int updated = data.getInt("updated");
String message;
if (added > 0 && updated > 0) {
message = getString(R.string.keysAddedAndUpdated, added, updated);
} else if (added > 0) {
message = getString(R.string.keysAdded, added);
} else if (updated > 0) {
message = getString(R.string.keysUpdated, updated);
} else {
message = getString(R.string.noKeysAddedOrUpdated);
}
Toast.makeText(PublicKeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
refreshList();
break;
}
case Id.message.export_done: {
removeDialog(Id.dialog.exporting);
String error = data.getString("error");
if (error != null) {
Toast.makeText(PublicKeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int exported = data.getInt("exported");
String message;
if (exported == 1) {
message = getString(R.string.keyExported);
} else if (exported > 0) {
message = getString(R.string.keysExported);
} else{
message = getString(R.string.noKeysExported);
}
Toast.makeText(PublicKeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
}
private static class PublicKeyListAdapter extends BaseExpandableListAdapter {
private LayoutInflater mInflater;
private class KeyChild {
public static final int KEY = 0;
public static final int USER_ID = 1;
public int type;
public PGPPublicKey key;
public String userId;
public KeyChild(PGPPublicKey key) {
type = KEY;
this.key = key;
}
public KeyChild(String userId) {
type = USER_ID;
this.userId = userId;
}
}
public PublicKeyListAdapter(Context context) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
protected Vector<KeyChild> getChildrenOfKeyRing(PGPPublicKeyRing keyRing) {
Vector<KeyChild> children = new Vector<KeyChild>();
PGPPublicKey masterKey = null;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
children.add(new KeyChild(key));
if (key.isMasterKey()) {
masterKey = key;
}
}
if (masterKey != null) {
boolean isFirst = true;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
if (isFirst) {
// ignore first, it's in the group already
isFirst = false;
continue;
}
children.add(new KeyChild(userId));
}
}
return children;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public int getGroupCount() {
return Apg.getPublicKeyRings().size();
}
public Object getChild(int groupPosition, int childPosition) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
KeyChild child = children.get(childPosition);
return child;
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public int getChildrenCount(int groupPosition) {
return getChildrenOfKeyRing(Apg.getPublicKeyRings().get(groupPosition)).size();
}
public Object getGroup(int position) {
return position;
}
public long getGroupId(int position) {
return position;
}
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
View view;
if (!key.isMasterKey()) {
continue;
}
view = mInflater.inflate(R.layout.key_list_group_item, null);
view.setBackgroundResource(android.R.drawable.list_selector_background);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText("");
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
String userId = Apg.getMainUserId(key);
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
if (mainUserId.getText().length() == 0) {
mainUserId.setText(R.string.unknownUserId);
}
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
return view;
}
return null;
}
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView,
ViewGroup parent) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
KeyChild child = children.get(childPosition);
View view = null;
switch (child.type) {
case KeyChild.KEY: {
PGPPublicKey key = child.key;
if (key.isMasterKey()) {
view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
} else {
view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
}
TextView keyId = (TextView) view.findViewById(R.id.keyId);
String keyIdStr = Long.toHexString(key.getKeyID() & 0xffffffffL);
while (keyIdStr.length() < 8) {
keyIdStr = "0" + keyIdStr;
}
keyId.setText(keyIdStr);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(key);
keyDetails.setText("(" + algorithmStr + ")");
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
if (!Apg.isEncryptionKey(key)) {
encryptIcon.setVisibility(View.GONE);
}
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
if (!Apg.isSigningKey(key)) {
signIcon.setVisibility(View.GONE);
}
break;
}
case KeyChild.USER_ID: {
view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
TextView userId = (TextView) view.findViewById(R.id.userId);
userId.setText(child.userId);
break;
}
}
return view;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {
// Get rid of URI prefix:
if (filename.startsWith("file://")) {
filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
FileDialog.setFilename(filename);
}
}
return;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
} }

View File

@ -16,55 +16,24 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView.OnChildClickListener; import android.widget.ExpandableListView.OnChildClickListener;
public class SecretKeyListActivity extends BaseActivity implements OnChildClickListener { public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
ExpandableListView mList;
protected int mSelectedItem = -1;
protected int mTask = 0;
private String mImportFilename = Constants.path.app_dir + "/secring.gpg";
private String mExportFilename = Constants.path.app_dir + "/secexport.asc";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
mExportFilename = Constants.path.app_dir + "/secexport.asc";
mKeyType = Id.type.secret_key;
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.key_list);
mList = (ExpandableListView) findViewById(R.id.list);
mList.setAdapter(new SecretKeyListAdapter(this));
registerForContextMenu(mList);
mList.setOnChildClickListener(this); mList.setOnChildClickListener(this);
} }
@ -86,16 +55,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case Id.menu.option.import_keys: {
showDialog(Id.dialog.import_keys);
return true;
}
case Id.menu.option.export_keys: {
showDialog(Id.dialog.export_keys);
return true;
}
case Id.menu.option.create: { case Id.menu.option.create: {
createKey(); createKey();
return true; return true;
@ -113,12 +72,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
ExpandableListView.ExpandableListContextMenuInfo info = ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo; (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition); int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) { if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition); // TODO: user id? menu.setHeaderTitle("Key");
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
menu.add(0, Id.menu.edit, 0, R.string.menu_editKey); 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.export, 1, R.string.menu_exportKey);
menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey); menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey);
@ -142,18 +98,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
return true; return true;
} }
case Id.menu.export: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.export_key);
return true;
}
case Id.menu.delete: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.delete_key);
return true;
}
default: { default: {
return super.onContextItemSelected(menuItem); return super.onContextItemSelected(menuItem);
} }
@ -170,96 +114,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
@Override @Override
protected Dialog onCreateDialog(int id) { protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) { switch (id) {
case Id.dialog.delete_key: {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.warning);
builder.setMessage(getString(R.string.secretKeyDeletionConfirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(R.string.btn_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
case Id.dialog.import_keys: {
return FileDialog.build(this, getString(R.string.title_importKeys),
getString(R.string.specifyFileToImportFrom),
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.import_keys);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.import_keys);
}
},
getString(R.string.filemanager_titleOpen),
getString(R.string.filemanager_btnOpen),
Id.request.filename);
}
case Id.dialog.export_key: {
singleKeyExport = true;
// break intentionally omitted, to use the Id.dialog.export_keys dialog
}
case Id.dialog.export_keys: {
String title = (singleKeyExport ?
getString(R.string.title_exportKey) :
getString(R.string.title_exportKeys));
final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
return FileDialog.build(this, title,
getString(R.string.specifyFileToExportSecretKeysTo),
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_titleSave),
getString(R.string.filemanager_btnSave),
Id.request.filename);
}
case Id.dialog.pass_phrase: { case Id.dialog.pass_phrase: {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem); long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
return AskForSecretKeyPassPhrase.createDialog(this, keyId, this); return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
} }
@ -270,8 +127,7 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
} }
public void checkPassPhraseAndEdit() { public void checkPassPhraseAndEdit() {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem); long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
String passPhrase = Apg.getCachedPassPhrase(keyId); String passPhrase = Apg.getCachedPassPhrase(keyId);
if (passPhrase == null) { if (passPhrase == null) {
showDialog(Id.dialog.pass_phrase); showDialog(Id.dialog.pass_phrase);
@ -295,10 +151,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
} }
private void editKey() { private void editKey() {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem); long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
Intent intent = new Intent(this, EditKeyActivity.class); Intent intent = new Intent(this, EditKeyActivity.class);
intent.putExtra("keyId", keyId); intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
startActivityForResult(intent, Id.message.edit_key); startActivityForResult(intent, Id.message.edit_key);
} }
@ -313,24 +168,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
break; break;
} }
case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {
// Get rid of URI prefix:
if (filename.startsWith("file://")) {
filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
FileDialog.setFilename(filename);
}
}
return;
}
default: { default: {
break; break;
} }
@ -338,320 +175,4 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
} }
public void importKeys() {
showDialog(Id.dialog.importing);
mTask = Id.task.import_keys;
startThread();
}
public void exportKeys() {
showDialog(Id.dialog.exporting);
mTask = Id.task.export_keys;
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == Id.task.import_keys) {
data = Apg.importKeyRings(this, Id.type.secret_key, filename, this);
} else {
Vector<Object> keys = new Vector<Object>();
if (mSelectedItem == -1) {
for (PGPSecretKeyRing key : Apg.getSecretKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getSecretKeyRings().get(mSelectedItem));
}
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
error = e.getMessage();
} catch (PGPException e) {
error = e.getMessage();
} catch (Apg.GeneralException e) {
error = e.getMessage();
}
if (mTask == Id.task.import_keys) {
data.putInt("type", Id.message.import_done);
} else {
data.putInt("type", Id.message.export_done);
}
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
sendMessage(msg);
}
private void deleteKey(int index) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(index);
Apg.deleteKey(this, keyRing);
refreshList();
}
private void refreshList() {
((SecretKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case Id.message.import_done: {
removeDialog(Id.dialog.importing);
String error = data.getString("error");
if (error != null) {
Toast.makeText(SecretKeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int added = data.getInt("added");
int updated = data.getInt("updated");
String message;
if (added > 0 && updated > 0) {
message = getString(R.string.keysAddedAndUpdated, added, updated);
} else if (added > 0) {
message = getString(R.string.keysAdded, added);
} else if (updated > 0) {
message = getString(R.string.keysUpdated, updated);
} else {
message = getString(R.string.noKeysAddedOrUpdated);
}
Toast.makeText(SecretKeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
refreshList();
break;
}
case Id.message.export_done: {
removeDialog(Id.dialog.exporting);
String error = data.getString("error");
if (error != null) {
Toast.makeText(SecretKeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int exported = data.getInt("exported");
String message;
if (exported == 1) {
message = getString(R.string.keyExported);
} else if (exported > 0) {
message = getString(R.string.keysExported);
} else{
message = getString(R.string.noKeysExported);
}
Toast.makeText(SecretKeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
}
private static class SecretKeyListAdapter extends BaseExpandableListAdapter {
private LayoutInflater mInflater;
private class KeyChild {
static final int KEY = 0;
static final int USER_ID = 1;
public int type;
public PGPSecretKey key;
public String userId;
public KeyChild(PGPSecretKey key) {
type = KEY;
this.key = key;
}
public KeyChild(String userId) {
type = USER_ID;
this.userId = userId;
}
}
public SecretKeyListAdapter(Context context) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
protected Vector<KeyChild> getChildrenOfKeyRing(PGPSecretKeyRing keyRing) {
Vector<KeyChild> children = new Vector<KeyChild>();
PGPSecretKey masterKey = null;
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
children.add(new KeyChild(key));
if (key.isMasterKey()) {
masterKey = key;
}
}
if (masterKey != null) {
boolean isFirst = true;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
if (isFirst) {
// ignore first, it's in the group already
isFirst = false;
continue;
}
children.add(new KeyChild(userId));
}
}
return children;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public int getGroupCount() {
return Apg.getSecretKeyRings().size();
}
public Object getChild(int groupPosition, int childPosition) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
KeyChild child = children.get(childPosition);
return child;
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public int getChildrenCount(int groupPosition) {
return getChildrenOfKeyRing(Apg.getSecretKeyRings().get(groupPosition)).size();
}
public Object getGroup(int position) {
return position;
}
public long getGroupId(int position) {
return position;
}
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
View view;
if (!key.isMasterKey()) {
continue;
}
view = mInflater.inflate(R.layout.key_list_group_item, null);
view.setBackgroundResource(android.R.drawable.list_selector_background);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText("");
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
String userId = Apg.getMainUserId(key);
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
if (mainUserId.getText().length() == 0) {
mainUserId.setText(R.string.unknownUserId);
}
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
return view;
}
return null;
}
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView,
ViewGroup parent) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
KeyChild child = children.get(childPosition);
View view = null;
switch (child.type) {
case KeyChild.KEY: {
PGPSecretKey key = child.key;
if (key.isMasterKey()) {
view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
} else {
view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
}
TextView keyId = (TextView) view.findViewById(R.id.keyId);
String keyIdStr = Long.toHexString(key.getKeyID() & 0xffffffffL);
while (keyIdStr.length() < 8) {
keyIdStr = "0" + keyIdStr;
}
keyId.setText(keyIdStr);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(key);
keyDetails.setText("(" + algorithmStr + ")");
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
if (!Apg.isEncryptionKey(key)) {
encryptIcon.setVisibility(View.GONE);
}
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
if (!Apg.isSigningKey(key)) {
signIcon.setVisibility(View.GONE);
}
break;
}
case KeyChild.USER_ID: {
view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
TextView userId = (TextView) view.findViewById(R.id.userId);
userId.setText(child.userId);
break;
}
}
return view;
}
}
} }

View File

@ -16,12 +16,8 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.util.Collections;
import java.util.Vector; import java.util.Vector;
import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
@ -42,27 +38,21 @@ public class SelectPublicKeyListActivity extends BaseActivity {
mIntent = getIntent(); mIntent = getIntent();
long selectedKeyIds[] = null; long selectedKeyIds[] = null;
if (mIntent.getExtras() != null) { if (mIntent.getExtras() != null) {
selectedKeyIds = mIntent.getExtras().getLongArray("selection"); selectedKeyIds = mIntent.getExtras().getLongArray(Apg.EXTRA_SELECTION);
} }
mList = (ListView) findViewById(R.id.list); mList = (ListView) findViewById(R.id.list);
// needed in Android 1.5, where the XML attribute gets ignored // needed in Android 1.5, where the XML attribute gets ignored
mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
Vector<PGPPublicKeyRing> keyRings = SelectPublicKeyListAdapter adapter = new SelectPublicKeyListAdapter(this, mList);
(Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone(); mList.setAdapter(adapter);
Collections.sort(keyRings, new Apg.PublicKeySorter());
mList.setAdapter(new SelectPublicKeyListAdapter(mList, keyRings));
if (selectedKeyIds != null) { if (selectedKeyIds != null) {
for (int i = 0; i < keyRings.size(); ++i) { for (int i = 0; i < adapter.getCount(); ++i) {
PGPPublicKeyRing keyRing = keyRings.get(i); long keyId = adapter.getItemId(i);
PGPPublicKey key = Apg.getMasterKey(keyRing);
if (key == null) {
continue;
}
for (int j = 0; j < selectedKeyIds.length; ++j) { for (int j = 0; j < selectedKeyIds.length; ++j) {
if (key.getKeyID() == selectedKeyIds[j]) { if (keyId == selectedKeyIds[j]) {
mList.setItemChecked(i, true); mList.setItemChecked(i, true);
break; break;
} }
@ -106,7 +96,7 @@ public class SelectPublicKeyListActivity extends BaseActivity {
for (int i = 0; i < vector.size(); ++i) { for (int i = 0; i < vector.size(); ++i) {
selectedKeyIds[i] = vector.get(i); selectedKeyIds[i] = vector.get(i);
} }
data.putExtra("selection", selectedKeyIds); data.putExtra(Apg.EXTRA_SELECTION, selectedKeyIds);
setResult(RESULT_OK, data); setResult(RESULT_OK, data);
finish(); finish();
} }

View File

@ -16,15 +16,16 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.text.DateFormat;
import java.util.Date; import java.util.Date;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPPublicKey; import org.thialfihar.android.apg.provider.KeyRings;
import org.bouncycastle2.openpgp.PGPPublicKeyRing; import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.utils.IterableIterator; import org.thialfihar.android.apg.provider.UserIds;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -34,40 +35,55 @@ import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
public class SelectPublicKeyListAdapter extends BaseAdapter { public class SelectPublicKeyListAdapter extends BaseAdapter {
protected Vector<PGPPublicKeyRing> mKeyRings;
protected LayoutInflater mInflater; protected LayoutInflater mInflater;
protected ListView mParent; protected ListView mParent;
protected SQLiteDatabase mDatabase;
protected Cursor mCursor;
public SelectPublicKeyListAdapter(ListView parent, public SelectPublicKeyListAdapter(Activity activity, ListView parent) {
Vector<PGPPublicKeyRing> keyRings) {
setKeyRings(keyRings);
mParent = parent; mParent = parent;
mDatabase = Apg.getDatabase().db();
mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} long now = new Date().getTime() / 1000;
mCursor = mDatabase.query(
KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
") " +
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
new String[] {
KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
"(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
"tmp." + Keys.KEY_RING_ID + " = " +
KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
"tmp." + Keys.IS_REVOKED + " = '0' AND " +
"tmp." + Keys.CAN_ENCRYPT + " = '1')", // 3
"(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
"tmp." + Keys.KEY_RING_ID + " = " +
KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
"tmp." + Keys.IS_REVOKED + " = '0' AND " +
"tmp." + Keys.CAN_ENCRYPT + " = '1' AND " +
"tmp." + Keys.CREATION + " <= '" + now + "' AND " +
"(tmp." + Keys.EXPIRY + " IS NULL OR " +
"tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
},
KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
new String[] { "" + Id.database.type_public },
null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
public void setKeyRings(Vector<PGPPublicKeyRing> keyRings) { activity.startManagingCursor(mCursor);
mKeyRings = keyRings;
notifyDataSetChanged();
}
public Vector<PGPPublicKeyRing> getKeyRings() {
return mKeyRings;
} }
@Override @Override
public boolean isEnabled(int position) { public boolean isEnabled(int position) {
PGPPublicKeyRing keyRing = mKeyRings.get(position); mCursor.moveToPosition(position);
return mCursor.getInt(4) > 0; // valid CAN_ENCRYPT
if (Apg.getMasterKey(keyRing) == null) {
return false;
}
Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
if (encryptKeys.size() == 0) {
return false;
}
return true;
} }
@Override @Override
@ -77,57 +93,37 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
@Override @Override
public int getCount() { public int getCount() {
return mKeyRings.size(); return mCursor.getCount();
} }
@Override @Override
public Object getItem(int position) { public Object getItem(int position) {
return mKeyRings.get(position); return position;
} }
@Override @Override
public long getItemId(int position) { public long getItemId(int position) {
PGPPublicKeyRing keyRing = mKeyRings.get(position); mCursor.moveToPosition(position);
PGPPublicKey key = Apg.getMasterKey(keyRing); return mCursor.getLong(1); // MASTER_KEY_ID
if (key != null) {
return key.getKeyID();
}
return 0;
} }
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
mCursor.moveToPosition(position);
View view = mInflater.inflate(R.layout.select_public_key_item, null); View view = mInflater.inflate(R.layout.select_public_key_item, null);
boolean enabled = isEnabled(position); boolean enabled = isEnabled(position);
PGPPublicKeyRing keyRing = mKeyRings.get(position);
PGPPublicKey key = null;
for (PGPPublicKey tKey : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
if (tKey.isMasterKey()) {
key = tKey;
break;
}
}
Vector<PGPPublicKey> encryptKeys = Apg.getEncryptKeys(keyRing);
Vector<PGPPublicKey> usableKeys = Apg.getUsableEncryptKeys(keyRing);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText(R.string.unknownUserId); mainUserId.setText(R.string.unknownUserId);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText(""); mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.keyId); TextView keyId = (TextView) view.findViewById(R.id.keyId);
keyId.setText(R.string.noKey); keyId.setText(R.string.noKey);
TextView creation = (TextView) view.findViewById(R.id.creation);
creation.setText(R.string.noDate);
TextView expiry = (TextView) view.findViewById(R.id.expiry);
expiry.setText(R.string.noExpiry);
TextView status = (TextView) view.findViewById(R.id.status); TextView status = (TextView) view.findViewById(R.id.status);
status.setText(R.string.unknownStatus); status.setText(R.string.unknownStatus);
if (key != null) { String userId = mCursor.getString(2); // USER_ID
String userId = Apg.getMainUserId(key);
if (userId != null) { if (userId != null) {
String chunks[] = userId.split(" <", 2); String chunks[] = userId.split(" <", 2);
userId = chunks[0]; userId = chunks[0];
@ -137,33 +133,22 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
mainUserId.setText(userId); mainUserId.setText(userId);
} }
keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL)); long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
} keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
if (mainUserIdRest.getText().length() == 0) { if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE); mainUserIdRest.setVisibility(View.GONE);
} }
PGPPublicKey timespanKey = key; if (enabled) {
if (usableKeys.size() > 0) {
timespanKey = usableKeys.get(0);
status.setText(R.string.canEncrypt); status.setText(R.string.canEncrypt);
} else if (encryptKeys.size() > 0) {
timespanKey = encryptKeys.get(0);
Date now = new Date();
if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
status.setText(R.string.notValid);
} else { } else {
if (mCursor.getInt(3) > 0) {
// has some CAN_ENCRYPT keys, but col(4) = 0, so must be revoked or expired
status.setText(R.string.expired); status.setText(R.string.expired);
}
} else { } else {
status.setText(R.string.noKey); status.setText(R.string.noKey);
} }
creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey)));
Date expiryDate = Apg.getExpiryDate(timespanKey);
if (expiryDate != null) {
expiry.setText(DateFormat.getDateInstance().format(expiryDate));
} }
status.setText(status.getText() + " "); status.setText(status.getText() + " ");
@ -176,8 +161,6 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
mainUserId.setEnabled(enabled); mainUserId.setEnabled(enabled);
mainUserIdRest.setEnabled(enabled); mainUserIdRest.setEnabled(enabled);
keyId.setEnabled(enabled); keyId.setEnabled(enabled);
creation.setEnabled(enabled);
expiry.setEnabled(enabled);
selected.setEnabled(enabled); selected.setEnabled(enabled);
status.setEnabled(enabled); status.setEnabled(enabled);

View File

@ -16,32 +16,16 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.text.DateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
public class SelectSecretKeyListActivity extends BaseActivity { public class SelectSecretKeyListActivity extends BaseActivity {
protected Vector<PGPSecretKeyRing> mKeyRings;
protected LayoutInflater mInflater;
protected Intent mIntent;
protected ListView mList; protected ListView mList;
protected SelectSecretKeyListAdapter mListAdapter;
protected long mSelectedKeyId = 0; protected long mSelectedKeyId = 0;
@ -49,158 +33,20 @@ public class SelectSecretKeyListActivity extends BaseActivity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// fill things
mIntent = getIntent();
mKeyRings = (Vector<PGPSecretKeyRing>) Apg.getSecretKeyRings().clone();
Collections.sort(mKeyRings, new Apg.SecretKeySorter());
setContentView(R.layout.select_secret_key); setContentView(R.layout.select_secret_key);
mList = (ListView) findViewById(R.id.list); mList = (ListView) findViewById(R.id.list);
mList.setAdapter(new SecretKeyListAdapter(this)); mListAdapter = new SelectSecretKeyListAdapter(this, mList);
mList.setAdapter(mListAdapter);
mList.setOnItemClickListener(new OnItemClickListener() { mList.setOnItemClickListener(new OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Intent data = new Intent(); Intent data = new Intent();
data.putExtra("selectedKeyId", id); data.putExtra(Apg.EXTRA_KEY_ID, id);
setResult(RESULT_OK, data); setResult(RESULT_OK, data);
finish(); finish();
} }
}); });
} }
private class SecretKeyListAdapter extends BaseAdapter {
public SecretKeyListAdapter(Context context) {
}
@Override
public boolean isEnabled(int position) {
PGPSecretKeyRing keyRing = mKeyRings.get(position);
if (Apg.getMasterKey(keyRing) == null) {
return false;
}
Vector<PGPSecretKey> usableKeys = Apg.getUsableSigningKeys(keyRing);
if (usableKeys.size() == 0) {
return false;
}
return true;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getCount() {
return mKeyRings.size();
}
@Override
public Object getItem(int position) {
return mKeyRings.get(position);
}
@Override
public long getItemId(int position) {
PGPSecretKeyRing keyRing = mKeyRings.get(position);
PGPSecretKey key = Apg.getMasterKey(keyRing);
if (key != null) {
return key.getKeyID();
}
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = mInflater.inflate(R.layout.select_secret_key_item, null);
boolean enabled = isEnabled(position);
PGPSecretKeyRing keyRing = mKeyRings.get(position);
PGPSecretKey key = null;
for (PGPSecretKey tKey : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (tKey.isMasterKey()) {
key = tKey;
break;
}
}
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText(R.string.unknownUserId);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.keyId);
keyId.setText(R.string.noKey);
TextView creation = (TextView) view.findViewById(R.id.creation);
creation.setText(R.string.noDate);
TextView expiry = (TextView) view.findViewById(R.id.expiry);
expiry.setText(R.string.noExpiry);
TextView status = (TextView) view.findViewById(R.id.status);
status.setText(R.string.unknownStatus);
if (key != null) {
String userId = Apg.getMainUserId(key);
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL));
}
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
Vector<PGPSecretKey> signingKeys = Apg.getSigningKeys(keyRing);
Vector<PGPSecretKey> usableKeys = Apg.getUsableSigningKeys(keyRing);
PGPSecretKey timespanKey = key;
if (usableKeys.size() > 0) {
timespanKey = usableKeys.get(0);
status.setText(R.string.canSign);
} else if (signingKeys.size() > 0) {
timespanKey = signingKeys.get(0);
Date now = new Date();
if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
status.setText(R.string.notValid);
} else {
status.setText(R.string.expired);
}
} else {
status.setText(R.string.noKey);
}
creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey)));
Date expiryDate = Apg.getExpiryDate(timespanKey);
if (expiryDate != null) {
expiry.setText(DateFormat.getDateInstance().format(expiryDate));
}
status.setText(status.getText() + " ");
view.setEnabled(enabled);
mainUserId.setEnabled(enabled);
mainUserIdRest.setEnabled(enabled);
keyId.setEnabled(enabled);
creation.setEnabled(enabled);
expiry.setEnabled(enabled);
status.setEnabled(enabled);
return view;
}
}
} }

View File

@ -0,0 +1,147 @@
package org.thialfihar.android.apg;
import java.util.Date;
import org.thialfihar.android.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class SelectSecretKeyListAdapter extends BaseAdapter {
protected LayoutInflater mInflater;
protected ListView mParent;
protected SQLiteDatabase mDatabase;
protected Cursor mCursor;
public SelectSecretKeyListAdapter(Activity activity, ListView parent) {
mParent = parent;
mDatabase = Apg.getDatabase().db();
mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
long now = new Date().getTime() / 1000;
mCursor = mDatabase.query(
KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
") " +
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
new String[] {
KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
"(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
"tmp." + Keys.KEY_RING_ID + " = " +
KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
"tmp." + Keys.IS_REVOKED + " = '0' AND " +
"tmp." + Keys.CAN_SIGN + " = '1')", // 3,
"(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
"tmp." + Keys.KEY_RING_ID + " = " +
KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
"tmp." + Keys.IS_REVOKED + " = '0' AND " +
"tmp." + Keys.CAN_SIGN + " = '1' AND " +
"tmp." + Keys.CREATION + " <= '" + now + "' AND " +
"(tmp." + Keys.EXPIRY + " IS NULL OR " +
"tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
},
KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
new String[] { "" + Id.database.type_secret },
null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
activity.startManagingCursor(mCursor);
}
@Override
public boolean isEnabled(int position) {
mCursor.moveToPosition(position);
return mCursor.getInt(4) > 0; // valid CAN_SIGN
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getCount() {
return mCursor.getCount();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
mCursor.moveToPosition(position);
return mCursor.getLong(1); // MASTER_KEY_ID
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
mCursor.moveToPosition(position);
View view = mInflater.inflate(R.layout.select_secret_key_item, null);
boolean enabled = isEnabled(position);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText(R.string.unknownUserId);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.keyId);
keyId.setText(R.string.noKey);
TextView status = (TextView) view.findViewById(R.id.status);
status.setText(R.string.unknownStatus);
String userId = mCursor.getString(2); // USER_ID
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
if (enabled) {
status.setText(R.string.canSign);
} else {
if (mCursor.getInt(3) > 0) {
// has some CAN_SIGN keys, but col(4) = 0, so must be revoked or expired
status.setText(R.string.expired);
} else {
status.setText(R.string.noKey);
}
}
status.setText(status.getText() + " ");
view.setEnabled(enabled);
mainUserId.setEnabled(enabled);
mainUserIdRest.setEnabled(enabled);
keyId.setEnabled(enabled);
status.setEnabled(enabled);
return view;
}
}

View File

@ -0,0 +1,81 @@
package org.thialfihar.android.apg;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
public class Service extends android.app.Service {
private final IBinder mBinder = new LocalBinder();
public static final String EXTRA_TTL = "ttl";
private int mPassPhraseCacheTtl = 15;
private Handler mCacheHandler = new Handler();
private Runnable mCacheTask = new Runnable() {
public void run() {
// TODO: I suppose we could read out the time left until the first cache entry
// expiration, then use that for the timer...
// check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
// and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl
int delay = mPassPhraseCacheTtl * 1000 / 2;
// also make sure the delay is not longer than one minute
if (delay > 60000) {
delay = 60000;
}
delay = Apg.cleanUpCache(mPassPhraseCacheTtl, delay);
// don't check too often, even if we were close
if (delay < 5000) {
delay = 5000;
}
mCacheHandler.postDelayed(this, delay);
}
};
static private boolean mIsRunning = false;
@Override
public void onCreate() {
super.onCreate();
mIsRunning = true;
}
@Override
public void onDestroy() {
super.onDestroy();
mIsRunning = false;
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
if (intent != null) {
mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15);
}
if (mPassPhraseCacheTtl < 15) {
mPassPhraseCacheTtl = 15;
}
mCacheHandler.removeCallbacks(mCacheTask);
mCacheHandler.postDelayed(mCacheTask, 1000);
}
static public boolean isRunning() {
return mIsRunning;
}
public class LocalBinder extends Binder {
Service getService() {
return Service.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}

View File

@ -16,7 +16,12 @@
package org.thialfihar.android.apg.provider; package org.thialfihar.android.apg.provider;
public class Accounts extends Accounts1 { import android.provider.BaseColumns;
private Accounts() {
} 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";
} }

View File

@ -18,15 +18,12 @@ package org.thialfihar.android.apg.provider;
import java.util.HashMap; import java.util.HashMap;
import org.thialfihar.android.apg.Id;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher; import android.content.UriMatcher;
import android.database.Cursor; import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
@ -34,153 +31,190 @@ import android.text.TextUtils;
public class DataProvider extends ContentProvider { public class DataProvider extends ContentProvider {
public static final String AUTHORITY = "org.thialfihar.android.apg.provider"; public static final String AUTHORITY = "org.thialfihar.android.apg.provider";
private static final String DATABASE_NAME = "apg"; private static final int PUBLIC_KEY_RINGS = 101;
private static final int DATABASE_VERSION = 1; private static final int PUBLIC_KEY_RING_ID = 102;
private static final int PUBLIC_KEY_RING_BY_KEY_ID = 103;
private static final int PUBLIC_KEY_RING_KEYS = 111;
private static final int PUBLIC_KEY_RING_KEY_RANK = 112;
private static final int PUBLIC_KEY_RING_USER_IDS = 121;
private static final int PUBLIC_KEY_RING_USER_ID_RANK = 122;
private static final int PUBLIC_KEYS = 101; private static final int SECRET_KEY_RINGS = 201;
private static final int PUBLIC_KEY_ID = 102; private static final int SECRET_KEY_RING_ID = 202;
private static final int PUBLIC_KEY_BY_KEY_ID = 103; private static final int SECRET_KEY_RING_BY_KEY_ID = 203;
private static final int SECRET_KEY_RING_KEYS = 211;
private static final int SECRET_KEY_RING_KEY_RANK = 212;
private static final int SECRET_KEY_RING_USER_IDS = 221;
private static final int SECRET_KEY_RING_USER_ID_RANK = 222;
private static final int SECRET_KEYS = 201; private static final String PUBLIC_KEY_RING_CONTENT_DIR_TYPE =
private static final int SECRET_KEY_ID = 202; "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key_ring";
private static final int SECRET_KEY_BY_KEY_ID = 203; private static final String PUBLIC_KEY_RING_CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.public.key_ring";
private static final int ACCOUNTS = 301; private static final String PUBLIC_KEY_CONTENT_DIR_TYPE =
private static final int ACCOUNT_ID = 302; "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key";
private static final String PUBLIC_KEY_CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.public.key";
private static final String SECRET_KEY_RING_CONTENT_DIR_TYPE =
"vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key_ring";
private static final String SECRET_KEY_RING_CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.secret.key_ring";
private static final String SECRET_KEY_CONTENT_DIR_TYPE =
"vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key";
private static final String SECRET_KEY_CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.secret.key";
private static final String USER_ID_CONTENT_DIR_TYPE =
"vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
private static final String USER_ID_CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
public static final String MASTER_KEY_ID = "master_key_id";
public static final String KEY_ID = "key_id";
public static final String USER_ID = "user_id";
private static final UriMatcher mUriMatcher; private static final UriMatcher mUriMatcher;
private static final HashMap<String, String> mPublicKeysProjectionMap;
private static final HashMap<String, String> mSecretKeysProjectionMap;
private static final HashMap<String, String> mAccountsProjectionMap;
private DatabaseHelper mdbHelper; private Database mDb;
static { static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys", PUBLIC_KEYS); mUriMatcher.addURI(AUTHORITY, "key_rings/public/key_id/*", PUBLIC_KEY_RING_BY_KEY_ID);
mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/#", PUBLIC_KEY_ID);
mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/key_id/*", PUBLIC_KEY_BY_KEY_ID);
mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys", SECRET_KEYS); mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys", PUBLIC_KEY_RING_KEYS);
mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/#", SECRET_KEY_ID); mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys/#", PUBLIC_KEY_RING_KEY_RANK);
mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/key_id/*", SECRET_KEY_BY_KEY_ID);
mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts", ACCOUNTS); mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids", PUBLIC_KEY_RING_USER_IDS);
mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts/#", ACCOUNT_ID); mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids/#", PUBLIC_KEY_RING_USER_ID_RANK);
mPublicKeysProjectionMap = new HashMap<String, String>(); mUriMatcher.addURI(AUTHORITY, "key_rings/public", PUBLIC_KEY_RINGS);
mPublicKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID); mUriMatcher.addURI(AUTHORITY, "key_rings/public/*", PUBLIC_KEY_RING_ID);
mPublicKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID);
mPublicKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA);
mPublicKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID);
mSecretKeysProjectionMap = new HashMap<String, String>(); mUriMatcher.addURI(AUTHORITY, "key_rings/secret/key_id/*", SECRET_KEY_RING_BY_KEY_ID);
mSecretKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID);
mSecretKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID);
mSecretKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA);
mSecretKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID);
mAccountsProjectionMap = new HashMap<String, String>(); mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys", SECRET_KEY_RING_KEYS);
mAccountsProjectionMap.put(Accounts._ID, Accounts._ID); mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys/#", SECRET_KEY_RING_KEY_RANK);
mAccountsProjectionMap.put(Accounts.NAME, Accounts.NAME);
}
/** mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids", SECRET_KEY_RING_USER_IDS);
* This class helps open, create, and upgrade the database file. mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids/#", SECRET_KEY_RING_USER_ID_RANK);
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) { mUriMatcher.addURI(AUTHORITY, "key_rings/secret", SECRET_KEY_RINGS);
super(context, DATABASE_NAME, null, DATABASE_VERSION); mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*", SECRET_KEY_RING_ID);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + PublicKeys.TABLE_NAME + " (" +
PublicKeys._ID + " " + PublicKeys._ID_type + "," +
PublicKeys.KEY_ID + " " + PublicKeys.KEY_ID_type + ", " +
PublicKeys.KEY_DATA + " " + PublicKeys.KEY_DATA_type + ", " +
PublicKeys.WHO_ID + " " + PublicKeys.WHO_ID_type + ");");
db.execSQL("CREATE TABLE " + SecretKeys.TABLE_NAME + " (" +
SecretKeys._ID + " " + SecretKeys._ID_type + "," +
SecretKeys.KEY_ID + " " + SecretKeys.KEY_ID_type + ", " +
SecretKeys.KEY_DATA + " " + SecretKeys.KEY_DATA_type + ", " +
SecretKeys.WHO_ID + " " + SecretKeys.WHO_ID_type + ");");
db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" +
Accounts._ID + " " + Accounts._ID_type + "," +
Accounts.NAME + " " + Accounts.NAME_type + ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO: upgrade db if necessary, and do that in a clever way
}
} }
@Override @Override
public boolean onCreate() { public boolean onCreate() {
mdbHelper = new DatabaseHelper(getContext()); mDb = new Database(getContext());
return true; return true;
} }
@Override @Override
public Cursor query(Uri uri, String[] projection, String selection, public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) { String[] selectionArgs, String sortOrder) {
// TODO: implement the others, then use them for the lists
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
HashMap<String, String> projectionMap = new HashMap<String, String>();
int match = mUriMatcher.match(uri);
int type;
switch (match) {
case PUBLIC_KEY_RINGS:
case PUBLIC_KEY_RING_ID:
case PUBLIC_KEY_RING_BY_KEY_ID:
case PUBLIC_KEY_RING_KEYS:
case PUBLIC_KEY_RING_KEY_RANK:
case PUBLIC_KEY_RING_USER_IDS:
case PUBLIC_KEY_RING_USER_ID_RANK:
type = Id.database.type_public;
break;
case SECRET_KEY_RINGS:
case SECRET_KEY_RING_ID:
case SECRET_KEY_RING_BY_KEY_ID:
case SECRET_KEY_RING_KEYS:
case SECRET_KEY_RING_KEY_RANK:
case SECRET_KEY_RING_USER_IDS:
case SECRET_KEY_RING_USER_ID_RANK:
type = Id.database.type_secret;
break;
default: {
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
qb.appendWhere(KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = " + type);
switch (match) {
case PUBLIC_KEY_RINGS:
case SECRET_KEY_RINGS: {
qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
") " +
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
projectionMap.put(MASTER_KEY_ID,
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
projectionMap.put(USER_ID,
UserIds.TABLE_NAME + "." + UserIds.USER_ID);
switch (mUriMatcher.match(uri)) {
case PUBLIC_KEYS: {
qb.setTables(PublicKeys.TABLE_NAME);
qb.setProjectionMap(mPublicKeysProjectionMap);
break; break;
} }
case PUBLIC_KEY_ID: { case PUBLIC_KEY_RING_ID:
qb.setTables(PublicKeys.TABLE_NAME); case SECRET_KEY_RING_ID: {
qb.setProjectionMap(mPublicKeysProjectionMap); qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
qb.appendWhere(PublicKeys._ID + "=" + uri.getPathSegments().get(1)); "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
") " +
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
projectionMap.put(MASTER_KEY_ID,
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
projectionMap.put(USER_ID,
UserIds.TABLE_NAME + "." + UserIds.USER_ID);
qb.appendWhere(" AND " +
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
break; break;
} }
case PUBLIC_KEY_BY_KEY_ID: { case SECRET_KEY_RING_BY_KEY_ID:
qb.setTables(PublicKeys.TABLE_NAME); case PUBLIC_KEY_RING_BY_KEY_ID: {
qb.setProjectionMap(mPublicKeysProjectionMap); qb.setTables(Keys.TABLE_NAME + " AS tmp INNER JOIN " +
qb.appendWhere(PublicKeys.KEY_ID + "=" + uri.getPathSegments().get(2)); KeyRings.TABLE_NAME + " ON (" +
break; KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
} "tmp." + Keys.KEY_RING_ID + ")" +
" INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
") " +
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
case SECRET_KEYS: { projectionMap.put(MASTER_KEY_ID,
qb.setTables(SecretKeys.TABLE_NAME); KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
qb.setProjectionMap(mSecretKeysProjectionMap); projectionMap.put(USER_ID,
break; UserIds.TABLE_NAME + "." + UserIds.USER_ID);
}
case SECRET_KEY_ID: { qb.appendWhere(" AND tmp." + Keys.KEY_ID + " = ");
qb.setTables(SecretKeys.TABLE_NAME); qb.appendWhereEscapeString(uri.getPathSegments().get(3));
qb.setProjectionMap(mSecretKeysProjectionMap);
qb.appendWhere(SecretKeys._ID + "=" + uri.getPathSegments().get(1));
break;
}
case SECRET_KEY_BY_KEY_ID: {
qb.setTables(SecretKeys.TABLE_NAME);
qb.setProjectionMap(mSecretKeysProjectionMap);
qb.appendWhere(SecretKeys.KEY_ID + "=" + uri.getPathSegments().get(2));
break;
}
case ACCOUNTS: {
qb.setTables(Accounts.TABLE_NAME);
qb.setProjectionMap(mAccountsProjectionMap);
break;
}
case ACCOUNT_ID: {
qb.setTables(Accounts.TABLE_NAME);
qb.setProjectionMap(mAccountsProjectionMap);
qb.appendWhere(Accounts._ID + "=" + uri.getPathSegments().get(1));
break; break;
} }
@ -189,20 +223,20 @@ public class DataProvider extends ContentProvider {
} }
} }
qb.setProjectionMap(projectionMap);
// If no sort order is specified use the default // If no sort order is specified use the default
String orderBy; String orderBy;
if (TextUtils.isEmpty(sortOrder)) { if (TextUtils.isEmpty(sortOrder)) {
orderBy = PublicKeys.DEFAULT_SORT_ORDER; orderBy = null;
} else { } else {
orderBy = sortOrder; orderBy = sortOrder;
} }
// Get the database and run the query //System.out.println(qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null).replace("WHERE", "WHERE\n"));
SQLiteDatabase db = mdbHelper.getReadableDatabase(); Cursor c = qb.query(mDb.db(), projection, selection, selectionArgs, null, null, orderBy);
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
// Tell the cursor what uri to watch, so it knows when its source data // Tell the cursor what uri to watch, so it knows when its source data changes
// changes
c.setNotificationUri(getContext().getContentResolver(), uri); c.setNotificationUri(getContext().getContentResolver(), uri);
return c; return c;
} }
@ -210,278 +244,68 @@ public class DataProvider extends ContentProvider {
@Override @Override
public String getType(Uri uri) { public String getType(Uri uri) {
switch (mUriMatcher.match(uri)) { switch (mUriMatcher.match(uri)) {
case PUBLIC_KEYS: { case PUBLIC_KEY_RINGS:
return PublicKeys.CONTENT_TYPE; return PUBLIC_KEY_RING_CONTENT_DIR_TYPE;
}
case PUBLIC_KEY_ID: { case PUBLIC_KEY_RING_ID:
return PublicKeys.CONTENT_ITEM_TYPE; return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
}
case PUBLIC_KEY_BY_KEY_ID: { case PUBLIC_KEY_RING_BY_KEY_ID:
return PublicKeys.CONTENT_ITEM_TYPE; return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
}
case SECRET_KEYS: { case PUBLIC_KEY_RING_KEYS:
return SecretKeys.CONTENT_TYPE; return PUBLIC_KEY_CONTENT_DIR_TYPE;
}
case SECRET_KEY_ID: { case PUBLIC_KEY_RING_KEY_RANK:
return SecretKeys.CONTENT_ITEM_TYPE; return PUBLIC_KEY_CONTENT_ITEM_TYPE;
}
case SECRET_KEY_BY_KEY_ID: { case PUBLIC_KEY_RING_USER_IDS:
return SecretKeys.CONTENT_ITEM_TYPE; return USER_ID_CONTENT_DIR_TYPE;
}
case ACCOUNTS: { case PUBLIC_KEY_RING_USER_ID_RANK:
return Accounts.CONTENT_TYPE; return USER_ID_CONTENT_ITEM_TYPE;
}
case ACCOUNT_ID: { case SECRET_KEY_RINGS:
return Accounts.CONTENT_ITEM_TYPE; return SECRET_KEY_RING_CONTENT_DIR_TYPE;
}
default: { case SECRET_KEY_RING_ID:
return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
case SECRET_KEY_RING_BY_KEY_ID:
return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
case SECRET_KEY_RING_KEYS:
return SECRET_KEY_CONTENT_DIR_TYPE;
case SECRET_KEY_RING_KEY_RANK:
return SECRET_KEY_CONTENT_ITEM_TYPE;
case SECRET_KEY_RING_USER_IDS:
return USER_ID_CONTENT_DIR_TYPE;
case SECRET_KEY_RING_USER_ID_RANK:
return USER_ID_CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri); throw new IllegalArgumentException("Unknown URI " + uri);
} }
} }
}
@Override @Override
public Uri insert(Uri uri, ContentValues initialValues) { public Uri insert(Uri uri, ContentValues initialValues) {
switch (mUriMatcher.match(uri)) { // not supported
case PUBLIC_KEYS: { return null;
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
if (!values.containsKey(PublicKeys.WHO_ID)) {
values.put(PublicKeys.WHO_ID, "");
}
SQLiteDatabase db = mdbHelper.getWritableDatabase();
long rowId = db.insert(PublicKeys.TABLE_NAME, PublicKeys.WHO_ID, values);
if (rowId > 0) {
Uri transferUri = ContentUris.withAppendedId(PublicKeys.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(transferUri, null);
return transferUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
case SECRET_KEYS: {
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
if (!values.containsKey(SecretKeys.WHO_ID)) {
values.put(SecretKeys.WHO_ID, "");
}
SQLiteDatabase db = mdbHelper.getWritableDatabase();
long rowId = db.insert(SecretKeys.TABLE_NAME, SecretKeys.WHO_ID, values);
if (rowId > 0) {
Uri transferUri = ContentUris.withAppendedId(SecretKeys.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(transferUri, null);
return transferUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
case ACCOUNTS: {
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
SQLiteDatabase db = mdbHelper.getWritableDatabase();
long rowId = db.insert(Accounts.TABLE_NAME, null, values);
if (rowId > 0) {
Uri transferUri = ContentUris.withAppendedId(Accounts.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(transferUri, null);
return transferUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
default: {
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
} }
@Override @Override
public int delete(Uri uri, String where, String[] whereArgs) { public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mdbHelper.getWritableDatabase(); // not supported
int count; return 0;
switch (mUriMatcher.match(uri)) {
case PUBLIC_KEYS: {
count = db.delete(PublicKeys.TABLE_NAME, where, whereArgs);
break;
}
case PUBLIC_KEY_ID: {
String publicKeyId = uri.getPathSegments().get(1);
count = db.delete(PublicKeys.TABLE_NAME,
PublicKeys._ID + "=" + publicKeyId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
case PUBLIC_KEY_BY_KEY_ID: {
String publicKeyKeyId = uri.getPathSegments().get(2);
count = db.delete(PublicKeys.TABLE_NAME,
PublicKeys.KEY_ID + "=" + publicKeyKeyId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
case SECRET_KEYS: {
count = db.delete(SecretKeys.TABLE_NAME, where, whereArgs);
break;
}
case SECRET_KEY_ID: {
String secretKeyId = uri.getPathSegments().get(1);
count = db.delete(SecretKeys.TABLE_NAME,
SecretKeys._ID + "=" + secretKeyId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
case SECRET_KEY_BY_KEY_ID: {
String secretKeyKeyId = uri.getPathSegments().get(2);
count = db.delete(SecretKeys.TABLE_NAME,
SecretKeys.KEY_ID + "=" + secretKeyKeyId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
case ACCOUNTS: {
count = db.delete(Accounts.TABLE_NAME, where, whereArgs);
break;
}
case ACCOUNT_ID: {
String accountId = uri.getPathSegments().get(1);
count = db.delete(Accounts.TABLE_NAME,
Accounts._ID + "=" + accountId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
default: {
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
} }
@Override @Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase db = mdbHelper.getWritableDatabase(); // not supported
int count; return 0;
switch (mUriMatcher.match(uri)) {
case PUBLIC_KEYS: {
count = db.update(PublicKeys.TABLE_NAME, values, where, whereArgs);
break;
}
case PUBLIC_KEY_ID: {
String publicKeyId = uri.getPathSegments().get(1);
count = db.update(PublicKeys.TABLE_NAME, values,
PublicKeys._ID + "=" + publicKeyId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
case PUBLIC_KEY_BY_KEY_ID: {
String publicKeyKeyId = uri.getPathSegments().get(2);
count = db.update(PublicKeys.TABLE_NAME, values,
PublicKeys.KEY_ID + "=" + publicKeyKeyId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
case SECRET_KEYS: {
count = db.update(SecretKeys.TABLE_NAME, values, where, whereArgs);
break;
}
case SECRET_KEY_ID: {
String secretKeyId = uri.getPathSegments().get(1);
count = db.update(SecretKeys.TABLE_NAME, values,
SecretKeys._ID + "=" + secretKeyId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
case SECRET_KEY_BY_KEY_ID: {
String secretKeyKeyId = uri.getPathSegments().get(2);
count = db.update(SecretKeys.TABLE_NAME, values,
SecretKeys.KEY_ID + "=" + secretKeyKeyId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
case ACCOUNTS: {
count = db.update(Accounts.TABLE_NAME, values, where, whereArgs);
break;
}
case ACCOUNT_ID: {
String accountId = uri.getPathSegments().get(1);
count = db.update(Accounts.TABLE_NAME, values,
Accounts._ID + "=" + accountId +
(!TextUtils.isEmpty(where) ?
" AND (" + where + ')' : ""),
whereArgs);
break;
}
default: {
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
} }
} }

View File

@ -0,0 +1,605 @@
package org.thialfihar.android.apg.provider;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.Apg;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class Database extends SQLiteOpenHelper {
public static class GeneralException extends Exception {
static final long serialVersionUID = 0xf812773343L;
public GeneralException(String message) {
super(message);
}
}
private static final String DATABASE_NAME = "apg";
private static final int DATABASE_VERSION = 2;
public static final String AUTHORITY = "org.thialfihar.android.apg.database";
public static HashMap<String, String> sKeyRingsProjection;
public static HashMap<String, String> sKeysProjection;
public static HashMap<String, String> sUserIdsProjection;
private SQLiteDatabase mDb = null;
private int mStatus = 0;
static {
sKeyRingsProjection = new HashMap<String, String>();
sKeyRingsProjection.put(KeyRings._ID, KeyRings._ID);
sKeyRingsProjection.put(KeyRings.MASTER_KEY_ID, KeyRings.MASTER_KEY_ID);
sKeyRingsProjection.put(KeyRings.TYPE, KeyRings.TYPE);
sKeyRingsProjection.put(KeyRings.WHO_ID, KeyRings.WHO_ID);
sKeyRingsProjection.put(KeyRings.KEY_RING_DATA, KeyRings.KEY_RING_DATA);
sKeysProjection = new HashMap<String, String>();
sKeysProjection.put(Keys._ID, Keys._ID);
sKeysProjection.put(Keys.KEY_ID, Keys.KEY_ID);
sKeysProjection.put(Keys.TYPE, Keys.TYPE);
sKeysProjection.put(Keys.IS_MASTER_KEY, Keys.IS_MASTER_KEY);
sKeysProjection.put(Keys.ALGORITHM, Keys.ALGORITHM);
sKeysProjection.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
sKeysProjection.put(Keys.CAN_SIGN, Keys.CAN_SIGN);
sKeysProjection.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
sKeysProjection.put(Keys.IS_REVOKED, Keys.IS_REVOKED);
sKeysProjection.put(Keys.CREATION, Keys.CREATION);
sKeysProjection.put(Keys.EXPIRY, Keys.EXPIRY);
sKeysProjection.put(Keys.KEY_DATA, Keys.KEY_DATA);
sKeysProjection.put(Keys.RANK, Keys.RANK);
sUserIdsProjection = new HashMap<String, String>();
sUserIdsProjection.put(UserIds._ID, UserIds._ID);
sUserIdsProjection.put(UserIds.KEY_ID, UserIds.KEY_ID);
sUserIdsProjection.put(UserIds.USER_ID, UserIds.USER_ID);
sUserIdsProjection.put(UserIds.RANK, UserIds.RANK);
}
public Database(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
// force upgrade to test things
//onUpgrade(getWritableDatabase(), 1, 2);
mDb = getWritableDatabase();
}
@Override
protected void finalize() throws Throwable {
mDb.close();
super.finalize();
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
KeyRings._ID + " " + KeyRings._ID_type + "," +
KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
Keys._ID + " " + Keys._ID_type + "," +
Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
Keys.TYPE + " " + Keys.TYPE_type + ", " +
Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
Keys.CREATION + " " + Keys.CREATION_type + ", " +
Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
Keys.RANK + " " + Keys.RANK_type + ");");
db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
UserIds._ID + " " + UserIds._ID_type + "," +
UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
UserIds.RANK + " " + UserIds.RANK_type + ");");
db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" +
Accounts._ID + " " + Accounts._ID_type + "," +
Accounts.NAME + " " + Accounts.NAME_type + ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
mDb = db;
for (int version = oldVersion; version < newVersion; ++version) {
switch (version) {
case 1: { // upgrade 1 to 2
db.execSQL("DROP TABLE IF EXISTS " + KeyRings.TABLE_NAME + ";");
db.execSQL("DROP TABLE IF EXISTS " + Keys.TABLE_NAME + ";");
db.execSQL("DROP TABLE IF EXISTS " + UserIds.TABLE_NAME + ";");
db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
KeyRings._ID + " " + KeyRings._ID_type + "," +
KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
Keys._ID + " " + Keys._ID_type + "," +
Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
Keys.TYPE + " " + Keys.TYPE_type + ", " +
Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
Keys.CREATION + " " + Keys.CREATION_type + ", " +
Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
Keys.RANK + " " + Keys.RANK_type + ");");
db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
UserIds._ID + " " + UserIds._ID_type + "," +
UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
UserIds.RANK + " " + UserIds.RANK_type + ");");
Cursor cursor = db.query("public_keys", new String[] { "c_key_data" },
null, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
do {
byte[] data = cursor.getBlob(0);
try {
PGPPublicKeyRing keyRing = new PGPPublicKeyRing(data);
saveKeyRing(keyRing);
} catch (IOException e) {
Log.e("apg.db.upgrade", "key import failed: " + e);
} catch (GeneralException e) {
Log.e("apg.db.upgrade", "key import failed: " + e);
}
} while (cursor.moveToNext());
}
if (cursor != null) {
cursor.close();
}
cursor = db.query("secret_keys", new String[]{ "c_key_data" },
null, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
do {
byte[] data = cursor.getBlob(0);
try {
PGPSecretKeyRing keyRing = new PGPSecretKeyRing(data);
saveKeyRing(keyRing);
} catch (IOException e) {
Log.e("apg.db.upgrade", "key import failed: " + e);
} catch (PGPException e) {
Log.e("apg.db.upgrade", "key import failed: " + e);
} catch (GeneralException e) {
Log.e("apg.db.upgrade", "key import failed: " + e);
}
} while (cursor.moveToNext());
}
if (cursor != null) {
cursor.close();
}
db.execSQL("DROP TABLE IF EXISTS public_keys;");
db.execSQL("DROP TABLE IF EXISTS secret_keys;");
break;
}
default: {
break;
}
}
}
mDb = null;
}
public int saveKeyRing(PGPPublicKeyRing keyRing) throws IOException, GeneralException {
mDb.beginTransaction();
ContentValues values = new ContentValues();
PGPPublicKey masterKey = keyRing.getPublicKey();
long masterKeyId = masterKey.getKeyID();
values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
values.put(KeyRings.TYPE, Id.database.type_public);
values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
long rowId = insertOrUpdateKeyRing(values);
int returnValue = mStatus;
if (rowId == -1) {
throw new GeneralException("saving public key ring " + masterKeyId + " failed");
}
Vector<Integer> seenIds = new Vector<Integer>();
int rank = 0;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
seenIds.add(saveKey(rowId, key, rank));
++rank;
}
String seenIdsStr = "";
for (Integer id : seenIds) {
if (seenIdsStr.length() > 0) {
seenIdsStr += ",";
}
seenIdsStr += id;
}
mDb.delete(Keys.TABLE_NAME,
Keys.KEY_RING_ID + " = ? AND " +
Keys._ID + " NOT IN (" + seenIdsStr + ")",
new String[] { "" + rowId });
mDb.setTransactionSuccessful();
mDb.endTransaction();
return returnValue;
}
public int saveKeyRing(PGPSecretKeyRing keyRing) throws IOException, GeneralException {
mDb.beginTransaction();
ContentValues values = new ContentValues();
PGPSecretKey masterKey = keyRing.getSecretKey();
long masterKeyId = masterKey.getKeyID();
values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
values.put(KeyRings.TYPE, Id.database.type_secret);
values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
long rowId = insertOrUpdateKeyRing(values);
int returnValue = mStatus;
if (rowId == -1) {
throw new GeneralException("saving secret key ring " + masterKeyId + " failed");
}
Vector<Integer> seenIds = new Vector<Integer>();
int rank = 0;
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
seenIds.add(saveKey(rowId, key, rank));
++rank;
}
String seenIdsStr = "";
for (Integer id : seenIds) {
if (seenIdsStr.length() > 0) {
seenIdsStr += ",";
}
seenIdsStr += id;
}
mDb.delete(Keys.TABLE_NAME,
Keys.KEY_RING_ID + " = ? AND " +
Keys._ID + " NOT IN (" + seenIdsStr + ")",
new String[] { "" + rowId });
mDb.setTransactionSuccessful();
mDb.endTransaction();
return returnValue;
}
private int saveKey(long keyRingId, PGPPublicKey key, int rank)
throws IOException, GeneralException {
ContentValues values = new ContentValues();
values.put(Keys.KEY_ID, key.getKeyID());
values.put(Keys.TYPE, Id.database.type_public);
values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.KEY_SIZE, key.getBitStrength());
values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
values.put(Keys.IS_REVOKED, key.isRevoked());
values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
Date expiryDate = Apg.getExpiryDate(key);
if (expiryDate != null) {
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
}
values.put(Keys.KEY_RING_ID, keyRingId);
values.put(Keys.KEY_DATA, key.getEncoded());
values.put(Keys.RANK, rank);
long rowId = insertOrUpdateKey(values);
if (rowId == -1) {
throw new GeneralException("saving public key " + key.getKeyID() + " failed");
}
Vector<Integer> seenIds = new Vector<Integer>();
int userIdRank = 0;
for (String userId : new IterableIterator<String>(key.getUserIDs())) {
seenIds.add(saveUserId(rowId, userId, userIdRank));
++userIdRank;
}
String seenIdsStr = "";
for (Integer id : seenIds) {
if (seenIdsStr.length() > 0) {
seenIdsStr += ",";
}
seenIdsStr += id;
}
mDb.delete(UserIds.TABLE_NAME,
UserIds.KEY_ID + " = ? AND " +
UserIds._ID + " NOT IN (" + seenIdsStr + ")",
new String[] { "" + rowId });
return (int)rowId;
}
private int saveKey(long keyRingId, PGPSecretKey key, int rank)
throws IOException, GeneralException {
ContentValues values = new ContentValues();
values.put(Keys.KEY_ID, key.getPublicKey().getKeyID());
values.put(Keys.TYPE, Id.database.type_secret);
values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
Date expiryDate = Apg.getExpiryDate(key);
if (expiryDate != null) {
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
}
values.put(Keys.KEY_RING_ID, keyRingId);
values.put(Keys.KEY_DATA, key.getEncoded());
values.put(Keys.RANK, rank);
long rowId = insertOrUpdateKey(values);
if (rowId == -1) {
throw new GeneralException("saving secret key " + key.getPublicKey().getKeyID() + " failed");
}
Vector<Integer> seenIds = new Vector<Integer>();
int userIdRank = 0;
for (String userId : new IterableIterator<String>(key.getUserIDs())) {
seenIds.add(saveUserId(rowId, userId, userIdRank));
++userIdRank;
}
String seenIdsStr = "";
for (Integer id : seenIds) {
if (seenIdsStr.length() > 0) {
seenIdsStr += ",";
}
seenIdsStr += id;
}
mDb.delete(UserIds.TABLE_NAME,
UserIds.KEY_ID + " = ? AND " +
UserIds._ID + " NOT IN (" + seenIdsStr + ")",
new String[] { "" + rowId });
return (int)rowId;
}
private int saveUserId(long keyId, String userId, int rank) throws GeneralException {
ContentValues values = new ContentValues();
values.put(UserIds.KEY_ID, keyId);
values.put(UserIds.USER_ID, userId);
values.put(UserIds.RANK, rank);
long rowId = insertOrUpdateUserId(values);
if (rowId == -1) {
throw new GeneralException("saving user id " + userId + " failed");
}
return (int)rowId;
}
private long insertOrUpdateKeyRing(ContentValues values) {
Cursor c = mDb.query(KeyRings.TABLE_NAME, new String[] { KeyRings._ID },
KeyRings.MASTER_KEY_ID + " = ? AND " + KeyRings.TYPE + " = ?",
new String[] {
values.getAsString(KeyRings.MASTER_KEY_ID),
values.getAsString(KeyRings.TYPE),
},
null, null, null);
long rowId = -1;
if (c != null && c.moveToFirst()) {
rowId = c.getLong(0);
mDb.update(KeyRings.TABLE_NAME, values,
KeyRings._ID + " = ?", new String[] { "" + rowId });
mStatus = Id.return_value.updated;
} else {
rowId = mDb.insert(KeyRings.TABLE_NAME, KeyRings.WHO_ID, values);
mStatus = Id.return_value.ok;
}
if (c != null) {
c.close();
}
return rowId;
}
private long insertOrUpdateKey(ContentValues values) {
Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
new String[] {
values.getAsString(Keys.KEY_ID),
values.getAsString(Keys.TYPE),
},
null, null, null);
long rowId = -1;
if (c != null && c.moveToFirst()) {
rowId = c.getLong(0);
mDb.update(Keys.TABLE_NAME, values,
Keys._ID + " = ?", new String[] { "" + rowId });
} else {
rowId = mDb.insert(Keys.TABLE_NAME, Keys.KEY_DATA, values);
}
if (c != null) {
c.close();
}
return rowId;
}
private long insertOrUpdateUserId(ContentValues values) {
Cursor c = mDb.query(UserIds.TABLE_NAME, new String[] { UserIds._ID },
UserIds.KEY_ID + " = ? AND " + UserIds.USER_ID + " = ?",
new String[] {
values.getAsString(UserIds.KEY_ID),
values.getAsString(UserIds.USER_ID),
},
null, null, null);
long rowId = -1;
if (c != null && c.moveToFirst()) {
rowId = c.getLong(0);
mDb.update(UserIds.TABLE_NAME, values,
UserIds._ID + " = ?", new String[] { "" + rowId });
} else {
rowId = mDb.insert(UserIds.TABLE_NAME, UserIds.USER_ID, values);
}
if (c != null) {
c.close();
}
return rowId;
}
public Object getKeyRing(int keyRingId) {
Cursor c = mDb.query(KeyRings.TABLE_NAME,
new String[] { KeyRings.KEY_RING_DATA, KeyRings.TYPE },
KeyRings._ID + " = ?",
new String[] {
"" + keyRingId,
},
null, null, null);
byte[] data = null;
Object keyRing = null;
if (c != null && c.moveToFirst()) {
data = c.getBlob(0);
if (data != null) {
try {
if (c.getInt(1) == Id.database.type_public) {
keyRing = new PGPPublicKeyRing(data);
} else {
keyRing = new PGPSecretKeyRing(data);
}
} catch (IOException e) {
// can't load it, then
} catch (PGPException e) {
// can't load it, then
}
}
}
if (c != null) {
c.close();
}
return keyRing;
}
public byte[] getKeyRingDataFromKeyId(int type, long keyId) {
Cursor c = mDb.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ")",
new String[] { KeyRings.TABLE_NAME + "." + KeyRings.KEY_RING_DATA },
Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
new String[] {
"" + keyId,
"" + type,
},
null, null, null);
byte[] data = null;
if (c != null && c.moveToFirst()) {
data = c.getBlob(0);
}
if (c != null) {
c.close();
}
return data;
}
public byte[] getKeyDataFromKeyId(int type, long keyId) {
Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys.KEY_DATA },
Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
new String[] {
"" + keyId,
"" + type,
},
null, null, null);
byte[] data = null;
if (c != null && c.moveToFirst()) {
data = c.getBlob(0);
}
if (c != null) {
c.close();
}
return data;
}
public void deleteKeyRing(int keyRingId) {
mDb.beginTransaction();
mDb.delete(KeyRings.TABLE_NAME,
KeyRings._ID + " = ?", new String[] { "" + keyRingId });
Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
Keys.KEY_RING_ID + " = ?",
new String[] {
"" + keyRingId,
},
null, null, null);
if (c != null && c.moveToFirst()) {
do {
int keyId = c.getInt(0);
deleteKey(keyId);
} while (c.moveToNext());
}
if (c != null) {
c.close();
}
mDb.setTransactionSuccessful();
mDb.endTransaction();
}
private void deleteKey(int keyId) {
mDb.delete(Keys.TABLE_NAME,
Keys._ID + " = ?", new String[] { "" + keyId });
mDb.delete(UserIds.TABLE_NAME,
UserIds.KEY_ID + " = ?", new String[] { "" + keyId });
}
public SQLiteDatabase db() {
return mDb;
}
}

View File

@ -16,21 +16,18 @@
package org.thialfihar.android.apg.provider; package org.thialfihar.android.apg.provider;
import android.net.Uri;
import android.provider.BaseColumns; import android.provider.BaseColumns;
class Accounts1 implements BaseColumns { public class KeyRings implements BaseColumns {
public static final String TABLE_NAME = "accounts"; public static final String TABLE_NAME = "key_rings";
public static final String _ID_type = "INTEGER PRIMARY KEY"; public static final String _ID_type = "INTEGER PRIMARY KEY";
public static final String NAME = "c_name"; public static final String MASTER_KEY_ID = "c_master_key_id";
public static final String NAME_type = "TEXT"; public static final String MASTER_KEY_ID_type = "INT64";
public static final String TYPE = "c_type";
public static final Uri CONTENT_URI = public static final String TYPE_type = "INTEGER";
Uri.parse("content://" + DataProvider.AUTHORITY + "/accounts"); public static final String WHO_ID = "c_who_id";
public static final String CONTENT_TYPE = public static final String WHO_ID_type = "INTEGER";
"vnd.android.cursor.dir/vnd.thialfihar.apg.account"; public static final String KEY_RING_DATA = "c_key_ring_data";
public static final String CONTENT_ITEM_TYPE = public static final String KEY_RING_DATA_type = "BLOB";
"vnd.android.cursor.item/vnd.thialfihar.apg.account";
public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
} }

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg.provider;
import android.provider.BaseColumns;
public class Keys implements BaseColumns {
public static final String TABLE_NAME = "keys";
public static final String _ID_type = "INTEGER PRIMARY KEY";
public static final String KEY_ID = "c_key_id";
public static final String KEY_ID_type = "INT64";
public static final String TYPE = "c_type";
public static final String TYPE_type = "INTEGER";
public static final String IS_MASTER_KEY = "c_is_master_key";
public static final String IS_MASTER_KEY_type = "INTEGER";
public static final String ALGORITHM = "c_algorithm";
public static final String ALGORITHM_type = "INTEGER";
public static final String KEY_SIZE = "c_key_size";
public static final String KEY_SIZE_type = "INTEGER";
public static final String CAN_SIGN = "c_can_sign";
public static final String CAN_SIGN_type = "INTEGER";
public static final String CAN_ENCRYPT = "c_can_encrypt";
public static final String CAN_ENCRYPT_type = "INTEGER";
public static final String IS_REVOKED = "c_is_revoked";
public static final String IS_REVOKED_type = "INTEGER";
public static final String CREATION = "c_creation";
public static final String CREATION_type = "INTEGER";
public static final String EXPIRY = "c_expiry";
public static final String EXPIRY_type = "INTEGER";
public static final String KEY_RING_ID = "c_key_ring_id";
public static final String KEY_RING_ID_type = "INTEGER";
public static final String KEY_DATA = "c_key_data";
public static final String KEY_DATA_type = "BLOB";
public static final String RANK = "c_key_data";
public static final String RANK_type = "INTEGER";
}

View File

@ -1,22 +0,0 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg.provider;
public class PublicKeys extends PublicKeys1 {
private PublicKeys() {
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg.provider;
import android.net.Uri;
import android.provider.BaseColumns;
class PublicKeys1 implements BaseColumns {
public static final String TABLE_NAME = "public_keys";
public static final String _ID_type = "INTEGER PRIMARY KEY";
public static final String KEY_ID = "c_key_id";
public static final String KEY_ID_type = "INT64";
public static final String KEY_DATA = "c_key_data";
public static final String KEY_DATA_type = "BLOB";
public static final String WHO_ID = "c_who_id";
public static final String WHO_ID_type = "INTEGER";
public static final Uri CONTENT_URI =
Uri.parse("content://" + DataProvider.AUTHORITY + "/public_keys");
public static final Uri CONTENT_URI_BY_KEY_ID =
Uri.parse("content://" + DataProvider.AUTHORITY + "/public_keys/key_id");
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.thialfihar.apg.public_key";
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.public_key";
public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg.provider;
import android.net.Uri;
import android.provider.BaseColumns;
class SecretKeys1 implements BaseColumns {
public static final String TABLE_NAME = "secret_keys";
public static final String _ID_type = "INTEGER PRIMARY KEY";
public static final String KEY_ID = "c_key_id";
public static final String KEY_ID_type = "INT64";
public static final String KEY_DATA = "c_key_data";
public static final String KEY_DATA_type = "BLOB";
public static final String WHO_ID = "c_who_id";
public static final String WHO_ID_type = "INTEGER";
public static final Uri CONTENT_URI =
Uri.parse("content://" + DataProvider.AUTHORITY + "/secret_keys");
public static final Uri CONTENT_URI_BY_KEY_ID =
Uri.parse("content://" + DataProvider.AUTHORITY + "/secret_keys/key_id");
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.thialfihar.apg.secret_key";
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.secret_key";
public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
}

View File

@ -16,7 +16,16 @@
package org.thialfihar.android.apg.provider; package org.thialfihar.android.apg.provider;
public class SecretKeys extends SecretKeys1 { import android.provider.BaseColumns;
private SecretKeys() {
} 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";
} }

View File

@ -76,7 +76,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
} }
} }
String error = data.getString("error"); String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) { if (error != null) {
Toast.makeText(getContext(), Toast.makeText(getContext(),
getContext().getString(R.string.errorMessage, error), getContext().getString(R.string.errorMessage, error),
@ -310,24 +310,24 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mNewKeySize, passPhrase, mNewKeySize, passPhrase,
masterKey); masterKey);
} catch (NoSuchProviderException e) { } catch (NoSuchProviderException e) {
error = e.getMessage(); error = "" + e;
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
error = e.getMessage(); error = "" + e;
} catch (PGPException e) { } catch (PGPException e) {
error = e.getMessage(); error = "" + e;
} catch (InvalidParameterException e) { } catch (InvalidParameterException e) {
error = e.getMessage(); error = "" + e;
} catch (InvalidAlgorithmParameterException e) { } catch (InvalidAlgorithmParameterException e) {
error = e.getMessage(); error = "" + e;
} catch (Apg.GeneralException e) { } catch (Apg.GeneralException e) {
error = e.getMessage(); error = "" + e;
} }
Message message = new Message(); Message message = new Message();
Bundle data = new Bundle(); Bundle data = new Bundle();
data.putBoolean("closeProgressDialog", true); data.putBoolean("closeProgressDialog", true);
if (error != null) { if (error != null) {
data.putString("error", error); data.putString(Apg.EXTRA_ERROR, error);
} else { } else {
data.putBoolean("gotNewKey", true); data.putBoolean("gotNewKey", true);
} }