From be569984b893e8cb412b9cc2b3826c92fd4c9d81 Mon Sep 17 00:00:00 2001 From: Dominik Date: Mon, 19 Nov 2012 17:56:58 +0100 Subject: rename main folders --- APG-API-Demo/.gitignore | 23 + APG-API-Demo/AndroidManifest.xml | 36 + APG-API-Demo/build.xml | 83 + APG-API-Demo/proguard-project.txt | 20 + APG-API-Demo/project.properties | 12 + APG-API-Demo/res/drawable-hdpi/icon.png | Bin 0 -> 5308 bytes APG-API-Demo/res/drawable-ldpi/icon.png | Bin 0 -> 2027 bytes APG-API-Demo/res/drawable-mdpi/icon.png | Bin 0 -> 3035 bytes APG-API-Demo/res/drawable-xhdpi/icon.png | Bin 0 -> 8165 bytes APG-API-Demo/res/layout/aidl_demo.xml | 72 + APG-API-Demo/res/layout/intent_demo.xml | 93 + APG-API-Demo/res/xml/base_preference.xml | 15 + .../android/apg/demo/AidlDemoActivity.java | 185 ++ .../thialfihar/android/apg/demo/BaseActivity.java | 69 + .../android/apg/demo/IntentDemoActivity.java | 110 ++ APG-API-Lib/.gitignore | 23 + APG-API-Lib/AndroidManifest.xml | 11 + APG-API-Lib/build.xml | 83 + APG-API-Lib/proguard-project.txt | 20 + APG-API-Lib/project.properties | 15 + APG-API-Lib/res/.readme | 1 + .../android/apg/integration/ApgData.java | 107 ++ .../android/apg/integration/ApgIntentHelper.java | 318 ++++ .../android/apg/integration/ApgServiceHelper.java | 102 ++ .../android/apg/integration/Constants.java | 27 + .../apg/integration/ContentProviderHelper.java | 195 +++ .../thialfihar/android/apg/integration/Util.java | 51 + .../apg/service/IApgEncryptDecryptHandler.aidl | 16 + .../android/apg/service/IApgHelperHandler.aidl | 9 + .../android/apg/service/IApgService.aidl | 130 ++ .../android/apg/service/IApgSignVerifyHandler.aidl | 11 + APG/.gitignore | 23 + APG/AndroidManifest.xml | 379 ++++ APG/build.xml | 83 + ...arcodescanner-android-integration-supportv4.jar | Bin 0 -> 8878 bytes APG/libs/htmlcleaner-2.2.jar | Bin 0 -> 107942 bytes APG/libs/htmlspanner-0.2-fork.jar | Bin 0 -> 31039 bytes APG/libs/sc-bzip2-1.47.0.2.jar | Bin 0 -> 26285 bytes APG/libs/sc-light-jdk15on-1.47.0.2.jar | Bin 0 -> 1482715 bytes APG/libs/scpg-jdk15on-1.47.0.2.jar | Bin 0 -> 278984 bytes APG/libs/scprov-jdk15on-1.47.0.2.jar | Bin 0 -> 987827 bytes APG/pom.xml | 67 + APG/proguard-project.txt | 20 + APG/project.properties | 12 + APG/res/anim/push_left_in.xml | 20 + APG/res/anim/push_left_out.xml | 20 + APG/res/anim/push_right_in.xml | 20 + APG/res/anim/push_right_out.xml | 20 + APG/res/drawable-finger/btn_circle.xml | 32 + .../drawable-hdpi-finger/btn_circle_disable.png | Bin 0 -> 2631 bytes .../btn_circle_disable_focused.png | Bin 0 -> 3001 bytes APG/res/drawable-hdpi-finger/btn_circle_normal.png | Bin 0 -> 1974 bytes .../drawable-hdpi-finger/btn_circle_pressed.png | Bin 0 -> 2624 bytes .../drawable-hdpi-finger/btn_circle_selected.png | Bin 0 -> 2554 bytes .../drawable-hdpi-finger/ic_btn_round_minus.png | Bin 0 -> 536 bytes APG/res/drawable-hdpi-finger/ic_btn_round_plus.png | Bin 0 -> 1316 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 144 bytes .../drawable-hdpi/dashboard_decrypt_default.png | Bin 0 -> 4996 bytes .../drawable-hdpi/dashboard_decrypt_pressed.png | Bin 0 -> 5032 bytes .../drawable-hdpi/dashboard_encrypt_default.png | Bin 0 -> 4926 bytes .../drawable-hdpi/dashboard_encrypt_pressed.png | Bin 0 -> 4967 bytes APG/res/drawable-hdpi/dashboard_help_default.png | Bin 0 -> 8607 bytes APG/res/drawable-hdpi/dashboard_help_pressed.png | Bin 0 -> 8343 bytes .../dashboard_manage_keys_default.png | Bin 0 -> 7306 bytes .../dashboard_manage_keys_pressed.png | Bin 0 -> 7147 bytes .../drawable-hdpi/dashboard_my_keys_default.png | Bin 0 -> 5438 bytes .../drawable-hdpi/dashboard_my_keys_pressed.png | Bin 0 -> 5414 bytes .../dashboard_scan_qrcode_default.png | Bin 0 -> 4241 bytes .../dashboard_scan_qrcode_pressed.png | Bin 0 -> 4418 bytes APG/res/drawable-hdpi/encrypted.png | Bin 0 -> 3561 bytes APG/res/drawable-hdpi/encrypted_large.png | Bin 0 -> 5244 bytes APG/res/drawable-hdpi/encrypted_small.png | Bin 0 -> 2187 bytes APG/res/drawable-hdpi/ic_menu_about.png | Bin 0 -> 1764 bytes APG/res/drawable-hdpi/ic_menu_decrypt.png | Bin 0 -> 1387 bytes APG/res/drawable-hdpi/ic_menu_encrypt.png | Bin 0 -> 1384 bytes APG/res/drawable-hdpi/ic_menu_filebrowser.png | Bin 0 -> 1443 bytes APG/res/drawable-hdpi/ic_menu_scan_qrcode.png | Bin 0 -> 1762 bytes APG/res/drawable-hdpi/ic_menu_search.png | Bin 0 -> 1218 bytes APG/res/drawable-hdpi/ic_menu_search_list.png | Bin 0 -> 1190 bytes APG/res/drawable-hdpi/ic_menu_settings.png | Bin 0 -> 1227 bytes APG/res/drawable-hdpi/ic_next.png | Bin 0 -> 1722 bytes APG/res/drawable-hdpi/ic_previous.png | Bin 0 -> 1712 bytes APG/res/drawable-hdpi/icon.png | Bin 0 -> 5308 bytes APG/res/drawable-hdpi/key.png | Bin 0 -> 3675 bytes APG/res/drawable-hdpi/key_large.png | Bin 0 -> 5550 bytes APG/res/drawable-hdpi/key_small.png | Bin 0 -> 2088 bytes APG/res/drawable-hdpi/overlay_error.png | Bin 0 -> 1986 bytes APG/res/drawable-hdpi/overlay_ok.png | Bin 0 -> 1702 bytes APG/res/drawable-hdpi/signed.png | Bin 0 -> 3858 bytes APG/res/drawable-hdpi/signed_large.png | Bin 0 -> 5928 bytes APG/res/drawable-hdpi/signed_small.png | Bin 0 -> 2219 bytes APG/res/drawable-ldpi/encrypted.png | Bin 0 -> 1513 bytes APG/res/drawable-ldpi/encrypted_large.png | Bin 0 -> 2486 bytes APG/res/drawable-ldpi/encrypted_small.png | Bin 0 -> 1176 bytes APG/res/drawable-ldpi/ic_next.png | Bin 0 -> 916 bytes APG/res/drawable-ldpi/ic_previous.png | Bin 0 -> 922 bytes APG/res/drawable-ldpi/icon.png | Bin 0 -> 2027 bytes APG/res/drawable-ldpi/key.png | Bin 0 -> 1484 bytes APG/res/drawable-ldpi/key_large.png | Bin 0 -> 2462 bytes APG/res/drawable-ldpi/key_small.png | Bin 0 -> 1074 bytes APG/res/drawable-ldpi/overlay_error.png | Bin 0 -> 1192 bytes APG/res/drawable-ldpi/overlay_ok.png | Bin 0 -> 1038 bytes APG/res/drawable-ldpi/signed.png | Bin 0 -> 1576 bytes APG/res/drawable-ldpi/signed_large.png | Bin 0 -> 2611 bytes APG/res/drawable-ldpi/signed_small.png | Bin 0 -> 1149 bytes .../drawable-mdpi-finger/btn_circle_disable.png | Bin 0 -> 938 bytes .../btn_circle_disable_focused.png | Bin 0 -> 1436 bytes APG/res/drawable-mdpi-finger/btn_circle_normal.png | Bin 0 -> 1249 bytes .../drawable-mdpi-finger/btn_circle_pressed.png | Bin 0 -> 1613 bytes .../drawable-mdpi-finger/btn_circle_selected.png | Bin 0 -> 1645 bytes .../drawable-mdpi-finger/ic_btn_round_minus.png | Bin 0 -> 288 bytes APG/res/drawable-mdpi-finger/ic_btn_round_plus.png | Bin 0 -> 526 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 134 bytes APG/res/drawable-mdpi/encrypted.png | Bin 0 -> 2486 bytes APG/res/drawable-mdpi/encrypted_large.png | Bin 0 -> 3561 bytes APG/res/drawable-mdpi/encrypted_small.png | Bin 0 -> 1513 bytes APG/res/drawable-mdpi/ic_menu_about.png | Bin 0 -> 1441 bytes APG/res/drawable-mdpi/ic_menu_decrypt.png | Bin 0 -> 1269 bytes APG/res/drawable-mdpi/ic_menu_encrypt.png | Bin 0 -> 1263 bytes APG/res/drawable-mdpi/ic_menu_filebrowser.png | Bin 0 -> 1307 bytes APG/res/drawable-mdpi/ic_menu_scan_qrcode.png | Bin 0 -> 1460 bytes APG/res/drawable-mdpi/ic_menu_search.png | Bin 0 -> 858 bytes APG/res/drawable-mdpi/ic_menu_search_list.png | Bin 0 -> 863 bytes APG/res/drawable-mdpi/ic_menu_settings.png | Bin 0 -> 866 bytes APG/res/drawable-mdpi/ic_next.png | Bin 0 -> 1360 bytes APG/res/drawable-mdpi/ic_previous.png | Bin 0 -> 1352 bytes APG/res/drawable-mdpi/icon.png | Bin 0 -> 3035 bytes APG/res/drawable-mdpi/key.png | Bin 0 -> 2462 bytes APG/res/drawable-mdpi/key_large.png | Bin 0 -> 3675 bytes APG/res/drawable-mdpi/key_small.png | Bin 0 -> 1484 bytes APG/res/drawable-mdpi/overlay_error.png | Bin 0 -> 1539 bytes APG/res/drawable-mdpi/overlay_ok.png | Bin 0 -> 1305 bytes APG/res/drawable-mdpi/signed.png | Bin 0 -> 2611 bytes APG/res/drawable-mdpi/signed_large.png | Bin 0 -> 3858 bytes APG/res/drawable-mdpi/signed_small.png | Bin 0 -> 1576 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 166 bytes APG/res/drawable-xhdpi/ic_menu_about.png | Bin 0 -> 2257 bytes APG/res/drawable-xhdpi/ic_menu_decrypt.png | Bin 0 -> 1542 bytes APG/res/drawable-xhdpi/ic_menu_encrypt.png | Bin 0 -> 1542 bytes APG/res/drawable-xhdpi/ic_menu_filebrowser.png | Bin 0 -> 1604 bytes APG/res/drawable-xhdpi/ic_menu_scan_qrcode.png | Bin 0 -> 2048 bytes APG/res/drawable-xhdpi/ic_menu_search.png | Bin 0 -> 1629 bytes APG/res/drawable-xhdpi/ic_menu_search_list.png | Bin 0 -> 1571 bytes APG/res/drawable-xhdpi/ic_menu_settings.png | Bin 0 -> 1622 bytes APG/res/drawable-xhdpi/icon.png | Bin 0 -> 8165 bytes APG/res/drawable/btn_circle_disable.png | Bin 0 -> 938 bytes APG/res/drawable/btn_circle_disable_focused.png | Bin 0 -> 1436 bytes APG/res/drawable/btn_circle_normal.png | Bin 0 -> 1249 bytes APG/res/drawable/btn_circle_pressed.png | Bin 0 -> 1613 bytes APG/res/drawable/btn_circle_selected.png | Bin 0 -> 1645 bytes APG/res/drawable/dashboard_decrypt.xml | 28 + APG/res/drawable/dashboard_encrypt.xml | 28 + APG/res/drawable/dashboard_help.xml | 28 + APG/res/drawable/dashboard_manage_keys.xml | 28 + APG/res/drawable/dashboard_my_keys.xml | 28 + APG/res/drawable/dashboard_scan_qrcode.xml | 28 + APG/res/drawable/encrypted.png | Bin 0 -> 2486 bytes APG/res/drawable/encrypted_large.png | Bin 0 -> 3561 bytes APG/res/drawable/encrypted_small.png | Bin 0 -> 1513 bytes APG/res/drawable/ic_btn_round_minus.png | Bin 0 -> 288 bytes APG/res/drawable/ic_btn_round_plus.png | Bin 0 -> 526 bytes APG/res/drawable/ic_launcher_folder.png | Bin 0 -> 2235 bytes APG/res/drawable/ic_launcher_folder_small.png | Bin 0 -> 1522 bytes APG/res/drawable/ic_next.png | Bin 0 -> 1360 bytes APG/res/drawable/ic_previous.png | Bin 0 -> 1352 bytes APG/res/drawable/key.png | Bin 0 -> 2462 bytes APG/res/drawable/key_large.png | Bin 0 -> 3675 bytes APG/res/drawable/key_small.png | Bin 0 -> 1484 bytes APG/res/drawable/overlay_error.png | Bin 0 -> 1539 bytes APG/res/drawable/overlay_ok.png | Bin 0 -> 1305 bytes APG/res/drawable/section_header.xml | 11 + APG/res/drawable/signed.png | Bin 0 -> 2611 bytes APG/res/drawable/signed_large.png | Bin 0 -> 3858 bytes APG/res/drawable/signed_small.png | Bin 0 -> 1576 bytes APG/res/layout/account_item.xml | 35 + APG/res/layout/create_key.xml | 73 + APG/res/layout/decrypt.xml | 181 ++ APG/res/layout/edit_key.xml | 59 + APG/res/layout/edit_key_key_item.xml | 134 ++ APG/res/layout/edit_key_section.xml | 57 + APG/res/layout/edit_key_user_id_item.xml | 107 ++ APG/res/layout/encrypt.xml | 306 ++++ APG/res/layout/file_dialog.xml | 49 + APG/res/layout/filter_info.xml | 38 + APG/res/layout/general.xml | 59 + APG/res/layout/help_activity.xml | 14 + APG/res/layout/help_fragment_about.xml | 62 + APG/res/layout/import_from_qr_code.xml | 66 + APG/res/layout/key_list_child_item.xml | 96 + APG/res/layout/key_list_group_item.xml | 40 + APG/res/layout/key_list_public_activity.xml | 25 + APG/res/layout/key_list_secret_activity.xml | 25 + APG/res/layout/key_server_editor.xml | 52 + APG/res/layout/key_server_export_layout.xml | 34 + APG/res/layout/key_server_preference.xml | 90 + APG/res/layout/key_server_query_layout.xml | 53 + APG/res/layout/key_server_query_result_item.xml | 97 ++ APG/res/layout/key_server_query_result_user_id.xml | 26 + APG/res/layout/main.xml | 124 ++ APG/res/layout/passphrase.xml | 40 + APG/res/layout/passphrase_repeat.xml | 62 + APG/res/layout/select_key_item.xml | 82 + APG/res/layout/select_public_key_activity.xml | 13 + APG/res/layout/select_secret_key_activity.xml | 13 + APG/res/layout/sign_key_layout.xml | 40 + APG/res/raw/help_about.html | 31 + APG/res/raw/help_changelog.html | 86 + APG/res/raw/help_start.html | 27 + APG/res/values/arrays.xml | 47 + APG/res/values/colors.xml | 7 + APG/res/values/static_strings.xml | 7 + APG/res/values/strings.xml | 352 ++++ APG/res/values/styles.xml | 52 + APG/res/xml/apg_preferences.xml | 63 + APG/res/xml/searchable_public_keys.xml | 22 + APG/res/xml/searchable_secret_keys.xml | 22 + .../org/thialfihar/android/apg/ApgApplication.java | 48 + APG/src/org/thialfihar/android/apg/Constants.java | 66 + APG/src/org/thialfihar/android/apg/Id.java | 228 +++ .../thialfihar/android/apg/helper/FileHelper.java | 121 ++ .../thialfihar/android/apg/helper/OtherHelper.java | 183 ++ .../android/apg/helper/PGPConversionHelper.java | 137 ++ .../thialfihar/android/apg/helper/PGPHelper.java | 452 +++++ .../org/thialfihar/android/apg/helper/PGPMain.java | 1827 ++++++++++++++++++++ .../thialfihar/android/apg/helper/Preferences.java | 172 ++ .../android/apg/provider/ApgContract.java | 209 +++ .../android/apg/provider/ApgDatabase.java | 104 ++ .../android/apg/provider/ApgProvider.java | 825 +++++++++ .../android/apg/provider/ApgProviderExternal.java | 32 + .../android/apg/provider/ApgProviderInternal.java | 30 + .../apg/provider/ApgServiceBlobContract.java | 40 + .../apg/provider/ApgServiceBlobDatabase.java | 47 + .../apg/provider/ApgServiceBlobProvider.java | 151 ++ .../android/apg/provider/ProviderHelper.java | 501 ++++++ .../android/apg/service/ApgIntentService.java | 881 ++++++++++ .../apg/service/ApgIntentServiceHandler.java | 109 ++ .../thialfihar/android/apg/service/ApgService.java | 326 ++++ .../apg/service/IApgEncryptDecryptHandler.aidl | 32 + .../android/apg/service/IApgHelperHandler.aidl | 25 + .../android/apg/service/IApgService.aidl | 146 ++ .../android/apg/service/IApgSignVerifyHandler.aidl | 27 + .../apg/service/PassphraseCacheService.java | 230 +++ .../thialfihar/android/apg/ui/DecryptActivity.java | 940 ++++++++++ .../thialfihar/android/apg/ui/EditKeyActivity.java | 569 ++++++ .../thialfihar/android/apg/ui/EncryptActivity.java | 1099 ++++++++++++ .../thialfihar/android/apg/ui/HelpActivity.java | 162 ++ .../android/apg/ui/HelpFragmentAbout.java | 107 ++ .../android/apg/ui/HelpFragmentHtml.java | 109 ++ .../android/apg/ui/ImportFromQRCodeActivity.java | 273 +++ .../thialfihar/android/apg/ui/KeyListActivity.java | 452 +++++ .../thialfihar/android/apg/ui/KeyListFragment.java | 82 + .../android/apg/ui/KeyListPublicActivity.java | 102 ++ .../android/apg/ui/KeyListPublicFragment.java | 183 ++ .../android/apg/ui/KeyListSecretActivity.java | 124 ++ .../android/apg/ui/KeyListSecretFragment.java | 154 ++ .../android/apg/ui/KeyServerQueryActivity.java | 353 ++++ .../android/apg/ui/KeyServerUploadActivity.java | 146 ++ .../thialfihar/android/apg/ui/MainActivity.java | 102 ++ .../android/apg/ui/PreferencesActivity.java | 240 +++ .../apg/ui/PreferencesKeyServerActivity.java | 162 ++ .../android/apg/ui/SelectPublicKeyActivity.java | 161 ++ .../android/apg/ui/SelectPublicKeyFragment.java | 243 +++ .../android/apg/ui/SelectSecretKeyActivity.java | 128 ++ .../android/apg/ui/SelectSecretKeyFragment.java | 154 ++ .../thialfihar/android/apg/ui/SignKeyActivity.java | 313 ++++ .../apg/ui/dialog/DeleteFileDialogFragment.java | 118 ++ .../apg/ui/dialog/DeleteKeyDialogFragment.java | 133 ++ .../android/apg/ui/dialog/FileDialogFragment.java | 193 +++ .../ui/dialog/LookupUnknownKeyDialogFragment.java | 133 ++ .../apg/ui/dialog/PassphraseDialogFragment.java | 273 +++ .../apg/ui/dialog/ProgressDialogFragment.java | 124 ++ .../apg/ui/dialog/SetPassphraseDialogFragment.java | 187 ++ .../android/apg/ui/widget/DashboardLayout.java | 186 ++ .../thialfihar/android/apg/ui/widget/Editor.java | 25 + .../apg/ui/widget/ExpandableListFragment.java | 530 ++++++ .../apg/ui/widget/IntegerListPreference.java | 95 + .../android/apg/ui/widget/KeyEditor.java | 236 +++ .../android/apg/ui/widget/KeyListAdapter.java | 264 +++ .../android/apg/ui/widget/KeyServerEditor.java | 78 + .../android/apg/ui/widget/SectionView.java | 327 ++++ .../apg/ui/widget/SelectKeyCursorAdapter.java | 142 ++ .../android/apg/ui/widget/UserIdEditor.java | 196 +++ .../org/thialfihar/android/apg/util/Choice.java | 45 + .../thialfihar/android/apg/util/Compatibility.java | 94 + .../thialfihar/android/apg/util/HkpKeyServer.java | 257 +++ .../org/thialfihar/android/apg/util/InputData.java | 40 + .../android/apg/util/IterableIterator.java | 31 + .../org/thialfihar/android/apg/util/KeyServer.java | 97 ++ APG/src/org/thialfihar/android/apg/util/Log.java | 83 + .../android/apg/util/PositionAwareInputStream.java | 81 + .../org/thialfihar/android/apg/util/Primes.java | 188 ++ .../android/apg/util/ProgressDialogUpdater.java | 25 + org_apg/.gitignore | 23 - org_apg/AndroidManifest.xml | 379 ---- org_apg/build.xml | 83 - ...arcodescanner-android-integration-supportv4.jar | Bin 8878 -> 0 bytes org_apg/libs/htmlcleaner-2.2.jar | Bin 107942 -> 0 bytes org_apg/libs/htmlspanner-0.2-fork.jar | Bin 31039 -> 0 bytes org_apg/libs/sc-bzip2-1.47.0.2.jar | Bin 26285 -> 0 bytes org_apg/libs/sc-light-jdk15on-1.47.0.2.jar | Bin 1482715 -> 0 bytes org_apg/libs/scpg-jdk15on-1.47.0.2.jar | Bin 278984 -> 0 bytes org_apg/libs/scprov-jdk15on-1.47.0.2.jar | Bin 987827 -> 0 bytes org_apg/pom.xml | 67 - org_apg/proguard-project.txt | 20 - org_apg/project.properties | 12 - org_apg/res/anim/push_left_in.xml | 20 - org_apg/res/anim/push_left_out.xml | 20 - org_apg/res/anim/push_right_in.xml | 20 - org_apg/res/anim/push_right_out.xml | 20 - org_apg/res/drawable-finger/btn_circle.xml | 32 - .../drawable-hdpi-finger/btn_circle_disable.png | Bin 2631 -> 0 bytes .../btn_circle_disable_focused.png | Bin 3001 -> 0 bytes .../res/drawable-hdpi-finger/btn_circle_normal.png | Bin 1974 -> 0 bytes .../drawable-hdpi-finger/btn_circle_pressed.png | Bin 2624 -> 0 bytes .../drawable-hdpi-finger/btn_circle_selected.png | Bin 2554 -> 0 bytes .../drawable-hdpi-finger/ic_btn_round_minus.png | Bin 536 -> 0 bytes .../res/drawable-hdpi-finger/ic_btn_round_plus.png | Bin 1316 -> 0 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 144 -> 0 bytes .../drawable-hdpi/dashboard_decrypt_default.png | Bin 4996 -> 0 bytes .../drawable-hdpi/dashboard_decrypt_pressed.png | Bin 5032 -> 0 bytes .../drawable-hdpi/dashboard_encrypt_default.png | Bin 4926 -> 0 bytes .../drawable-hdpi/dashboard_encrypt_pressed.png | Bin 4967 -> 0 bytes .../res/drawable-hdpi/dashboard_help_default.png | Bin 8607 -> 0 bytes .../res/drawable-hdpi/dashboard_help_pressed.png | Bin 8343 -> 0 bytes .../dashboard_manage_keys_default.png | Bin 7306 -> 0 bytes .../dashboard_manage_keys_pressed.png | Bin 7147 -> 0 bytes .../drawable-hdpi/dashboard_my_keys_default.png | Bin 5438 -> 0 bytes .../drawable-hdpi/dashboard_my_keys_pressed.png | Bin 5414 -> 0 bytes .../dashboard_scan_qrcode_default.png | Bin 4241 -> 0 bytes .../dashboard_scan_qrcode_pressed.png | Bin 4418 -> 0 bytes org_apg/res/drawable-hdpi/encrypted.png | Bin 3561 -> 0 bytes org_apg/res/drawable-hdpi/encrypted_large.png | Bin 5244 -> 0 bytes org_apg/res/drawable-hdpi/encrypted_small.png | Bin 2187 -> 0 bytes org_apg/res/drawable-hdpi/ic_menu_about.png | Bin 1764 -> 0 bytes org_apg/res/drawable-hdpi/ic_menu_decrypt.png | Bin 1387 -> 0 bytes org_apg/res/drawable-hdpi/ic_menu_encrypt.png | Bin 1384 -> 0 bytes org_apg/res/drawable-hdpi/ic_menu_filebrowser.png | Bin 1443 -> 0 bytes org_apg/res/drawable-hdpi/ic_menu_scan_qrcode.png | Bin 1762 -> 0 bytes org_apg/res/drawable-hdpi/ic_menu_search.png | Bin 1218 -> 0 bytes org_apg/res/drawable-hdpi/ic_menu_search_list.png | Bin 1190 -> 0 bytes org_apg/res/drawable-hdpi/ic_menu_settings.png | Bin 1227 -> 0 bytes org_apg/res/drawable-hdpi/ic_next.png | Bin 1722 -> 0 bytes org_apg/res/drawable-hdpi/ic_previous.png | Bin 1712 -> 0 bytes org_apg/res/drawable-hdpi/icon.png | Bin 5308 -> 0 bytes org_apg/res/drawable-hdpi/key.png | Bin 3675 -> 0 bytes org_apg/res/drawable-hdpi/key_large.png | Bin 5550 -> 0 bytes org_apg/res/drawable-hdpi/key_small.png | Bin 2088 -> 0 bytes org_apg/res/drawable-hdpi/overlay_error.png | Bin 1986 -> 0 bytes org_apg/res/drawable-hdpi/overlay_ok.png | Bin 1702 -> 0 bytes org_apg/res/drawable-hdpi/signed.png | Bin 3858 -> 0 bytes org_apg/res/drawable-hdpi/signed_large.png | Bin 5928 -> 0 bytes org_apg/res/drawable-hdpi/signed_small.png | Bin 2219 -> 0 bytes org_apg/res/drawable-ldpi/encrypted.png | Bin 1513 -> 0 bytes org_apg/res/drawable-ldpi/encrypted_large.png | Bin 2486 -> 0 bytes org_apg/res/drawable-ldpi/encrypted_small.png | Bin 1176 -> 0 bytes org_apg/res/drawable-ldpi/ic_next.png | Bin 916 -> 0 bytes org_apg/res/drawable-ldpi/ic_previous.png | Bin 922 -> 0 bytes org_apg/res/drawable-ldpi/icon.png | Bin 2027 -> 0 bytes org_apg/res/drawable-ldpi/key.png | Bin 1484 -> 0 bytes org_apg/res/drawable-ldpi/key_large.png | Bin 2462 -> 0 bytes org_apg/res/drawable-ldpi/key_small.png | Bin 1074 -> 0 bytes org_apg/res/drawable-ldpi/overlay_error.png | Bin 1192 -> 0 bytes org_apg/res/drawable-ldpi/overlay_ok.png | Bin 1038 -> 0 bytes org_apg/res/drawable-ldpi/signed.png | Bin 1576 -> 0 bytes org_apg/res/drawable-ldpi/signed_large.png | Bin 2611 -> 0 bytes org_apg/res/drawable-ldpi/signed_small.png | Bin 1149 -> 0 bytes .../drawable-mdpi-finger/btn_circle_disable.png | Bin 938 -> 0 bytes .../btn_circle_disable_focused.png | Bin 1436 -> 0 bytes .../res/drawable-mdpi-finger/btn_circle_normal.png | Bin 1249 -> 0 bytes .../drawable-mdpi-finger/btn_circle_pressed.png | Bin 1613 -> 0 bytes .../drawable-mdpi-finger/btn_circle_selected.png | Bin 1645 -> 0 bytes .../drawable-mdpi-finger/ic_btn_round_minus.png | Bin 288 -> 0 bytes .../res/drawable-mdpi-finger/ic_btn_round_plus.png | Bin 526 -> 0 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 134 -> 0 bytes org_apg/res/drawable-mdpi/encrypted.png | Bin 2486 -> 0 bytes org_apg/res/drawable-mdpi/encrypted_large.png | Bin 3561 -> 0 bytes org_apg/res/drawable-mdpi/encrypted_small.png | Bin 1513 -> 0 bytes org_apg/res/drawable-mdpi/ic_menu_about.png | Bin 1441 -> 0 bytes org_apg/res/drawable-mdpi/ic_menu_decrypt.png | Bin 1269 -> 0 bytes org_apg/res/drawable-mdpi/ic_menu_encrypt.png | Bin 1263 -> 0 bytes org_apg/res/drawable-mdpi/ic_menu_filebrowser.png | Bin 1307 -> 0 bytes org_apg/res/drawable-mdpi/ic_menu_scan_qrcode.png | Bin 1460 -> 0 bytes org_apg/res/drawable-mdpi/ic_menu_search.png | Bin 858 -> 0 bytes org_apg/res/drawable-mdpi/ic_menu_search_list.png | Bin 863 -> 0 bytes org_apg/res/drawable-mdpi/ic_menu_settings.png | Bin 866 -> 0 bytes org_apg/res/drawable-mdpi/ic_next.png | Bin 1360 -> 0 bytes org_apg/res/drawable-mdpi/ic_previous.png | Bin 1352 -> 0 bytes org_apg/res/drawable-mdpi/icon.png | Bin 3035 -> 0 bytes org_apg/res/drawable-mdpi/key.png | Bin 2462 -> 0 bytes org_apg/res/drawable-mdpi/key_large.png | Bin 3675 -> 0 bytes org_apg/res/drawable-mdpi/key_small.png | Bin 1484 -> 0 bytes org_apg/res/drawable-mdpi/overlay_error.png | Bin 1539 -> 0 bytes org_apg/res/drawable-mdpi/overlay_ok.png | Bin 1305 -> 0 bytes org_apg/res/drawable-mdpi/signed.png | Bin 2611 -> 0 bytes org_apg/res/drawable-mdpi/signed_large.png | Bin 3858 -> 0 bytes org_apg/res/drawable-mdpi/signed_small.png | Bin 1576 -> 0 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 166 -> 0 bytes org_apg/res/drawable-xhdpi/ic_menu_about.png | Bin 2257 -> 0 bytes org_apg/res/drawable-xhdpi/ic_menu_decrypt.png | Bin 1542 -> 0 bytes org_apg/res/drawable-xhdpi/ic_menu_encrypt.png | Bin 1542 -> 0 bytes org_apg/res/drawable-xhdpi/ic_menu_filebrowser.png | Bin 1604 -> 0 bytes org_apg/res/drawable-xhdpi/ic_menu_scan_qrcode.png | Bin 2048 -> 0 bytes org_apg/res/drawable-xhdpi/ic_menu_search.png | Bin 1629 -> 0 bytes org_apg/res/drawable-xhdpi/ic_menu_search_list.png | Bin 1571 -> 0 bytes org_apg/res/drawable-xhdpi/ic_menu_settings.png | Bin 1622 -> 0 bytes org_apg/res/drawable-xhdpi/icon.png | Bin 8165 -> 0 bytes org_apg/res/drawable/btn_circle_disable.png | Bin 938 -> 0 bytes .../res/drawable/btn_circle_disable_focused.png | Bin 1436 -> 0 bytes org_apg/res/drawable/btn_circle_normal.png | Bin 1249 -> 0 bytes org_apg/res/drawable/btn_circle_pressed.png | Bin 1613 -> 0 bytes org_apg/res/drawable/btn_circle_selected.png | Bin 1645 -> 0 bytes org_apg/res/drawable/dashboard_decrypt.xml | 28 - org_apg/res/drawable/dashboard_encrypt.xml | 28 - org_apg/res/drawable/dashboard_help.xml | 28 - org_apg/res/drawable/dashboard_manage_keys.xml | 28 - org_apg/res/drawable/dashboard_my_keys.xml | 28 - org_apg/res/drawable/dashboard_scan_qrcode.xml | 28 - org_apg/res/drawable/encrypted.png | Bin 2486 -> 0 bytes org_apg/res/drawable/encrypted_large.png | Bin 3561 -> 0 bytes org_apg/res/drawable/encrypted_small.png | Bin 1513 -> 0 bytes org_apg/res/drawable/ic_btn_round_minus.png | Bin 288 -> 0 bytes org_apg/res/drawable/ic_btn_round_plus.png | Bin 526 -> 0 bytes org_apg/res/drawable/ic_launcher_folder.png | Bin 2235 -> 0 bytes org_apg/res/drawable/ic_launcher_folder_small.png | Bin 1522 -> 0 bytes org_apg/res/drawable/ic_next.png | Bin 1360 -> 0 bytes org_apg/res/drawable/ic_previous.png | Bin 1352 -> 0 bytes org_apg/res/drawable/key.png | Bin 2462 -> 0 bytes org_apg/res/drawable/key_large.png | Bin 3675 -> 0 bytes org_apg/res/drawable/key_small.png | Bin 1484 -> 0 bytes org_apg/res/drawable/overlay_error.png | Bin 1539 -> 0 bytes org_apg/res/drawable/overlay_ok.png | Bin 1305 -> 0 bytes org_apg/res/drawable/section_header.xml | 11 - org_apg/res/drawable/signed.png | Bin 2611 -> 0 bytes org_apg/res/drawable/signed_large.png | Bin 3858 -> 0 bytes org_apg/res/drawable/signed_small.png | Bin 1576 -> 0 bytes org_apg/res/layout/account_item.xml | 35 - org_apg/res/layout/create_key.xml | 73 - org_apg/res/layout/decrypt.xml | 181 -- org_apg/res/layout/edit_key.xml | 59 - org_apg/res/layout/edit_key_key_item.xml | 134 -- org_apg/res/layout/edit_key_section.xml | 57 - org_apg/res/layout/edit_key_user_id_item.xml | 107 -- org_apg/res/layout/encrypt.xml | 306 ---- org_apg/res/layout/file_dialog.xml | 49 - org_apg/res/layout/filter_info.xml | 38 - org_apg/res/layout/general.xml | 59 - org_apg/res/layout/help_activity.xml | 14 - org_apg/res/layout/help_fragment_about.xml | 62 - org_apg/res/layout/import_from_qr_code.xml | 66 - org_apg/res/layout/key_list_child_item.xml | 96 - org_apg/res/layout/key_list_group_item.xml | 40 - org_apg/res/layout/key_list_public_activity.xml | 25 - org_apg/res/layout/key_list_secret_activity.xml | 25 - org_apg/res/layout/key_server_editor.xml | 52 - org_apg/res/layout/key_server_export_layout.xml | 34 - org_apg/res/layout/key_server_preference.xml | 90 - org_apg/res/layout/key_server_query_layout.xml | 53 - .../res/layout/key_server_query_result_item.xml | 97 -- .../res/layout/key_server_query_result_user_id.xml | 26 - org_apg/res/layout/main.xml | 124 -- org_apg/res/layout/passphrase.xml | 40 - org_apg/res/layout/passphrase_repeat.xml | 62 - org_apg/res/layout/select_key_item.xml | 82 - org_apg/res/layout/select_public_key_activity.xml | 13 - org_apg/res/layout/select_secret_key_activity.xml | 13 - org_apg/res/layout/sign_key_layout.xml | 40 - org_apg/res/raw/help_about.html | 31 - org_apg/res/raw/help_changelog.html | 86 - org_apg/res/raw/help_start.html | 27 - org_apg/res/values/arrays.xml | 47 - org_apg/res/values/colors.xml | 7 - org_apg/res/values/static_strings.xml | 7 - org_apg/res/values/strings.xml | 352 ---- org_apg/res/values/styles.xml | 52 - org_apg/res/xml/apg_preferences.xml | 63 - org_apg/res/xml/searchable_public_keys.xml | 22 - org_apg/res/xml/searchable_secret_keys.xml | 22 - .../org/thialfihar/android/apg/ApgApplication.java | 48 - .../src/org/thialfihar/android/apg/Constants.java | 66 - org_apg/src/org/thialfihar/android/apg/Id.java | 228 --- .../thialfihar/android/apg/helper/FileHelper.java | 121 -- .../thialfihar/android/apg/helper/OtherHelper.java | 183 -- .../android/apg/helper/PGPConversionHelper.java | 137 -- .../thialfihar/android/apg/helper/PGPHelper.java | 452 ----- .../org/thialfihar/android/apg/helper/PGPMain.java | 1827 -------------------- .../thialfihar/android/apg/helper/Preferences.java | 172 -- .../android/apg/provider/ApgContract.java | 209 --- .../android/apg/provider/ApgDatabase.java | 104 -- .../android/apg/provider/ApgProvider.java | 825 --------- .../android/apg/provider/ApgProviderExternal.java | 32 - .../android/apg/provider/ApgProviderInternal.java | 30 - .../apg/provider/ApgServiceBlobContract.java | 40 - .../apg/provider/ApgServiceBlobDatabase.java | 47 - .../apg/provider/ApgServiceBlobProvider.java | 151 -- .../android/apg/provider/ProviderHelper.java | 501 ------ .../android/apg/service/ApgIntentService.java | 881 ---------- .../apg/service/ApgIntentServiceHandler.java | 109 -- .../thialfihar/android/apg/service/ApgService.java | 326 ---- .../apg/service/IApgEncryptDecryptHandler.aidl | 32 - .../android/apg/service/IApgHelperHandler.aidl | 25 - .../android/apg/service/IApgService.aidl | 146 -- .../android/apg/service/IApgSignVerifyHandler.aidl | 27 - .../apg/service/PassphraseCacheService.java | 230 --- .../thialfihar/android/apg/ui/DecryptActivity.java | 940 ---------- .../thialfihar/android/apg/ui/EditKeyActivity.java | 569 ------ .../thialfihar/android/apg/ui/EncryptActivity.java | 1099 ------------ .../thialfihar/android/apg/ui/HelpActivity.java | 162 -- .../android/apg/ui/HelpFragmentAbout.java | 107 -- .../android/apg/ui/HelpFragmentHtml.java | 109 -- .../android/apg/ui/ImportFromQRCodeActivity.java | 273 --- .../thialfihar/android/apg/ui/KeyListActivity.java | 452 ----- .../thialfihar/android/apg/ui/KeyListFragment.java | 82 - .../android/apg/ui/KeyListPublicActivity.java | 102 -- .../android/apg/ui/KeyListPublicFragment.java | 183 -- .../android/apg/ui/KeyListSecretActivity.java | 124 -- .../android/apg/ui/KeyListSecretFragment.java | 154 -- .../android/apg/ui/KeyServerQueryActivity.java | 353 ---- .../android/apg/ui/KeyServerUploadActivity.java | 146 -- .../thialfihar/android/apg/ui/MainActivity.java | 102 -- .../android/apg/ui/PreferencesActivity.java | 240 --- .../apg/ui/PreferencesKeyServerActivity.java | 162 -- .../android/apg/ui/SelectPublicKeyActivity.java | 161 -- .../android/apg/ui/SelectPublicKeyFragment.java | 243 --- .../android/apg/ui/SelectSecretKeyActivity.java | 128 -- .../android/apg/ui/SelectSecretKeyFragment.java | 154 -- .../thialfihar/android/apg/ui/SignKeyActivity.java | 313 ---- .../apg/ui/dialog/DeleteFileDialogFragment.java | 118 -- .../apg/ui/dialog/DeleteKeyDialogFragment.java | 133 -- .../android/apg/ui/dialog/FileDialogFragment.java | 193 --- .../ui/dialog/LookupUnknownKeyDialogFragment.java | 133 -- .../apg/ui/dialog/PassphraseDialogFragment.java | 273 --- .../apg/ui/dialog/ProgressDialogFragment.java | 124 -- .../apg/ui/dialog/SetPassphraseDialogFragment.java | 187 -- .../android/apg/ui/widget/DashboardLayout.java | 186 -- .../thialfihar/android/apg/ui/widget/Editor.java | 25 - .../apg/ui/widget/ExpandableListFragment.java | 530 ------ .../apg/ui/widget/IntegerListPreference.java | 95 - .../android/apg/ui/widget/KeyEditor.java | 236 --- .../android/apg/ui/widget/KeyListAdapter.java | 264 --- .../android/apg/ui/widget/KeyServerEditor.java | 78 - .../android/apg/ui/widget/SectionView.java | 327 ---- .../apg/ui/widget/SelectKeyCursorAdapter.java | 142 -- .../android/apg/ui/widget/UserIdEditor.java | 196 --- .../org/thialfihar/android/apg/util/Choice.java | 45 - .../thialfihar/android/apg/util/Compatibility.java | 94 - .../thialfihar/android/apg/util/HkpKeyServer.java | 257 --- .../org/thialfihar/android/apg/util/InputData.java | 40 - .../android/apg/util/IterableIterator.java | 31 - .../org/thialfihar/android/apg/util/KeyServer.java | 97 -- .../src/org/thialfihar/android/apg/util/Log.java | 83 - .../android/apg/util/PositionAwareInputStream.java | 81 - .../org/thialfihar/android/apg/util/Primes.java | 188 -- .../android/apg/util/ProgressDialogUpdater.java | 25 - org_apg_integration_demo/.gitignore | 23 - org_apg_integration_demo/AndroidManifest.xml | 36 - org_apg_integration_demo/build.xml | 83 - org_apg_integration_demo/proguard-project.txt | 20 - org_apg_integration_demo/project.properties | 12 - .../res/drawable-hdpi/icon.png | Bin 5308 -> 0 bytes .../res/drawable-ldpi/icon.png | Bin 2027 -> 0 bytes .../res/drawable-mdpi/icon.png | Bin 3035 -> 0 bytes .../res/drawable-xhdpi/icon.png | Bin 8165 -> 0 bytes org_apg_integration_demo/res/layout/aidl_demo.xml | 72 - .../res/layout/intent_demo.xml | 93 - .../res/xml/base_preference.xml | 15 - .../android/apg/demo/AidlDemoActivity.java | 185 -- .../thialfihar/android/apg/demo/BaseActivity.java | 69 - .../android/apg/demo/IntentDemoActivity.java | 110 -- org_apg_integration_lib/.gitignore | 23 - org_apg_integration_lib/AndroidManifest.xml | 11 - org_apg_integration_lib/build.xml | 83 - org_apg_integration_lib/proguard-project.txt | 20 - org_apg_integration_lib/project.properties | 15 - org_apg_integration_lib/res/.readme | 1 - .../android/apg/integration/ApgData.java | 107 -- .../android/apg/integration/ApgIntentHelper.java | 318 ---- .../android/apg/integration/ApgServiceHelper.java | 102 -- .../android/apg/integration/Constants.java | 27 - .../apg/integration/ContentProviderHelper.java | 195 --- .../thialfihar/android/apg/integration/Util.java | 51 - .../apg/service/IApgEncryptDecryptHandler.aidl | 16 - .../android/apg/service/IApgHelperHandler.aidl | 9 - .../android/apg/service/IApgService.aidl | 130 -- .../android/apg/service/IApgSignVerifyHandler.aidl | 11 - 584 files changed, 23068 insertions(+), 23068 deletions(-) create mode 100644 APG-API-Demo/.gitignore create mode 100644 APG-API-Demo/AndroidManifest.xml create mode 100644 APG-API-Demo/build.xml create mode 100644 APG-API-Demo/proguard-project.txt create mode 100644 APG-API-Demo/project.properties create mode 100644 APG-API-Demo/res/drawable-hdpi/icon.png create mode 100644 APG-API-Demo/res/drawable-ldpi/icon.png create mode 100644 APG-API-Demo/res/drawable-mdpi/icon.png create mode 100644 APG-API-Demo/res/drawable-xhdpi/icon.png create mode 100644 APG-API-Demo/res/layout/aidl_demo.xml create mode 100644 APG-API-Demo/res/layout/intent_demo.xml create mode 100644 APG-API-Demo/res/xml/base_preference.xml create mode 100644 APG-API-Demo/src/org/thialfihar/android/apg/demo/AidlDemoActivity.java create mode 100644 APG-API-Demo/src/org/thialfihar/android/apg/demo/BaseActivity.java create mode 100644 APG-API-Demo/src/org/thialfihar/android/apg/demo/IntentDemoActivity.java create mode 100644 APG-API-Lib/.gitignore create mode 100644 APG-API-Lib/AndroidManifest.xml create mode 100644 APG-API-Lib/build.xml create mode 100644 APG-API-Lib/proguard-project.txt create mode 100644 APG-API-Lib/project.properties create mode 100644 APG-API-Lib/res/.readme create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgData.java create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgIntentHelper.java create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgServiceHelper.java create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/Constants.java create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ContentProviderHelper.java create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/Util.java create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/IApgService.aidl create mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl create mode 100644 APG/.gitignore create mode 100644 APG/AndroidManifest.xml create mode 100644 APG/build.xml create mode 100644 APG/libs/barcodescanner-android-integration-supportv4.jar create mode 100644 APG/libs/htmlcleaner-2.2.jar create mode 100644 APG/libs/htmlspanner-0.2-fork.jar create mode 100644 APG/libs/sc-bzip2-1.47.0.2.jar create mode 100644 APG/libs/sc-light-jdk15on-1.47.0.2.jar create mode 100644 APG/libs/scpg-jdk15on-1.47.0.2.jar create mode 100644 APG/libs/scprov-jdk15on-1.47.0.2.jar create mode 100644 APG/pom.xml create mode 100644 APG/proguard-project.txt create mode 100644 APG/project.properties create mode 100644 APG/res/anim/push_left_in.xml create mode 100644 APG/res/anim/push_left_out.xml create mode 100644 APG/res/anim/push_right_in.xml create mode 100644 APG/res/anim/push_right_out.xml create mode 100644 APG/res/drawable-finger/btn_circle.xml create mode 100644 APG/res/drawable-hdpi-finger/btn_circle_disable.png create mode 100644 APG/res/drawable-hdpi-finger/btn_circle_disable_focused.png create mode 100644 APG/res/drawable-hdpi-finger/btn_circle_normal.png create mode 100644 APG/res/drawable-hdpi-finger/btn_circle_pressed.png create mode 100644 APG/res/drawable-hdpi-finger/btn_circle_selected.png create mode 100644 APG/res/drawable-hdpi-finger/ic_btn_round_minus.png create mode 100644 APG/res/drawable-hdpi-finger/ic_btn_round_plus.png create mode 100644 APG/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100644 APG/res/drawable-hdpi/dashboard_decrypt_default.png create mode 100644 APG/res/drawable-hdpi/dashboard_decrypt_pressed.png create mode 100644 APG/res/drawable-hdpi/dashboard_encrypt_default.png create mode 100644 APG/res/drawable-hdpi/dashboard_encrypt_pressed.png create mode 100644 APG/res/drawable-hdpi/dashboard_help_default.png create mode 100644 APG/res/drawable-hdpi/dashboard_help_pressed.png create mode 100644 APG/res/drawable-hdpi/dashboard_manage_keys_default.png create mode 100644 APG/res/drawable-hdpi/dashboard_manage_keys_pressed.png create mode 100644 APG/res/drawable-hdpi/dashboard_my_keys_default.png create mode 100644 APG/res/drawable-hdpi/dashboard_my_keys_pressed.png create mode 100644 APG/res/drawable-hdpi/dashboard_scan_qrcode_default.png create mode 100644 APG/res/drawable-hdpi/dashboard_scan_qrcode_pressed.png create mode 100644 APG/res/drawable-hdpi/encrypted.png create mode 100644 APG/res/drawable-hdpi/encrypted_large.png create mode 100644 APG/res/drawable-hdpi/encrypted_small.png create mode 100644 APG/res/drawable-hdpi/ic_menu_about.png create mode 100644 APG/res/drawable-hdpi/ic_menu_decrypt.png create mode 100644 APG/res/drawable-hdpi/ic_menu_encrypt.png create mode 100644 APG/res/drawable-hdpi/ic_menu_filebrowser.png create mode 100644 APG/res/drawable-hdpi/ic_menu_scan_qrcode.png create mode 100644 APG/res/drawable-hdpi/ic_menu_search.png create mode 100644 APG/res/drawable-hdpi/ic_menu_search_list.png create mode 100644 APG/res/drawable-hdpi/ic_menu_settings.png create mode 100644 APG/res/drawable-hdpi/ic_next.png create mode 100644 APG/res/drawable-hdpi/ic_previous.png create mode 100644 APG/res/drawable-hdpi/icon.png create mode 100644 APG/res/drawable-hdpi/key.png create mode 100644 APG/res/drawable-hdpi/key_large.png create mode 100644 APG/res/drawable-hdpi/key_small.png create mode 100644 APG/res/drawable-hdpi/overlay_error.png create mode 100644 APG/res/drawable-hdpi/overlay_ok.png create mode 100644 APG/res/drawable-hdpi/signed.png create mode 100644 APG/res/drawable-hdpi/signed_large.png create mode 100644 APG/res/drawable-hdpi/signed_small.png create mode 100644 APG/res/drawable-ldpi/encrypted.png create mode 100644 APG/res/drawable-ldpi/encrypted_large.png create mode 100644 APG/res/drawable-ldpi/encrypted_small.png create mode 100644 APG/res/drawable-ldpi/ic_next.png create mode 100644 APG/res/drawable-ldpi/ic_previous.png create mode 100644 APG/res/drawable-ldpi/icon.png create mode 100644 APG/res/drawable-ldpi/key.png create mode 100644 APG/res/drawable-ldpi/key_large.png create mode 100644 APG/res/drawable-ldpi/key_small.png create mode 100644 APG/res/drawable-ldpi/overlay_error.png create mode 100644 APG/res/drawable-ldpi/overlay_ok.png create mode 100644 APG/res/drawable-ldpi/signed.png create mode 100644 APG/res/drawable-ldpi/signed_large.png create mode 100644 APG/res/drawable-ldpi/signed_small.png create mode 100644 APG/res/drawable-mdpi-finger/btn_circle_disable.png create mode 100644 APG/res/drawable-mdpi-finger/btn_circle_disable_focused.png create mode 100644 APG/res/drawable-mdpi-finger/btn_circle_normal.png create mode 100644 APG/res/drawable-mdpi-finger/btn_circle_pressed.png create mode 100644 APG/res/drawable-mdpi-finger/btn_circle_selected.png create mode 100644 APG/res/drawable-mdpi-finger/ic_btn_round_minus.png create mode 100644 APG/res/drawable-mdpi-finger/ic_btn_round_plus.png create mode 100644 APG/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100644 APG/res/drawable-mdpi/encrypted.png create mode 100644 APG/res/drawable-mdpi/encrypted_large.png create mode 100644 APG/res/drawable-mdpi/encrypted_small.png create mode 100644 APG/res/drawable-mdpi/ic_menu_about.png create mode 100644 APG/res/drawable-mdpi/ic_menu_decrypt.png create mode 100644 APG/res/drawable-mdpi/ic_menu_encrypt.png create mode 100644 APG/res/drawable-mdpi/ic_menu_filebrowser.png create mode 100644 APG/res/drawable-mdpi/ic_menu_scan_qrcode.png create mode 100644 APG/res/drawable-mdpi/ic_menu_search.png create mode 100644 APG/res/drawable-mdpi/ic_menu_search_list.png create mode 100644 APG/res/drawable-mdpi/ic_menu_settings.png create mode 100644 APG/res/drawable-mdpi/ic_next.png create mode 100644 APG/res/drawable-mdpi/ic_previous.png create mode 100644 APG/res/drawable-mdpi/icon.png create mode 100644 APG/res/drawable-mdpi/key.png create mode 100644 APG/res/drawable-mdpi/key_large.png create mode 100644 APG/res/drawable-mdpi/key_small.png create mode 100644 APG/res/drawable-mdpi/overlay_error.png create mode 100644 APG/res/drawable-mdpi/overlay_ok.png create mode 100644 APG/res/drawable-mdpi/signed.png create mode 100644 APG/res/drawable-mdpi/signed_large.png create mode 100644 APG/res/drawable-mdpi/signed_small.png create mode 100644 APG/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100644 APG/res/drawable-xhdpi/ic_menu_about.png create mode 100644 APG/res/drawable-xhdpi/ic_menu_decrypt.png create mode 100644 APG/res/drawable-xhdpi/ic_menu_encrypt.png create mode 100644 APG/res/drawable-xhdpi/ic_menu_filebrowser.png create mode 100644 APG/res/drawable-xhdpi/ic_menu_scan_qrcode.png create mode 100644 APG/res/drawable-xhdpi/ic_menu_search.png create mode 100644 APG/res/drawable-xhdpi/ic_menu_search_list.png create mode 100644 APG/res/drawable-xhdpi/ic_menu_settings.png create mode 100644 APG/res/drawable-xhdpi/icon.png create mode 100644 APG/res/drawable/btn_circle_disable.png create mode 100644 APG/res/drawable/btn_circle_disable_focused.png create mode 100644 APG/res/drawable/btn_circle_normal.png create mode 100644 APG/res/drawable/btn_circle_pressed.png create mode 100644 APG/res/drawable/btn_circle_selected.png create mode 100644 APG/res/drawable/dashboard_decrypt.xml create mode 100644 APG/res/drawable/dashboard_encrypt.xml create mode 100644 APG/res/drawable/dashboard_help.xml create mode 100644 APG/res/drawable/dashboard_manage_keys.xml create mode 100644 APG/res/drawable/dashboard_my_keys.xml create mode 100644 APG/res/drawable/dashboard_scan_qrcode.xml create mode 100644 APG/res/drawable/encrypted.png create mode 100644 APG/res/drawable/encrypted_large.png create mode 100644 APG/res/drawable/encrypted_small.png create mode 100644 APG/res/drawable/ic_btn_round_minus.png create mode 100644 APG/res/drawable/ic_btn_round_plus.png create mode 100644 APG/res/drawable/ic_launcher_folder.png create mode 100644 APG/res/drawable/ic_launcher_folder_small.png create mode 100644 APG/res/drawable/ic_next.png create mode 100644 APG/res/drawable/ic_previous.png create mode 100644 APG/res/drawable/key.png create mode 100644 APG/res/drawable/key_large.png create mode 100644 APG/res/drawable/key_small.png create mode 100644 APG/res/drawable/overlay_error.png create mode 100644 APG/res/drawable/overlay_ok.png create mode 100644 APG/res/drawable/section_header.xml create mode 100644 APG/res/drawable/signed.png create mode 100644 APG/res/drawable/signed_large.png create mode 100644 APG/res/drawable/signed_small.png create mode 100644 APG/res/layout/account_item.xml create mode 100644 APG/res/layout/create_key.xml create mode 100644 APG/res/layout/decrypt.xml create mode 100644 APG/res/layout/edit_key.xml create mode 100644 APG/res/layout/edit_key_key_item.xml create mode 100644 APG/res/layout/edit_key_section.xml create mode 100644 APG/res/layout/edit_key_user_id_item.xml create mode 100644 APG/res/layout/encrypt.xml create mode 100644 APG/res/layout/file_dialog.xml create mode 100644 APG/res/layout/filter_info.xml create mode 100644 APG/res/layout/general.xml create mode 100644 APG/res/layout/help_activity.xml create mode 100644 APG/res/layout/help_fragment_about.xml create mode 100644 APG/res/layout/import_from_qr_code.xml create mode 100644 APG/res/layout/key_list_child_item.xml create mode 100644 APG/res/layout/key_list_group_item.xml create mode 100644 APG/res/layout/key_list_public_activity.xml create mode 100644 APG/res/layout/key_list_secret_activity.xml create mode 100644 APG/res/layout/key_server_editor.xml create mode 100644 APG/res/layout/key_server_export_layout.xml create mode 100644 APG/res/layout/key_server_preference.xml create mode 100644 APG/res/layout/key_server_query_layout.xml create mode 100644 APG/res/layout/key_server_query_result_item.xml create mode 100644 APG/res/layout/key_server_query_result_user_id.xml create mode 100644 APG/res/layout/main.xml create mode 100644 APG/res/layout/passphrase.xml create mode 100644 APG/res/layout/passphrase_repeat.xml create mode 100644 APG/res/layout/select_key_item.xml create mode 100644 APG/res/layout/select_public_key_activity.xml create mode 100644 APG/res/layout/select_secret_key_activity.xml create mode 100644 APG/res/layout/sign_key_layout.xml create mode 100644 APG/res/raw/help_about.html create mode 100644 APG/res/raw/help_changelog.html create mode 100644 APG/res/raw/help_start.html create mode 100644 APG/res/values/arrays.xml create mode 100644 APG/res/values/colors.xml create mode 100644 APG/res/values/static_strings.xml create mode 100644 APG/res/values/strings.xml create mode 100644 APG/res/values/styles.xml create mode 100644 APG/res/xml/apg_preferences.xml create mode 100644 APG/res/xml/searchable_public_keys.xml create mode 100644 APG/res/xml/searchable_secret_keys.xml create mode 100644 APG/src/org/thialfihar/android/apg/ApgApplication.java create mode 100644 APG/src/org/thialfihar/android/apg/Constants.java create mode 100644 APG/src/org/thialfihar/android/apg/Id.java create mode 100644 APG/src/org/thialfihar/android/apg/helper/FileHelper.java create mode 100644 APG/src/org/thialfihar/android/apg/helper/OtherHelper.java create mode 100644 APG/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java create mode 100644 APG/src/org/thialfihar/android/apg/helper/PGPHelper.java create mode 100644 APG/src/org/thialfihar/android/apg/helper/PGPMain.java create mode 100644 APG/src/org/thialfihar/android/apg/helper/Preferences.java create mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgContract.java create mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgDatabase.java create mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgProvider.java create mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java create mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java create mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java create mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java create mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java create mode 100644 APG/src/org/thialfihar/android/apg/provider/ProviderHelper.java create mode 100644 APG/src/org/thialfihar/android/apg/service/ApgIntentService.java create mode 100644 APG/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java create mode 100644 APG/src/org/thialfihar/android/apg/service/ApgService.java create mode 100644 APG/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl create mode 100644 APG/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl create mode 100644 APG/src/org/thialfihar/android/apg/service/IApgService.aidl create mode 100644 APG/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl create mode 100644 APG/src/org/thialfihar/android/apg/service/PassphraseCacheService.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/DecryptActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/EditKeyActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/EncryptActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/HelpActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/ImportFromQRCodeActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/MainActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/PreferencesActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/SignKeyActivity.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/Editor.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/SectionView.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java create mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java create mode 100644 APG/src/org/thialfihar/android/apg/util/Choice.java create mode 100644 APG/src/org/thialfihar/android/apg/util/Compatibility.java create mode 100644 APG/src/org/thialfihar/android/apg/util/HkpKeyServer.java create mode 100644 APG/src/org/thialfihar/android/apg/util/InputData.java create mode 100644 APG/src/org/thialfihar/android/apg/util/IterableIterator.java create mode 100644 APG/src/org/thialfihar/android/apg/util/KeyServer.java create mode 100644 APG/src/org/thialfihar/android/apg/util/Log.java create mode 100644 APG/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java create mode 100644 APG/src/org/thialfihar/android/apg/util/Primes.java create mode 100644 APG/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java delete mode 100644 org_apg/.gitignore delete mode 100644 org_apg/AndroidManifest.xml delete mode 100644 org_apg/build.xml delete mode 100644 org_apg/libs/barcodescanner-android-integration-supportv4.jar delete mode 100644 org_apg/libs/htmlcleaner-2.2.jar delete mode 100644 org_apg/libs/htmlspanner-0.2-fork.jar delete mode 100644 org_apg/libs/sc-bzip2-1.47.0.2.jar delete mode 100644 org_apg/libs/sc-light-jdk15on-1.47.0.2.jar delete mode 100644 org_apg/libs/scpg-jdk15on-1.47.0.2.jar delete mode 100644 org_apg/libs/scprov-jdk15on-1.47.0.2.jar delete mode 100644 org_apg/pom.xml delete mode 100644 org_apg/proguard-project.txt delete mode 100644 org_apg/project.properties delete mode 100644 org_apg/res/anim/push_left_in.xml delete mode 100644 org_apg/res/anim/push_left_out.xml delete mode 100644 org_apg/res/anim/push_right_in.xml delete mode 100644 org_apg/res/anim/push_right_out.xml delete mode 100644 org_apg/res/drawable-finger/btn_circle.xml delete mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_disable.png delete mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_disable_focused.png delete mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_normal.png delete mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_pressed.png delete mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_selected.png delete mode 100644 org_apg/res/drawable-hdpi-finger/ic_btn_round_minus.png delete mode 100644 org_apg/res/drawable-hdpi-finger/ic_btn_round_plus.png delete mode 100644 org_apg/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_decrypt_default.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_decrypt_pressed.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_encrypt_default.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_encrypt_pressed.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_help_default.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_help_pressed.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_manage_keys_default.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_manage_keys_pressed.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_my_keys_default.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_my_keys_pressed.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_scan_qrcode_default.png delete mode 100644 org_apg/res/drawable-hdpi/dashboard_scan_qrcode_pressed.png delete mode 100644 org_apg/res/drawable-hdpi/encrypted.png delete mode 100644 org_apg/res/drawable-hdpi/encrypted_large.png delete mode 100644 org_apg/res/drawable-hdpi/encrypted_small.png delete mode 100644 org_apg/res/drawable-hdpi/ic_menu_about.png delete mode 100644 org_apg/res/drawable-hdpi/ic_menu_decrypt.png delete mode 100644 org_apg/res/drawable-hdpi/ic_menu_encrypt.png delete mode 100644 org_apg/res/drawable-hdpi/ic_menu_filebrowser.png delete mode 100644 org_apg/res/drawable-hdpi/ic_menu_scan_qrcode.png delete mode 100644 org_apg/res/drawable-hdpi/ic_menu_search.png delete mode 100644 org_apg/res/drawable-hdpi/ic_menu_search_list.png delete mode 100644 org_apg/res/drawable-hdpi/ic_menu_settings.png delete mode 100644 org_apg/res/drawable-hdpi/ic_next.png delete mode 100644 org_apg/res/drawable-hdpi/ic_previous.png delete mode 100644 org_apg/res/drawable-hdpi/icon.png delete mode 100644 org_apg/res/drawable-hdpi/key.png delete mode 100644 org_apg/res/drawable-hdpi/key_large.png delete mode 100644 org_apg/res/drawable-hdpi/key_small.png delete mode 100644 org_apg/res/drawable-hdpi/overlay_error.png delete mode 100644 org_apg/res/drawable-hdpi/overlay_ok.png delete mode 100644 org_apg/res/drawable-hdpi/signed.png delete mode 100644 org_apg/res/drawable-hdpi/signed_large.png delete mode 100644 org_apg/res/drawable-hdpi/signed_small.png delete mode 100644 org_apg/res/drawable-ldpi/encrypted.png delete mode 100644 org_apg/res/drawable-ldpi/encrypted_large.png delete mode 100644 org_apg/res/drawable-ldpi/encrypted_small.png delete mode 100644 org_apg/res/drawable-ldpi/ic_next.png delete mode 100644 org_apg/res/drawable-ldpi/ic_previous.png delete mode 100644 org_apg/res/drawable-ldpi/icon.png delete mode 100644 org_apg/res/drawable-ldpi/key.png delete mode 100644 org_apg/res/drawable-ldpi/key_large.png delete mode 100644 org_apg/res/drawable-ldpi/key_small.png delete mode 100644 org_apg/res/drawable-ldpi/overlay_error.png delete mode 100644 org_apg/res/drawable-ldpi/overlay_ok.png delete mode 100644 org_apg/res/drawable-ldpi/signed.png delete mode 100644 org_apg/res/drawable-ldpi/signed_large.png delete mode 100644 org_apg/res/drawable-ldpi/signed_small.png delete mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_disable.png delete mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_disable_focused.png delete mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_normal.png delete mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_pressed.png delete mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_selected.png delete mode 100644 org_apg/res/drawable-mdpi-finger/ic_btn_round_minus.png delete mode 100644 org_apg/res/drawable-mdpi-finger/ic_btn_round_plus.png delete mode 100644 org_apg/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png delete mode 100644 org_apg/res/drawable-mdpi/encrypted.png delete mode 100644 org_apg/res/drawable-mdpi/encrypted_large.png delete mode 100644 org_apg/res/drawable-mdpi/encrypted_small.png delete mode 100644 org_apg/res/drawable-mdpi/ic_menu_about.png delete mode 100644 org_apg/res/drawable-mdpi/ic_menu_decrypt.png delete mode 100644 org_apg/res/drawable-mdpi/ic_menu_encrypt.png delete mode 100644 org_apg/res/drawable-mdpi/ic_menu_filebrowser.png delete mode 100644 org_apg/res/drawable-mdpi/ic_menu_scan_qrcode.png delete mode 100644 org_apg/res/drawable-mdpi/ic_menu_search.png delete mode 100644 org_apg/res/drawable-mdpi/ic_menu_search_list.png delete mode 100644 org_apg/res/drawable-mdpi/ic_menu_settings.png delete mode 100644 org_apg/res/drawable-mdpi/ic_next.png delete mode 100644 org_apg/res/drawable-mdpi/ic_previous.png delete mode 100644 org_apg/res/drawable-mdpi/icon.png delete mode 100644 org_apg/res/drawable-mdpi/key.png delete mode 100644 org_apg/res/drawable-mdpi/key_large.png delete mode 100644 org_apg/res/drawable-mdpi/key_small.png delete mode 100644 org_apg/res/drawable-mdpi/overlay_error.png delete mode 100644 org_apg/res/drawable-mdpi/overlay_ok.png delete mode 100644 org_apg/res/drawable-mdpi/signed.png delete mode 100644 org_apg/res/drawable-mdpi/signed_large.png delete mode 100644 org_apg/res/drawable-mdpi/signed_small.png delete mode 100644 org_apg/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png delete mode 100644 org_apg/res/drawable-xhdpi/ic_menu_about.png delete mode 100644 org_apg/res/drawable-xhdpi/ic_menu_decrypt.png delete mode 100644 org_apg/res/drawable-xhdpi/ic_menu_encrypt.png delete mode 100644 org_apg/res/drawable-xhdpi/ic_menu_filebrowser.png delete mode 100644 org_apg/res/drawable-xhdpi/ic_menu_scan_qrcode.png delete mode 100644 org_apg/res/drawable-xhdpi/ic_menu_search.png delete mode 100644 org_apg/res/drawable-xhdpi/ic_menu_search_list.png delete mode 100644 org_apg/res/drawable-xhdpi/ic_menu_settings.png delete mode 100644 org_apg/res/drawable-xhdpi/icon.png delete mode 100644 org_apg/res/drawable/btn_circle_disable.png delete mode 100644 org_apg/res/drawable/btn_circle_disable_focused.png delete mode 100644 org_apg/res/drawable/btn_circle_normal.png delete mode 100644 org_apg/res/drawable/btn_circle_pressed.png delete mode 100644 org_apg/res/drawable/btn_circle_selected.png delete mode 100644 org_apg/res/drawable/dashboard_decrypt.xml delete mode 100644 org_apg/res/drawable/dashboard_encrypt.xml delete mode 100644 org_apg/res/drawable/dashboard_help.xml delete mode 100644 org_apg/res/drawable/dashboard_manage_keys.xml delete mode 100644 org_apg/res/drawable/dashboard_my_keys.xml delete mode 100644 org_apg/res/drawable/dashboard_scan_qrcode.xml delete mode 100644 org_apg/res/drawable/encrypted.png delete mode 100644 org_apg/res/drawable/encrypted_large.png delete mode 100644 org_apg/res/drawable/encrypted_small.png delete mode 100644 org_apg/res/drawable/ic_btn_round_minus.png delete mode 100644 org_apg/res/drawable/ic_btn_round_plus.png delete mode 100644 org_apg/res/drawable/ic_launcher_folder.png delete mode 100644 org_apg/res/drawable/ic_launcher_folder_small.png delete mode 100644 org_apg/res/drawable/ic_next.png delete mode 100644 org_apg/res/drawable/ic_previous.png delete mode 100644 org_apg/res/drawable/key.png delete mode 100644 org_apg/res/drawable/key_large.png delete mode 100644 org_apg/res/drawable/key_small.png delete mode 100644 org_apg/res/drawable/overlay_error.png delete mode 100644 org_apg/res/drawable/overlay_ok.png delete mode 100644 org_apg/res/drawable/section_header.xml delete mode 100644 org_apg/res/drawable/signed.png delete mode 100644 org_apg/res/drawable/signed_large.png delete mode 100644 org_apg/res/drawable/signed_small.png delete mode 100644 org_apg/res/layout/account_item.xml delete mode 100644 org_apg/res/layout/create_key.xml delete mode 100644 org_apg/res/layout/decrypt.xml delete mode 100644 org_apg/res/layout/edit_key.xml delete mode 100644 org_apg/res/layout/edit_key_key_item.xml delete mode 100644 org_apg/res/layout/edit_key_section.xml delete mode 100644 org_apg/res/layout/edit_key_user_id_item.xml delete mode 100644 org_apg/res/layout/encrypt.xml delete mode 100644 org_apg/res/layout/file_dialog.xml delete mode 100644 org_apg/res/layout/filter_info.xml delete mode 100644 org_apg/res/layout/general.xml delete mode 100644 org_apg/res/layout/help_activity.xml delete mode 100644 org_apg/res/layout/help_fragment_about.xml delete mode 100644 org_apg/res/layout/import_from_qr_code.xml delete mode 100644 org_apg/res/layout/key_list_child_item.xml delete mode 100644 org_apg/res/layout/key_list_group_item.xml delete mode 100644 org_apg/res/layout/key_list_public_activity.xml delete mode 100644 org_apg/res/layout/key_list_secret_activity.xml delete mode 100644 org_apg/res/layout/key_server_editor.xml delete mode 100644 org_apg/res/layout/key_server_export_layout.xml delete mode 100644 org_apg/res/layout/key_server_preference.xml delete mode 100644 org_apg/res/layout/key_server_query_layout.xml delete mode 100644 org_apg/res/layout/key_server_query_result_item.xml delete mode 100644 org_apg/res/layout/key_server_query_result_user_id.xml delete mode 100644 org_apg/res/layout/main.xml delete mode 100644 org_apg/res/layout/passphrase.xml delete mode 100644 org_apg/res/layout/passphrase_repeat.xml delete mode 100644 org_apg/res/layout/select_key_item.xml delete mode 100644 org_apg/res/layout/select_public_key_activity.xml delete mode 100644 org_apg/res/layout/select_secret_key_activity.xml delete mode 100644 org_apg/res/layout/sign_key_layout.xml delete mode 100644 org_apg/res/raw/help_about.html delete mode 100644 org_apg/res/raw/help_changelog.html delete mode 100644 org_apg/res/raw/help_start.html delete mode 100644 org_apg/res/values/arrays.xml delete mode 100644 org_apg/res/values/colors.xml delete mode 100644 org_apg/res/values/static_strings.xml delete mode 100644 org_apg/res/values/strings.xml delete mode 100644 org_apg/res/values/styles.xml delete mode 100644 org_apg/res/xml/apg_preferences.xml delete mode 100644 org_apg/res/xml/searchable_public_keys.xml delete mode 100644 org_apg/res/xml/searchable_secret_keys.xml delete mode 100644 org_apg/src/org/thialfihar/android/apg/ApgApplication.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/Constants.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/Id.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/helper/FileHelper.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/helper/OtherHelper.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/helper/PGPHelper.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/helper/PGPMain.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/helper/Preferences.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/provider/ApgContract.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/provider/ApgDatabase.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/provider/ApgProvider.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/provider/ProviderHelper.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/service/ApgIntentService.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/service/ApgService.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl delete mode 100644 org_apg/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl delete mode 100644 org_apg/src/org/thialfihar/android/apg/service/IApgService.aidl delete mode 100644 org_apg/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl delete mode 100644 org_apg/src/org/thialfihar/android/apg/service/PassphraseCacheService.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/DecryptActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/EditKeyActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/EncryptActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/HelpActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/ImportFromQRCodeActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/KeyListActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/KeyListFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/MainActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/PreferencesActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/SignKeyActivity.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/Editor.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/SectionView.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/Choice.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/Compatibility.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/HkpKeyServer.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/InputData.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/IterableIterator.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/KeyServer.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/Log.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/Primes.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java delete mode 100644 org_apg_integration_demo/.gitignore delete mode 100644 org_apg_integration_demo/AndroidManifest.xml delete mode 100644 org_apg_integration_demo/build.xml delete mode 100644 org_apg_integration_demo/proguard-project.txt delete mode 100644 org_apg_integration_demo/project.properties delete mode 100644 org_apg_integration_demo/res/drawable-hdpi/icon.png delete mode 100644 org_apg_integration_demo/res/drawable-ldpi/icon.png delete mode 100644 org_apg_integration_demo/res/drawable-mdpi/icon.png delete mode 100644 org_apg_integration_demo/res/drawable-xhdpi/icon.png delete mode 100644 org_apg_integration_demo/res/layout/aidl_demo.xml delete mode 100644 org_apg_integration_demo/res/layout/intent_demo.xml delete mode 100644 org_apg_integration_demo/res/xml/base_preference.xml delete mode 100644 org_apg_integration_demo/src/org/thialfihar/android/apg/demo/AidlDemoActivity.java delete mode 100644 org_apg_integration_demo/src/org/thialfihar/android/apg/demo/BaseActivity.java delete mode 100644 org_apg_integration_demo/src/org/thialfihar/android/apg/demo/IntentDemoActivity.java delete mode 100644 org_apg_integration_lib/.gitignore delete mode 100644 org_apg_integration_lib/AndroidManifest.xml delete mode 100644 org_apg_integration_lib/build.xml delete mode 100644 org_apg_integration_lib/proguard-project.txt delete mode 100644 org_apg_integration_lib/project.properties delete mode 100644 org_apg_integration_lib/res/.readme delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/integration/ApgData.java delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/integration/ApgIntentHelper.java delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/integration/ApgServiceHelper.java delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/integration/Constants.java delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/integration/ContentProviderHelper.java delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/integration/Util.java delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/service/IApgService.aidl delete mode 100644 org_apg_integration_lib/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl diff --git a/APG-API-Demo/.gitignore b/APG-API-Demo/.gitignore new file mode 100644 index 000000000..2e423e1a3 --- /dev/null +++ b/APG-API-Demo/.gitignore @@ -0,0 +1,23 @@ +#Android generated +bin +gen +obj +libs/armeabi +lint.xml +local.properties + +#Eclipse +.project +.classpath +.settings + +#IntelliJ IDEA +.idea +*.iml + +#Maven +target +release.properties + +#Mac +.DS_Store \ No newline at end of file diff --git a/APG-API-Demo/AndroidManifest.xml b/APG-API-Demo/AndroidManifest.xml new file mode 100644 index 000000000..8d7a9e970 --- /dev/null +++ b/APG-API-Demo/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/APG-API-Demo/build.xml b/APG-API-Demo/build.xml new file mode 100644 index 000000000..e72865c84 --- /dev/null +++ b/APG-API-Demo/build.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/APG-API-Demo/proguard-project.txt b/APG-API-Demo/proguard-project.txt new file mode 100644 index 000000000..f2fe1559a --- /dev/null +++ b/APG-API-Demo/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/APG-API-Demo/project.properties b/APG-API-Demo/project.properties new file mode 100644 index 000000000..8abe35df9 --- /dev/null +++ b/APG-API-Demo/project.properties @@ -0,0 +1,12 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-15 +android.library.reference.1=../org_apg_integration_lib diff --git a/APG-API-Demo/res/drawable-hdpi/icon.png b/APG-API-Demo/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..6b8cc3d73 Binary files /dev/null and b/APG-API-Demo/res/drawable-hdpi/icon.png differ diff --git a/APG-API-Demo/res/drawable-ldpi/icon.png b/APG-API-Demo/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..a1adf6bcb Binary files /dev/null and b/APG-API-Demo/res/drawable-ldpi/icon.png differ diff --git a/APG-API-Demo/res/drawable-mdpi/icon.png b/APG-API-Demo/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..6b10a2ad3 Binary files /dev/null and b/APG-API-Demo/res/drawable-mdpi/icon.png differ diff --git a/APG-API-Demo/res/drawable-xhdpi/icon.png b/APG-API-Demo/res/drawable-xhdpi/icon.png new file mode 100644 index 000000000..03ee31bbd Binary files /dev/null and b/APG-API-Demo/res/drawable-xhdpi/icon.png differ diff --git a/APG-API-Demo/res/layout/aidl_demo.xml b/APG-API-Demo/res/layout/aidl_demo.xml new file mode 100644 index 000000000..59977869d --- /dev/null +++ b/APG-API-Demo/res/layout/aidl_demo.xml @@ -0,0 +1,72 @@ + + + + + + + + diff --git a/APG/res/raw/help_about.html b/APG/res/raw/help_about.html new file mode 100644 index 000000000..1b1f0b52e --- /dev/null +++ b/APG/res/raw/help_about.html @@ -0,0 +1,31 @@ + + + + + +

https://github.com/dschuermann/apg

+

Android Privacy Guard (APG) is an OpenPGP implementation for Android.

+

License: Apache License v2

+ +

Developer

+ + +

Libraries

+ + + \ No newline at end of file diff --git a/APG/res/raw/help_changelog.html b/APG/res/raw/help_changelog.html new file mode 100644 index 000000000..bf013342b --- /dev/null +++ b/APG/res/raw/help_changelog.html @@ -0,0 +1,86 @@ + + + + + +

2.0

+ + +

1.08

+ + +

1.0.7

+ + +

1.0.6

+ + +

1.0.5

+ + +

1.0.4

+ + +

1.0.3

+ + +

1.0.2

+ + +

1.0.1

+ + +

1.0.0

+ + + \ No newline at end of file diff --git a/APG/res/raw/help_start.html b/APG/res/raw/help_start.html new file mode 100644 index 000000000..db7559632 --- /dev/null +++ b/APG/res/raw/help_start.html @@ -0,0 +1,27 @@ + + + + + +

Beta software

+

This is beta software. It contains many remaining bugs!

+ +

APG

+

Android Privacy Guard (APG) is a OpenPGP implementation for Android. +
This is a fork based on the original APG to introduce more features and a new user interface.

+ +

WARNING

+

Be careful editing your existing keys, as they WILL be stripped of certificates right now. +

Also: key cross-certification is NOT supported, so signing with those keys will get a warning when the signature is checked.

+ +

Getting started

+

Install K-9 Mail for the best integration, it supports APG for PGP/INLINE and lets you directly encrypt/decrypt emails. +
It is recommended that you install OI File Manager to be able to use the browse button for file selection in APG. +
First you need some keys. Either import them via the option menus in "Manage Keys" and "My Keys" or create them in "My Keys".

+ +

I found a bug in APG!

+

Please report it in the issue tracker of APG.

+ + \ No newline at end of file diff --git a/APG/res/values/arrays.xml b/APG/res/values/arrays.xml new file mode 100644 index 000000000..05320c609 --- /dev/null +++ b/APG/res/values/arrays.xml @@ -0,0 +1,47 @@ + + + + + + + @string/choice_15secs + @string/choice_1min + @string/choice_3mins + @string/choice_5mins + @string/choice_10mins + @string/choice_20mins + @string/choice_40mins + @string/choice_1hour + @string/choice_2hours + @string/choice_4hours + @string/choice_8hours + + + 15 + 60 + 180 + 300 + 600 + 1200 + 2400 + 3600 + 7200 + 14400 + 28800 + + + \ No newline at end of file diff --git a/APG/res/values/colors.xml b/APG/res/values/colors.xml new file mode 100644 index 000000000..d1dc6c1f4 --- /dev/null +++ b/APG/res/values/colors.xml @@ -0,0 +1,7 @@ + + + + #31b6e7 + #cecbce + + \ No newline at end of file diff --git a/APG/res/values/static_strings.xml b/APG/res/values/static_strings.xml new file mode 100644 index 000000000..c69cf8adf --- /dev/null +++ b/APG/res/values/static_strings.xml @@ -0,0 +1,7 @@ + + + + APG + https://github.com/dschuermann/apg + + \ No newline at end of file diff --git a/APG/res/values/strings.xml b/APG/res/values/strings.xml new file mode 100644 index 000000000..d2858559d --- /dev/null +++ b/APG/res/values/strings.xml @@ -0,0 +1,352 @@ + + + + + + + Mail Inbox + Manage Public Keys + Manage Secret Keys + Select Recipients + Select Signature + Encrypt + Decrypt + Passphrase + Create Key + Edit Key + Create Key + Preferences + Key Server Preference + Change Passphrase + Set Passphrase + "Send Mail…" + Encrypt To File + Decrypt To File + Import Keys + Export Key + Export Keys + Key Not Found + Getting Started + Query Key Server + Export to Key Server + Unknown Signature Key + Import from QR Code + Sign Key + About + Help + + + User IDs + Keys + General + Defaults + Advanced + + + Sign (Clipboard) + Encrypt (Clipboard) + Encrypt (Email) + Sign (Email) + Encrypt + Sign + Decrypt + Verify + Select Recipients + Reply + Encrypt Message + Decrypt Message + Encrypt File + Decrypt File + Save + Cancel + Delete + None + Okay + Clear Filter + Change Passphrase + Set Passphrase + Search + Export To Server + + + About + Delete Account + Manage Public Keys + Manage Secret Keys + Settings + Import Keys + Export All Keys + Export Key + Delete Key + Create Key + Edit Key + Search + Help + Key Server + Update from Server + Export To Server + Share public key with QR Code + Scan QR Code + Sign Key + + + Sign + Message + File + No Passphrase + Passphrase + Again + Algorithm + ASCII Armour + Public Key(s) + Delete After Encryption + Delete After Decryption + Delete After Import + Encryption Algorithm + Hash Algorithm + Public Key + Passphrase + Passphrase Cache + Message Compression + File Compression + Force V3 Signatures + Key Servers + Key ID + Creation + Expiry + Usage + Key Size + Main User ID + Name + Comment + Email + Send Key to Server? + Select + 1 Selected + Selected + <unknown> + <none> + <no key> + - + <no expiry> + + can encrypt + can sign + expired + not valid + %s key server(s) + fingerprint + + + None + Sign only + Encrypt only + Sign and Encrypt + 15 secs + 1 min + 3 mins + 5 mins + 10 mins + 20 mins + 40 mins + 1 hour + 2 hours + 4 hours + 8 hours + until quit + System default + DSA + ElGamal + RSA + Open… + Save As… + Select File To Encrypt… + Select File To Decrypt… + Open + Save + Warning + Error + Warning: %s + Error: %s + + + Wrong passphrase. + Using clipboard content. + Key saved. + Set a passphrase first. + No compatible file manager installed. + The passphrases didn\'t match. + Empty passphrases are not allowed. + Symmetric encryption. + Enter passphrase for \'%s\' + Are you sure you want to delete\n%s? + Successfully deleted. + Select a file first. + Successfully decrypted. + Successfully encrypted. + Successfully encrypted to clipboard. + Enter the passphrase twice. + Select at least one encryption key. + Select at least one encryption key or a signature key. + Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists. + Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists. + Please specify which file to import keys from. (.asc or .gpg) + Please specify which file to export to.\nWARNING! File will be overwritten if it exists. + Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists. + Do you really want to delete the key \'%s\'?\nYou can\'t undo this! + Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this! + Successfully added %1$s key(s) and updated %2$s key(s). + Successfully added %s key(s). + Successfully updated %s key(s). + No keys added or updated. + Successfully exported 1 key. + Successfully exported %s keys. + No keys exported. + Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used. + Couldn\'t find key %08X. + Found %s key(s). + Unknown signature, touch to look up key. + Key editing is still kind of beta. + %s bad secret key(s) ignored. Perhaps you exported with the option\n --export-secret-subkeys\nMake sure you export with\n --export-secret-keys\ninstead. + Unknown key %s, do you want to try finding it on a keyserver? + Successfully sent key to server + Successfully signed key + Successfully validated and imported key + Long press one entry in this list to show more options! + This list is empty! + + + deleting \'%s\' failed + file not found + no suitable secret key found + no known kind of encryption found + external storage not ready + adding account \'%s\' failed + invalid email \'%s\' + key size must be at least 512bit + the master key cannot be an ElGamal key + unknown algorithm choice + you need to specify a name + you need to specify an email address + need at least one user id + main user id must not be empty + need at least a master key + expiry date must come after creation date + no encryption key(s) or passphrase given + signature failed + no passphrase given + no signature key given + not valid encryption data + corrupt data + couldn\'t find a packet with symmetric encryption + wrong passphrase + error saving some key(s) + could not extract private key + Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_ENCRYPT_STREAM_AND_RETURN. + + + done. + initializing… + saving… + importing… + exporting… + generating key, this can take a while… + building key… + preparing master key… + certifying master key… + building master key ring… + adding sub keys… + saving key ring… + importing secret keys… + importing public keys… + reloading keys… + exporting key… + exporting keys… + extracting signature key… + extracting key… + preparing streams… + encrypting data… + decrypting data… + preparing signature… + generating signature… + processing signature… + verifying signature… + signing… + reading data… + finding key… + decompressing data… + verifying integrity… + deleting \'%s\' securely… + querying… + querying %s… + + + APG + Permissions to use APG + Read key details of public and secret keys (The keys themselves can NOT be read.) + Read key details of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read. + Encrypt/Sign/Decrypt/Create keys without user interaction + Encrypt/Sign/Decrypt/Create keys (by using Intents or Remote Service) without user interaction + + + Encrypt + Decrypt + Import Public Keys + Import Secret Keys + Search Public Keys + Search Secret Keys + Filter: \"%s\" + + + fast + slow + very slow + + + + + + Manage Keys + My Keys + Encrypt + Decrypt + Help + Scan QRCode + + + Start + Changelog + About + Version: + + + Import key (only locally) + Import, Sign, and upload key + Scan qr code again + Finish + + + APG: Decrypt File + APG: Import Key + APG: Encrypt + APG: Decrypt + + \ No newline at end of file diff --git a/APG/res/values/styles.xml b/APG/res/values/styles.xml new file mode 100644 index 000000000..ef3ff63f6 --- /dev/null +++ b/APG/res/values/styles.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/APG/res/xml/apg_preferences.xml b/APG/res/xml/apg_preferences.xml new file mode 100644 index 000000000..778f51375 --- /dev/null +++ b/APG/res/xml/apg_preferences.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/APG/res/xml/searchable_public_keys.xml b/APG/res/xml/searchable_public_keys.xml new file mode 100644 index 000000000..e9602b121 --- /dev/null +++ b/APG/res/xml/searchable_public_keys.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/APG/res/xml/searchable_secret_keys.xml b/APG/res/xml/searchable_secret_keys.xml new file mode 100644 index 000000000..a7e8873d6 --- /dev/null +++ b/APG/res/xml/searchable_secret_keys.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ApgApplication.java b/APG/src/org/thialfihar/android/apg/ApgApplication.java new file mode 100644 index 000000000..f89375079 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ApgApplication.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg; + +import java.io.File; +import java.security.Security; + +import org.spongycastle.jce.provider.BouncyCastleProvider; + +import android.app.Application; +import android.os.Environment; + +public class ApgApplication extends Application { + + static { + // Define Java Security Provider to be Bouncy Castle + Security.addProvider(new BouncyCastleProvider()); + } + + @Override + public void onCreate() { + super.onCreate(); + + // Create APG directory on sdcard if not existing + 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 + } + } + } + +} diff --git a/APG/src/org/thialfihar/android/apg/Constants.java b/APG/src/org/thialfihar/android/apg/Constants.java new file mode 100644 index 000000000..95ab8158d --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/Constants.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg; + +import android.os.Environment; + +public final class Constants { + + public static final boolean DEBUG = true; + + public static final String TAG = "APG"; + + public static final String PACKAGE_NAME = "org.thialfihar.android.apg"; + + public static final String PERMISSION_ACCESS_KEY_DATABASE = PACKAGE_NAME + + ".permission.ACCESS_KEY_DATABASE"; + public static final String PERMISSION_ACCESS_API = PACKAGE_NAME + ".permission.ACCESS_API"; + + /* + * TODO: + * + * this would require patching K9Mail + * + * better naming scheme would be: + * + * - x.action.DECRYPT (with action as preferred in Android) + * + * - even better and shorter (without .android.): org.apg.action.DECRYPT + */ + public static final String INTENT_PREFIX = PACKAGE_NAME + ".intent."; + + public static final class path { + public static final String APP_DIR = Environment.getExternalStorageDirectory() + "/APG"; + } + + public static final class pref { + public static final String DEFAULT_ENCRYPTION_ALGORITHM = "defaultEncryptionAlgorithm"; + public static final String DEFAULT_HASH_ALGORITHM = "defaultHashAlgorithm"; + public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour"; + public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression"; + public static final String DEFAULT_FILE_COMPRESSION = "defaultFileCompression"; + public static final String PASS_PHRASE_CACHE_TTL = "passPhraseCacheTtl"; + public static final String LANGUAGE = "language"; + public static final String FORCE_V3_SIGNATURES = "forceV3Signatures"; + public static final String KEY_SERVERS = "keyServers"; + } + + public static final class defaults { + public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu"; + } + +} diff --git a/APG/src/org/thialfihar/android/apg/Id.java b/APG/src/org/thialfihar/android/apg/Id.java new file mode 100644 index 000000000..e683f43b4 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/Id.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg; + +import org.spongycastle.bcpg.CompressionAlgorithmTags; + +/** + * + * TODO: + * + * - refactor ids, some are not needed and can be done with xml + * + */ +public final class Id { + + public static final String TAG = "APG"; + + public static final class menu { + public static final int export = 0x21070001; + public static final int delete = 0x21070002; + public static final int edit = 0x21070003; + public static final int update = 0x21070004; + public static final int exportToServer = 0x21070005; + public static final int share_qr_code = 0x21070006; + public static final int signKey = 0x21070007; + + public static final class option { + public static final int new_pass_phrase = 0x21070001; + public static final int create = 0x21070002; + public static final int about = 0x21070003; + public static final int manage_public_keys = 0x21070004; + public static final int manage_secret_keys = 0x21070005; + public static final int import_keys = 0x21070006; + public static final int export_keys = 0x21070007; + public static final int preferences = 0x21070008; + public static final int search = 0x21070009; + public static final int help = 0x21070010; + public static final int key_server = 0x21070011; + public static final int scanQRCode = 0x21070012; + public static final int encrypt = 0x21070013; + public static final int encrypt_to_clipboard = 0x21070014; + public static final int decrypt = 0x21070015; + public static final int reply = 0x21070016; + public static final int cancel = 0x21070017; + public static final int save = 0x21070018; + public static final int okay = 0x21070019; + + } + } + + // use only lower 16 bits due to compatibility lib + public static final class message { + public static final int progress_update = 0x00006001; + public static final int done = 0x00006002; + public static final int import_keys = 0x00006003; + public static final int export_keys = 0x00006004; + public static final int import_done = 0x00006005; + public static final int export_done = 0x00006006; + public static final int create_key = 0x00006007; + public static final int edit_key = 0x00006008; + public static final int delete_done = 0x00006009; + public static final int query_done = 0x00006010; + public static final int unknown_signature_key = 0x00006011; + } + + // public static final class message { + // public static final int progress_update = 0x21070001; + // public static final int done = 0x21070002; + // public static final int import_keys = 0x21070003; + // public static final int export_keys = 0x21070004; + // public static final int import_done = 0x21070005; + // public static final int export_done = 0x21070006; + // public static final int create_key = 0x21070007; + // public static final int edit_key = 0x21070008; + // public static final int delete_done = 0x21070009; + // public static final int query_done = 0x21070010; + // public static final int unknown_signature_key = 0x21070011; + // } + + // use only lower 16 bits due to compatibility lib + public static final class request { + public static final int public_keys = 0x00007001; + public static final int secret_keys = 0x00007002; + public static final int filename = 0x00007003; + public static final int output_filename = 0x00007004; + public static final int key_server_preference = 0x00007005; + public static final int look_up_key_id = 0x00007006; + public static final int export_to_server = 0x00007007; + public static final int import_from_qr_code = 0x00007008; + public static final int sign_key = 0x00007009; + } + + // public static final class request { + // public static final int public_keys = 0x21070001; + // public static final int secret_keys = 0x21070002; + // public static final int filename = 0x21070003; + // public static final int output_filename = 0x21070004; + // public static final int key_server_preference = 0x21070005; + // public static final int look_up_key_id = 0x21070006; + // public static final int export_to_server = 0x21070007; + // public static final int import_from_qr_code = 0x21070008; + // public static final int sign_key = 0x21070009; + // } + + public static final class dialog { + public static final int pass_phrase = 0x21070001; + public static final int encrypting = 0x21070002; + public static final int decrypting = 0x21070003; + public static final int new_pass_phrase = 0x21070004; + public static final int pass_phrases_do_not_match = 0x21070005; + public static final int no_pass_phrase = 0x21070006; + public static final int saving = 0x21070007; + public static final int delete_key = 0x21070008; + public static final int import_keys = 0x21070009; + public static final int importing = 0x2107000a; + public static final int export_key = 0x2107000b; + public static final int export_keys = 0x2107000c; + public static final int exporting = 0x2107000d; + public static final int new_account = 0x2107000e; + // public static final int about = 0x2107000f; + public static final int change_log = 0x21070010; + public static final int output_filename = 0x21070011; + public static final int delete_file = 0x21070012; + public static final int deleting = 0x21070013; + public static final int help = 0x21070014; + public static final int querying = 0x21070015; + public static final int lookup_unknown_key = 0x21070016; + public static final int signing = 0x21070017; + } + + public static final class task { + public static final int import_keys = 0x21070001; + public static final int export_keys = 0x21070002; + } + +// public static final class database { +// public static final int type_public = 0; +// public static final int type_secret = 1; +// } + + public static final class type { + public static final int public_key = 0x21070001; + public static final int secret_key = 0x21070002; + public static final int user_id = 0x21070003; + public static final int key = 0x21070004; + } + + public static final class choice { + public static final class algorithm { + public static final int dsa = 0x21070001; + public static final int elgamal = 0x21070002; + public static final int rsa = 0x21070003; + } + + public static final class compression { + public static final int none = 0x21070001; + public static final int zlib = CompressionAlgorithmTags.ZLIB; + public static final int bzip2 = CompressionAlgorithmTags.BZIP2; + public static final int zip = CompressionAlgorithmTags.ZIP; + } + + public static final class usage { + public static final int sign_only = 0x21070001; + public static final int encrypt_only = 0x21070002; + public static final int sign_and_encrypt = 0x21070003; + } + + public static final class action { + public static final int encrypt = 0x21070001; + public static final int decrypt = 0x21070002; + public static final int import_public = 0x21070003; + public static final int import_secret = 0x21070004; + } + } + + public static final class return_value { + public static final int ok = 0; + public static final int error = -1; + public static final int no_master_key = -2; + public static final int updated = 1; + public static final int bad = -3; + } + + public static final class target { + public static final int clipboard = 0x21070001; + public static final int email = 0x21070002; + public static final int file = 0x21070003; + public static final int message = 0x21070004; + } + + public static final class mode { + public static final int undefined = 0x21070001; + public static final int byte_array = 0x21070002; + public static final int file = 0x21070003; + public static final int stream = 0x21070004; + } + + public static final class key { + public static final int none = 0; + public static final int symmetric = -1; + } + + public static final class content { + public static final int unknown = 0; + public static final int encrypted_data = 1; + public static final int keys = 2; + } + + public static final class keyserver { + public static final int search = 0x21070001; + public static final int get = 0x21070002; + public static final int add = 0x21070003; + } +} diff --git a/APG/src/org/thialfihar/android/apg/helper/FileHelper.java b/APG/src/org/thialfihar/android/apg/helper/FileHelper.java new file mode 100644 index 000000000..6efee19c0 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/helper/FileHelper.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.helper; + +import java.net.URISyntaxException; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.util.Log; + +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Environment; +import android.widget.Toast; + +public class FileHelper { + + /** + * Checks if external storage is mounted if file is located on external storage + * + * @param file + * @return true if storage is mounted + */ + public static boolean isStorageMounted(String file) { + if (file.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + return false; + } + } + + return true; + } + + /** + * Opens the preferred installed file manager on Android and shows a toast if no manager is + * installed. + * + * @param activity + * @param filename + * default selected file, not supported by all file managers + * @param type + * can be text/plain for example + * @param requestCode + * requestCode used to identify the result coming back from file manager to + * onActivityResult() in your activity + */ + public static void openFile(Activity activity, String filename, String type, int requestCode) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + + intent.setData(Uri.parse("file://" + filename)); + intent.setType(type); + + try { + activity.startActivityForResult(intent, requestCode); + } catch (ActivityNotFoundException e) { + // No compatible file manager was found. + Toast.makeText(activity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show(); + } + } + + /** + * Get a file path from a Uri. + * + * from https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/ + * afilechooser/utils/FileUtils.java + * + * @param context + * @param uri + * @return + * + * @author paulburke + */ + public static String getPath(Context context, Uri uri) { + + Log.d(Constants.TAG + " File -", + "Authority: " + uri.getAuthority() + ", Fragment: " + uri.getFragment() + + ", Port: " + uri.getPort() + ", Query: " + uri.getQuery() + ", Scheme: " + + uri.getScheme() + ", Host: " + uri.getHost() + ", Segments: " + + uri.getPathSegments().toString()); + + if ("content".equalsIgnoreCase(uri.getScheme())) { + String[] projection = { "_data" }; + Cursor cursor = null; + + try { + cursor = context.getContentResolver().query(uri, projection, null, null, null); + int column_index = cursor.getColumnIndexOrThrow("_data"); + if (cursor.moveToFirst()) { + return cursor.getString(column_index); + } + } catch (Exception e) { + // Eat it + } + } + + else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + + return null; + } +} diff --git a/APG/src/org/thialfihar/android/apg/helper/OtherHelper.java b/APG/src/org/thialfihar/android/apg/helper/OtherHelper.java new file mode 100644 index 000000000..bd5db2410 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/helper/OtherHelper.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.helper; + +import java.io.InputStream; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Iterator; +import java.util.Set; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.util.Log; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.widget.Toast; + +public class OtherHelper { + + /** + * Gets input stream of raw resource + * + * @param context + * current context + * @param resourceID + * of html file to read + * @return input stream of resource + */ + public static InputStream getInputStreamFromResource(Context context, int resourceID) { + InputStream raw = context.getResources().openRawResource(resourceID); + return raw; + } + + /** + * Return the number if days between two dates + * + * @param first + * @param second + * @return number of days + */ + public static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) { + GregorianCalendar tmp = new GregorianCalendar(); + tmp.setTime(first.getTime()); + long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400; + tmp.add(Calendar.DAY_OF_MONTH, (int) numDays); + while (tmp.before(second)) { + tmp.add(Calendar.DAY_OF_MONTH, 1); + ++numDays; + } + return numDays; + } + + /** + * Logs bundle content to debug for inspecting the content + * + * @param bundle + * @param bundleName + */ + public static void logDebugBundle(Bundle bundle, String bundleName) { + if (Constants.DEBUG) { + if (bundle != null) { + Set ks = bundle.keySet(); + Iterator iterator = ks.iterator(); + + Log.d(Constants.TAG, "Bundle " + bundleName + ":"); + Log.d(Constants.TAG, "------------------------------"); + while (iterator.hasNext()) { + String key = iterator.next(); + Object value = bundle.get(key); + + if (value != null) { + Log.d(Constants.TAG, key + " : " + value.toString()); + } else { + Log.d(Constants.TAG, key + " : null"); + } + } + Log.d(Constants.TAG, "------------------------------"); + } else { + Log.d(Constants.TAG, "Bundle " + bundleName + ": null"); + } + } + } + + /** + * Set actionbar without home button if called from another app + * + * @param activity + */ + public static void setActionBarBackButton(SherlockFragmentActivity activity) { + // set actionbar without home button if called from another app + final ActionBar actionBar = activity.getSupportActionBar(); + Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)=" + + activity.getCallingPackage()); + if (activity.getCallingPackage() != null + && activity.getCallingPackage().equals(Constants.PACKAGE_NAME)) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + } else { + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + } + } + + /** + * Check if the calling package has the needed permission to invoke an intent with specific + * restricted actions. + * + * If pkgName is null, this will also deny the use of the given action + * + * @param activity + * @param pkgName + * @param permName + * @param action + * @param restrictedActions + */ + public static void checkPackagePermissionForActions(Activity activity, String pkgName, + String permName, String action, String[] restrictedActions) { + if (action != null) { + PackageManager pkgManager = activity.getPackageManager(); + + for (int i = 0; i < restrictedActions.length; i++) { + if (restrictedActions[i].equals(action)) { + if (pkgName != null + && (pkgManager.checkPermission(permName, pkgName) == PackageManager.PERMISSION_GRANTED || pkgName + .equals(Constants.PACKAGE_NAME))) { + Log.d(Constants.TAG, pkgName + " has permission " + permName + ". Action " + + action + " was granted!"); + } else { + String error = pkgName + " does NOT have permission " + permName + + ". Action " + action + " was NOT granted!"; + Log.e(Constants.TAG, error); + Toast.makeText(activity, activity.getString(R.string.errorMessage, error), + Toast.LENGTH_LONG).show(); + + // end activity + activity.setResult(Activity.RESULT_CANCELED, null); + activity.finish(); + } + } + } + } + } + + /** + * Splits userId string into naming part and email part + * + * @param userId + * @return array with naming (0) and email (1) + */ + public static String[] splitUserId(String userId) { + String[] output = new String[2]; + + String chunks[] = userId.split(" <", 2); + userId = chunks[0]; + if (chunks.length > 1) { + output[1] = "<" + chunks[1]; + } + output[0] = userId; + + return output; + } + +} diff --git a/APG/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java b/APG/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java new file mode 100644 index 000000000..040ff2a9e --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.helper; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.Constants; + +import org.thialfihar.android.apg.util.Log; + +public class PGPConversionHelper { + + /** + * Convert from byte[] to PGPKeyRing + * + * @param keysBytes + * @return + */ + public static PGPKeyRing BytesToPGPKeyRing(byte[] keysBytes) { + PGPObjectFactory factory = new PGPObjectFactory(keysBytes); + PGPKeyRing keyRing = null; + try { + if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) { + Log.e(Constants.TAG, "No keys given!"); + } + } catch (IOException e) { + Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e); + } + + return keyRing; + } + + /** + * Convert from byte[] to ArrayList + * + * @param keysBytes + * @return + */ + public static ArrayList BytesToPGPSecretKeyList(byte[] keysBytes) { + PGPSecretKeyRing keyRing = (PGPSecretKeyRing) BytesToPGPKeyRing(keysBytes); + ArrayList keys = new ArrayList(); + + @SuppressWarnings("unchecked") + Iterator itr = keyRing.getSecretKeys(); + while (itr.hasNext()) { + keys.add(itr.next()); + } + + return keys; + } + + /** + * Convert from byte[] to PGPSecretKey + * + * Singles keys are encoded as keyRings with one single key in it by Bouncy Castle + * + * @param keysBytes + * @return + */ + public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) { + PGPSecretKey key = BytesToPGPSecretKeyList(keyBytes).get(0); + + return key; + } + + /** + * Convert from ArrayList to byte[] + * + * @param keys + * @return + */ + public static byte[] PGPSecretKeyArrayListToBytes(ArrayList keys) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + for (PGPSecretKey key : keys) { + try { + key.encode(os); + } catch (IOException e) { + Log.e(Constants.TAG, "Error while converting ArrayList to byte[]!", e); + } + } + + return os.toByteArray(); + } + + /** + * Convert from PGPSecretKey to byte[] + * + * @param keysBytes + * @return + */ + public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) { + try { + return key.getEncoded(); + } catch (IOException e) { + Log.e(Constants.TAG, "Encoding failed", e); + + return null; + } + } + + /** + * Convert from PGPSecretKeyRing to byte[] + * + * @param keysBytes + * @return + */ + public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) { + try { + return keyRing.getEncoded(); + } catch (IOException e) { + Log.e(Constants.TAG, "Encoding failed", e); + + return null; + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/helper/PGPHelper.java b/APG/src/org/thialfihar/android/apg/helper/PGPHelper.java new file mode 100644 index 000000000..b566cffa0 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/helper/PGPHelper.java @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * 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.helper; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Vector; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUtil; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.util.IterableIterator; +import org.thialfihar.android.apg.util.Log; + +import android.content.Context; + +public class PGPHelper { + public static PGPKeyRing decodeKeyRing(InputStream is) throws IOException { + InputStream in = PGPUtil.getDecoderStream(is); + PGPObjectFactory objectFactory = new PGPObjectFactory(in); + Object obj = objectFactory.nextObject(); + + if (obj instanceof PGPKeyRing) { + return (PGPKeyRing) obj; + } + + return null; + } + + public static Date getCreationDate(PGPPublicKey key) { + return key.getCreationTime(); + } + + public static Date getCreationDate(PGPSecretKey key) { + return key.getPublicKey().getCreationTime(); + } + + @SuppressWarnings("unchecked") + public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) { + if (keyRing == null) { + return null; + } + for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + if (key.isMasterKey()) { + return key; + } + } + + return null; + } + + @SuppressWarnings("unchecked") + public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) { + if (keyRing == null) { + return null; + } + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + if (key.isMasterKey()) { + return key; + } + } + + return null; + } + + @SuppressWarnings("unchecked") + public static Vector getEncryptKeys(PGPPublicKeyRing keyRing) { + Vector encryptKeys = new Vector(); + + for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + if (isEncryptionKey(key)) { + encryptKeys.add(key); + } + } + + return encryptKeys; + } + + @SuppressWarnings("unchecked") + public static Vector getSigningKeys(PGPSecretKeyRing keyRing) { + Vector signingKeys = new Vector(); + + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + if (isSigningKey(key)) { + signingKeys.add(key); + } + } + + return signingKeys; + } + + public static Vector getUsableEncryptKeys(PGPPublicKeyRing keyRing) { + Vector usableKeys = new Vector(); + Vector encryptKeys = getEncryptKeys(keyRing); + PGPPublicKey masterKey = null; + for (int i = 0; i < encryptKeys.size(); ++i) { + PGPPublicKey key = encryptKeys.get(i); + if (!isExpired(key)) { + if (key.isMasterKey()) { + masterKey = key; + } else { + usableKeys.add(key); + } + } + } + if (masterKey != null) { + usableKeys.add(masterKey); + } + return usableKeys; + } + + public static boolean isExpired(PGPPublicKey key) { + Date creationDate = getCreationDate(key); + Date expiryDate = getExpiryDate(key); + Date now = new Date(); + if (now.compareTo(creationDate) >= 0 + && (expiryDate == null || now.compareTo(expiryDate) <= 0)) { + return false; + } + return true; + } + + public static boolean isExpired(PGPSecretKey key) { + return isExpired(key.getPublicKey()); + } + + public static Vector getUsableSigningKeys(PGPSecretKeyRing keyRing) { + Vector usableKeys = new Vector(); + Vector signingKeys = getSigningKeys(keyRing); + PGPSecretKey masterKey = null; + for (int i = 0; i < signingKeys.size(); ++i) { + PGPSecretKey key = signingKeys.get(i); + if (key.isMasterKey()) { + masterKey = key; + } else { + usableKeys.add(key); + } + } + if (masterKey != null) { + usableKeys.add(masterKey); + } + return usableKeys; + } + + public static Date getExpiryDate(PGPPublicKey key) { + Date creationDate = getCreationDate(key); + if (key.getValidDays() == 0) { + // no expiry + return null; + } + Calendar calendar = GregorianCalendar.getInstance(); + calendar.setTime(creationDate); + calendar.add(Calendar.DATE, key.getValidDays()); + Date expiryDate = calendar.getTime(); + + return expiryDate; + } + + public static Date getExpiryDate(PGPSecretKey key) { + return getExpiryDate(key.getPublicKey()); + } + + public static PGPPublicKey getEncryptPublicKey(Context context, long masterKeyId) { + PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(context, masterKeyId); + if (keyRing == null) { + Log.e(Constants.TAG, "keyRing is null!"); + return null; + } + Vector encryptKeys = getUsableEncryptKeys(keyRing); + if (encryptKeys.size() == 0) { + Log.e(Constants.TAG, "encryptKeys is null!"); + return null; + } + return encryptKeys.get(0); + } + + public static PGPSecretKey getSigningKey(Context context, long masterKeyId) { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, masterKeyId); + if (keyRing == null) { + return null; + } + Vector signingKeys = getUsableSigningKeys(keyRing); + if (signingKeys.size() == 0) { + return null; + } + return signingKeys.get(0); + } + + @SuppressWarnings("unchecked") + public static String getMainUserId(PGPPublicKey key) { + for (String userId : new IterableIterator(key.getUserIDs())) { + return userId; + } + return null; + } + + @SuppressWarnings("unchecked") + public static String getMainUserId(PGPSecretKey key) { + for (String userId : new IterableIterator(key.getUserIDs())) { + return userId; + } + return null; + } + + public static String getMainUserIdSafe(Context context, PGPPublicKey key) { + String userId = getMainUserId(key); + if (userId == null || userId.equals("")) { + userId = context.getString(R.string.unknownUserId); + } + return userId; + } + + public static String getMainUserIdSafe(Context context, PGPSecretKey key) { + String userId = getMainUserId(key); + if (userId == null || userId.equals("")) { + userId = context.getString(R.string.unknownUserId); + } + return userId; + } + + @SuppressWarnings("unchecked") + public static boolean isEncryptionKey(PGPPublicKey key) { + if (!key.isEncryptionKey()) { + return false; + } + + if (key.getVersion() <= 3) { + // this must be true now + return key.isEncryptionKey(); + } + + // special cases + if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) { + return true; + } + + if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) { + return true; + } + + for (PGPSignature sig : new IterableIterator(key.getSignatures())) { + if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { + continue; + } + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + + if (hashed != null + && (hashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { + return true; + } + + PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); + + if (unhashed != null + && (unhashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { + return true; + } + } + return false; + } + + public static boolean isEncryptionKey(PGPSecretKey key) { + return isEncryptionKey(key.getPublicKey()); + } + + @SuppressWarnings("unchecked") + public static boolean isSigningKey(PGPPublicKey key) { + if (key.getVersion() <= 3) { + return true; + } + + // special case + if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) { + return true; + } + + for (PGPSignature sig : new IterableIterator(key.getSignatures())) { + if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { + continue; + } + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + + if (hashed != null && (hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) { + return true; + } + + PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); + + if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) { + return true; + } + } + + return false; + } + + public static boolean isSigningKey(PGPSecretKey key) { + return isSigningKey(key.getPublicKey()); + } + + public static String getAlgorithmInfo(PGPPublicKey key) { + return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength()); + } + + public static String getAlgorithmInfo(PGPSecretKey key) { + return getAlgorithmInfo(key.getPublicKey()); + } + + public static String getAlgorithmInfo(int algorithm, int keySize) { + String algorithmStr = null; + + switch (algorithm) { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + case PGPPublicKey.RSA_SIGN: { + algorithmStr = "RSA"; + break; + } + + case PGPPublicKey.DSA: { + algorithmStr = "DSA"; + break; + } + + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: { + algorithmStr = "ElGamal"; + break; + } + + default: { + algorithmStr = "???"; + break; + } + } + return algorithmStr + ", " + keySize + "bit"; + } + + public static String convertToHex(byte[] fp) { + String fingerPrint = ""; + for (int i = 0; i < fp.length; ++i) { + if (i != 0 && i % 10 == 0) { + fingerPrint += " "; + } else if (i != 0 && i % 2 == 0) { + fingerPrint += " "; + } + String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(); + while (chunk.length() < 2) { + chunk = "0" + chunk; + } + fingerPrint += chunk; + } + + return fingerPrint; + + } + + public static String getPubkeyAsArmoredString(Context context, long keyId) { + PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId); + // if it is no public key get it from your own keys... + if (key == null) { + PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); + if (secretKey == null) { + Log.e(Constants.TAG, "Key could not be found!"); + return null; + } + key = secretKey.getPublicKey(); + } + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ArmoredOutputStream aos = new ArmoredOutputStream(bos); + aos.setHeader("Version", PGPMain.getFullVersion(context)); + + String armouredKey = null; + try { + aos.write(key.getEncoded()); + aos.close(); + + armouredKey = bos.toString("UTF-8"); + } catch (IOException e) { + Log.e(Constants.TAG, "Problems while encoding key as armored string", e); + } + + Log.d(Constants.TAG, "key:" + armouredKey); + + return armouredKey; + } + + public static String getFingerPrint(Context context, long keyId) { + PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId); + // if it is no public key get it from your own keys... + if (key == null) { + PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); + if (secretKey == null) { + Log.e(Constants.TAG, "Key could not be found!"); + return null; + } + key = secretKey.getPublicKey(); + } + + return convertToHex(key.getFingerprint()); + } + + public static String getSmallFingerPrint(long keyId) { + String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(); + while (fingerPrint.length() < 8) { + fingerPrint = "0" + fingerPrint; + } + return fingerPrint; + } + + public static String keyToHex(long keyId) { + return getSmallFingerPrint(keyId >> 32) + getSmallFingerPrint(keyId); + } + + public static long keyFromHex(String data) { + int len = data.length(); + String s2 = data.substring(len - 8); + String s1 = data.substring(0, len - 8); + return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16); + } +} diff --git a/APG/src/org/thialfihar/android/apg/helper/PGPMain.java b/APG/src/org/thialfihar/android/apg/helper/PGPMain.java new file mode 100644 index 000000000..0cde19ae5 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/helper/PGPMain.java @@ -0,0 +1,1827 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * 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.helper; + +import org.spongycastle.bcpg.ArmoredInputStream; +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.PGPV3SignatureGenerator; +import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.service.ApgIntentService; +import org.thialfihar.android.apg.util.HkpKeyServer; +import org.thialfihar.android.apg.util.InputData; +import org.thialfihar.android.apg.util.PositionAwareInputStream; +import org.thialfihar.android.apg.util.Primes; +import org.thialfihar.android.apg.util.ProgressDialogUpdater; +import org.thialfihar.android.apg.util.KeyServer.AddKeyException; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.os.Environment; +import org.thialfihar.android.apg.util.Log; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.Vector; +import java.util.regex.Pattern; + +/** + * TODO: + * + * - Separate this file into different helpers + * + */ +public class PGPMain { + + static { + // register spongy castle provider + Security.addProvider(new BouncyCastleProvider()); + } + + // Not BC due to the use of Spongy Castle for Android + public static final String BOUNCY_CASTLE_PROVIDER_NAME = "SC"; + + private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] { + SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, + SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5, + SymmetricKeyAlgorithmTags.TRIPLE_DES }; + private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1, + HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160 }; + private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] { + CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2, + CompressionAlgorithmTags.ZIP }; + + public static Pattern PGP_MESSAGE = Pattern.compile( + ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL); + + public static Pattern PGP_SIGNED_MESSAGE = Pattern + .compile( + ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*", + Pattern.DOTALL); + + public static Pattern PGP_PUBLIC_KEY = Pattern.compile( + ".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*", + Pattern.DOTALL); + + private static String mEditPassPhrase = null; + + public static class ApgGeneralException extends Exception { + static final long serialVersionUID = 0xf812773342L; + + public ApgGeneralException(String message) { + super(message); + } + } + + public static class NoAsymmetricEncryptionException extends Exception { + static final long serialVersionUID = 0xf812773343L; + + public NoAsymmetricEncryptionException() { + super(); + } + } + + public static void setEditPassPhrase(String passPhrase) { + mEditPassPhrase = passPhrase; + } + + public static String getEditPassPhrase() { + return mEditPassPhrase; + } + + public static void updateProgress(ProgressDialogUpdater progress, int message, int current, + int total) { + if (progress != null) { + progress.setProgress(message, current, total); + } + } + + public static void updateProgress(ProgressDialogUpdater progress, int current, int total) { + if (progress != null) { + progress.setProgress(current, total); + } + } + + /** + * Creates new secret key. The returned PGPSecretKeyRing contains only one newly generated key + * when this key is the new masterkey. If a masterkey is supplied in the parameters + * PGPSecretKeyRing contains the masterkey and the new key as a subkey (certified by the + * masterkey). + * + * @param context + * @param algorithmChoice + * @param keySize + * @param passPhrase + * @param masterSecretKey + * @return + * @throws NoSuchAlgorithmException + * @throws PGPException + * @throws NoSuchProviderException + * @throws ApgGeneralException + * @throws InvalidAlgorithmParameterException + */ + public static PGPSecretKeyRing createKey(Context context, int algorithmChoice, int keySize, + String passPhrase, PGPSecretKey masterSecretKey) throws NoSuchAlgorithmException, + PGPException, NoSuchProviderException, ApgGeneralException, + InvalidAlgorithmParameterException { + + if (keySize < 512) { + throw new ApgGeneralException(context.getString(R.string.error_keySizeMinimum512bit)); + } + + if (passPhrase == null) { + passPhrase = ""; + } + + int algorithm = 0; + KeyPairGenerator keyGen = null; + + switch (algorithmChoice) { + case Id.choice.algorithm.dsa: { + keyGen = KeyPairGenerator.getInstance("DSA", BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); + algorithm = PGPPublicKey.DSA; + break; + } + + case Id.choice.algorithm.elgamal: { + if (masterSecretKey == null) { + throw new ApgGeneralException( + context.getString(R.string.error_masterKeyMustNotBeElGamal)); + } + keyGen = KeyPairGenerator.getInstance("ElGamal", BOUNCY_CASTLE_PROVIDER_NAME); + BigInteger p = Primes.getBestPrime(keySize); + BigInteger g = new BigInteger("2"); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + keyGen.initialize(elParams); + algorithm = PGPPublicKey.ELGAMAL_ENCRYPT; + break; + } + + case Id.choice.algorithm.rsa: { + keyGen = KeyPairGenerator.getInstance("RSA", BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); + + algorithm = PGPPublicKey.RSA_GENERAL; + break; + } + + default: { + throw new ApgGeneralException(context.getString(R.string.error_unknownAlgorithmChoice)); + } + } + + // build new key pair + PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); + + // define hashing and signing algos + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( + HashAlgorithmTags.SHA1); + + // Build key encrypter and decrypter based on passphrase + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( + passPhrase.toCharArray()); + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(passPhrase.toCharArray()); + + PGPKeyRingGenerator ringGen = null; + PGPContentSignerBuilder certificationSignerBuilder = null; + if (masterSecretKey == null) { + certificationSignerBuilder = new JcaPGPContentSignerBuilder(keyPair.getPublicKey() + .getAlgorithm(), HashAlgorithmTags.SHA1); + + // build keyRing with only this one master key in it! + ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, keyPair, "", + sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); + } else { + PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); + PGPPrivateKey masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); + + certificationSignerBuilder = new JcaPGPContentSignerBuilder(masterKeyPair + .getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); + + // build keyRing with master key and new key as subkey (certified by masterkey) + ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, + "", sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); + + ringGen.addSubKey(keyPair); + } + + PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing(); + + return secKeyRing; + } + + public static void buildSecretKey(Context context, ArrayList userIds, + ArrayList keys, ArrayList keysUsages, long masterKeyId, + String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) + throws ApgGeneralException, NoSuchProviderException, PGPException, + NoSuchAlgorithmException, SignatureException, IOException { + + Log.d(Constants.TAG, "userIds: " + userIds.toString()); + + updateProgress(progress, R.string.progress_buildingKey, 0, 100); + + if (oldPassPhrase == null) { + oldPassPhrase = ""; + } + if (newPassPhrase == null) { + newPassPhrase = ""; + } + + updateProgress(progress, R.string.progress_preparingMasterKey, 10, 100); + + int usageId = keysUsages.get(0); + boolean canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); + boolean canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); + + String mainUserId = userIds.get(0); + + PGPSecretKey masterKey = keys.get(0); + + // this removes all userIds and certifications previously attached to the masterPublicKey + PGPPublicKey tmpKey = masterKey.getPublicKey(); + PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(), + tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime()); + + // already done by code above: + // PGPPublicKey masterPublicKey = masterKey.getPublicKey(); + // // Somehow, the PGPPublicKey already has an empty certification attached to it when the + // // keyRing is generated the first time, we remove that when it exists, before adding the + // new + // // ones + // PGPPublicKey masterPublicKeyRmCert = PGPPublicKey.removeCertification(masterPublicKey, + // ""); + // if (masterPublicKeyRmCert != null) { + // masterPublicKey = masterPublicKeyRmCert; + // } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); + PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); + + updateProgress(progress, R.string.progress_certifyingMasterKey, 20, 100); + + for (String userId : userIds) { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); + + PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); + + masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); + } + + // TODO: cross-certify the master key with every sub key (APG 1) + + PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); + + PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); + + int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA; + if (canEncrypt) { + keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; + } + hashedPacketsGen.setKeyFlags(true, keyFlags); + + hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); + hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); + hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); + + // TODO: this doesn't work quite right yet (APG 1) + // if (keyEditor.getExpiryDate() != null) { + // GregorianCalendar creationDate = new GregorianCalendar(); + // creationDate.setTime(getCreationDate(masterKey)); + // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); + // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); + // if (numDays <= 0) { + // throw new GeneralException( + // context.getString(R.string.error_expiryMustComeAfterCreation)); + // } + // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); + // } + + updateProgress(progress, R.string.progress_buildingMasterKeyRing, 30, 100); + + // define hashing and signing algos + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( + HashAlgorithmTags.SHA1); + PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( + masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); + + // Build key encrypter based on passphrase + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( + newPassPhrase.toCharArray()); + + PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, + masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), + unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); + + updateProgress(progress, R.string.progress_addingSubKeys, 40, 100); + + for (int i = 1; i < keys.size(); ++i) { + updateProgress(progress, 40 + 50 * (i - 1) / (keys.size() - 1), 100); + + PGPSecretKey subKey = keys.get(i); + PGPPublicKey subPublicKey = subKey.getPublicKey(); + + PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); + PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); + + // TODO: now used without algorithm and creation time?! (APG 1) + PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); + + hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); + + keyFlags = 0; + + usageId = keysUsages.get(i); + canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); + canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); + if (canSign) { + keyFlags |= KeyFlags.SIGN_DATA; + } + if (canEncrypt) { + keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; + } + hashedPacketsGen.setKeyFlags(true, keyFlags); + + // TODO: this doesn't work quite right yet (APG 1) + // if (keyEditor.getExpiryDate() != null) { + // GregorianCalendar creationDate = new GregorianCalendar(); + // creationDate.setTime(getCreationDate(masterKey)); + // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); + // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); + // if (numDays <= 0) { + // throw new GeneralException( + // context.getString(R.string.error_expiryMustComeAfterCreation)); + // } + // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); + // } + + keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); + } + + PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); + PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); + + updateProgress(progress, R.string.progress_savingKeyRing, 90, 100); + + ProviderHelper.saveKeyRing(context, secretKeyRing); + ProviderHelper.saveKeyRing(context, publicKeyRing); + + updateProgress(progress, R.string.progress_done, 100, 100); + } + + public static int storeKeyRingInCache(Context context, PGPKeyRing keyring) { + int status = Integer.MIN_VALUE; // out of bounds value (Id.retrun_value.*) + try { + if (keyring instanceof PGPSecretKeyRing) { + PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring; + boolean save = true; + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); + PGPPrivateKey testKey = secretKeyRing.getSecretKey().extractPrivateKey( + keyDecryptor); + if (testKey == null) { + // this is bad, something is very wrong... likely a --export-secret-subkeys + // export + save = false; + status = Id.return_value.bad; + } + } catch (PGPException e) { + // all good if this fails, we likely didn't use the right password + } + + if (save) { + ProviderHelper.saveKeyRing(context, secretKeyRing); + // TODO: remove status returns, use exceptions! + status = Id.return_value.ok; + } + } else if (keyring instanceof PGPPublicKeyRing) { + PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring; + ProviderHelper.saveKeyRing(context, publicKeyRing); + // TODO: remove status returns, use exceptions! + status = Id.return_value.ok; + } + } catch (IOException e) { + status = Id.return_value.error; + } + + return status; + } + + public static boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ArmoredOutputStream aos = new ArmoredOutputStream(bos); + try { + aos.write(keyring.getEncoded()); + aos.close(); + + String armouredKey = bos.toString("UTF-8"); + server.add(armouredKey); + + return true; + } catch (IOException e) { + return false; + } catch (AddKeyException e) { + // TODO: tell the user? + return false; + } finally { + try { + bos.close(); + } catch (IOException e) { + } + } + } + + public static Bundle importKeyRings(Context context, int type, InputData data, + ProgressDialogUpdater progress) throws ApgGeneralException, FileNotFoundException, + PGPException, IOException { + Bundle returnData = new Bundle(); + + if (type == Id.type.secret_key) { + if (progress != null) + progress.setProgress(R.string.progress_importingSecretKeys, 0, 100); + } else { + if (progress != null) + progress.setProgress(R.string.progress_importingPublicKeys, 0, 100); + } + + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new ApgGeneralException(context.getString(R.string.error_externalStorageNotReady)); + } + + PositionAwareInputStream progressIn = new PositionAwareInputStream(data.getInputStream()); + // need to have access to the bufferedInput, so we can reuse it for the possible + // PGPObject chunks after the first one, e.g. files with several consecutive ASCII + // armour blocks + BufferedInputStream bufferedInput = new BufferedInputStream(progressIn); + int newKeys = 0; + int oldKeys = 0; + int badKeys = 0; + try { + PGPKeyRing keyring = PGPHelper.decodeKeyRing(bufferedInput); + while (keyring != null) { + int status = Integer.MIN_VALUE; // out of bounds value + + // if this key is what we expect it to be, save it + if ((type == Id.type.secret_key && keyring instanceof PGPSecretKeyRing) + || (type == Id.type.public_key && keyring instanceof PGPPublicKeyRing)) { + status = storeKeyRingInCache(context, keyring); + } + + if (status == Id.return_value.error) { + throw new ApgGeneralException(context.getString(R.string.error_savingKeys)); + } + + // update the counts to display to the user at the end + if (status == Id.return_value.updated) { + ++oldKeys; + } else if (status == Id.return_value.ok) { + ++newKeys; + } else if (status == Id.return_value.bad) { + ++badKeys; + } + + updateProgress(progress, (int) (100 * progressIn.position() / data.getSize()), 100); + + // TODO: needed? + // obj = objectFactory.nextObject(); + + keyring = PGPHelper.decodeKeyRing(bufferedInput); + } + } catch (EOFException e) { + // nothing to do, we are done + } + + returnData.putInt(ApgIntentService.RESULT_IMPORT_ADDED, newKeys); + returnData.putInt(ApgIntentService.RESULT_IMPORT_UPDATED, oldKeys); + returnData.putInt(ApgIntentService.RESULT_IMPORT_BAD, badKeys); + + updateProgress(progress, R.string.progress_done, 100, 100); + + return returnData; + } + + public static Bundle exportKeyRings(Context context, Vector keyRingIds, + OutputStream outStream, ProgressDialogUpdater progress) throws ApgGeneralException, + FileNotFoundException, PGPException, IOException { + Bundle returnData = new Bundle(); + + if (keyRingIds.size() == 1) { + updateProgress(progress, R.string.progress_exportingKey, 0, 100); + } else { + updateProgress(progress, R.string.progress_exportingKeys, 0, 100); + } + + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new ApgGeneralException(context.getString(R.string.error_externalStorageNotReady)); + } + ArmoredOutputStream out = new ArmoredOutputStream(outStream); + out.setHeader("Version", getFullVersion(context)); + + int numKeys = 0; + for (int i = 0; i < keyRingIds.size(); ++i) { + updateProgress(progress, i * 100 / keyRingIds.size(), 100); + + // try to get it as a PGPPublicKeyRing, if that fails try to get it as a SecretKeyRing + PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId( + context, keyRingIds.get(i)); + if (publicKeyRing != null) { + publicKeyRing.encode(out); + } else { + PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( + context, keyRingIds.get(i)); + if (secretKeyRing != null) { + secretKeyRing.encode(out); + } else { + continue; + } + } + ++numKeys; + } + out.close(); + returnData.putInt(ApgIntentService.RESULT_EXPORT, numKeys); + + updateProgress(progress, R.string.progress_done, 100, 100); + + return returnData; + } + + /** + * Encrypt and Sign data + * + * @param context + * @param progress + * @param data + * @param outStream + * @param useAsciiArmor + * @param compression + * @param encryptionKeyIds + * @param symmetricEncryptionAlgorithm + * @param encryptionPassphrase + * @param signatureKeyId + * @param signatureHashAlgorithm + * @param signatureForceV3 + * @param signaturePassphrase + * @throws IOException + * @throws ApgGeneralException + * @throws PGPException + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws SignatureException + */ + public static void encryptAndSign(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, boolean useAsciiArmor, int compression, + long encryptionKeyIds[], String encryptionPassphrase, int symmetricEncryptionAlgorithm, + long signatureKeyId, int signatureHashAlgorithm, boolean signatureForceV3, + String signaturePassphrase) throws IOException, ApgGeneralException, PGPException, + NoSuchProviderException, NoSuchAlgorithmException, SignatureException { + + if (encryptionKeyIds == null) { + encryptionKeyIds = new long[0]; + } + + ArmoredOutputStream armorOut = null; + OutputStream out = null; + OutputStream encryptOut = null; + if (useAsciiArmor) { + armorOut = new ArmoredOutputStream(outStream); + armorOut.setHeader("Version", getFullVersion(context)); + out = armorOut; + } else { + out = outStream; + } + PGPSecretKey signingKey = null; + PGPSecretKeyRing signingKeyRing = null; + PGPPrivateKey signaturePrivateKey = null; + + if (encryptionKeyIds.length == 0 && encryptionPassphrase == null) { + throw new ApgGeneralException( + context.getString(R.string.error_noEncryptionKeysOrPassPhrase)); + } + + if (signatureKeyId != Id.key.none) { + signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); + signingKey = PGPHelper.getSigningKey(context, signatureKeyId); + if (signingKey == null) { + throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassphrase == null) { + throw new ApgGeneralException( + context.getString(R.string.error_noSignaturePassPhrase)); + } + + updateProgress(progress, R.string.progress_extractingSignatureKey, 0, 100); + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); + signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + throw new ApgGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + } + updateProgress(progress, R.string.progress_preparingStreams, 5, 100); + + // encrypt and compress input file content + JcePGPDataEncryptorBuilder encryptorBuilder = new JcePGPDataEncryptorBuilder( + symmetricEncryptionAlgorithm).setProvider(BOUNCY_CASTLE_PROVIDER_NAME) + .setWithIntegrityPacket(true); + + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(encryptorBuilder); + + if (encryptionKeyIds.length == 0) { + // Symmetric encryption + Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption"); + + JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = new JcePBEKeyEncryptionMethodGenerator( + encryptionPassphrase.toCharArray()); + cPk.addMethod(symmetricEncryptionGenerator); + } else { + // Asymmetric encryption + for (int i = 0; i < encryptionKeyIds.length; ++i) { + PGPPublicKey key = PGPHelper.getEncryptPublicKey(context, encryptionKeyIds[i]); + if (key != null) { + + JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator = new JcePublicKeyKeyEncryptionMethodGenerator( + key); + cPk.addMethod(pubKeyEncryptionGenerator); + } + } + } + encryptOut = cPk.open(out, new byte[1 << 16]); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + if (signatureKeyId != Id.key.none) { + updateProgress(progress, R.string.progress_preparingSignature, 10, 100); + + // content signer based on signing key algorithm and choosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( + signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + if (signatureForceV3) { + signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); + } else { + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); + + String userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signingKeyRing)); + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + spGen.setSignerUserID(false, userId); + signatureGenerator.setHashedSubpackets(spGen.generate()); + } + } + + PGPCompressedDataGenerator compressGen = null; + BCPGOutputStream bcpgOut = null; + if (compression == Id.choice.compression.none) { + bcpgOut = new BCPGOutputStream(encryptOut); + } else { + compressGen = new PGPCompressedDataGenerator(compression); + bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut)); + } + if (signatureKeyId != Id.key.none) { + if (signatureForceV3) { + signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut); + } else { + signatureGenerator.generateOnePassVersion(false).encode(bcpgOut); + } + } + + PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator(); + // file name not needed, so empty string + OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(), + new byte[1 << 16]); + updateProgress(progress, R.string.progress_encrypting, 20, 100); + + long done = 0; + int n = 0; + byte[] buffer = new byte[1 << 16]; + InputStream in = data.getInputStream(); + while ((n = in.read(buffer)) > 0) { + pOut.write(buffer, 0, n); + if (signatureKeyId != Id.key.none) { + if (signatureForceV3) { + signatureV3Generator.update(buffer, 0, n); + } else { + signatureGenerator.update(buffer, 0, n); + } + } + done += n; + if (data.getSize() != 0) { + updateProgress(progress, (int) (20 + (95 - 20) * done / data.getSize()), 100); + } + } + + literalGen.close(); + + if (signatureKeyId != Id.key.none) { + updateProgress(progress, R.string.progress_generatingSignature, 95, 100); + if (signatureForceV3) { + signatureV3Generator.generate().encode(pOut); + } else { + signatureGenerator.generate().encode(pOut); + } + } + if (compressGen != null) { + compressGen.close(); + } + encryptOut.close(); + if (useAsciiArmor) { + armorOut.close(); + } + + updateProgress(progress, R.string.progress_done, 100, 100); + } + + public static void signText(Context context, ProgressDialogUpdater progress, InputData data, + OutputStream outStream, long signatureKeyId, String signaturePassphrase, + int signatureHashAlgorithm, boolean forceV3Signature) throws ApgGeneralException, + PGPException, IOException, NoSuchAlgorithmException, SignatureException { + + ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream); + armorOut.setHeader("Version", getFullVersion(context)); + + PGPSecretKey signingKey = null; + PGPSecretKeyRing signingKeyRing = null; + PGPPrivateKey signaturePrivateKey = null; + + if (signatureKeyId == 0) { + armorOut.close(); + throw new ApgGeneralException(context.getString(R.string.error_noSignatureKey)); + } + + signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); + signingKey = PGPHelper.getSigningKey(context, signatureKeyId); + if (signingKey == null) { + armorOut.close(); + throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassphrase == null) { + armorOut.close(); + throw new ApgGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); + } + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); + signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + armorOut.close(); + throw new ApgGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + updateProgress(progress, R.string.progress_preparingStreams, 0, 100); + + updateProgress(progress, R.string.progress_preparingSignature, 30, 100); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + // content signer based on signing key algorithm and choosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey + .getPublicKey().getAlgorithm(), signatureHashAlgorithm) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + if (forceV3Signature) { + signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); + } else { + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + String userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signingKeyRing)); + spGen.setSignerUserID(false, userId); + signatureGenerator.setHashedSubpackets(spGen.generate()); + } + + updateProgress(progress, R.string.progress_signing, 40, 100); + + armorOut.beginClearText(signatureHashAlgorithm); + + InputStream inStream = data.getInputStream(); + final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream)); + + final byte[] newline = "\r\n".getBytes("UTF-8"); + + if (forceV3Signature) { + processLine(reader.readLine(), armorOut, signatureV3Generator); + } else { + processLine(reader.readLine(), armorOut, signatureGenerator); + } + + while (true) { + final String line = reader.readLine(); + + if (line == null) { + armorOut.write(newline); + break; + } + + armorOut.write(newline); + if (forceV3Signature) { + signatureV3Generator.update(newline); + processLine(line, armorOut, signatureV3Generator); + } else { + signatureGenerator.update(newline); + processLine(line, armorOut, signatureGenerator); + } + } + + armorOut.endClearText(); + + BCPGOutputStream bOut = new BCPGOutputStream(armorOut); + if (forceV3Signature) { + signatureV3Generator.generate().encode(bOut); + } else { + signatureGenerator.generate().encode(bOut); + } + armorOut.close(); + + updateProgress(progress, R.string.progress_done, 100, 100); + } + + public static void generateSignature(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, boolean armored, boolean binary, + long signatureKeyId, String signaturePassPhrase, int hashAlgorithm, + boolean forceV3Signature) throws ApgGeneralException, PGPException, IOException, + NoSuchAlgorithmException, SignatureException { + + OutputStream out = null; + + // Ascii Armor (Base64) + ArmoredOutputStream armorOut = null; + if (armored) { + armorOut = new ArmoredOutputStream(outStream); + armorOut.setHeader("Version", getFullVersion(context)); + out = armorOut; + } else { + out = outStream; + } + + PGPSecretKey signingKey = null; + PGPSecretKeyRing signingKeyRing = null; + PGPPrivateKey signaturePrivateKey = null; + + if (signatureKeyId == 0) { + throw new ApgGeneralException(context.getString(R.string.error_noSignatureKey)); + } + + signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); + signingKey = PGPHelper.getSigningKey(context, signatureKeyId); + if (signingKey == null) { + throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassPhrase == null) { + throw new ApgGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); + } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassPhrase.toCharArray()); + signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + throw new ApgGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + updateProgress(progress, R.string.progress_preparingStreams, 0, 100); + + updateProgress(progress, R.string.progress_preparingSignature, 30, 100); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + int type = PGPSignature.CANONICAL_TEXT_DOCUMENT; + if (binary) { + type = PGPSignature.BINARY_DOCUMENT; + } + + // content signer based on signing key algorithm and choosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey + .getPublicKey().getAlgorithm(), hashAlgorithm) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + if (forceV3Signature) { + signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(type, signaturePrivateKey); + } else { + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(type, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + String userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signingKeyRing)); + spGen.setSignerUserID(false, userId); + signatureGenerator.setHashedSubpackets(spGen.generate()); + } + + updateProgress(progress, R.string.progress_signing, 40, 100); + + InputStream inStream = data.getInputStream(); + if (binary) { + byte[] buffer = new byte[1 << 16]; + int n = 0; + while ((n = inStream.read(buffer)) > 0) { + if (forceV3Signature) { + signatureV3Generator.update(buffer, 0, n); + } else { + signatureGenerator.update(buffer, 0, n); + } + } + } else { + final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream)); + final byte[] newline = "\r\n".getBytes("UTF-8"); + + while (true) { + final String line = reader.readLine(); + + if (line == null) { + break; + } + + if (forceV3Signature) { + processLine(line, null, signatureV3Generator); + signatureV3Generator.update(newline); + } else { + processLine(line, null, signatureGenerator); + signatureGenerator.update(newline); + } + } + } + + BCPGOutputStream bOut = new BCPGOutputStream(out); + if (forceV3Signature) { + signatureV3Generator.generate().encode(bOut); + } else { + signatureGenerator.generate().encode(bOut); + } + out.close(); + outStream.close(); + + if (progress != null) + progress.setProgress(R.string.progress_done, 100, 100); + } + + public static PGPPublicKeyRing signKey(Context context, long masterKeyId, long pubKeyId, + String passphrase) throws ApgGeneralException, NoSuchAlgorithmException, + NoSuchProviderException, PGPException, SignatureException { + if (passphrase == null || passphrase.length() <= 0) { + throw new ApgGeneralException("Unable to obtain passphrase"); + } else { + PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByKeyId(context, pubKeyId); + + PGPSecretKey signingKey = PGPHelper.getSigningKey(context, masterKeyId); + if (signingKey == null) { + throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); + } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + throw new ApgGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + + // TODO: SHA256 fixed? + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( + signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( + contentSignerBuilder); + + signatureGenerator.init(PGPSignature.DIRECT_KEY, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + PGPSignatureSubpacketVector packetVector = spGen.generate(); + signatureGenerator.setHashedSubpackets(packetVector); + + PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId), + signatureGenerator.generate()); + pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey); + + return pubring; + } + } + + public static long getDecryptionKeyId(Context context, InputStream inputStream) + throws ApgGeneralException, NoAsymmetricEncryptionException, IOException { + InputStream in = PGPUtil.getDecoderStream(inputStream); + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + + // the first object might be a PGP marker packet. + if (o instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) o; + } else { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + + if (enc == null) { + throw new ApgGeneralException(context.getString(R.string.error_invalidData)); + } + + // TODO: currently we always only look at the first known key + // find the secret key + PGPSecretKey secretKey = null; + Iterator it = enc.getEncryptedDataObjects(); + boolean gotAsymmetricEncryption = false; + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPublicKeyEncryptedData) { + gotAsymmetricEncryption = true; + PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj; + secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, pbe.getKeyID()); + if (secretKey != null) { + break; + } + } + } + + if (!gotAsymmetricEncryption) { + throw new NoAsymmetricEncryptionException(); + } + + if (secretKey == null) { + return Id.key.none; + } + + return secretKey.getKeyID(); + } + + public static boolean hasSymmetricEncryption(Context context, InputStream inputStream) + throws ApgGeneralException, IOException { + InputStream in = PGPUtil.getDecoderStream(inputStream); + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + + // the first object might be a PGP marker packet. + if (o instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) o; + } else { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + + if (enc == null) { + throw new ApgGeneralException(context.getString(R.string.error_invalidData)); + } + + Iterator it = enc.getEncryptedDataObjects(); + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPBEEncryptedData) { + return true; + } + } + + return false; + } + + public static Bundle decryptAndVerify(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, String passphrase, boolean assumeSymmetric) + throws IOException, ApgGeneralException, PGPException, SignatureException { + if (passphrase == null) { + passphrase = ""; + } + + Bundle returnData = new Bundle(); + InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + long signatureKeyId = 0; + + int currentProgress = 0; + if (progress != null) + progress.setProgress(R.string.progress_readingData, currentProgress, 100); + + if (o instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) o; + } else { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + + if (enc == null) { + throw new ApgGeneralException(context.getString(R.string.error_invalidData)); + } + + InputStream clear = null; + PGPEncryptedData encryptedData = null; + + currentProgress += 5; + + // TODO: currently we always only look at the first known key or symmetric encryption, + // there might be more... + if (assumeSymmetric) { + PGPPBEEncryptedData pbe = null; + Iterator it = enc.getEncryptedDataObjects(); + // find secret key + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPBEEncryptedData) { + pbe = (PGPPBEEncryptedData) obj; + break; + } + } + + if (pbe == null) { + throw new ApgGeneralException( + context.getString(R.string.error_noSymmetricEncryptionPacket)); + } + + updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); + + PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(); + PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( + digestCalcProvider).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( + passphrase.toCharArray()); + + clear = pbe.getDataStream(decryptorFactory); + + encryptedData = pbe; + currentProgress += 5; + } else { + if (progress != null) + progress.setProgress(R.string.progress_findingKey, currentProgress, 100); + PGPPublicKeyEncryptedData pbe = null; + PGPSecretKey secretKey = null; + Iterator it = enc.getEncryptedDataObjects(); + // find secret key + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPublicKeyEncryptedData) { + PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; + secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID()); + if (secretKey != null) { + pbe = encData; + break; + } + } + } + + if (secretKey == null) { + throw new ApgGeneralException(context.getString(R.string.error_noSecretKeyFound)); + } + + currentProgress += 5; + updateProgress(progress, R.string.progress_extractingKey, currentProgress, 100); + PGPPrivateKey privateKey = null; + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + privateKey = secretKey.extractPrivateKey(keyDecryptor); + } catch (PGPException e) { + throw new PGPException(context.getString(R.string.error_wrongPassPhrase)); + } + if (privateKey == null) { + throw new ApgGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + currentProgress += 5; + updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); + + PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey); + + clear = pbe.getDataStream(decryptorFactory); + + encryptedData = pbe; + currentProgress += 5; + } + + PGPObjectFactory plainFact = new PGPObjectFactory(clear); + Object dataChunk = plainFact.nextObject(); + PGPOnePassSignature signature = null; + PGPPublicKey signatureKey = null; + int signatureIndex = -1; + + if (dataChunk instanceof PGPCompressedData) { + if (progress != null) + progress.setProgress(R.string.progress_decompressingData, currentProgress, 100); + PGPObjectFactory fact = new PGPObjectFactory( + ((PGPCompressedData) dataChunk).getDataStream()); + dataChunk = fact.nextObject(); + plainFact = fact; + currentProgress += 10; + } + + if (dataChunk instanceof PGPOnePassSignatureList) { + if (progress != null) + progress.setProgress(R.string.progress_processingSignature, currentProgress, 100); + returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE, true); + PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; + for (int i = 0; i < sigList.size(); ++i) { + signature = sigList.get(i); + signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); + if (signatureKeyId == 0) { + signatureKeyId = signature.getKeyID(); + } + if (signatureKey == null) { + signature = null; + } else { + signatureIndex = i; + signatureKeyId = signature.getKeyID(); + String userId = null; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId( + context, signatureKeyId); + if (signKeyRing != null) { + userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signKeyRing)); + } + returnData.putString(ApgIntentService.RESULT_SIGNATURE_USER_ID, userId); + break; + } + } + + returnData.putLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + + if (signature != null) { + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + signature.init(contentVerifierBuilderProvider, signatureKey); + } else { + returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN, true); + } + + dataChunk = plainFact.nextObject(); + currentProgress += 10; + } + + if (dataChunk instanceof PGPSignatureList) { + dataChunk = plainFact.nextObject(); + } + + if (dataChunk instanceof PGPLiteralData) { + if (progress != null) + progress.setProgress(R.string.progress_decrypting, currentProgress, 100); + PGPLiteralData literalData = (PGPLiteralData) dataChunk; + OutputStream out = outStream; + + byte[] buffer = new byte[1 << 16]; + InputStream dataIn = literalData.getInputStream(); + + int startProgress = currentProgress; + int endProgress = 100; + if (signature != null) { + endProgress = 90; + } else if (encryptedData.isIntegrityProtected()) { + endProgress = 95; + } + int n = 0; + int done = 0; + long startPos = data.getStreamPosition(); + while ((n = dataIn.read(buffer)) > 0) { + out.write(buffer, 0, n); + done += n; + if (signature != null) { + try { + signature.update(buffer, 0, n); + } catch (SignatureException e) { + returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS, false); + signature = null; + } + } + // unknown size, but try to at least have a moving, slowing down progress bar + currentProgress = startProgress + (endProgress - startProgress) * done + / (done + 100000); + if (data.getSize() - startPos == 0) { + currentProgress = endProgress; + } else { + currentProgress = (int) (startProgress + (endProgress - startProgress) + * (data.getStreamPosition() - startPos) / (data.getSize() - startPos)); + } + updateProgress(progress, currentProgress, 100); + } + + if (signature != null) { + if (progress != null) + progress.setProgress(R.string.progress_verifyingSignature, 90, 100); + PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); + PGPSignature messageSignature = signatureList.get(signatureIndex); + if (signature.verify(messageSignature)) { + returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS, true); + } else { + returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS, false); + } + } + } + + // TODO: add integrity somewhere + if (encryptedData.isIntegrityProtected()) { + if (progress != null) + progress.setProgress(R.string.progress_verifyingIntegrity, 95, 100); + if (encryptedData.verify()) { + // passed + } else { + // failed + } + } else { + // no integrity check + } + + updateProgress(progress, R.string.progress_done, 100, 100); + return returnData; + } + + public static Bundle verifyText(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, boolean lookupUnknownKey) throws IOException, + ApgGeneralException, PGPException, SignatureException { + Bundle returnData = new Bundle(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream()); + + updateProgress(progress, R.string.progress_done, 0, 100); + + // mostly taken from ClearSignedFileProcessor + ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + int lookAhead = readInputLine(lineOut, aIn); + byte[] lineSep = getLineSeparator(); + + byte[] line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparator(line)); + out.write(lineSep); + + while (lookAhead != -1 && aIn.isClearText()) { + lookAhead = readInputLine(lineOut, lookAhead, aIn); + line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparator(line)); + out.write(lineSep); + } + + out.close(); + + byte[] clearText = out.toByteArray(); + outStream.write(clearText); + + returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE, true); + + updateProgress(progress, R.string.progress_processingSignature, 60, 100); + PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); + + PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); + if (sigList == null) { + throw new ApgGeneralException(context.getString(R.string.error_corruptData)); + } + PGPSignature signature = null; + long signatureKeyId = 0; + PGPPublicKey signatureKey = null; + for (int i = 0; i < sigList.size(); ++i) { + signature = sigList.get(i); + signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); + if (signatureKeyId == 0) { + signatureKeyId = signature.getKeyID(); + } + // if key is not known and we want to lookup unknown ones... + if (signatureKey == null && lookupUnknownKey) { + + returnData = new Bundle(); + returnData.putLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_LOOKUP_KEY, true); + + // return directly now, decrypt will be done again after importing unknown key + return returnData; + } + + if (signatureKey == null) { + signature = null; + } else { + signatureKeyId = signature.getKeyID(); + String userId = null; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context, + signatureKeyId); + if (signKeyRing != null) { + userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signKeyRing)); + } + returnData.putString(ApgIntentService.RESULT_SIGNATURE_USER_ID, userId); + break; + } + } + + returnData.putLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + + if (signature == null) { + returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN, true); + if (progress != null) + progress.setProgress(R.string.progress_done, 100, 100); + return returnData; + } + + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + signature.init(contentVerifierBuilderProvider, signatureKey); + + InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText)); + + lookAhead = readInputLine(lineOut, sigIn); + + processLine(signature, lineOut.toByteArray()); + + if (lookAhead != -1) { + do { + lookAhead = readInputLine(lineOut, lookAhead, sigIn); + + signature.update((byte) '\r'); + signature.update((byte) '\n'); + + processLine(signature, lineOut.toByteArray()); + } while (lookAhead != -1); + } + + returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS, signature.verify()); + + updateProgress(progress, R.string.progress_done, 100, 100); + return returnData; + } + + public static int getStreamContent(Context context, InputStream inStream) throws IOException { + InputStream in = PGPUtil.getDecoderStream(inStream); + PGPObjectFactory pgpF = new PGPObjectFactory(in); + Object object = pgpF.nextObject(); + while (object != null) { + if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) { + return Id.content.keys; + } else if (object instanceof PGPEncryptedDataList) { + return Id.content.encrypted_data; + } + object = pgpF.nextObject(); + } + + return Id.content.unknown; + } + + private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, + final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException { + + if (pLine == null) { + return; + } + + final char[] chars = pLine.toCharArray(); + int len = chars.length; + + while (len > 0) { + if (!Character.isWhitespace(chars[len - 1])) { + break; + } + len--; + } + + final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); + + if (pArmoredOutput != null) { + pArmoredOutput.write(data); + } + pSignatureGenerator.update(data); + } + + private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, + final PGPV3SignatureGenerator pSignatureGenerator) throws IOException, + SignatureException { + + if (pLine == null) { + return; + } + + final char[] chars = pLine.toCharArray(); + int len = chars.length; + + while (len > 0) { + if (!Character.isWhitespace(chars[len - 1])) { + break; + } + len--; + } + + final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); + + if (pArmoredOutput != null) { + pArmoredOutput.write(data); + } + pSignatureGenerator.update(data); + } + + // taken from ClearSignedFileProcessor in BC + private static void processLine(PGPSignature sig, byte[] line) throws SignatureException, + IOException { + int length = getLengthWithoutWhiteSpace(line); + if (length > 0) { + sig.update(line, 0, length); + } + } + + private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) + throws IOException { + bOut.reset(); + + int lookAhead = -1; + int ch; + + while ((ch = fIn.read()) >= 0) { + bOut.write(ch); + if (ch == '\r' || ch == '\n') { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } + + return lookAhead; + } + + private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn) + throws IOException { + bOut.reset(); + + int ch = lookAhead; + + do { + bOut.write(ch); + if (ch == '\r' || ch == '\n') { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } while ((ch = fIn.read()) >= 0); + + if (ch < 0) { + lookAhead = -1; + } + + return lookAhead; + } + + private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn) + throws IOException { + int lookAhead = fIn.read(); + + if (lastCh == '\r' && lookAhead == '\n') { + bOut.write(lookAhead); + lookAhead = fIn.read(); + } + + return lookAhead; + } + + private static int getLengthWithoutSeparator(byte[] line) { + int end = line.length - 1; + + while (end >= 0 && isLineEnding(line[end])) { + end--; + } + + return end + 1; + } + + private static boolean isLineEnding(byte b) { + return b == '\r' || b == '\n'; + } + + private static int getLengthWithoutWhiteSpace(byte[] line) { + int end = line.length - 1; + + while (end >= 0 && isWhiteSpace(line[end])) { + end--; + } + + return end + 1; + } + + private static boolean isWhiteSpace(byte b) { + return b == '\r' || b == '\n' || b == '\t' || b == ' '; + } + + private static byte[] getLineSeparator() { + String nl = System.getProperty("line.separator"); + byte[] nlBytes = new byte[nl.length()]; + + for (int i = 0; i != nlBytes.length; i++) { + nlBytes[i] = (byte) nl.charAt(i); + } + + return nlBytes; + } + + public static boolean isReleaseVersion(Context context) { + try { + PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); + if (pi.versionCode % 100 == 99) { + return true; + } else { + return false; + } + } catch (NameNotFoundException e) { + // impossible! + return false; + } + } + + public static String getVersion(Context context) { + String version = null; + try { + PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); + version = pi.versionName; + return version; + } catch (NameNotFoundException e) { + Log.e(Constants.TAG, "Version could not be retrieved!", e); + return "0.0.0"; + } + } + + public static String getFullVersion(Context context) { + return "APG v" + getVersion(context); + } + + /** + * Generate a random filename + * + * @param length + * @return + */ + public static String generateRandomFilename(int length) { + SecureRandom random = new SecureRandom(); + + byte bytes[] = new byte[length]; + random.nextBytes(bytes); + String result = ""; + for (int i = 0; i < length; ++i) { + int v = (bytes[i] + 256) % 64; + if (v < 10) { + result += (char) ('0' + v); + } else if (v < 36) { + result += (char) ('A' + v - 10); + } else if (v < 62) { + result += (char) ('a' + v - 36); + } else if (v == 62) { + result += '_'; + } else if (v == 63) { + result += '.'; + } + } + return result; + } + + /** + * Go once through stream to get length of stream. The length is later used to display progress + * when encrypting/decrypting + * + * @param in + * @return + * @throws IOException + */ + public static long getLengthOfStream(InputStream in) throws IOException { + long size = 0; + long n = 0; + byte dummy[] = new byte[0x10000]; + while ((n = in.read(dummy)) > 0) { + size += n; + } + return size; + } + + /** + * Deletes file securely by overwriting it with random data before deleting it. + * + * TODO: Does this really help on flash storage? + * + * @param context + * @param progress + * @param file + * @throws FileNotFoundException + * @throws IOException + */ + public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file) + throws FileNotFoundException, IOException { + long length = file.length(); + SecureRandom random = new SecureRandom(); + RandomAccessFile raf = new RandomAccessFile(file, "rws"); + raf.seek(0); + raf.getFilePointer(); + byte[] data = new byte[1 << 16]; + int pos = 0; + String msg = context.getString(R.string.progress_deletingSecurely, file.getName()); + while (pos < length) { + if (progress != null) + progress.setProgress(msg, (int) (100 * pos / length), 100); + random.nextBytes(data); + raf.write(data); + pos += data.length; + } + raf.close(); + file.delete(); + } +} diff --git a/APG/src/org/thialfihar/android/apg/helper/Preferences.java b/APG/src/org/thialfihar/android/apg/helper/Preferences.java new file mode 100644 index 000000000..14adf357a --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/helper/Preferences.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * 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.helper; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; + +import android.content.Context; +import android.content.SharedPreferences; + +import java.util.Vector; + +/** + * Singleton Implementation of a Preference Helper + */ +public class Preferences { + private static Preferences mPreferences; + private SharedPreferences mSharedPreferences; + + public static synchronized Preferences getPreferences(Context context) { + return getPreferences(context, false); + } + + public static synchronized Preferences getPreferences(Context context, boolean force_new) { + if (mPreferences == null || force_new) { + mPreferences = new Preferences(context); + } + return mPreferences; + } + + private Preferences(Context context) { + mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE); + } + + public String getLanguage() { + return mSharedPreferences.getString(Constants.pref.LANGUAGE, ""); + } + + public void setLanguage(String value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putString(Constants.pref.LANGUAGE, value); + editor.commit(); + } + + public long getPassPhraseCacheTtl() { + int ttl = mSharedPreferences.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; + } + return (long) ttl; + } + + public void setPassPhraseCacheTtl(int value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putInt(Constants.pref.PASS_PHRASE_CACHE_TTL, value); + editor.commit(); + } + + public int getDefaultEncryptionAlgorithm() { + return mSharedPreferences.getInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM, + PGPEncryptedData.AES_256); + } + + public void setDefaultEncryptionAlgorithm(int value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM, value); + editor.commit(); + } + + public int getDefaultHashAlgorithm() { + return mSharedPreferences.getInt(Constants.pref.DEFAULT_HASH_ALGORITHM, + HashAlgorithmTags.SHA256); + } + + public void setDefaultHashAlgorithm(int value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putInt(Constants.pref.DEFAULT_HASH_ALGORITHM, value); + editor.commit(); + } + + public int getDefaultMessageCompression() { + return mSharedPreferences.getInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION, + Id.choice.compression.zlib); + } + + public void setDefaultMessageCompression(int value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION, value); + editor.commit(); + } + + public int getDefaultFileCompression() { + return mSharedPreferences.getInt(Constants.pref.DEFAULT_FILE_COMPRESSION, + Id.choice.compression.none); + } + + public void setDefaultFileCompression(int value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putInt(Constants.pref.DEFAULT_FILE_COMPRESSION, value); + editor.commit(); + } + + public boolean getDefaultAsciiArmour() { + return mSharedPreferences.getBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, false); + } + + public void setDefaultAsciiArmour(boolean value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, value); + editor.commit(); + } + + public boolean getForceV3Signatures() { + return mSharedPreferences.getBoolean(Constants.pref.FORCE_V3_SIGNATURES, false); + } + + public void setForceV3Signatures(boolean value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putBoolean(Constants.pref.FORCE_V3_SIGNATURES, value); + editor.commit(); + } + + public String[] getKeyServers() { + String rawData = mSharedPreferences.getString(Constants.pref.KEY_SERVERS, + Constants.defaults.KEY_SERVERS); + Vector servers = new Vector(); + String chunks[] = rawData.split(","); + for (int i = 0; i < chunks.length; ++i) { + String tmp = chunks[i].trim(); + if (tmp.length() > 0) { + servers.add(tmp); + } + } + return servers.toArray(chunks); + } + + public void setKeyServers(String[] value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + String rawData = ""; + for (int i = 0; i < value.length; ++i) { + String tmp = value[i].trim(); + if (tmp.length() == 0) { + continue; + } + if (!"".equals(rawData)) { + rawData += ","; + } + rawData += tmp; + } + editor.putString(Constants.pref.KEY_SERVERS, rawData); + editor.commit(); + } +} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgContract.java b/APG/src/org/thialfihar/android/apg/provider/ApgContract.java new file mode 100644 index 000000000..c68eec750 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/provider/ApgContract.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * 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 org.thialfihar.android.apg.Constants; + +import android.net.Uri; +import android.provider.BaseColumns; + +public class ApgContract { + + interface KeyRingsColumns { + String MASTER_KEY_ID = "master_key_id"; // not a database id + String TYPE = "type"; // see KeyTypes + String KEY_RING_DATA = "key_ring_data"; // PGPPublicKeyRing / PGPSecretKeyRing blob + } + + interface KeysColumns { + String KEY_ID = "key_id"; // not a database id + String TYPE = "type"; // see KeyTypes + String IS_MASTER_KEY = "is_master_key"; + String ALGORITHM = "algorithm"; + String KEY_SIZE = "key_size"; + String CAN_SIGN = "can_sign"; + String CAN_ENCRYPT = "can_encrypt"; + String IS_REVOKED = "is_revoked"; + String CREATION = "creation"; + String EXPIRY = "expiry"; + String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID + String KEY_DATA = "key_data"; // PGPPublicKey / PGPSecretKey blob + String RANK = "rank"; + } + + interface UserIdsColumns { + String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID + String USER_ID = "user_id"; // not a database id + String RANK = "rank"; + } + + public static final class KeyTypes { + public static final int PUBLIC = 0; + public static final int SECRET = 1; + } + + public static final String CONTENT_AUTHORITY_EXTERNAL = Constants.PACKAGE_NAME; + public static final String CONTENT_AUTHORITY_INTERNAL = Constants.PACKAGE_NAME + ".internal"; + + private static final Uri BASE_CONTENT_URI_INTERNAL = Uri.parse("content://" + + CONTENT_AUTHORITY_INTERNAL); + + public static final String BASE_KEY_RINGS = "key_rings"; + public static final String BASE_DATA = "data"; + + public static final String PATH_PUBLIC = "public"; + public static final String PATH_SECRET = "secret"; + + public static final String PATH_BY_MASTER_KEY_ID = "master_key_id"; + public static final String PATH_BY_KEY_ID = "key_id"; + public static final String PATH_BY_EMAILS = "emails"; + + public static final String PATH_USER_IDS = "user_ids"; + public static final String PATH_KEYS = "keys"; + + public static class KeyRings implements KeyRingsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_KEY_RINGS).build(); + + /** Use if multiple items get returned */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring"; + + /** Use if a single item is returned */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring"; + + public static Uri buildPublicKeyRingsUri() { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build(); + } + + public static Uri buildPublicKeyRingsUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId).build(); + } + + public static Uri buildPublicKeyRingsByMasterKeyIdUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC) + .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); + } + + public static Uri buildPublicKeyRingsByKeyIdUri(String keyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_KEY_ID) + .appendPath(keyId).build(); + } + + public static Uri buildPublicKeyRingsByEmailsUri(String emails) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_EMAILS) + .appendPath(emails).build(); + } + + public static Uri buildSecretKeyRingsUri() { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build(); + } + + public static Uri buildSecretKeyRingsUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId).build(); + } + + public static Uri buildSecretKeyRingsByMasterKeyIdUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET) + .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); + } + + public static Uri buildSecretKeyRingsByKeyIdUri(String keyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_KEY_ID) + .appendPath(keyId).build(); + } + + public static Uri buildSecretKeyRingsByEmailsUri(String emails) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS) + .appendPath(emails).build(); + } + } + + public static class Keys implements KeysColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_KEY_RINGS).build(); + + /** Use if multiple items get returned */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key"; + + /** Use if a single item is returned */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key"; + + public static Uri buildPublicKeysUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) + .appendPath(PATH_KEYS).build(); + } + + public static Uri buildPublicKeysUri(String keyRingRowId, String keyRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) + .appendPath(PATH_KEYS).appendPath(keyRowId).build(); + } + + public static Uri buildSecretKeysUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) + .appendPath(PATH_KEYS).build(); + } + + public static Uri buildSecretKeysUri(String keyRingRowId, String keyRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) + .appendPath(PATH_KEYS).appendPath(keyRowId).build(); + } + } + + public static class UserIds implements UserIdsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_KEY_RINGS).build(); + + /** Use if multiple items get returned */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id"; + + /** Use if a single item is returned */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id"; + + public static Uri buildPublicUserIdsUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) + .appendPath(PATH_USER_IDS).build(); + } + + public static Uri buildPublicUserIdsUri(String keyRingRowId, String userIdRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) + .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); + } + + public static Uri buildSecretUserIdsUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) + .appendPath(PATH_USER_IDS).build(); + } + + public static Uri buildSecretUserIdsUri(String keyRingRowId, String userIdRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) + .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); + } + } + + public static class DataStream { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_DATA).build(); + + public static Uri buildDataStreamUri(String streamFilename) { + return CONTENT_URI.buildUpon().appendPath(streamFilename).build(); + } + } + + private ApgContract() { + } +} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgDatabase.java b/APG/src/org/thialfihar/android/apg/provider/ApgDatabase.java new file mode 100644 index 000000000..e25fce3f0 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/provider/ApgDatabase.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * 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 org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.provider.ApgContract.KeyRingsColumns; +import org.thialfihar.android.apg.provider.ApgContract.KeysColumns; +import org.thialfihar.android.apg.provider.ApgContract.UserIdsColumns; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.provider.BaseColumns; + +import org.thialfihar.android.apg.util.Log; + +public class ApgDatabase extends SQLiteOpenHelper { + private static final String DATABASE_NAME = "apg.db"; + private static final int DATABASE_VERSION = 3; + + public interface Tables { + String KEY_RINGS = "key_rings"; + String KEYS = "keys"; + String USER_IDS = "user_ids"; + } + + private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS + + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + KeyRingsColumns.MASTER_KEY_ID + " INT64, " + KeyRingsColumns.TYPE + " INTEGER, " + + KeyRingsColumns.KEY_RING_DATA + " BLOB)"; + + private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " (" + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + KeysColumns.KEY_ID + + " INT64, " + KeysColumns.TYPE + " INTEGER, " + KeysColumns.IS_MASTER_KEY + + " INTEGER, " + KeysColumns.ALGORITHM + " INTEGER, " + KeysColumns.KEY_SIZE + + " INTEGER, " + KeysColumns.CAN_SIGN + " INTEGER, " + KeysColumns.CAN_ENCRYPT + + " INTEGER, " + KeysColumns.IS_REVOKED + " INTEGER, " + KeysColumns.CREATION + + " INTEGER, " + KeysColumns.EXPIRY + " INTEGER, " + KeysColumns.KEY_DATA + " BLOB," + + KeysColumns.RANK + " INTEGER, " + KeysColumns.KEY_RING_ROW_ID + + " INTEGER NOT NULL, FOREIGN KEY(" + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)"; + + private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + UserIdsColumns.USER_ID + " TEXT, " + UserIdsColumns.RANK + " INTEGER, " + + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" + + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" + + BaseColumns._ID + ") ON DELETE CASCADE)"; + + ApgDatabase(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + Log.w(Constants.TAG, "Creating database..."); + + db.execSQL(CREATE_KEY_RINGS); + db.execSQL(CREATE_KEYS); + db.execSQL(CREATE_USER_IDS); + } + + @Override + public void onOpen(SQLiteDatabase db) { + super.onOpen(db); + if (!db.isReadOnly()) { + // Enable foreign key constraints + db.execSQL("PRAGMA foreign_keys=ON;"); + } + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); + + // Upgrade from oldVersion through all methods to newest one + for (int version = oldVersion; version < newVersion; ++version) { + Log.w(Constants.TAG, "Upgrading database to version " + version); + + switch (version) { + + default: + break; + + } + } + } + +} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgProvider.java b/APG/src/org/thialfihar/android/apg/provider/ApgProvider.java new file mode 100644 index 000000000..a7ec253a3 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/provider/ApgProvider.java @@ -0,0 +1,825 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * 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 java.io.File; +import java.io.FileNotFoundException; +import java.util.Arrays; +import java.util.HashMap; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.provider.ApgContract.KeyRings; +import org.thialfihar.android.apg.provider.ApgContract.KeyRingsColumns; +import org.thialfihar.android.apg.provider.ApgContract.KeyTypes; +import org.thialfihar.android.apg.provider.ApgContract.KeysColumns; +import org.thialfihar.android.apg.provider.ApgContract.UserIds; +import org.thialfihar.android.apg.provider.ApgContract.Keys; +import org.thialfihar.android.apg.provider.ApgContract.UserIdsColumns; +import org.thialfihar.android.apg.provider.ApgDatabase.Tables; +import org.thialfihar.android.apg.util.Log; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteConstraintException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.provider.BaseColumns; +import android.text.TextUtils; + +public class ApgProvider extends ContentProvider { + private static final int PUBLIC_KEY_RING = 101; + private static final int PUBLIC_KEY_RING_BY_ROW_ID = 102; + private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID = 103; + private static final int PUBLIC_KEY_RING_BY_KEY_ID = 104; + private static final int PUBLIC_KEY_RING_BY_EMAILS = 105; + + private static final int PUBLIC_KEY_RING_KEY = 111; + private static final int PUBLIC_KEY_RING_KEY_BY_ROW_ID = 112; + + private static final int PUBLIC_KEY_RING_USER_ID = 121; + private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122; + + private static final int SECRET_KEY_RING = 201; + private static final int SECRET_KEY_RING_BY_ROW_ID = 202; + private static final int SECRET_KEY_RING_BY_MASTER_KEY_ID = 203; + private static final int SECRET_KEY_RING_BY_KEY_ID = 204; + private static final int SECRET_KEY_RING_BY_EMAILS = 205; + + private static final int SECRET_KEY_RING_KEY = 211; + private static final int SECRET_KEY_RING_KEY_BY_ROW_ID = 212; + + private static final int SECRET_KEY_RING_USER_ID = 221; + private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; + + private static final int DATA_STREAM = 301; + + protected static boolean sInternalProvider; + protected static UriMatcher sUriMatcher; + + /** + * Build and return a {@link UriMatcher} that catches all {@link Uri} variations supported by + * this {@link ContentProvider}. + */ + protected static UriMatcher buildUriMatcher(boolean internalProvider) { + final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); + + String authority; + if (internalProvider) { + authority = ApgContract.CONTENT_AUTHORITY_INTERNAL; + } else { + authority = ApgContract.CONTENT_AUTHORITY_EXTERNAL; + } + + /** + * public key rings + * + *
+         * key_rings/public
+         * key_rings/public/#
+         * key_rings/public/master_key_id/_
+         * key_rings/public/key_id/_
+         * key_rings/public/emails/_
+         * 
+ */ + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC, + PUBLIC_KEY_RING); + matcher.addURI(authority, + ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/#", + PUBLIC_KEY_RING_BY_ROW_ID); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" + + ApgContract.PATH_BY_MASTER_KEY_ID + "/*", PUBLIC_KEY_RING_BY_MASTER_KEY_ID); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" + + ApgContract.PATH_BY_KEY_ID + "/*", PUBLIC_KEY_RING_BY_KEY_ID); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" + + ApgContract.PATH_BY_EMAILS + "/*", PUBLIC_KEY_RING_BY_EMAILS); + + /** + * public keys + * + *
+         * key_rings/public/#/keys
+         * key_rings/public/#/keys/#
+         * 
+ */ + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + + "/#/" + ApgContract.PATH_KEYS, PUBLIC_KEY_RING_KEY); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + + "/#/" + ApgContract.PATH_KEYS + "/#", PUBLIC_KEY_RING_KEY_BY_ROW_ID); + + /** + * public user ids + * + *
+         * key_rings/public/#/user_ids
+         * key_rings/public/#/user_ids/#
+         * 
+ */ + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + + "/#/" + ApgContract.PATH_USER_IDS, PUBLIC_KEY_RING_USER_ID); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + + "/#/" + ApgContract.PATH_USER_IDS + "/#", PUBLIC_KEY_RING_USER_ID_BY_ROW_ID); + + /** + * secret key rings + * + *
+         * key_rings/secret
+         * key_rings/secret/#
+         * key_rings/secret/master_key_id/_
+         * key_rings/secret/key_id/_
+         * key_rings/secret/emails/_
+         * 
+ */ + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET, + SECRET_KEY_RING); + matcher.addURI(authority, + ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/#", + SECRET_KEY_RING_BY_ROW_ID); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" + + ApgContract.PATH_BY_MASTER_KEY_ID + "/*", SECRET_KEY_RING_BY_MASTER_KEY_ID); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" + + ApgContract.PATH_BY_KEY_ID + "/*", SECRET_KEY_RING_BY_KEY_ID); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" + + ApgContract.PATH_BY_EMAILS + "/*", SECRET_KEY_RING_BY_EMAILS); + + /** + * secret keys + * + *
+         * key_rings/secret/#/keys
+         * key_rings/secret/#/keys/#
+         * 
+ */ + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + + "/#/" + ApgContract.PATH_KEYS, SECRET_KEY_RING_KEY); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + + "/#/" + ApgContract.PATH_KEYS + "/#", SECRET_KEY_RING_KEY_BY_ROW_ID); + + /** + * secret user ids + * + *
+         * key_rings/secret/#/user_ids
+         * key_rings/secret/#/user_ids/#
+         * 
+ */ + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + + "/#/" + ApgContract.PATH_USER_IDS, SECRET_KEY_RING_USER_ID); + matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + + "/#/" + ApgContract.PATH_USER_IDS + "/#", SECRET_KEY_RING_USER_ID_BY_ROW_ID); + + /** + * data stream + * + *
+         * data / _
+         * 
+ */ + matcher.addURI(authority, ApgContract.BASE_DATA + "/*", DATA_STREAM); + + return matcher; + } + + private ApgDatabase mApgDatabase; + + /** {@inheritDoc} */ + @Override + public boolean onCreate() { + mApgDatabase = new ApgDatabase(getContext()); + return true; + } + + /** {@inheritDoc} */ + @Override + public String getType(Uri uri) { + final int match = sUriMatcher.match(uri); + switch (match) { + case PUBLIC_KEY_RING: + case PUBLIC_KEY_RING_BY_EMAILS: + case SECRET_KEY_RING: + case SECRET_KEY_RING_BY_EMAILS: + return KeyRings.CONTENT_TYPE; + + case PUBLIC_KEY_RING_BY_ROW_ID: + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case PUBLIC_KEY_RING_BY_KEY_ID: + case SECRET_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_KEY_ID: + return KeyRings.CONTENT_ITEM_TYPE; + + case PUBLIC_KEY_RING_KEY: + case SECRET_KEY_RING_KEY: + return Keys.CONTENT_TYPE; + + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + return Keys.CONTENT_ITEM_TYPE; + + case PUBLIC_KEY_RING_USER_ID: + case SECRET_KEY_RING_USER_ID: + return UserIds.CONTENT_TYPE; + + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + return UserIds.CONTENT_ITEM_TYPE; + + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + } + + /** + * Returns type of the query (secret/public) + * + * @param uri + * @return + */ + private int getKeyType(int match) { + int type; + switch (match) { + case PUBLIC_KEY_RING: + case PUBLIC_KEY_RING_BY_ROW_ID: + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case PUBLIC_KEY_RING_BY_KEY_ID: + case PUBLIC_KEY_RING_BY_EMAILS: + case PUBLIC_KEY_RING_KEY: + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case PUBLIC_KEY_RING_USER_ID: + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + type = KeyTypes.PUBLIC; + break; + + case SECRET_KEY_RING: + case SECRET_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_KEY_ID: + case SECRET_KEY_RING_BY_EMAILS: + case SECRET_KEY_RING_KEY: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + type = KeyTypes.SECRET; + break; + + default: + throw new IllegalArgumentException("Unknown match " + match); + + } + + return type; + } + + /** + * Set result of query to specific columns, don't show blob column for external content provider + * + * @return + */ + private HashMap getProjectionMapForKeyRings() { + HashMap projectionMap = new HashMap(); + + projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID); + projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + + KeyRingsColumns.MASTER_KEY_ID); + // only give out keyRing blob when we are using the internal content provider + if (sInternalProvider) { + projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "." + + KeyRingsColumns.KEY_RING_DATA); + } + projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID); + + return projectionMap; + } + + /** + * Set result of query to specific columns, don't show blob column for external content provider + * + * @return + */ + private HashMap getProjectionMapForKeys() { + HashMap projectionMap = new HashMap(); + + projectionMap.put(BaseColumns._ID, BaseColumns._ID); + projectionMap.put(KeysColumns.KEY_ID, KeysColumns.KEY_ID); + projectionMap.put(KeysColumns.IS_MASTER_KEY, KeysColumns.IS_MASTER_KEY); + projectionMap.put(KeysColumns.ALGORITHM, KeysColumns.ALGORITHM); + projectionMap.put(KeysColumns.KEY_SIZE, KeysColumns.KEY_SIZE); + projectionMap.put(KeysColumns.CAN_SIGN, KeysColumns.CAN_SIGN); + projectionMap.put(KeysColumns.CAN_ENCRYPT, KeysColumns.CAN_ENCRYPT); + projectionMap.put(KeysColumns.IS_REVOKED, KeysColumns.IS_REVOKED); + projectionMap.put(KeysColumns.CREATION, KeysColumns.CREATION); + projectionMap.put(KeysColumns.EXPIRY, KeysColumns.EXPIRY); + projectionMap.put(KeysColumns.KEY_RING_ROW_ID, KeysColumns.KEY_RING_ROW_ID); + // only give out keyRing blob when we are using the internal content provider + if (sInternalProvider) { + projectionMap.put(KeysColumns.KEY_DATA, KeysColumns.KEY_DATA); + } + projectionMap.put(KeysColumns.RANK, KeysColumns.RANK); + + return projectionMap; + } + + /** + * Builds default query for keyRings: KeyRings table is joined with Keys and UserIds + * + * @param qb + * @param match + * @param isMasterKey + * @param sortOrder + * @return + */ + private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match, String sortOrder) { + qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + + qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.USER_IDS + " ON " + "(" + + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." + + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." + + UserIdsColumns.RANK + " = '0')"); + + qb.setProjectionMap(getProjectionMapForKeyRings()); + + return qb; + } + + /** + * Builds default query for keyRings: KeyRings table is joined with Keys and UserIds + * + * @param qb + * @param match + * @param isMasterKey + * @param sortOrder + * @return + */ + private SQLiteQueryBuilder buildKeyRingQueryWithKeys(SQLiteQueryBuilder qb, int match, + String sortOrder) { + qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + + qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "(" + + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "." + + KeysColumns.KEY_RING_ROW_ID + " AND " + Tables.KEYS + "." + + KeysColumns.IS_MASTER_KEY + " = '1') " + " INNER JOIN " + Tables.USER_IDS + + " ON " + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + + "." + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." + + UserIdsColumns.RANK + " = '0')"); + + qb.setProjectionMap(getProjectionMapForKeyRings()); + + return qb; + } + + /** {@inheritDoc} */ + @SuppressWarnings("deprecation") + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")"); + + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + SQLiteDatabase db = mApgDatabase.getReadableDatabase(); + + int match = sUriMatcher.match(uri); + + switch (match) { + case PUBLIC_KEY_RING: + case SECRET_KEY_RING: + qb = buildKeyRingQuery(qb, match, sortOrder); + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + } + + break; + + case PUBLIC_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_ROW_ID: + qb = buildKeyRingQuery(qb, match, sortOrder); + + qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + } + + break; + + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + qb = buildKeyRingQuery(qb, match, sortOrder); + + qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + } + + break; + + case SECRET_KEY_RING_BY_KEY_ID: + case PUBLIC_KEY_RING_BY_KEY_ID: + qb = buildKeyRingQueryWithKeys(qb, match, sortOrder); + + qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + } + + break; + + case SECRET_KEY_RING_BY_EMAILS: + case PUBLIC_KEY_RING_BY_EMAILS: + qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + + qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "(" + + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "." + + KeysColumns.KEY_RING_ROW_ID + " AND " + Tables.KEYS + "." + + KeysColumns.IS_MASTER_KEY + " = '1'" + ") " + " INNER JOIN " + + Tables.USER_IDS + " ON " + "(" + Tables.KEYS + "." + BaseColumns._ID + " = " + + Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " AND " + + Tables.USER_IDS + "." + UserIdsColumns.RANK + " = '0')"); + + qb.setProjectionMap(getProjectionMapForKeyRings()); + + String emails = uri.getLastPathSegment(); + String chunks[] = emails.split(" *, *"); + boolean gotCondition = false; + String emailWhere = ""; + for (int i = 0; i < chunks.length; ++i) { + if (chunks[i].length() == 0) { + continue; + } + if (i != 0) { + emailWhere += " OR "; + } + emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE "; + // match '*', so it has to be at the *end* of the user id + emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">"); + gotCondition = true; + } + + if (gotCondition) { + qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " + + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID + + " = " + Tables.KEYS + "." + BaseColumns._ID + " AND (" + emailWhere + + "))"); + } + + break; + + case PUBLIC_KEY_RING_KEY: + case SECRET_KEY_RING_KEY: + qb.setTables(Tables.KEYS); + qb.appendWhere(KeysColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + + qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + qb.setProjectionMap(getProjectionMapForKeys()); + + break; + + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + qb.setTables(Tables.KEYS); + qb.appendWhere(KeysColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + + qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + qb.appendWhere(" AND " + BaseColumns._ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + qb.setProjectionMap(getProjectionMapForKeys()); + + break; + + case PUBLIC_KEY_RING_USER_ID: + case SECRET_KEY_RING_USER_ID: + qb.setTables(Tables.USER_IDS); + qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + break; + + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + qb.setTables(Tables.USER_IDS); + qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + qb.appendWhere(" AND " + BaseColumns._ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + + } + + // If no sort order is specified use the default + String orderBy; + if (TextUtils.isEmpty(sortOrder)) { + orderBy = null; + } else { + orderBy = sortOrder; + } + + 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 changes + c.setNotificationUri(getContext().getContentResolver(), uri); + + if (Constants.DEBUG) { + Log.d(Constants.TAG, + "Query: " + + qb.buildQuery(projection, selection, selectionArgs, null, null, + orderBy, null)); + Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(c)); + } + + return c; + } + + /** {@inheritDoc} */ + @Override + public Uri insert(Uri uri, ContentValues values) { + Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")"); + + final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + + Uri rowUri = null; + long rowId = -1; + try { + final int match = sUriMatcher.match(uri); + + switch (match) { + case PUBLIC_KEY_RING: + values.put(KeyRings.TYPE, KeyTypes.PUBLIC); + + rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); + rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); + + break; + case PUBLIC_KEY_RING_KEY: + values.put(Keys.TYPE, KeyTypes.PUBLIC); + + rowId = db.insertOrThrow(Tables.KEYS, null, values); + rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); + + break; + case PUBLIC_KEY_RING_USER_ID: + rowId = db.insertOrThrow(Tables.USER_IDS, null, values); + rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); + + break; + case SECRET_KEY_RING: + values.put(KeyRings.TYPE, KeyTypes.SECRET); + + rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); + rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); + + break; + case SECRET_KEY_RING_KEY: + values.put(Keys.TYPE, KeyTypes.SECRET); + + rowId = db.insertOrThrow(Tables.KEYS, null, values); + rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); + + break; + case SECRET_KEY_RING_USER_ID: + rowId = db.insertOrThrow(Tables.USER_IDS, null, values); + rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); + + break; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + } catch (SQLiteConstraintException e) { + Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?"); + } + + // notify of changes in db + getContext().getContentResolver().notifyChange(uri, null); + + return rowUri; + } + + /** {@inheritDoc} */ + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + Log.v(Constants.TAG, "delete(uri=" + uri + ")"); + + final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + + int count; + final int match = sUriMatcher.match(uri); + + String defaultSelection = null; + switch (match) { + case PUBLIC_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_ROW_ID: + defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); + // corresponding keys and userIds are deleted by ON DELETE CASCADE + count = db.delete(Tables.KEY_RINGS, + buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), + selectionArgs); + break; + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); + // corresponding keys and userIds are deleted by ON DELETE CASCADE + count = db.delete(Tables.KEY_RINGS, + buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), + selectionArgs); + break; + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + count = db.delete(Tables.KEYS, + buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs); + break; + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection), + selectionArgs); + break; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + + // notify of changes in db + getContext().getContentResolver().notifyChange(uri, null); + + return count; + } + + /** {@inheritDoc} */ + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")"); + + final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + + String defaultSelection = null; + int count = 0; + try { + final int match = sUriMatcher.match(uri); + switch (match) { + case PUBLIC_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_ROW_ID: + defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); + + count = db.update( + Tables.KEY_RINGS, + values, + buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), + selection), selectionArgs); + break; + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); + + count = db.update( + Tables.KEY_RINGS, + values, + buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), + selection), selectionArgs); + break; + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + count = db + .update(Tables.KEYS, values, + buildDefaultKeysSelection(uri, getKeyType(match), selection), + selectionArgs); + break; + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + count = db.update(Tables.USER_IDS, values, + buildDefaultUserIdsSelection(uri, selection), selectionArgs); + break; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + } catch (SQLiteConstraintException e) { + Log.e(Constants.TAG, "Constraint exception on update! Entry already existing?"); + } + + // notify of changes in db + getContext().getContentResolver().notifyChange(uri, null); + + return count; + } + + /** + * Build default selection statement for KeyRings. If no extra selection is specified only build + * where clause with rowId + * + * @param uri + * @param selection + * @return + */ + private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType, + String selection) { + String andType = ""; + if (keyType != null) { + andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType; + } + + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; + } + + return defaultSelection + andType + andSelection; + } + + /** + * Build default selection statement for Keys. If no extra selection is specified only build + * where clause with rowId + * + * @param uri + * @param selection + * @return + */ + private String buildDefaultKeysSelection(Uri uri, Integer keyType, String selection) { + String rowId = uri.getLastPathSegment(); + + String foreignKeyRingRowId = uri.getPathSegments().get(2); + String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " + + foreignKeyRingRowId; + + String andType = ""; + if (keyType != null) { + andType = " AND " + KeysColumns.TYPE + "=" + keyType; + } + + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; + } + + return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andType + andSelection; + } + + /** + * Build default selection statement for UserIds. If no extra selection is specified only build + * where clause with rowId + * + * @param uri + * @param selection + * @return + */ + private String buildDefaultUserIdsSelection(Uri uri, String selection) { + String rowId = uri.getLastPathSegment(); + + String foreignKeyRingRowId = uri.getPathSegments().get(2); + String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " + + foreignKeyRingRowId; + + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; + } + + return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andSelection; + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + int match = sUriMatcher.match(uri); + if (match != DATA_STREAM) { + throw new FileNotFoundException(); + } + String fileName = uri.getLastPathSegment(); + File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName); + return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); + } +} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java b/APG/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java new file mode 100644 index 000000000..61cca8bed --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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; + +/** + * The same content provider as ApgProviderInternal except that it does not give out keyRing and key + * blob data when querying. + * + * This provider is exported with a readPermission in AndroidManifest.xml + */ +public class ApgProviderExternal extends ApgProvider { + + static { + sInternalProvider = false; + sUriMatcher = buildUriMatcher(sInternalProvider); + } + +} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java b/APG/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java new file mode 100644 index 000000000..e2226a0fa --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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; + +/** + * This provider is NOT exported in AndroidManifest.xml as it also return the actual secret keys + * from the database + */ +public class ApgProviderInternal extends ApgProvider { + + static { + sInternalProvider = true; + sUriMatcher = buildUriMatcher(sInternalProvider); + } + +} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java b/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java new file mode 100644 index 000000000..5bf1dd329 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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 org.thialfihar.android.apg.Constants; + +import android.net.Uri; +import android.provider.BaseColumns; + +public class ApgServiceBlobContract { + + interface BlobsColumns { + String KEY = "key"; + } + + public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME + ".blobs"; + + private static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); + + public static class Blobs implements BlobsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI; + } + + private ApgServiceBlobContract() { + } +} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java b/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java new file mode 100644 index 000000000..417486e33 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2011 Markus Doits + * + * 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.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.provider.BaseColumns; + +import org.thialfihar.android.apg.provider.ApgServiceBlobContract.BlobsColumns; + +public class ApgServiceBlobDatabase extends SQLiteOpenHelper { + private static final String DATABASE_NAME = "apg_blob.db"; + private static final int DATABASE_VERSION = 2; + + public static final String TABLE = "data"; + + public ApgServiceBlobDatabase(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + TABLE + " ( " + BaseColumns._ID + + " INTEGER PRIMARY KEY AUTOINCREMENT, " + BlobsColumns.KEY + " TEXT NOT NULL)"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // no upgrade necessary yet + } +} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java b/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java new file mode 100644 index 000000000..8053aecbf --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2011 Markus Doits + * + * 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 org.thialfihar.android.apg.Constants; + +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.provider.BaseColumns; + +import org.thialfihar.android.apg.provider.ApgServiceBlobContract.Blobs; +import org.thialfihar.android.apg.provider.ApgServiceBlobContract.BlobsColumns; +import org.thialfihar.android.apg.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +public class ApgServiceBlobProvider extends ContentProvider { + private static final String STORE_PATH = Constants.path.APP_DIR + "/ApgBlobs"; + + private ApgServiceBlobDatabase mBlobDatabase = null; + + public ApgServiceBlobProvider() { + File dir = new File(STORE_PATH); + dir.mkdirs(); + } + + @Override + public boolean onCreate() { + mBlobDatabase = new ApgServiceBlobDatabase(getContext()); + return true; + } + + /** {@inheritDoc} */ + @Override + public Uri insert(Uri uri, ContentValues ignored) { + // ContentValues are actually ignored, because we want to store a blob with no more + // information but have to create an record with the password generated here first + ContentValues vals = new ContentValues(); + + // Insert a random key in the database. This has to provided by the caller when updating or + // getting the blob + String password = UUID.randomUUID().toString(); + vals.put(BlobsColumns.KEY, password); + + SQLiteDatabase db = mBlobDatabase.getWritableDatabase(); + long newRowId = db.insert(ApgServiceBlobDatabase.TABLE, null, vals); + Uri insertedUri = ContentUris.withAppendedId(Blobs.CONTENT_URI, newRowId); + + return Uri.withAppendedPath(insertedUri, password); + } + + /** {@inheritDoc} */ + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException, + FileNotFoundException { + Log.d(Constants.TAG, "openFile() called with uri: " + uri.toString() + " and mode: " + mode); + + List segments = uri.getPathSegments(); + if (segments.size() < 2) { + throw new SecurityException("Password not found in URI"); + } + String id = segments.get(0); + String key = segments.get(1); + + Log.d(Constants.TAG, "Got id: " + id + " and key: " + key); + + // get the data + SQLiteDatabase db = mBlobDatabase.getReadableDatabase(); + Cursor result = db.query(ApgServiceBlobDatabase.TABLE, new String[] { BaseColumns._ID }, + BaseColumns._ID + " = ? and " + BlobsColumns.KEY + " = ?", + new String[] { id, key }, null, null, null); + + if (result.getCount() == 0) { + // either the key is wrong or no id exists + throw new FileNotFoundException("No file found with that ID and/or password"); + } + + File targetFile = new File(STORE_PATH, id); + if (mode.equals("w")) { + Log.d(Constants.TAG, "Try to open file w"); + if (!targetFile.exists()) { + try { + targetFile.createNewFile(); + } catch (IOException e) { + Log.e(Constants.TAG, "Got IEOException on creating new file", e); + throw new FileNotFoundException("Could not create file to write to"); + } + } + return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_WRITE_ONLY + | ParcelFileDescriptor.MODE_TRUNCATE); + } else if (mode.equals("r")) { + Log.d(Constants.TAG, "Try to open file r"); + if (!targetFile.exists()) { + throw new FileNotFoundException("Error: Could not find the file requested"); + } + return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY); + } + + return null; + } + + /** {@inheritDoc} */ + @Override + public String getType(Uri uri) { + return null; + } + + /** {@inheritDoc} */ + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + /** {@inheritDoc} */ + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + /** {@inheritDoc} */ + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + +} diff --git a/APG/src/org/thialfihar/android/apg/provider/ProviderHelper.java b/APG/src/org/thialfihar/android/apg/provider/ProviderHelper.java new file mode 100644 index 000000000..915c0aea5 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/provider/ProviderHelper.java @@ -0,0 +1,501 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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 java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Vector; + +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.helper.PGPConversionHelper; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.provider.ApgContract.KeyRings; +import org.thialfihar.android.apg.provider.ApgContract.UserIds; +import org.thialfihar.android.apg.provider.ApgContract.Keys; +import org.thialfihar.android.apg.util.IterableIterator; +import org.thialfihar.android.apg.util.Log; + +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.net.Uri; +import android.os.RemoteException; + +public class ProviderHelper { + + /** + * Private helper method to get PGPKeyRing from database + * + * @param context + * @param queryUri + * @return + */ + private static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) { + Cursor cursor = context.getContentResolver().query(queryUri, + new String[] { KeyRings._ID, KeyRings.KEY_RING_DATA }, null, null, null); + + PGPKeyRing keyRing = null; + if (cursor != null && cursor.moveToFirst()) { + int keyRingDataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); + + byte[] data = cursor.getBlob(keyRingDataCol); + if (data != null) { + keyRing = PGPConversionHelper.BytesToPGPKeyRing(data); + } + } + + if (cursor != null) { + cursor.close(); + } + + return keyRing; + } + + /** + * Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId + * + * @param context + * @param rowId + * @return + */ + public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) { + Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); + return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId + * + * @param context + * @param masterKeyId + * @return + */ + public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context, + long masterKeyId) { + Uri queryUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key + * with this keyId + * + * @param context + * @param keyId + * @return + */ + public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) { + Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId)); + return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPPublicKey object from the database blob associated with a key with + * this keyId + * + * @param context + * @param keyId + * @return + */ + public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) { + PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId); + if (keyRing == null) { + return null; + } + + return keyRing.getPublicKey(keyId); + } + + /** + * Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId + * + * @param context + * @param rowId + * @return + */ + public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) { + Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); + return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId + * + * @param context + * @param masterKeyId + * @return + */ + public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context, + long masterKeyId) { + Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key + * with this keyId + * + * @param context + * @param keyId + * @return + */ + public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) { + Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId)); + return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPSecretKey object from the database blob associated with a key with + * this keyId + * + * @param context + * @param keyId + * @return + */ + public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) { + PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId); + if (keyRing == null) { + return null; + } + + return keyRing.getSecretKey(keyId); + } + + /** + * Saves PGPPublicKeyRing with its keys and userIds in DB + * + * @param context + * @param keyRing + * @return + * @throws IOException + * @throws GeneralException + */ + @SuppressWarnings("unchecked") + public static void saveKeyRing(Context context, PGPPublicKeyRing keyRing) throws IOException { + PGPPublicKey masterKey = keyRing.getPublicKey(); + long masterKeyId = masterKey.getKeyID(); + + // delete old version of this keyRing, which also deletes all keys and userIds on cascade + Uri deleteUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + + try { + context.getContentResolver().delete(deleteUri, null, null); + } catch (UnsupportedOperationException e) { + Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); + } + + ContentValues values = new ContentValues(); + values.put(KeyRings.MASTER_KEY_ID, masterKeyId); + values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); + + // insert new version of this keyRing + Uri uri = KeyRings.buildPublicKeyRingsUri(); + Uri insertedUri = context.getContentResolver().insert(uri, values); + long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); + + // save all keys and userIds included in keyRing object in database + ArrayList operations = new ArrayList(); + + int rank = 0; + for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank)); + ++rank; + } + + int userIdRank = 0; + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + operations.add(buildPublicUserIdOperations(context, keyRingRowId, userId, userIdRank)); + ++userIdRank; + } + + try { + context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY_INTERNAL, operations); + } catch (RemoteException e) { + Log.e(Constants.TAG, "applyBatch failed!", e); + } catch (OperationApplicationException e) { + Log.e(Constants.TAG, "applyBatch failed!", e); + } + } + + /** + * Saves PGPSecretKeyRing with its keys and userIds in DB + * + * @param context + * @param keyRing + * @return + * @throws IOException + * @throws GeneralException + */ + @SuppressWarnings("unchecked") + public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException { + PGPSecretKey masterKey = keyRing.getSecretKey(); + long masterKeyId = masterKey.getKeyID(); + + // delete old version of this keyRing, which also deletes all keys and userIds on cascade + Uri deleteUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + + try { + context.getContentResolver().delete(deleteUri, null, null); + } catch (UnsupportedOperationException e) { + Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); + } + + ContentValues values = new ContentValues(); + values.put(KeyRings.MASTER_KEY_ID, masterKeyId); + values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); + + // insert new version of this keyRing + Uri uri = KeyRings.buildSecretKeyRingsUri(); + Uri insertedUri = context.getContentResolver().insert(uri, values); + long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); + + // save all keys and userIds included in keyRing object in database + ArrayList operations = new ArrayList(); + + int rank = 0; + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + operations.add(buildSecretKeyOperations(context, keyRingRowId, key, rank)); + ++rank; + } + + int userIdRank = 0; + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + operations.add(buildSecretUserIdOperations(context, keyRingRowId, userId, userIdRank)); + ++userIdRank; + } + + try { + context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY_INTERNAL, operations); + } catch (RemoteException e) { + Log.e(Constants.TAG, "applyBatch failed!", e); + } catch (OperationApplicationException e) { + Log.e(Constants.TAG, "applyBatch failed!", e); + } + } + + /** + * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing + * + * @param context + * @param keyRingRowId + * @param key + * @param rank + * @return + * @throws IOException + */ + private static ContentProviderOperation buildPublicKeyOperations(Context context, + long keyRingRowId, PGPPublicKey key, int rank) throws IOException { + ContentValues values = new ContentValues(); + values.put(Keys.KEY_ID, key.getKeyID()); + 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, PGPHelper.isSigningKey(key)); + values.put(Keys.CAN_ENCRYPT, PGPHelper.isEncryptionKey(key)); + values.put(Keys.IS_REVOKED, key.isRevoked()); + values.put(Keys.CREATION, PGPHelper.getCreationDate(key).getTime() / 1000); + Date expiryDate = PGPHelper.getExpiryDate(key); + if (expiryDate != null) { + values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); + } + values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); + values.put(Keys.KEY_DATA, key.getEncoded()); + values.put(Keys.RANK, rank); + + Uri uri = Keys.buildPublicKeysUri(Long.toString(keyRingRowId)); + + return ContentProviderOperation.newInsert(uri).withValues(values).build(); + } + + /** + * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing + * + * @param context + * @param keyRingRowId + * @param key + * @param rank + * @return + * @throws IOException + */ + private static ContentProviderOperation buildPublicUserIdOperations(Context context, + long keyRingRowId, String userId, int rank) { + ContentValues values = new ContentValues(); + values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); + values.put(UserIds.USER_ID, userId); + values.put(UserIds.RANK, rank); + + Uri uri = UserIds.buildPublicUserIdsUri(Long.toString(keyRingRowId)); + + return ContentProviderOperation.newInsert(uri).withValues(values).build(); + } + + /** + * Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing + * + * @param context + * @param keyRingRowId + * @param key + * @param rank + * @return + * @throws IOException + */ + private static ContentProviderOperation buildSecretKeyOperations(Context context, + long keyRingRowId, PGPSecretKey key, int rank) throws IOException { + ContentValues values = new ContentValues(); + values.put(Keys.KEY_ID, key.getKeyID()); + 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, PGPHelper.isSigningKey(key)); + values.put(Keys.CAN_ENCRYPT, PGPHelper.isEncryptionKey(key)); + values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked()); + values.put(Keys.CREATION, PGPHelper.getCreationDate(key).getTime() / 1000); + Date expiryDate = PGPHelper.getExpiryDate(key); + if (expiryDate != null) { + values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); + } + values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); + values.put(Keys.KEY_DATA, key.getEncoded()); + values.put(Keys.RANK, rank); + + Uri uri = Keys.buildSecretKeysUri(Long.toString(keyRingRowId)); + + return ContentProviderOperation.newInsert(uri).withValues(values).build(); + } + + /** + * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing + * + * @param context + * @param keyRingRowId + * @param key + * @param rank + * @return + * @throws IOException + */ + private static ContentProviderOperation buildSecretUserIdOperations(Context context, + long keyRingRowId, String userId, int rank) { + ContentValues values = new ContentValues(); + values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); + values.put(UserIds.USER_ID, userId); + values.put(UserIds.RANK, rank); + + Uri uri = UserIds.buildSecretUserIdsUri(Long.toString(keyRingRowId)); + + return ContentProviderOperation.newInsert(uri).withValues(values).build(); + } + + /** + * Private helper method + * + * @param context + * @param queryUri + * @return + */ + private static Vector getKeyRingsRowIds(Context context, Uri queryUri) { + Cursor cursor = context.getContentResolver().query(queryUri, new String[] { KeyRings._ID }, + null, null, null); + + Vector keyIds = new Vector(); + if (cursor != null) { + int idCol = cursor.getColumnIndex(KeyRings._ID); + if (cursor.moveToFirst()) { + do { + keyIds.add(cursor.getInt(idCol)); + } while (cursor.moveToNext()); + } + } + + if (cursor != null) { + cursor.close(); + } + + return keyIds; + } + + /** + * Retrieves ids of all SecretKeyRings + * + * @param context + * @return + */ + public static Vector getSecretKeyRingsRowIds(Context context) { + Uri queryUri = KeyRings.buildSecretKeyRingsUri(); + return getKeyRingsRowIds(context, queryUri); + } + + /** + * Retrieves ids of all PublicKeyRings + * + * @param context + * @return + */ + public static Vector getPublicKeyRingsRowIds(Context context) { + Uri queryUri = KeyRings.buildPublicKeyRingsUri(); + return getKeyRingsRowIds(context, queryUri); + } + + public static void deletePublicKeyRing(Context context, long rowId) { + ContentResolver cr = context.getContentResolver(); + cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null); + } + + public static void deleteSecretKeyRing(Context context, long rowId) { + ContentResolver cr = context.getContentResolver(); + cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null); + } + + public static long getPublicMasterKeyId(Context context, long keyRingRowId) { + Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId)); + return getMasterKeyId(context, queryUri, keyRingRowId); + } + + public static long getSecretMasterKeyId(Context context, long keyRingRowId) { + Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); + return getMasterKeyId(context, queryUri, keyRingRowId); + } + + private static long getMasterKeyId(Context context, Uri queryUri, long keyRingRowId) { + String[] projection = new String[] { KeyRings.MASTER_KEY_ID }; + + ContentResolver cr = context.getContentResolver(); + Cursor cursor = cr.query(queryUri, projection, null, null, null); + + long masterKeyId = -1; + if (cursor != null && cursor.moveToFirst()) { + int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); + + masterKeyId = cursor.getLong(masterKeyIdCol); + } + + if (cursor != null) { + cursor.close(); + } + + return masterKeyId; + } + +} diff --git a/APG/src/org/thialfihar/android/apg/service/ApgIntentService.java b/APG/src/org/thialfihar/android/apg/service/ApgIntentService.java new file mode 100644 index 000000000..8a6ca6b4c --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/service/ApgIntentService.java @@ -0,0 +1,881 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.service; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Vector; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.FileHelper; +import org.thialfihar.android.apg.helper.OtherHelper; +import org.thialfihar.android.apg.helper.PGPMain; +import org.thialfihar.android.apg.helper.Preferences; +import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; +import org.thialfihar.android.apg.helper.PGPConversionHelper; +import org.thialfihar.android.apg.provider.ApgContract.DataStream; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.util.HkpKeyServer; +import org.thialfihar.android.apg.util.InputData; +import org.thialfihar.android.apg.util.KeyServer.KeyInfo; +import org.thialfihar.android.apg.util.ProgressDialogUpdater; + +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +import org.thialfihar.android.apg.util.Log; + +/** + * This Service contains all important long lasting operations for APG. It receives Intents with + * data from the activities or other apps, queues these intents, executes them, and stops itself + * after doing them. + */ +public class ApgIntentService extends IntentService implements ProgressDialogUpdater { + + /* extras that can be given by intent */ + public static final String EXTRA_MESSENGER = "messenger"; + public static final String EXTRA_ACTION = "action"; + public static final String EXTRA_DATA = "data"; + + /* possible EXTRA_ACTIONs */ + public static final int ACTION_ENCRYPT_SIGN = 10; + + public static final int ACTION_DECRYPT_VERIFY = 20; + + public static final int ACTION_SAVE_KEYRING = 30; + public static final int ACTION_GENERATE_KEY = 31; + public static final int ACTION_GENERATE_DEFAULT_RSA_KEYS = 32; + + public static final int ACTION_DELETE_FILE_SECURELY = 40; + + public static final int ACTION_IMPORT_KEY = 50; + public static final int ACTION_EXPORT_KEY = 51; + + public static final int ACTION_UPLOAD_KEY = 60; + public static final int ACTION_QUERY_KEY = 61; + + public static final int ACTION_SIGN_KEY = 70; + + /* keys for data bundle */ + + // encrypt, decrypt, import export + public static final String TARGET = "target"; + // possible targets: + public static final int TARGET_BYTES = 1; + public static final int TARGET_FILE = 2; + public static final int TARGET_STREAM = 3; + + // encrypt + public static final String SECRET_KEY_ID = "secretKeyId"; + public static final String USE_ASCII_AMOR = "useAsciiAmor"; + public static final String ENCRYPTION_KEYS_IDS = "encryptionKeysIds"; + public static final String COMPRESSION_ID = "compressionId"; + public static final String GENERATE_SIGNATURE = "generateSignature"; + public static final String SIGN_ONLY = "signOnly"; + public static final String MESSAGE_BYTES = "messageBytes"; + public static final String INPUT_FILE = "inputFile"; + public static final String OUTPUT_FILE = "outputFile"; + public static final String PROVIDER_URI = "providerUri"; + + // decrypt/verify + public static final String SIGNED_ONLY = "signedOnly"; + public static final String RETURN_BYTES = "returnBinary"; + public static final String CIPHERTEXT_BYTES = "ciphertextBytes"; + public static final String ASSUME_SYMMETRIC = "assumeSymmetric"; + public static final String LOOKUP_UNKNOWN_KEY = "lookupUnknownKey"; + + // edit keys + public static final String NEW_PASSPHRASE = "newPassphrase"; + public static final String CURRENT_PASSPHRASE = "currentPassphrase"; + public static final String USER_IDS = "userIds"; + public static final String KEYS = "keys"; + public static final String KEYS_USAGES = "keysUsages"; + public static final String MASTER_KEY_ID = "masterKeyId"; + + // generate key + public static final String ALGORITHM = "algorithm"; + public static final String KEY_SIZE = "keySize"; + public static final String SYMMETRIC_PASSPHRASE = "passphrase"; + public static final String MASTER_KEY = "masterKey"; + + // delete file securely + public static final String DELETE_FILE = "deleteFile"; + + // import key + public static final String IMPORT_INPUT_STREAM = "importInputStream"; + public static final String IMPORT_FILENAME = "importFilename"; + public static final String IMPORT_BYTES = "importBytes"; + public static final String IMPORT_KEY_TYPE = "importKeyType"; + + // export key + public static final String EXPORT_OUTPUT_STREAM = "exportOutputStream"; + public static final String EXPORT_FILENAME = "exportFilename"; + public static final String EXPORT_KEY_TYPE = "exportKeyType"; + public static final String EXPORT_ALL = "exportAll"; + public static final String EXPORT_KEY_RING_ID = "exportKeyRingId"; + + // upload key + public static final String UPLOAD_KEY_SERVER = "uploadKeyServer"; + public static final String UPLOAD_KEY_KEYRING_ROW_ID = "uploadKeyRingId"; + + // query key + public static final String QUERY_KEY_SERVER = "queryKeyServer"; + public static final String QUERY_KEY_TYPE = "queryKeyType"; + public static final String QUERY_KEY_STRING = "queryKeyString"; + public static final String QUERY_KEY_ID = "queryKeyId"; + + // sign key + public static final String SIGN_KEY_MASTER_KEY_ID = "signKeyMasterKeyId"; + public static final String SIGN_KEY_PUB_KEY_ID = "signKeyPubKeyId"; + + /* + * possible data keys as result send over messenger + */ + // keys + public static final String RESULT_NEW_KEY = "newKey"; + public static final String RESULT_NEW_KEY2 = "newKey2"; + + // encrypt + public static final String RESULT_SIGNATURE_BYTES = "signatureData"; + public static final String RESULT_SIGNATURE_STRING = "signatureText"; + public static final String RESULT_ENCRYPTED_STRING = "encryptedMessage"; + public static final String RESULT_ENCRYPTED_BYTES = "encryptedData"; + public static final String RESULT_URI = "resultUri"; + + // decrypt/verify + public static final String RESULT_DECRYPTED_STRING = "decryptedMessage"; + public static final String RESULT_DECRYPTED_BYTES = "decryptedData"; + public static final String RESULT_SIGNATURE = "signature"; + public static final String RESULT_SIGNATURE_KEY_ID = "signatureKeyId"; + public static final String RESULT_SIGNATURE_USER_ID = "signatureUserId"; + + public static final String RESULT_SIGNATURE_SUCCESS = "signatureSuccess"; + public static final String RESULT_SIGNATURE_UNKNOWN = "signatureUnknown"; + public static final String RESULT_SIGNATURE_LOOKUP_KEY = "lookupKey"; + + // import + public static final String RESULT_IMPORT_ADDED = "added"; + public static final String RESULT_IMPORT_UPDATED = "updated"; + public static final String RESULT_IMPORT_BAD = "bad"; + + // export + public static final String RESULT_EXPORT = "exported"; + + // query + public static final String RESULT_QUERY_KEY_KEY_DATA = "queryKeyKeyData"; + public static final String RESULT_QUERY_KEY_SEARCH_RESULT = "queryKeySearchResult"; + + Messenger mMessenger; + + public ApgIntentService() { + super("ApgService"); + } + + /** + * The IntentService calls this method from the default worker thread with the intent that + * started the service. When this method returns, IntentService stops the service, as + * appropriate. + */ + @Override + protected void onHandleIntent(Intent intent) { + Bundle extras = intent.getExtras(); + if (extras == null) { + Log.e(Constants.TAG, "Extras bundle is null!"); + return; + } + + if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || extras + .containsKey(EXTRA_ACTION))) { + Log.e(Constants.TAG, + "Extra bundle must contain a messenger, a data bundle, and an action!"); + return; + } + + mMessenger = (Messenger) extras.get(EXTRA_MESSENGER); + Bundle data = extras.getBundle(EXTRA_DATA); + + OtherHelper.logDebugBundle(data, "EXTRA_DATA"); + + int action = extras.getInt(EXTRA_ACTION); + + // execute action from extra bundle + switch (action) { + case ACTION_ENCRYPT_SIGN: + + try { + /* Input */ + int target = data.getInt(TARGET); + + long secretKeyId = data.getLong(SECRET_KEY_ID); + String encryptionPassphrase = data.getString(SYMMETRIC_PASSPHRASE); + + boolean useAsciiArmor = data.getBoolean(USE_ASCII_AMOR); + long encryptionKeyIds[] = data.getLongArray(ENCRYPTION_KEYS_IDS); + int compressionId = data.getInt(COMPRESSION_ID); + boolean generateSignature = data.getBoolean(GENERATE_SIGNATURE); + boolean signOnly = data.getBoolean(SIGN_ONLY); + + InputStream inStream = null; + long inLength = -1; + InputData inputData = null; + OutputStream outStream = null; + String streamFilename = null; + switch (target) { + case TARGET_BYTES: /* encrypting bytes directly */ + byte[] bytes = data.getByteArray(MESSAGE_BYTES); + + inStream = new ByteArrayInputStream(bytes); + inLength = bytes.length; + + inputData = new InputData(inStream, inLength); + outStream = new ByteArrayOutputStream(); + + break; + case TARGET_FILE: /* encrypting file */ + String inputFile = data.getString(INPUT_FILE); + String outputFile = data.getString(OUTPUT_FILE); + + // check if storage is ready + if (!FileHelper.isStorageMounted(inputFile) + || !FileHelper.isStorageMounted(outputFile)) { + throw new ApgGeneralException( + getString(R.string.error_externalStorageNotReady)); + } + + inStream = new FileInputStream(inputFile); + File file = new File(inputFile); + inLength = file.length(); + inputData = new InputData(inStream, inLength); + + outStream = new FileOutputStream(outputFile); + + break; + + case TARGET_STREAM: /* Encrypting stream from content uri */ + Uri providerUri = (Uri) data.getParcelable(PROVIDER_URI); + + // InputStream + InputStream in = getContentResolver().openInputStream(providerUri); + inLength = PGPMain.getLengthOfStream(in); + inputData = new InputData(in, inLength); + + // OutputStream + try { + while (true) { + streamFilename = PGPMain.generateRandomFilename(32); + if (streamFilename == null) { + throw new PGPMain.ApgGeneralException( + "couldn't generate random file name"); + } + openFileInput(streamFilename).close(); + } + } catch (FileNotFoundException e) { + // found a name that isn't used yet + } + outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE); + + break; + + default: + throw new PGPMain.ApgGeneralException("No target choosen!"); + + } + + /* Operation */ + + if (generateSignature) { + Log.d(Constants.TAG, "generating signature..."); + PGPMain.generateSignature(this, this, inputData, outStream, useAsciiArmor, + false, secretKeyId, PassphraseCacheService.getCachedPassphrase(this, + secretKeyId), Preferences.getPreferences(this) + .getDefaultHashAlgorithm(), Preferences.getPreferences(this) + .getForceV3Signatures()); + } else if (signOnly) { + Log.d(Constants.TAG, "sign only..."); + PGPMain.signText(this, this, inputData, outStream, secretKeyId, + PassphraseCacheService.getCachedPassphrase(this, secretKeyId), + Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences + .getPreferences(this).getForceV3Signatures()); + } else { + Log.d(Constants.TAG, "encrypt..."); + PGPMain.encryptAndSign(this, this, inputData, outStream, useAsciiArmor, + compressionId, encryptionKeyIds, encryptionPassphrase, Preferences + .getPreferences(this).getDefaultEncryptionAlgorithm(), + secretKeyId, + Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences + .getPreferences(this).getForceV3Signatures(), + PassphraseCacheService.getCachedPassphrase(this, secretKeyId)); + } + + outStream.close(); + + /* Output */ + + Bundle resultData = new Bundle(); + + switch (target) { + case TARGET_BYTES: + if (useAsciiArmor) { + String output = new String( + ((ByteArrayOutputStream) outStream).toByteArray()); + if (generateSignature) { + resultData.putString(RESULT_SIGNATURE_STRING, output); + } else { + resultData.putString(RESULT_ENCRYPTED_STRING, output); + } + } else { + byte output[] = ((ByteArrayOutputStream) outStream).toByteArray(); + if (generateSignature) { + resultData.putByteArray(RESULT_SIGNATURE_BYTES, output); + } else { + resultData.putByteArray(RESULT_ENCRYPTED_BYTES, output); + } + } + + break; + case TARGET_FILE: + // nothing, file was written, just send okay + + break; + case TARGET_STREAM: + String uri = DataStream.buildDataStreamUri(streamFilename).toString(); + resultData.putString(RESULT_URI, uri); + + break; + } + + OtherHelper.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_DECRYPT_VERIFY: + try { + /* Input */ + int target = data.getInt(TARGET); + + long secretKeyId = data.getLong(SECRET_KEY_ID); + byte[] bytes = data.getByteArray(CIPHERTEXT_BYTES); + boolean signedOnly = data.getBoolean(SIGNED_ONLY); + boolean returnBytes = data.getBoolean(RETURN_BYTES); + boolean assumeSymmetricEncryption = data.getBoolean(ASSUME_SYMMETRIC); + + boolean lookupUnknownKey = data.getBoolean(LOOKUP_UNKNOWN_KEY); + + InputStream inStream = null; + long inLength = -1; + InputData inputData = null; + OutputStream outStream = null; + String streamFilename = null; + switch (target) { + case TARGET_BYTES: /* decrypting bytes directly */ + inStream = new ByteArrayInputStream(bytes); + inLength = bytes.length; + + inputData = new InputData(inStream, inLength); + outStream = new ByteArrayOutputStream(); + + break; + + case TARGET_FILE: /* decrypting file */ + String inputFile = data.getString(INPUT_FILE); + String outputFile = data.getString(OUTPUT_FILE); + + // check if storage is ready + if (!FileHelper.isStorageMounted(inputFile) + || !FileHelper.isStorageMounted(outputFile)) { + throw new ApgGeneralException( + getString(R.string.error_externalStorageNotReady)); + } + + // InputStream + inLength = -1; + inStream = new FileInputStream(inputFile); + File file = new File(inputFile); + inLength = file.length(); + inputData = new InputData(inStream, inLength); + + // OutputStream + outStream = new FileOutputStream(outputFile); + + break; + + case TARGET_STREAM: /* decrypting stream from content uri */ + Uri providerUri = (Uri) data.getParcelable(PROVIDER_URI); + + // InputStream + InputStream in = getContentResolver().openInputStream(providerUri); + inLength = PGPMain.getLengthOfStream(in); + inputData = new InputData(in, inLength); + + // OutputStream + try { + while (true) { + streamFilename = PGPMain.generateRandomFilename(32); + if (streamFilename == null) { + throw new PGPMain.ApgGeneralException( + "couldn't generate random file name"); + } + openFileInput(streamFilename).close(); + } + } catch (FileNotFoundException e) { + // found a name that isn't used yet + } + outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE); + + break; + + default: + throw new PGPMain.ApgGeneralException("No target choosen!"); + + } + + /* Operation */ + + Bundle resultData = new Bundle(); + + // verifyText and decrypt returning additional resultData values for the + // verification of signatures + if (signedOnly) { + resultData = PGPMain.verifyText(this, this, inputData, outStream, + lookupUnknownKey); + } else { + resultData = PGPMain.decryptAndVerify(this, this, inputData, outStream, + PassphraseCacheService.getCachedPassphrase(this, secretKeyId), + assumeSymmetricEncryption); + } + + outStream.close(); + + /* Output */ + + switch (target) { + case TARGET_BYTES: + if (returnBytes) { + byte output[] = ((ByteArrayOutputStream) outStream).toByteArray(); + resultData.putByteArray(RESULT_DECRYPTED_BYTES, output); + } else { + String output = new String( + ((ByteArrayOutputStream) outStream).toByteArray()); + resultData.putString(RESULT_DECRYPTED_STRING, output); + } + + break; + case TARGET_FILE: + // nothing, file was written, just send okay and verification bundle + + break; + case TARGET_STREAM: + String uri = DataStream.buildDataStreamUri(streamFilename).toString(); + resultData.putString(RESULT_URI, uri); + + break; + } + + OtherHelper.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_SAVE_KEYRING: + + try { + /* Input */ + String oldPassPhrase = data.getString(CURRENT_PASSPHRASE); + String newPassPhrase = data.getString(NEW_PASSPHRASE); + if (newPassPhrase == null) { + newPassPhrase = oldPassPhrase; + } + ArrayList userIds = data.getStringArrayList(USER_IDS); + ArrayList keys = PGPConversionHelper.BytesToPGPSecretKeyList(data + .getByteArray(KEYS)); + ArrayList keysUsages = data.getIntegerArrayList(KEYS_USAGES); + long masterKeyId = data.getLong(MASTER_KEY_ID); + + /* Operation */ + PGPMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId, oldPassPhrase, + newPassPhrase, this); + PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); + + /* Output */ + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_GENERATE_KEY: + + try { + /* Input */ + int algorithm = data.getInt(ALGORITHM); + String passphrase = data.getString(SYMMETRIC_PASSPHRASE); + int keysize = data.getInt(KEY_SIZE); + PGPSecretKey masterKey = null; + if (data.containsKey(MASTER_KEY)) { + masterKey = PGPConversionHelper.BytesToPGPSecretKey(data + .getByteArray(MASTER_KEY)); + } + + /* Operation */ + PGPSecretKeyRing newKeyRing = PGPMain.createKey(this, algorithm, keysize, + passphrase, masterKey); + + /* Output */ + Bundle resultData = new Bundle(); + resultData.putByteArray(RESULT_NEW_KEY, + PGPConversionHelper.PGPSecretKeyRingToBytes(newKeyRing)); + + OtherHelper.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_GENERATE_DEFAULT_RSA_KEYS: + // generate one RSA 2048 key for signing and one subkey for encrypting! + try { + /* Input */ + String passphrase = data.getString(SYMMETRIC_PASSPHRASE); + + /* Operation */ + PGPSecretKeyRing masterKeyRing = PGPMain.createKey(this, Id.choice.algorithm.rsa, + 2048, passphrase, null); + + PGPSecretKeyRing subKeyRing = PGPMain.createKey(this, Id.choice.algorithm.rsa, + 2048, passphrase, masterKeyRing.getSecretKey()); + + /* Output */ + Bundle resultData = new Bundle(); + resultData.putByteArray(RESULT_NEW_KEY, + PGPConversionHelper.PGPSecretKeyRingToBytes(masterKeyRing)); + resultData.putByteArray(RESULT_NEW_KEY2, + PGPConversionHelper.PGPSecretKeyRingToBytes(subKeyRing)); + + OtherHelper.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_DELETE_FILE_SECURELY: + try { + /* Input */ + String deleteFile = data.getString(DELETE_FILE); + + /* Operation */ + try { + PGPMain.deleteFileSecurely(this, this, new File(deleteFile)); + } catch (FileNotFoundException e) { + throw new PGPMain.ApgGeneralException(getString(R.string.error_fileNotFound, + deleteFile)); + } catch (IOException e) { + throw new PGPMain.ApgGeneralException(getString( + R.string.error_fileDeleteFailed, deleteFile)); + } + + /* Output */ + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_IMPORT_KEY: + try { + + /* Input */ + int target = data.getInt(TARGET); + + int keyType = Id.type.public_key; + if (data.containsKey(IMPORT_KEY_TYPE)) { + keyType = data.getInt(IMPORT_KEY_TYPE); + } + + /* Operation */ + InputStream inStream = null; + long inLength = -1; + InputData inputData = null; + switch (target) { + case TARGET_BYTES: /* import key from bytes directly */ + byte[] bytes = data.getByteArray(IMPORT_BYTES); + + inStream = new ByteArrayInputStream(bytes); + inLength = bytes.length; + + inputData = new InputData(inStream, inLength); + + break; + case TARGET_FILE: /* import key from file */ + String inputFile = data.getString(IMPORT_FILENAME); + + inStream = new FileInputStream(inputFile); + File file = new File(inputFile); + inLength = file.length(); + inputData = new InputData(inStream, inLength); + + break; + + case TARGET_STREAM: + // TODO: not implemented + break; + } + + Bundle resultData = new Bundle(); + resultData = PGPMain.importKeyRings(this, keyType, inputData, this); + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_EXPORT_KEY: + try { + + /* Input */ + int keyType = Id.type.public_key; + if (data.containsKey(EXPORT_KEY_TYPE)) { + keyType = data.getInt(EXPORT_KEY_TYPE); + } + + String outputFile = data.getString(EXPORT_FILENAME); + + boolean exportAll = data.getBoolean(EXPORT_ALL); + int keyRingId = -1; + if (!exportAll) { + keyRingId = data.getInt(EXPORT_KEY_RING_ID); + } + + /* Operation */ + + // check if storage is ready + if (!FileHelper.isStorageMounted(outputFile)) { + throw new ApgGeneralException(getString(R.string.error_externalStorageNotReady)); + } + + // OutputStream + FileOutputStream outStream = new FileOutputStream(outputFile); + + Vector keyRingIds = new Vector(); + if (exportAll) { + if (keyType == Id.type.public_key) { + keyRingIds = ProviderHelper.getPublicKeyRingsRowIds(this); + } else { + keyRingIds = ProviderHelper.getSecretKeyRingsRowIds(this); + } + } else { + keyRingIds.add(keyRingId); + } + + Bundle resultData = new Bundle(); + resultData = PGPMain.exportKeyRings(this, keyRingIds, outStream, this); + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_UPLOAD_KEY: + try { + + /* Input */ + int keyRingRowId = data.getInt(UPLOAD_KEY_KEYRING_ROW_ID); + String keyServer = data.getString(UPLOAD_KEY_SERVER); + + /* Operation */ + HkpKeyServer server = new HkpKeyServer(keyServer); + + PGPPublicKeyRing keyring = ProviderHelper.getPGPPublicKeyRingByRowId(this, keyRingRowId); + if (keyring != null) { + boolean uploaded = PGPMain.uploadKeyRingToServer(server, + (PGPPublicKeyRing) keyring); + if (!uploaded) { + throw new ApgGeneralException("Unable to export key to selected server"); + } + } + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_QUERY_KEY: + try { + + /* Input */ + int queryType = data.getInt(QUERY_KEY_TYPE); + String keyServer = data.getString(QUERY_KEY_SERVER); + + String queryString = data.getString(QUERY_KEY_STRING); + long keyId = data.getLong(QUERY_KEY_ID); + + /* Operation */ + Bundle resultData = new Bundle(); + + HkpKeyServer server = new HkpKeyServer(keyServer); + if (queryType == Id.keyserver.search) { + ArrayList searchResult = server.search(queryString); + + resultData.putParcelableArrayList(RESULT_QUERY_KEY_SEARCH_RESULT, searchResult); + } else if (queryType == Id.keyserver.get) { + String keyData = server.get(keyId); + + resultData.putString(RESULT_QUERY_KEY_KEY_DATA, keyData); + } + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_SIGN_KEY: + try { + + /* Input */ + long masterKeyId = data.getLong(SIGN_KEY_MASTER_KEY_ID); + long pubKeyId = data.getLong(SIGN_KEY_PUB_KEY_ID); + + /* Operation */ + String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this, + masterKeyId); + + PGPPublicKeyRing signedPubKeyRing = PGPMain.signKey(this, masterKeyId, pubKeyId, + signaturePassPhrase); + + // store the signed key in our local cache + int retval = PGPMain.storeKeyRingInCache(this, signedPubKeyRing); + if (retval != Id.return_value.ok && retval != Id.return_value.updated) { + throw new ApgGeneralException("Failed to store signed key in local cache"); + } + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + default: + break; + } + + } + + private void sendErrorToHandler(Exception e) { + Log.e(Constants.TAG, "ApgService Exception: ", e); + e.printStackTrace(); + + Bundle data = new Bundle(); + data.putString(ApgIntentServiceHandler.DATA_ERROR, e.getMessage()); + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_EXCEPTION, null, data); + } + + private void sendMessageToHandler(Integer arg1, Integer arg2, Bundle data) { + Message msg = Message.obtain(); + msg.arg1 = arg1; + if (arg2 != null) { + msg.arg2 = arg2; + } + if (data != null) { + msg.setData(data); + } + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } + + private void sendMessageToHandler(Integer arg1, Bundle data) { + sendMessageToHandler(arg1, null, data); + } + + private void sendMessageToHandler(Integer arg1) { + sendMessageToHandler(arg1, null, null); + } + + /** + * Set progress of ProgressDialog by sending message to handler on UI thread + */ + public void setProgress(String message, int progress, int max) { + Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max=" + + max); + + Bundle data = new Bundle(); + if (message != null) { + data.putString(ApgIntentServiceHandler.DATA_MESSAGE, message); + } + data.putInt(ApgIntentServiceHandler.DATA_PROGRESS, progress); + data.putInt(ApgIntentServiceHandler.DATA_PROGRESS_MAX, max); + + sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_UPDATE_PROGRESS, null, data); + } + + public void setProgress(int resourceId, int progress, int max) { + setProgress(getString(resourceId), progress, max); + } + + public void setProgress(int progress, int max) { + setProgress(null, progress, max); + } +} diff --git a/APG/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java b/APG/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java new file mode 100644 index 000000000..8494cf035 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.service; + +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.ui.dialog.ProgressDialogFragment; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.support.v4.app.FragmentActivity; +import android.widget.Toast; + +public class ApgIntentServiceHandler extends Handler { + + // possible messages send from this service to handler on ui + public static final int MESSAGE_OKAY = 1; + public static final int MESSAGE_EXCEPTION = 2; + public static final int MESSAGE_UPDATE_PROGRESS = 3; + + // possible data keys for messages + public static final String DATA_ERROR = "error"; + public static final String DATA_PROGRESS = "progress"; + public static final String DATA_PROGRESS_MAX = "max"; + public static final String DATA_MESSAGE = "message"; + public static final String DATA_MESSAGE_ID = "message_id"; + + Activity mActivity; + ProgressDialogFragment mProgressDialogFragment; + + public ApgIntentServiceHandler(Activity activity) { + this.mActivity = activity; + } + + public ApgIntentServiceHandler(Activity activity, ProgressDialogFragment progressDialogFragment) { + this.mActivity = activity; + this.mProgressDialogFragment = progressDialogFragment; + } + + public ApgIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) { + this.mActivity = activity; + this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId, + progressDialogStyle); + } + + public void showProgressDialog(FragmentActivity activity) { + mProgressDialogFragment.show(activity.getSupportFragmentManager(), "progressDialog"); + } + + @Override + public void handleMessage(Message message) { + Bundle data = message.getData(); + + switch (message.arg1) { + case MESSAGE_OKAY: + mProgressDialogFragment.dismiss(); + + break; + + case MESSAGE_EXCEPTION: + mProgressDialogFragment.dismiss(); + + // show error from service + if (data.containsKey(DATA_ERROR)) { + Toast.makeText(mActivity, + mActivity.getString(R.string.errorMessage, data.getString(DATA_ERROR)), + Toast.LENGTH_SHORT).show(); + } + + break; + + case MESSAGE_UPDATE_PROGRESS: + if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) { + + // update progress from service + if (data.containsKey(DATA_MESSAGE)) { + mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE), + data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX)); + } else if (data.containsKey(DATA_MESSAGE_ID)) { + mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID), + data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX)); + } else { + mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS), + data.getInt(DATA_PROGRESS_MAX)); + } + } + + break; + + default: + break; + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/service/ApgService.java b/APG/src/org/thialfihar/android/apg/service/ApgService.java new file mode 100644 index 000000000..9e618b545 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/service/ApgService.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.service; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; + +import org.spongycastle.openpgp.PGPException; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.PGPMain; +import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; +import org.thialfihar.android.apg.util.InputData; +import org.thialfihar.android.apg.util.Log; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * TODO: + * + * - is this service thread safe? Probably not! + * + */ +public class ApgService extends Service { + Context mContext; + + @Override + public void onCreate() { + super.onCreate(); + mContext = this; + Log.d(Constants.TAG, "ApgService, onCreate()"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(Constants.TAG, "ApgService, onDestroy()"); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException { + byte[] buffer = new byte[8]; + int len = 0; + while ((len = is.read(buffer)) != -1) { + os.write(buffer, 0, len); + } + } + + void encryptAndSignImplementation(byte[] inputBytes, String inputUri, boolean useAsciiArmor, + int compression, long[] encryptionKeyIds, String encryptionPassphrase, + int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, + boolean signatureForceV3, String signaturePassphrase, IApgEncryptDecryptHandler handler) + throws RemoteException { + + try { + // TODO use inputUri + +// InputStream inStream = null; +// if (isBlob) { +// ContentResolver cr = getContentResolver(); +// try { +// inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); +// } catch (Exception e) { +// Log.e(TAG, "... exception on opening blob", e); +// } +// } else { +// inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes()); +// } +// InputData in = new InputData(inStream, 0); // XXX Size second param? + + // build InputData and write into OutputStream + InputStream inputStream = new ByteArrayInputStream(inputBytes); + long inputLength = inputBytes.length; + InputData input = new InputData(inputStream, inputLength); + + OutputStream output = new ByteArrayOutputStream(); + + PGPMain.encryptAndSign(mContext, null, input, output, useAsciiArmor, compression, + encryptionKeyIds, encryptionPassphrase, symmetricEncryptionAlgorithm, + signatureKeyId, signatureHashAlgorithm, signatureForceV3, signaturePassphrase); + + output.close(); + +// if (isBlob) { +// ContentResolver cr = getContentResolver(); +// try { +// OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB +// .name()))); +// writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream); +// outStream.close(); +// } catch (Exception e) { +// Log.e(TAG, "... exception on writing blob", e); +// } +// } else { +// pReturn.putString(ret.RESULT.name(), out.toString()); +// } + + byte[] outputBytes = ((ByteArrayOutputStream) output).toByteArray(); + + // return over handler on client side + handler.onSuccessEncrypt(outputBytes, null); + } catch (Exception e) { + Log.e(Constants.TAG, "ApgService, Exception!", e); + + try { + handler.onException(getExceptionId(e), e.getMessage()); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + + public void decryptAndVerifyImplementation(byte[] inputBytes, String inputUri, + String passphrase, boolean assumeSymmetric, IApgEncryptDecryptHandler handler) + throws RemoteException { + + try { + // build InputData and write into OutputStream + InputStream inputStream = new ByteArrayInputStream(inputBytes); + long inputLength = inputBytes.length; + InputData inputData = new InputData(inputStream, inputLength); + + OutputStream outputStream = new ByteArrayOutputStream(); + + Bundle outputBundle = PGPMain.decryptAndVerify(mContext, null, inputData, outputStream, + passphrase, assumeSymmetric); + + outputStream.close(); + + byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); + + // get signature informations from bundle + boolean signature = outputBundle.getBoolean(ApgIntentService.RESULT_SIGNATURE); + long signatureKeyId = outputBundle.getLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID); + String signatureUserId = outputBundle + .getString(ApgIntentService.RESULT_SIGNATURE_USER_ID); + boolean signatureSuccess = outputBundle + .getBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS); + boolean signatureUnknown = outputBundle + .getBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN); + + // return over handler on client side + handler.onSuccessDecrypt(outputBytes, null, signature, signatureKeyId, signatureUserId, + signatureSuccess, signatureUnknown); + } catch (Exception e) { + Log.e(Constants.TAG, "ApgService, Exception!", e); + + try { + handler.onException(getExceptionId(e), e.getMessage()); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + + private void getDecryptionKeyImplementation(byte[] inputBytes, String inputUri, + IApgHelperHandler handler) { + + // TODO: implement inputUri + + try { + InputStream inputStream = new ByteArrayInputStream(inputBytes); + + long secretKeyId = Id.key.none; + boolean symmetric; + + try { + secretKeyId = PGPMain.getDecryptionKeyId(ApgService.this, inputStream); + if (secretKeyId == Id.key.none) { + throw new ApgGeneralException(getString(R.string.error_noSecretKeyFound)); + } + symmetric = false; + } catch (PGPMain.NoAsymmetricEncryptionException e) { + secretKeyId = Id.key.symmetric; + if (!PGPMain.hasSymmetricEncryption(ApgService.this, inputStream)) { + throw new ApgGeneralException(getString(R.string.error_noKnownEncryptionFound)); + } + symmetric = true; + } + + handler.onSuccessGetDecryptionKey(secretKeyId, symmetric); + + } catch (Exception e) { + Log.e(Constants.TAG, "ApgService, Exception!", e); + + try { + handler.onException(getExceptionId(e), e.getMessage()); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + + /** + * This is the implementation of the interface IApgService. All methods are oneway, meaning + * asynchronous and return to the client using IApgHandler. + * + * The real PGP code is located in PGPMain. + */ + private final IApgService.Stub mBinder = new IApgService.Stub() { + + @Override + public void encryptAsymmetric(byte[] inputBytes, String inputUri, boolean useAsciiArmor, + int compression, long[] encryptionKeyIds, int symmetricEncryptionAlgorithm, + IApgEncryptDecryptHandler handler) throws RemoteException { + + encryptAndSignImplementation(inputBytes, inputUri, useAsciiArmor, compression, + encryptionKeyIds, null, symmetricEncryptionAlgorithm, Id.key.none, 0, false, + null, handler); + } + + @Override + public void encryptSymmetric(byte[] inputBytes, String inputUri, boolean useAsciiArmor, + int compression, String encryptionPassphrase, int symmetricEncryptionAlgorithm, + IApgEncryptDecryptHandler handler) throws RemoteException { + + encryptAndSignImplementation(inputBytes, inputUri, useAsciiArmor, compression, null, + encryptionPassphrase, symmetricEncryptionAlgorithm, Id.key.none, 0, false, + null, handler); + } + + @Override + public void encryptAndSignAsymmetric(byte[] inputBytes, String inputUri, + boolean useAsciiArmor, int compression, long[] encryptionKeyIds, + int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, + boolean signatureForceV3, String signaturePassphrase, + IApgEncryptDecryptHandler handler) throws RemoteException { + + encryptAndSignImplementation(inputBytes, inputUri, useAsciiArmor, compression, + encryptionKeyIds, null, symmetricEncryptionAlgorithm, signatureKeyId, + signatureHashAlgorithm, signatureForceV3, signaturePassphrase, handler); + } + + @Override + public void encryptAndSignSymmetric(byte[] inputBytes, String inputUri, + boolean useAsciiArmor, int compression, String encryptionPassphrase, + int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, + boolean signatureForceV3, String signaturePassphrase, + IApgEncryptDecryptHandler handler) throws RemoteException { + + encryptAndSignImplementation(inputBytes, inputUri, useAsciiArmor, compression, null, + encryptionPassphrase, symmetricEncryptionAlgorithm, signatureKeyId, + signatureHashAlgorithm, signatureForceV3, signaturePassphrase, handler); + } + + @Override + public void decryptAndVerifyAsymmetric(byte[] inputBytes, String inputUri, + String keyPassphrase, IApgEncryptDecryptHandler handler) throws RemoteException { + + decryptAndVerifyImplementation(inputBytes, inputUri, keyPassphrase, false, handler); + } + + @Override + public void decryptAndVerifySymmetric(byte[] inputBytes, String inputUri, + String encryptionPassphrase, IApgEncryptDecryptHandler handler) + throws RemoteException { + + decryptAndVerifyImplementation(inputBytes, inputUri, encryptionPassphrase, true, + handler); + } + + @Override + public void getDecryptionKey(byte[] inputBytes, String inputUri, IApgHelperHandler handler) + throws RemoteException { + + getDecryptionKeyImplementation(inputBytes, inputUri, handler); + } + + }; + + /** + * As we can not throw an exception through Android RPC, we assign identifiers to the exception + * types. + * + * @param e + * @return + */ + private int getExceptionId(Exception e) { + if (e instanceof NoSuchProviderException) { + return 0; + } else if (e instanceof NoSuchAlgorithmException) { + return 1; + } else if (e instanceof SignatureException) { + return 2; + } else if (e instanceof IOException) { + return 3; + } else if (e instanceof ApgGeneralException) { + return 4; + } else if (e instanceof PGPException) { + return 5; + } else { + return -1; + } + } + +} diff --git a/APG/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl b/APG/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl new file mode 100644 index 000000000..318e5f344 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.service; + +interface IApgEncryptDecryptHandler { + /** + * Either output or streamUri is given. One of them is null + * + */ + oneway void onSuccessEncrypt(in byte[] outputBytes, in String outputUri); + + oneway void onSuccessDecrypt(in byte[] outputBytes, in String outputUri, in boolean signature, + in long signatureKeyId, in String signatureUserId, in boolean signatureSuccess, + in boolean signatureUnknown); + + + oneway void onException(in int exceptionNumber, in String message); +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl b/APG/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl new file mode 100644 index 000000000..29ff2dd3e --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.service; + +interface IApgHelperHandler { + + oneway void onSuccessGetDecryptionKey(in long secretKeyId, in boolean symmetric); + + + oneway void onException(in int exceptionNumber, in String message); +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/IApgService.aidl b/APG/src/org/thialfihar/android/apg/service/IApgService.aidl new file mode 100644 index 000000000..7753d1e3d --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/service/IApgService.aidl @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.service; + +import org.thialfihar.android.apg.service.IApgEncryptDecryptHandler; +import org.thialfihar.android.apg.service.IApgSignVerifyHandler; +import org.thialfihar.android.apg.service.IApgHelperHandler; + +/** + * All methods are oneway, which means they are asynchronous and non-blocking. + * Results are returned into given Handler, which has to be implemented on client side. + */ +interface IApgService { + + /** + * Encrypt + * + * Either inputBytes or inputUri is given, the other should be null. + * + * @param inputBytes + * Byte array you want to encrypt + * @param inputUri + * Blob in ContentProvider you want to encrypt + * @param useAsciiArmor + * Convert bytes to ascii armored text to guard against encoding problems + * @param compression + * Compression: 0x21070001: none, 1: Zip, 2: Zlib, 3: BZip2 + * @param encryptionKeyIds + * Ids of public keys used for encryption + * @param symmetricEncryptionAlgorithm + * 7: AES-128, 8: AES-192, 9: AES-256, 4: Blowfish, 10: Twofish, 3: CAST5, + * 6: DES, 2: Triple DES, 1: IDEA + * @param handler + * Results are returned to this IApgEncryptDecryptHandler Handler + * to onSuccessEncrypt(in byte[] output), after successful encryption + */ + oneway void encryptAsymmetric(in byte[] inputBytes, in String inputUri, in boolean useAsciiArmor, + in int compression, in long[] encryptionKeyIds, in int symmetricEncryptionAlgorithm, + in IApgEncryptDecryptHandler handler); + + /** + * Same as encryptAsymmetric but using a passphrase for symmetric encryption + * + * @param encryptionPassphrase + * Passphrase for direct symmetric encryption using symmetricEncryptionAlgorithm + */ + oneway void encryptSymmetric(in byte[] inputBytes, in String inputUri, in boolean useAsciiArmor, + in int compression, in String encryptionPassphrase, in int symmetricEncryptionAlgorithm, + in IApgEncryptDecryptHandler handler); + + /** + * Encrypt and sign + * + * Either inputBytes or inputUri is given, the other should be null. + * + * @param inputBytes + * Byte array you want to encrypt + * @param inputUri + * Blob in ContentProvider you want to encrypt + * @param useAsciiArmor + * Convert bytes to ascii armored text to guard against encoding problems + * @param compression + * Compression: 0x21070001: none, 1: Zip, 2: Zlib, 3: BZip2 + * @param encryptionKeyIds + * Ids of public keys used for encryption + * @param symmetricEncryptionAlgorithm + * 7: AES-128, 8: AES-192, 9: AES-256, 4: Blowfish, 10: Twofish, 3: CAST5, + * 6: DES, 2: Triple DES, 1: IDEA + * @param signatureKeyId + * Key id of key to sign with + * @param signatureHashAlgorithm + * 1: MD5, 3: RIPEMD-160, 2: SHA-1, 11: SHA-224, 8: SHA-256, 9: SHA-384, + * 10: SHA-512 + * @param signatureForceV3 + * Force V3 signatures + * @param signaturePassphrase + * Passphrase to unlock signature key + * @param handler + * Results are returned to this IApgEncryptDecryptHandler Handler + * to onSuccessEncrypt(in byte[] output), after successful encryption and signing + */ + oneway void encryptAndSignAsymmetric(in byte[] inputBytes, in String inputUri, + in boolean useAsciiArmor, in int compression, in long[] encryptionKeyIds, + in int symmetricEncryptionAlgorithm, in long signatureKeyId, in int signatureHashAlgorithm, + in boolean signatureForceV3, in String signaturePassphrase, + in IApgEncryptDecryptHandler handler); + + /** + * Same as encryptAndSignAsymmetric but using a passphrase for symmetric encryption + * + * @param encryptionPassphrase + * Passphrase for direct symmetric encryption using symmetricEncryptionAlgorithm + */ + oneway void encryptAndSignSymmetric(in byte[] inputBytes, in String inputUri, + in boolean useAsciiArmor, in int compression, in String encryptionPassphrase, + in int symmetricEncryptionAlgorithm, in long signatureKeyId, in int signatureHashAlgorithm, + in boolean signatureForceV3, in String signaturePassphrase, + in IApgEncryptDecryptHandler handler); + + /** + * Decrypts and verifies given input bytes. If no signature is present this method + * will only decrypt. + * + * @param inputBytes + * Byte array you want to decrypt and verify + * @param inputUri + * Blob in ContentProvider you want to decrypt and verify + * @param keyPassphrase + * Passphrase to unlock secret key for decryption. + * @param handler + * Handler where to return results to after successful encryption + */ + oneway void decryptAndVerifyAsymmetric(in byte[] inputBytes, in String inputUri, + in String keyPassphrase, in IApgEncryptDecryptHandler handler); + + /** + * Same as decryptAndVerifyAsymmetric but for symmetric decryption. + * + * @param encryptionPassphrase + * Passphrase to decrypt + */ + oneway void decryptAndVerifySymmetric(in byte[] inputBytes, in String inputUri, + in String encryptionPassphrase, in IApgEncryptDecryptHandler handler); + + /** + * + */ + oneway void getDecryptionKey(in byte[] inputBytes, in String inputUri, + in IApgHelperHandler handler); + + +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl b/APG/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl new file mode 100644 index 000000000..cc854e540 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.service; + +interface IApgSignVerifyHandler { + oneway void onSuccessSign(in byte[] outputBytes, in String outputUri); + + oneway void onSuccessVerify(in boolean signature, in long signatureKeyId, + in String signatureUserId, in boolean signatureSuccess, in boolean signatureUnknown); + + + oneway void onException(in int exceptionNumber, in String message); +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/PassphraseCacheService.java b/APG/src/org/thialfihar/android/apg/service/PassphraseCacheService.java new file mode 100644 index 000000000..c2bc48aa7 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/service/PassphraseCacheService.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.service; + +import java.util.Date; +import java.util.HashMap; + +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.helper.Preferences; +import org.thialfihar.android.apg.provider.ProviderHelper; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +public class PassphraseCacheService extends Service { + public static final String TAG = Constants.TAG + ": PassphraseCacheService"; + + public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX + + "PASSPHRASE_CACHE_SERVICE"; + + public static final String EXTRA_TTL = "ttl"; + public static final String EXTRA_KEY_ID = "keyId"; + public static final String EXTRA_PASSPHRASE = "passphrase"; + + private static final int REQUEST_ID = 0; + private static final long DEFAULT_TTL = 15; + + private BroadcastReceiver mIntentReceiver; + + // This is static to be easily retrieved by getCachedPassphrase() without the need of callback + // functions + private static HashMap mPassphraseCache = new HashMap(); + + /** + * This caches a new passphrase by sending a new command to the service. An android service is + * only run once. Thus, when the service is already started, new commands just add new events to + * the alarm manager for new passphrases to let them timeout in the future. + * + * @param context + * @param keyId + * @param passphrase + */ + public static void addCachedPassphrase(Context context, long keyId, String passphrase) { + Log.d(TAG, "cacheNewPassphrase() for " + keyId); + + Intent intent = new Intent(context, PassphraseCacheService.class); + intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassPhraseCacheTtl()); + intent.putExtra(EXTRA_PASSPHRASE, passphrase); + intent.putExtra(EXTRA_KEY_ID, keyId); + + context.startService(intent); + } + + /** + * Gets a cached passphrase from memory + * + * @param context + * @param keyId + * @return + */ + public static String getCachedPassphrase(Context context, long keyId) { + // try to get master key id which is used as an identifier for cached passphrases + long masterKeyId = keyId; + if (masterKeyId != Id.key.symmetric) { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, keyId); + if (keyRing == null) { + return null; + } + PGPSecretKey masterKey = PGPHelper.getMasterKey(keyRing); + if (masterKey == null) { + return null; + } + masterKeyId = masterKey.getKeyID(); + } + + // get cached passphrase + String cachedPassphrase = mPassphraseCache.get(masterKeyId); + if (cachedPassphrase == null) { + return null; + } + // set it again to reset the cache life cycle + Log.d(TAG, "Cache passphrase again when getting it!"); + addCachedPassphrase(context, masterKeyId, cachedPassphrase); + + return cachedPassphrase; + } + + /** + * Register BroadcastReceiver that is unregistered when service is destroyed. This + * BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to then timeout + * specific passphrases in memory. + */ + private void registerReceiver() { + if (mIntentReceiver == null) { + mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + Log.d(TAG, "Received broadcast..."); + + if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) { + long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); + timeout(context, keyId); + } + } + }; + + IntentFilter filter = new IntentFilter(); + filter.addAction(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); + registerReceiver(mIntentReceiver, filter); + } + } + + /** + * Build pending intent that is executed by alarm manager to time out a specific passphrase + * + * @param context + * @param keyId + * @return + */ + private static PendingIntent buildIntent(Context context, long keyId) { + Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); + intent.putExtra(EXTRA_KEY_ID, keyId); + PendingIntent sender = PendingIntent.getBroadcast(context, REQUEST_ID, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + + return sender; + } + + @Override + public void onCreate() { + Log.d(TAG, "onCreate()"); + } + + /** + * Executed when service is started by intent + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG, "onStartCommand()"); + + // register broadcastreceiver + registerReceiver(); + + if (intent != null) { + long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL); + long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); + String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE); + + Log.d(TAG, "Received intent in onStartCommand() with keyId: " + keyId + ", ttl: " + ttl); + + // add keyId and passphrase to memory + mPassphraseCache.put(keyId, passphrase); + + // register new alarm with keyId for this passphrase + long triggerTime = new Date().getTime() + (ttl * 1000); + AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); + am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId)); + } + + return START_STICKY; + } + + /** + * Called when one specific passphrase for keyId timed out + * + * @param context + * @param keyId + */ + private void timeout(Context context, long keyId) { + // remove passphrase corresponding to keyId from memory + mPassphraseCache.remove(keyId); + + Log.d(TAG, "Timeout of " + keyId + ", removed from memory!"); + + // stop whole service if no cached passphrases remaining + if (mPassphraseCache.isEmpty()) { + Log.d(TAG, "No passphrases remaining in memory, stopping service!"); + stopSelf(); + } + } + + @Override + public void onDestroy() { + Log.d(TAG, "onDestroy()"); + + unregisterReceiver(mIntentReceiver); + } + + public class PassphraseCacheBinder extends Binder { + public PassphraseCacheService getService() { + return PassphraseCacheService.this; + } + } + + private final IBinder mBinder = new PassphraseCacheBinder(); + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/DecryptActivity.java b/APG/src/org/thialfihar/android/apg/ui/DecryptActivity.java new file mode 100644 index 000000000..f1de44312 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/DecryptActivity.java @@ -0,0 +1,940 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.helper.FileHelper; +import org.thialfihar.android.apg.helper.OtherHelper; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.helper.PGPMain; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.service.ApgIntentService; +import org.thialfihar.android.apg.service.PassphraseCacheService; +import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; +import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; +import org.thialfihar.android.apg.ui.dialog.LookupUnknownKeyDialogFragment; +import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; +import org.thialfihar.android.apg.util.Compatibility; +import org.thialfihar.android.apg.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import org.thialfihar.android.apg.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.animation.AnimationUtils; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ViewFlipper; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.regex.Matcher; + +public class DecryptActivity extends SherlockFragmentActivity { + + // possible intent actions for this activity + public static final String ACTION_DECRYPT = Constants.INTENT_PREFIX + "DECRYPT"; + public static final String ACTION_DECRYPT_FILE = Constants.INTENT_PREFIX + "DECRYPT_FILE"; + public static final String ACTION_DECRYPT_AND_RETURN = Constants.INTENT_PREFIX + + "DECRYPT_AND_RETURN"; + + // possible extra keys + public static final String EXTRA_TEXT = "text"; + public static final String EXTRA_DATA = "data"; + public static final String EXTRA_REPLY_TO = "replyTo"; + public static final String EXTRA_SUBJECT = "subject"; + public static final String EXTRA_BINARY = "binary"; + + private long mSignatureKeyId = 0; + + private boolean mReturnResult = false; + private String mReplyTo = null; + private String mSubject = null; + private boolean mSignedOnly = false; + private boolean mAssumeSymmetricEncryption = false; + + private EditText mMessage = null; + private LinearLayout mSignatureLayout = null; + private ImageView mSignatureStatusImage = null; + private TextView mUserId = null; + private TextView mUserIdRest = null; + + private ViewFlipper mSource = null; + private TextView mSourceLabel = null; + private ImageView mSourcePrevious = null; + private ImageView mSourceNext = null; + + private boolean mDecryptEnabled = true; + private String mDecryptString = ""; + private boolean mReplyEnabled = true; + private String mReplyString = ""; + + private int mDecryptTarget; + + private EditText mFilename = null; + private CheckBox mDeleteAfter = null; + private ImageButton mBrowse = null; + + private String mInputFilename = null; + private String mOutputFilename = null; + + private Uri mContentUri = null; + private byte[] mDataBytes = null; + private boolean mReturnBinary = false; + + private long mUnknownSignatureKeyId = 0; + + private long mSecretKeyId = Id.key.none; + + private FileDialogFragment mFileDialog; + + private boolean mLookupUnknownKey = true; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + if (mDecryptEnabled) { + menu.add(1, Id.menu.option.decrypt, 0, mDecryptString).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + if (mReplyEnabled) { + menu.add(1, Id.menu.option.reply, 1, mReplyString).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.decrypt: { + decryptClicked(); + + return true; + } + case Id.menu.option.reply: { + replyClicked(); + + return true; + } + + default: { + return super.onOptionsItemSelected(item); + } + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // check permissions for intent actions without user interaction + String[] restrictedActions = new String[] { ACTION_DECRYPT_AND_RETURN }; + OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), + Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); + + setContentView(R.layout.decrypt); + + // set actionbar without home button if called from another app + OtherHelper.setActionBarBackButton(this); + + mSource = (ViewFlipper) findViewById(R.id.source); + mSourceLabel = (TextView) findViewById(R.id.sourceLabel); + mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious); + mSourceNext = (ImageView) findViewById(R.id.sourceNext); + + mSourcePrevious.setClickable(true); + mSourcePrevious.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this, + R.anim.push_right_in)); + mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this, + R.anim.push_right_out)); + mSource.showPrevious(); + updateSource(); + } + }); + + mSourceNext.setClickable(true); + OnClickListener nextSourceClickListener = new OnClickListener() { + public void onClick(View v) { + mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this, + R.anim.push_left_in)); + mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this, + R.anim.push_left_out)); + mSource.showNext(); + updateSource(); + } + }; + mSourceNext.setOnClickListener(nextSourceClickListener); + + mSourceLabel.setClickable(true); + mSourceLabel.setOnClickListener(nextSourceClickListener); + + mMessage = (EditText) findViewById(R.id.message); + mSignatureLayout = (LinearLayout) findViewById(R.id.signature); + mSignatureStatusImage = (ImageView) findViewById(R.id.ic_signature_status); + mUserId = (TextView) findViewById(R.id.mainUserId); + mUserIdRest = (TextView) findViewById(R.id.mainUserIdRest); + + // measure the height of the source_file view and set the message view's min height to that, + // so it fills mSource fully... bit of a hack. + View tmp = findViewById(R.id.sourceFile); + tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + int height = tmp.getMeasuredHeight(); + mMessage.setMinimumHeight(height); + + mFilename = (EditText) findViewById(R.id.filename); + mBrowse = (ImageButton) findViewById(R.id.btn_browse); + mBrowse.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*", + Id.request.filename); + } + }); + + mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterDecryption); + + // default: message source + mSource.setInAnimation(null); + mSource.setOutAnimation(null); + while (mSource.getCurrentView().getId() != R.id.sourceMessage) { + mSource.showNext(); + } + + boolean decryptImmediately = false; + + // Get intent, action and MIME type + Intent intent = getIntent(); + String action = intent.getAction(); + String type = intent.getType(); + + if (Intent.ACTION_VIEW.equals(action)) { + // Android's Action when opening file associated to APG (see AndroidManifest.xml) + + // This gets the Uri, where an inputStream can be opened from + mContentUri = intent.getData(); + + // TODO: old implementation of ACTION_VIEW. Is this used in K9? + // Uri uri = mIntent.getData(); + // try { + // InputStream attachment = getContentResolver().openInputStream(uri); + // ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + // byte bytes[] = new byte[1 << 16]; + // int length; + // while ((length = attachment.read(bytes)) > 0) { + // byteOut.write(bytes, 0, length); + // } + // byteOut.close(); + // String data = new String(byteOut.toByteArray()); + // mMessage.setText(data); + // } catch (FileNotFoundException e) { + // // ignore, then + // } catch (IOException e) { + // // ignore, then + // } + + // same as ACTION_DECRYPT_FILE but decrypt it immediately + handleActionDecryptFile(intent); + decryptImmediately = true; + } else if (Intent.ACTION_SEND.equals(action) && type != null) { + // Android's Action when sending to APG Decrypt + + if ("text/plain".equals(type)) { + // plain text + String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); + if (sharedText != null) { + intent.putExtra(EXTRA_TEXT, sharedText); + handleActionDecrypt(intent); + decryptImmediately = true; + } + } else { + // binary via content provider (could also be files) + Uri uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); + if (uri != null) { + mContentUri = uri; + } + } + } else if (ACTION_DECRYPT.equals(action)) { + handleActionDecrypt(intent); + } else if (ACTION_DECRYPT_FILE.equals(action)) { + handleActionDecryptFile(intent); + } else if (ACTION_DECRYPT_AND_RETURN.equals(action)) { + handleActionDecryptAndReturn(intent); + } + + if (mSource.getCurrentView().getId() == R.id.sourceMessage + && mMessage.getText().length() == 0) { + + CharSequence clipboardText = Compatibility.getClipboardText(this); + + String data = ""; + if (clipboardText != null) { + Matcher matcher = PGPMain.PGP_MESSAGE.matcher(clipboardText); + if (!matcher.matches()) { + matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(clipboardText); + } + if (matcher.matches()) { + data = matcher.group(1); + mMessage.setText(data); + Toast.makeText(this, R.string.usingClipboardContent, Toast.LENGTH_SHORT).show(); + } + } + } + + mSignatureLayout.setVisibility(View.GONE); + mSignatureLayout.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + if (mSignatureKeyId == 0) { + return; + } + PGPPublicKeyRing key = ProviderHelper.getPGPPublicKeyRingByKeyId( + DecryptActivity.this, mSignatureKeyId); + if (key != null) { + Intent intent = new Intent(DecryptActivity.this, KeyServerQueryActivity.class); + intent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID); + intent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, mSignatureKeyId); + startActivity(intent); + } + } + }); + + mReplyEnabled = false; + + // build new actionbar + invalidateOptionsMenu(); + + 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 (decryptImmediately + || (mSource.getCurrentView().getId() == R.id.sourceMessage && (mMessage.getText() + .length() > 0 || mDataBytes != null || mContentUri != null))) { + decryptClicked(); + } + } + + /** + * Handles activity intent with ACTION_DECRYPT + * + * @param intent + */ + private void handleActionDecrypt(Intent intent) { + Log.d(Constants.TAG, "Apg Intent DECRYPT startet"); + + Bundle extras = intent.getExtras(); + if (extras == null) { + Log.d(Constants.TAG, "extra bundle was null"); + extras = new Bundle(); + } else { + Log.d(Constants.TAG, "got extras"); + } + + mDataBytes = extras.getByteArray(EXTRA_DATA); + String textData = null; + if (mDataBytes == null) { + Log.d(Constants.TAG, "EXTRA_DATA was null"); + textData = extras.getString(EXTRA_TEXT); + } else { + Log.d(Constants.TAG, "Got data from EXTRA_DATA"); + } + if (textData != null) { + Log.d(Constants.TAG, "textData null, matching text ..."); + Matcher matcher = PGPMain.PGP_MESSAGE.matcher(textData); + if (matcher.matches()) { + Log.d(Constants.TAG, "PGP_MESSAGE matched"); + textData = matcher.group(1); + // replace non breakable spaces + textData = textData.replaceAll("\\xa0", " "); + mMessage.setText(textData); + } else { + matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(textData); + if (matcher.matches()) { + Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched"); + textData = matcher.group(1); + // replace non breakable spaces + textData = textData.replaceAll("\\xa0", " "); + mMessage.setText(textData); + + mDecryptString = getString(R.string.btn_verify); + // build new action bar + invalidateOptionsMenu(); + } else { + Log.d(Constants.TAG, "Nothing matched!"); + } + } + } + mReplyTo = extras.getString(EXTRA_REPLY_TO); + mSubject = extras.getString(EXTRA_SUBJECT); + } + + /** + * Handles activity intent with ACTION_DECRYPT_FILE + * + * @param intent + */ + private void handleActionDecryptFile(Intent intent) { + mInputFilename = intent.getData().getPath(); + mFilename.setText(mInputFilename); + guessOutputFilename(); + mSource.setInAnimation(null); + mSource.setOutAnimation(null); + while (mSource.getCurrentView().getId() != R.id.sourceFile) { + mSource.showNext(); + } + } + + /** + * Handles activity intent with ACTION_DECRYPT_AND_RETURN + * + * @param intent + */ + private void handleActionDecryptAndReturn(Intent intent) { + Bundle extras = intent.getExtras(); + if (extras == null) { + extras = new Bundle(); + } + + mReturnBinary = extras.getBoolean(EXTRA_BINARY, false); + + if (mContentUri == null) { + mDataBytes = extras.getByteArray(EXTRA_DATA); + String data = extras.getString(EXTRA_TEXT); + if (data != null) { + Matcher matcher = PGPMain.PGP_MESSAGE.matcher(data); + if (matcher.matches()) { + data = matcher.group(1); + // replace non breakable spaces + data = data.replaceAll("\\xa0", " "); + mMessage.setText(data); + } else { + matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(data); + if (matcher.matches()) { + data = matcher.group(1); + // replace non breakable spaces + data = data.replaceAll("\\xa0", " "); + mMessage.setText(data); + mDecryptString = getString(R.string.btn_verify); + + // build new action bar + invalidateOptionsMenu(); + } + } + } + } + mReturnResult = true; + } + + private void guessOutputFilename() { + mInputFilename = mFilename.getText().toString(); + File file = new File(mInputFilename); + String filename = file.getName(); + if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) { + filename = filename.substring(0, filename.length() - 4); + } + mOutputFilename = Constants.path.APP_DIR + "/" + filename; + } + + private void updateSource() { + switch (mSource.getCurrentView().getId()) { + case R.id.sourceFile: { + mSourceLabel.setText(R.string.label_file); + mDecryptString = getString(R.string.btn_decrypt); + + // build new action bar + invalidateOptionsMenu(); + break; + } + + case R.id.sourceMessage: { + mSourceLabel.setText(R.string.label_message); + mDecryptString = getString(R.string.btn_decrypt); + + // build new action bar + invalidateOptionsMenu(); + break; + } + + default: { + break; + } + } + } + + private void decryptClicked() { + if (mSource.getCurrentView().getId() == R.id.sourceFile) { + mDecryptTarget = Id.target.file; + } else { + mDecryptTarget = Id.target.message; + } + initiateDecryption(); + } + + private void initiateDecryption() { + if (mDecryptTarget == Id.target.file) { + String currentFilename = mFilename.getText().toString(); + if (mInputFilename == null || !mInputFilename.equals(currentFilename)) { + guessOutputFilename(); + } + + if (mInputFilename.equals("")) { + Toast.makeText(this, R.string.noFileSelected, Toast.LENGTH_SHORT).show(); + return; + } + + if (mInputFilename.startsWith("file")) { + File file = new File(mInputFilename); + if (!file.exists() || !file.isFile()) { + Toast.makeText( + this, + getString(R.string.errorMessage, getString(R.string.error_fileNotFound)), + Toast.LENGTH_SHORT).show(); + return; + } + } + } + + if (mDecryptTarget == Id.target.message) { + String messageData = mMessage.getText().toString(); + Matcher matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(messageData); + if (matcher.matches()) { + mSignedOnly = true; + decryptStart(); + return; + } + } + + // else treat it as an decrypted message/file + mSignedOnly = false; + + getDecryptionKeyFromInputStream(); + + // if we need a symmetric passphrase or a passphrase to use a secret key ask for it + if (mSecretKeyId == Id.key.symmetric + || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { + showPassphraseDialog(); + } else { + if (mDecryptTarget == Id.target.file) { + askForOutputFilename(); + } else { + decryptStart(); + } + } + } + + /** + * Shows passphrase dialog to cache a new passphrase the user enters for using it later for + * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks + * for a symmetric passphrase + */ + private void showPassphraseDialog() { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + if (mDecryptTarget == Id.target.file) { + askForOutputFilename(); + } else { + decryptStart(); + } + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + try { + PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, + messenger, mSecretKeyId); + + passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); + } catch (PGPMain.ApgGeneralException e) { + Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); + // send message to handler to start encryption directly + returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); + } + } + + /** + * TODO: Rework function, remove global variables + */ + private void getDecryptionKeyFromInputStream() { + InputStream inStream = null; + if (mContentUri != null) { + try { + inStream = getContentResolver().openInputStream(mContentUri); + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "File not found!", e); + Toast.makeText(this, getString(R.string.error_fileNotFound, e.getMessage()), + Toast.LENGTH_SHORT).show(); + } + } else if (mDecryptTarget == Id.target.file) { + // check if storage is ready + if (!FileHelper.isStorageMounted(mInputFilename)) { + Toast.makeText(this, getString(R.string.error_externalStorageNotReady), + Toast.LENGTH_SHORT).show(); + return; + } + + try { + inStream = new FileInputStream(mInputFilename); + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "File not found!", e); + Toast.makeText(this, getString(R.string.error_fileNotFound, e.getMessage()), + Toast.LENGTH_SHORT).show(); + } + } else { + if (mDataBytes != null) { + inStream = new ByteArrayInputStream(mDataBytes); + } else { + inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes()); + } + } + + // get decryption key for this inStream + try { + try { + mSecretKeyId = PGPMain.getDecryptionKeyId(this, inStream); + if (mSecretKeyId == Id.key.none) { + throw new PGPMain.ApgGeneralException( + getString(R.string.error_noSecretKeyFound)); + } + mAssumeSymmetricEncryption = false; + } catch (PGPMain.NoAsymmetricEncryptionException e) { + mSecretKeyId = Id.key.symmetric; + if (!PGPMain.hasSymmetricEncryption(this, inStream)) { + throw new PGPMain.ApgGeneralException( + getString(R.string.error_noKnownEncryptionFound)); + } + mAssumeSymmetricEncryption = true; + } + } catch (Exception e) { + Toast.makeText(this, getString(R.string.errorMessage, e.getMessage()), + Toast.LENGTH_SHORT).show(); + } + } + + private void replyClicked() { + Intent intent = new Intent(this, EncryptActivity.class); + intent.setAction(EncryptActivity.ACTION_ENCRYPT); + String data = mMessage.getText().toString(); + data = data.replaceAll("(?m)^", "> "); + data = "\n\n" + data; + intent.putExtra(EncryptActivity.EXTRA_TEXT, data); + intent.putExtra(EncryptActivity.EXTRA_SUBJECT, "Re: " + mSubject); + intent.putExtra(EncryptActivity.EXTRA_SEND_TO, mReplyTo); + intent.putExtra(EncryptActivity.EXTRA_SIGNATURE_KEY_ID, mSecretKeyId); + intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId }); + startActivity(intent); + } + + private void askForOutputFilename() { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == FileDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); + decryptStart(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + mFileDialog = FileDialogFragment.newInstance(messenger, + getString(R.string.title_decryptToFile), + getString(R.string.specifyFileToDecryptTo), mOutputFilename, null, + Id.request.output_filename); + + mFileDialog.show(getSupportFragmentManager(), "fileDialog"); + } + + private void lookupUnknownKey(long unknownKeyId) { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_OKAY) { + // the result is handled by onActivityResult() as LookupUnknownKeyDialogFragment + // starts a new Intent which then returns data + } else if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_CANCEL) { + // decrypt again, but don't lookup unknown keys! + mLookupUnknownKey = false; + decryptStart(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + LookupUnknownKeyDialogFragment lookupKeyDialog = LookupUnknownKeyDialogFragment + .newInstance(messenger, unknownKeyId); + + lookupKeyDialog.show(getSupportFragmentManager(), "unknownKeyDialog"); + } + + private void decryptStart() { + Log.d(Constants.TAG, "decryptStart"); + + // Send all information needed to service to decrypt in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_DECRYPT_VERIFY); + + // choose action based on input: decrypt stream, file or bytes + if (mContentUri != null) { + data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_STREAM); + + data.putParcelable(ApgIntentService.PROVIDER_URI, mContentUri); + } else if (mDecryptTarget == Id.target.file) { + data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_FILE); + + Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" + + mOutputFilename); + + data.putString(ApgIntentService.INPUT_FILE, mInputFilename); + data.putString(ApgIntentService.OUTPUT_FILE, mOutputFilename); + } else { + data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); + + if (mDataBytes != null) { + data.putByteArray(ApgIntentService.CIPHERTEXT_BYTES, mDataBytes); + } else { + String message = mMessage.getText().toString(); + data.putByteArray(ApgIntentService.CIPHERTEXT_BYTES, message.getBytes()); + } + } + + data.putLong(ApgIntentService.SECRET_KEY_ID, mSecretKeyId); + + data.putBoolean(ApgIntentService.SIGNED_ONLY, mSignedOnly); + data.putBoolean(ApgIntentService.LOOKUP_UNKNOWN_KEY, mLookupUnknownKey); + data.putBoolean(ApgIntentService.RETURN_BYTES, mReturnBinary); + data.putBoolean(ApgIntentService.ASSUME_SYMMETRIC, mAssumeSymmetricEncryption); + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after encrypting is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, + R.string.progress_decrypting, ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + // if key is unknown show lookup dialog + if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE_LOOKUP_KEY) + && mLookupUnknownKey) { + mUnknownSignatureKeyId = returnData + .getLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID); + lookupUnknownKey(mUnknownSignatureKeyId); + return; + } + + mSignatureKeyId = 0; + mSignatureLayout.setVisibility(View.GONE); + mReplyEnabled = false; + + // build new action bar + invalidateOptionsMenu(); + + Toast.makeText(DecryptActivity.this, R.string.decryptionSuccessful, + Toast.LENGTH_SHORT).show(); + if (mReturnResult) { + Intent intent = new Intent(); + intent.putExtras(returnData); + setResult(RESULT_OK, intent); + finish(); + return; + } + + switch (mDecryptTarget) { + case Id.target.message: + String decryptedMessage = returnData + .getString(ApgIntentService.RESULT_DECRYPTED_STRING); + mMessage.setText(decryptedMessage); + mMessage.setHorizontallyScrolling(false); + mReplyEnabled = false; + + // build new action bar + invalidateOptionsMenu(); + break; + + case Id.target.file: + if (mDeleteAfter.isChecked()) { + // Create and show dialog to delete original file + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment + .newInstance(mInputFilename); + deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); + } + break; + + default: + // shouldn't happen + break; + + } + + if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE)) { + String userId = returnData + .getString(ApgIntentService.RESULT_SIGNATURE_USER_ID); + mSignatureKeyId = returnData + .getLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID); + mUserIdRest + .setText("id: " + PGPHelper.getSmallFingerPrint(mSignatureKeyId)); + if (userId == null) { + userId = getResources().getString(R.string.unknownUserId); + } + String chunks[] = userId.split(" <", 2); + userId = chunks[0]; + if (chunks.length > 1) { + mUserIdRest.setText("<" + chunks[1]); + } + mUserId.setText(userId); + + if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS)) { + mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); + } else if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN)) { + mSignatureStatusImage.setImageResource(R.drawable.overlay_error); + Toast.makeText(DecryptActivity.this, + R.string.unknownSignatureKeyTouchToLookUp, Toast.LENGTH_LONG) + .show(); + } else { + mSignatureStatusImage.setImageResource(R.drawable.overlay_error); + } + mSignatureLayout.setVisibility(View.VISIBLE); + } + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = FileHelper.getPath(this, data.getData()); + Log.d(Constants.TAG, "path=" + path); + + mFilename.setText(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!"); + } + } + return; + } + + case Id.request.output_filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = data.getData().getPath(); + Log.d(Constants.TAG, "path=" + path); + + mFileDialog.setFilename(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!"); + } + } + return; + } + + // this request is returned after LookupUnknownKeyDialogFragment started + // KeyServerQueryActivity and user looked uo key + case Id.request.look_up_key_id: { + Log.d(Constants.TAG, "Returning from Lookup Key..."); + // decrypt again without lookup + mLookupUnknownKey = false; + decryptStart(); + return; + } + + default: { + break; + } + } + + super.onActivityResult(requestCode, resultCode, data); + } + +} diff --git a/APG/src/org/thialfihar/android/apg/ui/EditKeyActivity.java b/APG/src/org/thialfihar/android/apg/ui/EditKeyActivity.java new file mode 100644 index 000000000..0e480a3ae --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/EditKeyActivity.java @@ -0,0 +1,569 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.helper.OtherHelper; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.helper.PGPMain; +import org.thialfihar.android.apg.helper.PGPConversionHelper; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.service.ApgIntentService; +import org.thialfihar.android.apg.ui.dialog.SetPassphraseDialogFragment; +import org.thialfihar.android.apg.ui.widget.KeyEditor; +import org.thialfihar.android.apg.ui.widget.SectionView; +import org.thialfihar.android.apg.ui.widget.UserIdEditor; +import org.thialfihar.android.apg.util.IterableIterator; +import org.thialfihar.android.apg.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import org.thialfihar.android.apg.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.Toast; +import android.widget.CompoundButton.OnCheckedChangeListener; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Vector; + +public class EditKeyActivity extends SherlockFragmentActivity { + + // possible intent actions for this activity + public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY"; + public static final String ACTION_EDIT_KEY = Constants.INTENT_PREFIX + "EDIT_KEY"; + + // possible extra keys + public static final String EXTRA_USER_IDS = "userIds"; + public static final String EXTRA_NO_PASSPHRASE = "noPassphrase"; + public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generateDefaultKeys"; + public static final String EXTRA_KEY_ID = "keyId"; + + private ActionBar mActionBar; + + private PGPSecretKeyRing mKeyRing = null; + + private SectionView mUserIdsView; + private SectionView mKeysView; + + private String mCurrentPassPhrase = null; + private String mNewPassPhrase = null; + + private Button mChangePassPhrase; + + private CheckBox mNoPassphrase; + + Vector mUserIds; + Vector mKeys; + Vector mKeysUsages; + + // will be set to false to build layout later in handler + private boolean mBuildLayout = true; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(1, Id.menu.option.cancel, 0, R.string.btn_doNotSave).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(1, Id.menu.option.save, 1, R.string.btn_save).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, KeyListSecretActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.save: + saveClicked(); + return true; + + case Id.menu.option.cancel: + finish(); + return true; + + default: + break; + + } + return false; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // check permissions for intent actions without user interaction + String[] restrictedActions = new String[] { ACTION_CREATE_KEY }; + OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), + Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); + + setContentView(R.layout.edit_key); + + mActionBar = getSupportActionBar(); + mActionBar.setDisplayShowTitleEnabled(true); + + // set actionbar without home button if called from another app + OtherHelper.setActionBarBackButton(this); + + // find views + mChangePassPhrase = (Button) findViewById(R.id.edit_key_btn_change_pass_phrase); + mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase); + + mUserIds = new Vector(); + mKeys = new Vector(); + mKeysUsages = new Vector(); + + // Catch Intents opened from other apps + Intent intent = getIntent(); + String action = intent.getAction(); + if (ACTION_CREATE_KEY.equals(action)) { + handleActionCreateKey(intent); + } else if (ACTION_EDIT_KEY.equals(action)) { + handleActionEditKey(intent); + } + + mChangePassPhrase.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + showSetPassphraseDialog(); + } + }); + + // disable passphrase when no passphrase checkobox is checked! + mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() { + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + // remove passphrase + mNewPassPhrase = null; + + mChangePassPhrase.setVisibility(View.GONE); + } else { + mChangePassPhrase.setVisibility(View.VISIBLE); + } + } + }); + + if (mBuildLayout) { + buildLayout(); + } + } + + /** + * Handle intent action to create new key + * + * @param intent + */ + private void handleActionCreateKey(Intent intent) { + Bundle extras = intent.getExtras(); + + mActionBar.setTitle(R.string.title_createKey); + + mCurrentPassPhrase = ""; + + if (extras != null) { + // if userId is given, prefill the fields + if (extras.containsKey(EXTRA_USER_IDS)) { + Log.d(Constants.TAG, "UserIds are given!"); + mUserIds.add(extras.getString(EXTRA_USER_IDS)); + } + + // if no passphrase is given + if (extras.containsKey(EXTRA_NO_PASSPHRASE)) { + boolean noPassphrase = extras.getBoolean(EXTRA_NO_PASSPHRASE); + if (noPassphrase) { + // check "no passphrase" checkbox and remove button + mNoPassphrase.setChecked(true); + mChangePassPhrase.setVisibility(View.GONE); + } + } + + // generate key + if (extras.containsKey(EXTRA_GENERATE_DEFAULT_KEYS)) { + boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS); + if (generateDefaultKeys) { + + // build layout in handler after generating keys not directly in onCreate + mBuildLayout = false; + + // Send all information needed to service generate keys in other thread + Intent serviceIntent = new Intent(this, ApgIntentService.class); + serviceIntent.putExtra(ApgIntentService.EXTRA_ACTION, + ApgIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(ApgIntentService.SYMMETRIC_PASSPHRASE, mCurrentPassPhrase); + + serviceIntent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after generating is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, + R.string.progress_generating, ProgressDialog.STYLE_SPINNER) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + // get new key from data bundle returned from service + Bundle data = message.getData(); + PGPSecretKeyRing masterKeyRing = (PGPSecretKeyRing) PGPConversionHelper + .BytesToPGPKeyRing(data + .getByteArray(ApgIntentService.RESULT_NEW_KEY)); + PGPSecretKeyRing subKeyRing = (PGPSecretKeyRing) PGPConversionHelper + .BytesToPGPKeyRing(data + .getByteArray(ApgIntentService.RESULT_NEW_KEY2)); + + // add master key + @SuppressWarnings("unchecked") + Iterator masterIt = masterKeyRing.getSecretKeys(); + mKeys.add(masterIt.next()); + mKeysUsages.add(Id.choice.usage.sign_only); + + // add sub key + @SuppressWarnings("unchecked") + Iterator subIt = subKeyRing.getSecretKeys(); + subIt.next(); // masterkey + mKeys.add(subIt.next()); + mKeysUsages.add(Id.choice.usage.encrypt_only); + + buildLayout(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + serviceIntent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + saveHandler.showProgressDialog(this); + + // start service with intent + startService(serviceIntent); + } + } + } + } + + /** + * Handle intent action to edit existing key + * + * @param intent + */ + private void handleActionEditKey(Intent intent) { + Bundle extras = intent.getExtras(); + + mActionBar.setTitle(R.string.title_editKey); + + mCurrentPassPhrase = PGPMain.getEditPassPhrase(); + if (mCurrentPassPhrase == null) { + mCurrentPassPhrase = ""; + } + + if (mCurrentPassPhrase.equals("")) { + // check "no passphrase" checkbox and remove button + mNoPassphrase.setChecked(true); + mChangePassPhrase.setVisibility(View.GONE); + } + + if (extras != null) { + if (extras.containsKey(EXTRA_KEY_ID)) { + long keyId = extras.getLong(EXTRA_KEY_ID); + + if (keyId != 0) { + PGPSecretKey masterKey = null; + mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, keyId); + if (mKeyRing != null) { + masterKey = PGPHelper.getMasterKey(mKeyRing); + for (PGPSecretKey key : new IterableIterator( + mKeyRing.getSecretKeys())) { + mKeys.add(key); + mKeysUsages.add(-1); // get usage when view is created + } + } + if (masterKey != null) { + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + Log.d(Constants.TAG, "Added userId " + userId); + mUserIds.add(userId); + } + } + } + } + } + } + + /** + * Shows the dialog to set a new passphrase + */ + private void showSetPassphraseDialog() { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + + // set new returned passphrase! + mNewPassPhrase = data + .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE); + + updatePassPhraseButtonText(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + // set title based on isPassphraseSet() + int title = -1; + if (isPassphraseSet()) { + title = R.string.title_changePassPhrase; + } else { + title = R.string.title_setPassPhrase; + } + + SetPassphraseDialogFragment setPassphraseDialog = SetPassphraseDialogFragment.newInstance( + messenger, title); + + setPassphraseDialog.show(getSupportFragmentManager(), "setPassphraseDialog"); + } + + /** + * Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user + * id and key. + */ + private void buildLayout() { + // Build layout based on given userIds and keys + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container); + mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); + mUserIdsView.setType(Id.type.user_id); + mUserIdsView.setUserIds(mUserIds); + container.addView(mUserIdsView); + mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); + mKeysView.setType(Id.type.key); + mKeysView.setKeys(mKeys, mKeysUsages); + container.addView(mKeysView); + + updatePassPhraseButtonText(); + } + + private long getMasterKeyId() { + if (mKeysView.getEditors().getChildCount() == 0) { + return 0; + } + return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyID(); + } + + public boolean isPassphraseSet() { + if (mNoPassphrase.isChecked()) { + return true; + } else if ((!mCurrentPassPhrase.equals("")) + || (mNewPassPhrase != null && !mNewPassPhrase.equals(""))) { + return true; + } else { + return false; + } + } + + private void saveClicked() { + try { + if (!isPassphraseSet()) { + throw new PGPMain.ApgGeneralException(this.getString(R.string.setAPassPhrase)); + } + + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_SAVE_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(ApgIntentService.CURRENT_PASSPHRASE, mCurrentPassPhrase); + data.putString(ApgIntentService.NEW_PASSPHRASE, mNewPassPhrase); + data.putStringArrayList(ApgIntentService.USER_IDS, getUserIds(mUserIdsView)); + ArrayList keys = getKeys(mKeysView); + data.putByteArray(ApgIntentService.KEYS, + PGPConversionHelper.PGPSecretKeyArrayListToBytes(keys)); + data.putIntegerArrayList(ApgIntentService.KEYS_USAGES, getKeysUsages(mKeysView)); + data.putLong(ApgIntentService.MASTER_KEY_ID, getMasterKeyId()); + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after saving is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, + R.string.progress_saving, ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + finish(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } catch (PGPMain.ApgGeneralException e) { + Toast.makeText(this, getString(R.string.errorMessage, e.getMessage()), + Toast.LENGTH_SHORT).show(); + } + } + + /** + * Returns user ids from the SectionView + * + * @param userIdsView + * @return + */ + private ArrayList getUserIds(SectionView userIdsView) + throws PGPMain.ApgGeneralException { + ArrayList userIds = new ArrayList(); + + ViewGroup userIdEditors = userIdsView.getEditors(); + + boolean gotMainUserId = false; + for (int i = 0; i < userIdEditors.getChildCount(); ++i) { + UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i); + String userId = null; + try { + userId = editor.getValue(); + } catch (UserIdEditor.NoNameException e) { + throw new PGPMain.ApgGeneralException( + this.getString(R.string.error_userIdNeedsAName)); + } catch (UserIdEditor.NoEmailException e) { + throw new PGPMain.ApgGeneralException( + this.getString(R.string.error_userIdNeedsAnEmailAddress)); + } catch (UserIdEditor.InvalidEmailException e) { + throw new PGPMain.ApgGeneralException(e.getMessage()); + } + + if (userId.equals("")) { + continue; + } + + if (editor.isMainUserId()) { + userIds.add(0, userId); + gotMainUserId = true; + } else { + userIds.add(userId); + } + } + + if (userIds.size() == 0) { + throw new PGPMain.ApgGeneralException(getString(R.string.error_keyNeedsAUserId)); + } + + if (!gotMainUserId) { + throw new PGPMain.ApgGeneralException( + getString(R.string.error_mainUserIdMustNotBeEmpty)); + } + + return userIds; + } + + /** + * Returns keys from the SectionView + * + * @param keysView + * @return + */ + private ArrayList getKeys(SectionView keysView) + throws PGPMain.ApgGeneralException { + ArrayList keys = new ArrayList(); + + ViewGroup keyEditors = keysView.getEditors(); + + if (keyEditors.getChildCount() == 0) { + throw new PGPMain.ApgGeneralException(getString(R.string.error_keyNeedsMasterKey)); + } + + for (int i = 0; i < keyEditors.getChildCount(); ++i) { + KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); + keys.add(editor.getValue()); + } + + return keys; + } + + /** + * Returns usage selections of keys from the SectionView + * + * @param keysView + * @return + */ + private ArrayList getKeysUsages(SectionView keysView) + throws PGPMain.ApgGeneralException { + ArrayList getKeysUsages = new ArrayList(); + + ViewGroup keyEditors = keysView.getEditors(); + + if (keyEditors.getChildCount() == 0) { + throw new PGPMain.ApgGeneralException(getString(R.string.error_keyNeedsMasterKey)); + } + + for (int i = 0; i < keyEditors.getChildCount(); ++i) { + KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); + getKeysUsages.add(editor.getUsage()); + } + + return getKeysUsages; + } + + private void updatePassPhraseButtonText() { + mChangePassPhrase.setText(isPassphraseSet() ? R.string.btn_changePassPhrase + : R.string.btn_setPassPhrase); + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/EncryptActivity.java b/APG/src/org/thialfihar/android/apg/ui/EncryptActivity.java new file mode 100644 index 000000000..482372c51 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/EncryptActivity.java @@ -0,0 +1,1099 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.helper.FileHelper; +import org.thialfihar.android.apg.helper.OtherHelper; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.helper.PGPMain; +import org.thialfihar.android.apg.helper.Preferences; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.service.ApgIntentService; +import org.thialfihar.android.apg.service.PassphraseCacheService; +import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; +import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; +import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; +import org.thialfihar.android.apg.util.Choice; +import org.thialfihar.android.apg.util.Compatibility; +import org.thialfihar.android.apg.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import org.thialfihar.android.apg.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.animation.AnimationUtils; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ViewFlipper; + +import java.io.File; +import java.util.Vector; + +public class EncryptActivity extends SherlockFragmentActivity { + + // possible intent actions for this activity + public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT"; + public static final String ACTION_ENCRYPT_AND_RETURN = Constants.INTENT_PREFIX + + "ENCRYPT_AND_RETURN"; + public static final String ACTION_GENERATE_SIGNATURE_AND_RETURN = Constants.INTENT_PREFIX + + "GENERATE_SIGNATURE"; + + public static final String ACTION_ENCRYPT_FILE = Constants.INTENT_PREFIX + "ENCRYPT_FILE"; + public static final String ACTION_ENCRYPT_STREAM_AND_RETURN = Constants.INTENT_PREFIX + + "ENCRYPT_STREAM_AND_RETURN"; + + // possible extra keys + public static final String EXTRA_TEXT = "text"; + public static final String EXTRA_DATA = "data"; + public static final String EXTRA_ASCII_ARMOUR = "asciiArmour"; + public static final String EXTRA_SEND_TO = "sendTo"; + public static final String EXTRA_SUBJECT = "subject"; + public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId"; + public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds"; + + private String mSubject = null; + private String mSendTo = null; + + private long mEncryptionKeyIds[] = null; + + private boolean mReturnResult = false; + private EditText mMessage = null; + private Button mSelectKeysButton = null; + + private boolean mEncryptEnabled = false; + private String mEncryptString = ""; + private boolean mEncryptToClipboardEnabled = false; + private String mEncryptToClipboardString = ""; + + private CheckBox mSign = null; + private TextView mMainUserId = null; + private TextView mMainUserIdRest = null; + + private ViewFlipper mSource = null; + private TextView mSourceLabel = null; + private ImageView mSourcePrevious = null; + private ImageView mSourceNext = null; + + private ViewFlipper mMode = null; + private TextView mModeLabel = null; + private ImageView mModePrevious = null; + private ImageView mModeNext = null; + + private int mEncryptTarget; + + private EditText mPassPhrase = null; + private EditText mPassPhraseAgain = null; + private CheckBox mAsciiArmour = null; + private Spinner mFileCompression = null; + + private EditText mFilename = null; + private CheckBox mDeleteAfter = null; + private ImageButton mBrowse = null; + + private String mInputFilename = null; + private String mOutputFilename = null; + + private boolean mAsciiArmorDemand = false; + private boolean mOverrideAsciiArmour = false; + private Uri mStreamAndReturnUri = null; + private byte[] mData = null; + + private boolean mGenerateSignature = false; + + private long mSecretKeyId = Id.key.none; + + private FileDialogFragment mFileDialog; + + /** + * ActionBar menu is created based on class variables to change it at runtime + * + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (mEncryptToClipboardEnabled) { + menu.add(1, Id.menu.option.encrypt_to_clipboard, 0, mEncryptToClipboardString) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + if (mEncryptEnabled) { + menu.add(1, Id.menu.option.encrypt, 1, mEncryptString).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.encrypt_to_clipboard: + encryptToClipboardClicked(); + + return true; + + case Id.menu.option.encrypt: + encryptClicked(); + + return true; + + default: + return super.onOptionsItemSelected(item); + + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // check permissions for intent actions without user interaction + String[] restrictedActions = new String[] { ACTION_ENCRYPT_AND_RETURN, + ACTION_GENERATE_SIGNATURE_AND_RETURN, ACTION_ENCRYPT_STREAM_AND_RETURN }; + OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), + Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); + + setContentView(R.layout.encrypt); + + // set actionbar without home button if called from another app + OtherHelper.setActionBarBackButton(this); + + initView(); + + // Handle intent actions + handleActions(getIntent()); + + updateView(); + updateSource(); + updateMode(); + + if (mReturnResult) { + mSourcePrevious.setClickable(false); + mSourcePrevious.setEnabled(false); + mSourcePrevious.setVisibility(View.INVISIBLE); + + mSourceNext.setClickable(false); + mSourceNext.setEnabled(false); + mSourceNext.setVisibility(View.INVISIBLE); + + mSourceLabel.setClickable(false); + mSourceLabel.setEnabled(false); + } + + updateActionBarButtons(); + + if (mReturnResult + && (mMessage.getText().length() > 0 || mData != null) + && ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) || mSecretKeyId != 0)) { + encryptClicked(); + } + } + + /** + * Handles all actions with this intent + * + * @param intent + */ + private void handleActions(Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + String type = intent.getType(); + Uri uri = intent.getData(); + + if (extras == null) { + extras = new Bundle(); + } + + if (Intent.ACTION_SEND.equals(action) && type != null) { + // Android's Action when sending to APG Encrypt + + if ("text/plain".equals(type)) { + // plain text + String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); + if (sharedText != null) { + // handle like normal text encryption, override action and extras + action = ACTION_ENCRYPT; + extras.putString(EXTRA_TEXT, sharedText); + extras.putBoolean(EXTRA_ASCII_ARMOUR, true); + } + } else { + // files via content provider, override uri and action + uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); + action = ACTION_ENCRYPT_FILE; + } + } + + if (ACTION_ENCRYPT_AND_RETURN.equals(action) + || ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { + mReturnResult = true; + } + + if (ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { + mGenerateSignature = true; + mOverrideAsciiArmour = true; + mAsciiArmorDemand = false; + } + + if (extras.containsKey(EXTRA_ASCII_ARMOUR)) { + mAsciiArmorDemand = extras.getBoolean(EXTRA_ASCII_ARMOUR, true); + mOverrideAsciiArmour = true; + mAsciiArmour.setChecked(mAsciiArmorDemand); + } + + mData = extras.getByteArray(EXTRA_DATA); + String textData = null; + if (mData == null) { + textData = extras.getString(EXTRA_TEXT); + } + mSendTo = extras.getString(EXTRA_SEND_TO); + mSubject = extras.getString(EXTRA_SUBJECT); + long signatureKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); + long[] encryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); + + // preselect keys given by intent + preselectKeys(signatureKeyId, encryptionKeyIds); + + if (ACTION_ENCRYPT.equals(action) || ACTION_ENCRYPT_AND_RETURN.equals(action) + || ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { + if (textData != null) { + mMessage.setText(textData); + } + mSource.setInAnimation(null); + mSource.setOutAnimation(null); + while (mSource.getCurrentView().getId() != R.id.sourceMessage) { + mSource.showNext(); + } + } else if (ACTION_ENCRYPT_FILE.equals(action)) { + // get file path from uri + String path = FileHelper.getPath(this, uri); + + if (path != null) { + mInputFilename = path; + mFilename.setText(mInputFilename); + + mSource.setInAnimation(null); + mSource.setOutAnimation(null); + while (mSource.getCurrentView().getId() != R.id.sourceFile) { + mSource.showNext(); + } + } else { + Log.e(Constants.TAG, + "Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_ENCRYPT_STREAM_AND_RETURN."); + Toast.makeText(this, R.string.error_onlyFilesAreSupported, Toast.LENGTH_LONG) + .show(); + // end activity + finish(); + } + + } else if (ACTION_ENCRYPT_STREAM_AND_RETURN.equals(action)) { + // use mIntentDataUri to encrypt any stream and return + // TODO + + mStreamAndReturnUri = null; + } + } + + /** + * If an Intent gives a signatureKeyId and/or encryptionKeyIds, preselect those! + * + * @param preselectedSignatureKeyId + * @param preselectedEncryptionKeyIds + */ + private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds) { + if (preselectedSignatureKeyId != 0) { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, + preselectedSignatureKeyId); + PGPSecretKey masterKey = null; + if (keyRing != null) { + masterKey = PGPHelper.getMasterKey(keyRing); + if (masterKey != null) { + Vector signKeys = PGPHelper.getUsableSigningKeys(keyRing); + if (signKeys.size() > 0) { + mSecretKeyId = masterKey.getKeyID(); + } + } + } + } + + if (preselectedEncryptionKeyIds != null) { + Vector goodIds = new Vector(); + for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) { + PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, + preselectedEncryptionKeyIds[i]); + PGPPublicKey masterKey = null; + if (keyRing == null) { + continue; + } + masterKey = PGPHelper.getMasterKey(keyRing); + if (masterKey == null) { + continue; + } + Vector encryptKeys = PGPHelper.getUsableEncryptKeys(keyRing); + if (encryptKeys.size() == 0) { + continue; + } + goodIds.add(masterKey.getKeyID()); + } + if (goodIds.size() > 0) { + mEncryptionKeyIds = new long[goodIds.size()]; + for (int i = 0; i < goodIds.size(); ++i) { + mEncryptionKeyIds[i] = goodIds.get(i); + } + } + } + } + + /** + * Guess output filename based on input path + * + * @param path + * @return Suggestion for output filename + */ + private String guessOutputFilename(String path) { + // output in the same directory but with additional ending + File file = new File(path); + String ending = (mAsciiArmour.isChecked() ? ".asc" : ".gpg"); + String outputFilename = file.getParent() + File.separator + file.getName() + ending; + + return outputFilename; + } + + private void updateSource() { + switch (mSource.getCurrentView().getId()) { + case R.id.sourceFile: { + mSourceLabel.setText(R.string.label_file); + break; + } + + case R.id.sourceMessage: { + mSourceLabel.setText(R.string.label_message); + break; + } + + default: { + break; + } + } + updateActionBarButtons(); + } + + /** + * Set ActionBar buttons based on parameters + * + * @param encryptEnabled + * @param encryptStringRes + * @param encryptToClipboardEnabled + * @param encryptToClipboardStringRes + */ + private void setActionbarButtons(boolean encryptEnabled, int encryptStringRes, + boolean encryptToClipboardEnabled, int encryptToClipboardStringRes) { + mEncryptEnabled = encryptEnabled; + if (encryptEnabled) { + mEncryptString = getString(encryptStringRes); + } + mEncryptToClipboardEnabled = encryptToClipboardEnabled; + if (encryptToClipboardEnabled) { + mEncryptToClipboardString = getString(encryptToClipboardStringRes); + } + + // build new action bar based on these class variables + invalidateOptionsMenu(); + } + + /** + * Update ActionBar buttons based on current selection in view + */ + private void updateActionBarButtons() { + switch (mSource.getCurrentView().getId()) { + case R.id.sourceFile: { + setActionbarButtons(true, R.string.btn_encryptFile, false, 0); + + break; + } + + case R.id.sourceMessage: { + mSourceLabel.setText(R.string.label_message); + + if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { + if (mReturnResult) { + setActionbarButtons(true, R.string.btn_encrypt, false, 0); + } else { + setActionbarButtons(true, R.string.btn_encryptAndEmail, true, + R.string.btn_encryptToClipboard); + } + } else { + if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) { + if (mSecretKeyId == 0) { + setActionbarButtons(false, 0, false, 0); + } else { + if (mReturnResult) { + setActionbarButtons(true, R.string.btn_sign, false, 0); + } else { + setActionbarButtons(true, R.string.btn_signAndEmail, true, + R.string.btn_signToClipboard); + } + } + } else { + if (mReturnResult) { + setActionbarButtons(true, R.string.btn_encrypt, false, 0); + } else { + setActionbarButtons(true, R.string.btn_encryptAndEmail, true, + R.string.btn_encryptToClipboard); + } + } + } + break; + } + + default: { + break; + } + } + + } + + private void updateMode() { + switch (mMode.getCurrentView().getId()) { + case R.id.modeAsymmetric: { + mModeLabel.setText(R.string.label_asymmetric); + break; + } + + case R.id.modeSymmetric: { + mModeLabel.setText(R.string.label_symmetric); + break; + } + + default: { + break; + } + } + updateActionBarButtons(); + } + + private void encryptToClipboardClicked() { + mEncryptTarget = Id.target.clipboard; + initiateEncryption(); + } + + private void encryptClicked() { + Log.d(Constants.TAG, "encryptClicked invoked!"); + + if (mSource.getCurrentView().getId() == R.id.sourceFile) { + mEncryptTarget = Id.target.file; + } else { + mEncryptTarget = Id.target.email; + } + initiateEncryption(); + } + + private void initiateEncryption() { + if (mEncryptTarget == Id.target.file) { + String currentFilename = mFilename.getText().toString(); + if (mInputFilename == null || !mInputFilename.equals(currentFilename)) { + mInputFilename = mFilename.getText().toString(); + } + + mOutputFilename = guessOutputFilename(mInputFilename); + + if (mInputFilename.equals("")) { + Toast.makeText(this, R.string.noFileSelected, Toast.LENGTH_SHORT).show(); + return; + } + + if (!mInputFilename.startsWith("content")) { + File file = new File(mInputFilename); + if (!file.exists() || !file.isFile()) { + Toast.makeText( + this, + getString(R.string.errorMessage, getString(R.string.error_fileNotFound)), + Toast.LENGTH_SHORT).show(); + return; + } + } + } + + // symmetric encryption + if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { + boolean gotPassPhrase = false; + String passPhrase = mPassPhrase.getText().toString(); + String passPhraseAgain = mPassPhraseAgain.getText().toString(); + if (!passPhrase.equals(passPhraseAgain)) { + Toast.makeText(this, R.string.passPhrasesDoNotMatch, Toast.LENGTH_SHORT).show(); + return; + } + + gotPassPhrase = (passPhrase.length() != 0); + if (!gotPassPhrase) { + Toast.makeText(this, R.string.passPhraseMustNotBeEmpty, Toast.LENGTH_SHORT).show(); + return; + } + } else { + boolean encryptIt = (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0); + // for now require at least one form of encryption for files + if (!encryptIt && mEncryptTarget == Id.target.file) { + Toast.makeText(this, R.string.selectEncryptionKey, Toast.LENGTH_SHORT).show(); + return; + } + + if (!encryptIt && mSecretKeyId == 0) { + Toast.makeText(this, R.string.selectEncryptionOrSignatureKey, Toast.LENGTH_SHORT) + .show(); + return; + } + + if (mSecretKeyId != 0 + && PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { + showPassphraseDialog(); + + return; + } + } + + if (mEncryptTarget == Id.target.file) { + showOutputFileDialog(); + } else { + encryptStart(); + } + } + + /** + * Shows passphrase dialog to cache a new passphrase the user enters for using it later for + * encryption + */ + private void showPassphraseDialog() { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + if (mEncryptTarget == Id.target.file) { + showOutputFileDialog(); + } else { + encryptStart(); + } + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + try { + PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( + EncryptActivity.this, messenger, mSecretKeyId); + + passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); + } catch (PGPMain.ApgGeneralException e) { + Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); + // send message to handler to start encryption directly + returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); + } + } + + private void showOutputFileDialog() { + // Message is received after file is selected + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == FileDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); + encryptStart(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + mFileDialog = FileDialogFragment.newInstance(messenger, + getString(R.string.title_encryptToFile), + getString(R.string.specifyFileToEncryptTo), mOutputFilename, null, + Id.request.output_filename); + + mFileDialog.show(getSupportFragmentManager(), "fileDialog"); + } + + private void encryptStart() { + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + + boolean useAsciiArmor = true; + long encryptionKeyIds[] = null; + int compressionId = 0; + boolean signOnly = false; + + if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { + Log.d(Constants.TAG, "Symmetric encryption enabled!"); + String passPhrase = mPassPhrase.getText().toString(); + if (passPhrase.length() == 0) { + passPhrase = null; + } + + data.putString(ApgIntentService.SYMMETRIC_PASSPHRASE, passPhrase); + } else { + encryptionKeyIds = mEncryptionKeyIds; + signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0); + } + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_ENCRYPT_SIGN); + + // choose default settings, target and data bundle by target + if (mStreamAndReturnUri != null) { + // mIntentDataUri is only defined when ACTION_ENCRYPT_STREAM_AND_RETURN is used + data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_STREAM); + data.putParcelable(ApgIntentService.PROVIDER_URI, mStreamAndReturnUri); + + } else if (mEncryptTarget == Id.target.file) { + useAsciiArmor = mAsciiArmour.isChecked(); + compressionId = ((Choice) mFileCompression.getSelectedItem()).getId(); + + data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_FILE); + + Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" + + mOutputFilename); + + data.putString(ApgIntentService.INPUT_FILE, mInputFilename); + data.putString(ApgIntentService.OUTPUT_FILE, mOutputFilename); + + } else { + useAsciiArmor = true; + compressionId = Preferences.getPreferences(this).getDefaultMessageCompression(); + + data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); + + if (mData != null) { + data.putByteArray(ApgIntentService.MESSAGE_BYTES, mData); + } else { + String message = mMessage.getText().toString(); + if (signOnly && !mReturnResult) { + fixBadCharactersForGmail(message); + } + data.putByteArray(ApgIntentService.MESSAGE_BYTES, message.getBytes()); + } + } + + if (mOverrideAsciiArmour) { + useAsciiArmor = mAsciiArmorDemand; + } + + data.putLong(ApgIntentService.SECRET_KEY_ID, mSecretKeyId); + data.putBoolean(ApgIntentService.USE_ASCII_AMOR, useAsciiArmor); + data.putLongArray(ApgIntentService.ENCRYPTION_KEYS_IDS, encryptionKeyIds); + data.putInt(ApgIntentService.COMPRESSION_ID, compressionId); + data.putBoolean(ApgIntentService.GENERATE_SIGNATURE, mGenerateSignature); + data.putBoolean(ApgIntentService.SIGN_ONLY, signOnly); + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after encrypting is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, + R.string.progress_encrypting, ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle data = message.getData(); + + String output; + switch (mEncryptTarget) { + case Id.target.clipboard: + output = data.getString(ApgIntentService.RESULT_ENCRYPTED_STRING); + Log.d(Constants.TAG, "output: " + output); + Compatibility.copyToClipboard(EncryptActivity.this, output); + Toast.makeText(EncryptActivity.this, + R.string.encryptionToClipboardSuccessful, Toast.LENGTH_SHORT) + .show(); + break; + + case Id.target.email: + if (mReturnResult) { + Intent intent = new Intent(); + intent.putExtras(data); + setResult(RESULT_OK, intent); + finish(); + return; + } + + output = data.getString(ApgIntentService.RESULT_ENCRYPTED_STRING); + Log.d(Constants.TAG, "output: " + output); + + Intent emailIntent = new Intent(Intent.ACTION_SEND); + emailIntent.setType("text/plain; charset=utf-8"); + emailIntent.putExtra(Intent.EXTRA_TEXT, output); + if (mSubject != null) { + emailIntent.putExtra(Intent.EXTRA_SUBJECT, mSubject); + } + if (mSendTo != null) { + emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { mSendTo }); + } + startActivity(Intent.createChooser(emailIntent, + getString(R.string.title_sendEmail))); + break; + + case Id.target.file: + Toast.makeText(EncryptActivity.this, R.string.encryptionSuccessful, + Toast.LENGTH_SHORT).show(); + + if (mDeleteAfter.isChecked()) { + // Create and show dialog to delete original file + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment + .newInstance(mInputFilename); + deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); + } + break; + + default: + // shouldn't happen + break; + + } + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + /** + * Fixes bad message characters for gmail + * + * @param message + * @return + */ + private String fixBadCharactersForGmail(String message) { + // fix the message a bit, trailing spaces and newlines break stuff, + // because GMail sends as HTML and such things fuck up the + // signature, + // TODO: things like "<" and ">" also fuck up the signature + message = message.replaceAll(" +\n", "\n"); + message = message.replaceAll("\n\n+", "\n\n"); + message = message.replaceFirst("^\n+", ""); + // make sure there'll be exactly one newline at the end + message = message.replaceFirst("\n*$", "\n"); + + return message; + } + + private void initView() { + mSource = (ViewFlipper) findViewById(R.id.source); + mSourceLabel = (TextView) findViewById(R.id.sourceLabel); + mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious); + mSourceNext = (ImageView) findViewById(R.id.sourceNext); + + mSourcePrevious.setClickable(true); + mSourcePrevious.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, + R.anim.push_right_in)); + mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, + R.anim.push_right_out)); + mSource.showPrevious(); + updateSource(); + } + }); + + mSourceNext.setClickable(true); + OnClickListener nextSourceClickListener = new OnClickListener() { + public void onClick(View v) { + mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, + R.anim.push_left_in)); + mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, + R.anim.push_left_out)); + mSource.showNext(); + updateSource(); + } + }; + mSourceNext.setOnClickListener(nextSourceClickListener); + + mSourceLabel.setClickable(true); + mSourceLabel.setOnClickListener(nextSourceClickListener); + + mMode = (ViewFlipper) findViewById(R.id.mode); + mModeLabel = (TextView) findViewById(R.id.modeLabel); + mModePrevious = (ImageView) findViewById(R.id.modePrevious); + mModeNext = (ImageView) findViewById(R.id.modeNext); + + mModePrevious.setClickable(true); + mModePrevious.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, + R.anim.push_right_in)); + mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, + R.anim.push_right_out)); + mMode.showPrevious(); + updateMode(); + } + }); + + OnClickListener nextModeClickListener = new OnClickListener() { + public void onClick(View v) { + mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, + R.anim.push_left_in)); + mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, + R.anim.push_left_out)); + mMode.showNext(); + updateMode(); + } + }; + mModeNext.setOnClickListener(nextModeClickListener); + + mModeLabel.setClickable(true); + mModeLabel.setOnClickListener(nextModeClickListener); + + mMessage = (EditText) findViewById(R.id.message); + mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys); + mSign = (CheckBox) findViewById(R.id.sign); + mMainUserId = (TextView) findViewById(R.id.mainUserId); + mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest); + + mPassPhrase = (EditText) findViewById(R.id.passPhrase); + mPassPhraseAgain = (EditText) findViewById(R.id.passPhraseAgain); + + // measure the height of the source_file view and set the message view's min height to that, + // so it fills mSource fully... bit of a hack. + View tmp = findViewById(R.id.sourceFile); + tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + int height = tmp.getMeasuredHeight(); + mMessage.setMinimumHeight(height); + + mFilename = (EditText) findViewById(R.id.filename); + mBrowse = (ImageButton) findViewById(R.id.btn_browse); + mBrowse.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + FileHelper.openFile(EncryptActivity.this, mFilename.getText().toString(), "*/*", + Id.request.filename); + } + }); + + mFileCompression = (Spinner) findViewById(R.id.fileCompression); + Choice[] choices = new Choice[] { + new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " (" + + getString(R.string.fast) + ")"), + new Choice(Id.choice.compression.zip, "ZIP (" + getString(R.string.fast) + ")"), + new Choice(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.fast) + ")"), + new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow) + + ")"), }; + ArrayAdapter adapter = new ArrayAdapter(this, + android.R.layout.simple_spinner_item, choices); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mFileCompression.setAdapter(adapter); + + int defaultFileCompression = Preferences.getPreferences(this).getDefaultFileCompression(); + for (int i = 0; i < choices.length; ++i) { + if (choices[i].getId() == defaultFileCompression) { + mFileCompression.setSelection(i); + break; + } + } + + mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption); + + mAsciiArmour = (CheckBox) findViewById(R.id.asciiArmour); + mAsciiArmour.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour()); + + mSelectKeysButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + selectPublicKeys(); + } + }); + + mSign.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + CheckBox checkBox = (CheckBox) v; + if (checkBox.isChecked()) { + selectSecretKey(); + } else { + mSecretKeyId = Id.key.none; + updateView(); + } + } + }); + } + + private void updateView() { + if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) { + mSelectKeysButton.setText(R.string.noKeysSelected); + } else if (mEncryptionKeyIds.length == 1) { + mSelectKeysButton.setText(R.string.oneKeySelected); + } else { + mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " " + + getResources().getString(R.string.nKeysSelected)); + } + + if (mSecretKeyId == Id.key.none) { + mSign.setChecked(false); + mMainUserId.setText(""); + mMainUserIdRest.setText(""); + } else { + String uid = getResources().getString(R.string.unknownUserId); + String uidExtra = ""; + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, + mSecretKeyId); + if (keyRing != null) { + PGPSecretKey key = PGPHelper.getMasterKey(keyRing); + if (key != null) { + String userId = PGPHelper.getMainUserIdSafe(this, key); + String chunks[] = userId.split(" <", 2); + uid = chunks[0]; + if (chunks.length > 1) { + uidExtra = "<" + chunks[1]; + } + } + } + mMainUserId.setText(uid); + mMainUserIdRest.setText(uidExtra); + mSign.setChecked(true); + } + + updateActionBarButtons(); + } + + private void selectPublicKeys() { + Intent intent = new Intent(this, SelectPublicKeyActivity.class); + Vector keyIds = new Vector(); + if (mSecretKeyId != 0) { + keyIds.add(mSecretKeyId); + } + if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) { + for (int i = 0; i < mEncryptionKeyIds.length; ++i) { + keyIds.add(mEncryptionKeyIds[i]); + } + } + long[] initialKeyIds = null; + if (keyIds.size() > 0) { + initialKeyIds = new long[keyIds.size()]; + for (int i = 0; i < keyIds.size(); ++i) { + initialKeyIds[i] = keyIds.get(i); + } + } + intent.putExtra(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds); + startActivityForResult(intent, Id.request.public_keys); + } + + private void selectSecretKey() { + Intent intent = new Intent(this, SelectSecretKeyActivity.class); + startActivityForResult(intent, Id.request.secret_keys); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = FileHelper.getPath(this, data.getData()); + Log.d(Constants.TAG, "path=" + path); + + mFilename.setText(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!"); + } + } + return; + } + + case Id.request.output_filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = data.getData().getPath(); + Log.d(Constants.TAG, "path=" + path); + + mFileDialog.setFilename(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!"); + } + } + return; + } + + case Id.request.public_keys: { + if (resultCode == RESULT_OK) { + Bundle bundle = data.getExtras(); + mEncryptionKeyIds = bundle + .getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS); + } + updateView(); + break; + } + + case Id.request.secret_keys: { + if (resultCode == RESULT_OK) { + Bundle bundle = data.getExtras(); + mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); + } else { + mSecretKeyId = Id.key.none; + } + updateView(); + break; + } + + default: { + break; + } + } + + super.onActivityResult(requestCode, resultCode, data); + } + +} diff --git a/APG/src/org/thialfihar/android/apg/ui/HelpActivity.java b/APG/src/org/thialfihar/android/apg/ui/HelpActivity.java new file mode 100644 index 000000000..bd9d68ce7 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/HelpActivity.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.R; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.widget.TextView; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.ActionBar.Tab; +import com.actionbarsherlock.view.MenuItem; + +import java.util.ArrayList; + +import android.content.Context; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; + +import com.actionbarsherlock.app.SherlockFragmentActivity; + +public class HelpActivity extends SherlockFragmentActivity { + ViewPager mViewPager; + TabsAdapter mTabsAdapter; + TextView tabCenter; + TextView tabText; + + /** + * Menu Items + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mViewPager = new ViewPager(this); + mViewPager.setId(R.id.pager); + + setContentView(mViewPager); + ActionBar bar = getSupportActionBar(); + bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + bar.setDisplayShowTitleEnabled(true); + bar.setDisplayHomeAsUpEnabled(true); + + mTabsAdapter = new TabsAdapter(this, mViewPager); + + Bundle startBundle = new Bundle(); + startBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_start); + mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_start)), + HelpFragmentHtml.class, startBundle); + + Bundle changelogBundle = new Bundle(); + changelogBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_changelog); + mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_changelog)), + HelpFragmentHtml.class, changelogBundle); + + mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_about)), + HelpFragmentAbout.class, null); + } + + public static class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, + ViewPager.OnPageChangeListener { + private final Context mContext; + private final ActionBar mActionBar; + private final ViewPager mViewPager; + private final ArrayList mTabs = new ArrayList(); + + static final class TabInfo { + private final Class clss; + private final Bundle args; + + TabInfo(Class _class, Bundle _args) { + clss = _class; + args = _args; + } + } + + public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) { + super(activity.getSupportFragmentManager()); + mContext = activity; + mActionBar = activity.getSupportActionBar(); + mViewPager = pager; + mViewPager.setAdapter(this); + mViewPager.setOnPageChangeListener(this); + } + + public void addTab(ActionBar.Tab tab, Class clss, Bundle args) { + TabInfo info = new TabInfo(clss, args); + tab.setTag(info); + tab.setTabListener(this); + mTabs.add(info); + mActionBar.addTab(tab); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Fragment getItem(int position) { + TabInfo info = mTabs.get(position); + return Fragment.instantiate(mContext, info.clss.getName(), info.args); + } + + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + public void onPageSelected(int position) { + mActionBar.setSelectedNavigationItem(position); + } + + public void onPageScrollStateChanged(int state) { + } + + public void onTabSelected(Tab tab, FragmentTransaction ft) { + Object tag = tab.getTag(); + for (int i = 0; i < mTabs.size(); i++) { + if (mTabs.get(i) == tag) { + mViewPager.setCurrentItem(i); + } + } + } + + public void onTabUnselected(Tab tab, FragmentTransaction ft) { + } + + public void onTabReselected(Tab tab, FragmentTransaction ft) { + } + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java b/APG/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java new file mode 100644 index 000000000..1b80db66b --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import java.io.IOException; +import java.io.InputStream; + +import net.nightwhistler.htmlspanner.HtmlSpanner; +import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.OtherHelper; + +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import org.thialfihar.android.apg.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.actionbarsherlock.app.SherlockFragment; + +public class HelpFragmentAbout extends SherlockFragment { + + /** + * Workaround for Android Bug. See + * http://stackoverflow.com/questions/8748064/starting-activity-from + * -fragment-causes-nullpointerexception + */ + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + setUserVisibleHint(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.help_fragment_about, container, false); + + // load html from html file from /res/raw + InputStream inputStreamText = OtherHelper.getInputStreamFromResource(this.getActivity(), + R.raw.help_about); + + TextView versionText = (TextView) view.findViewById(R.id.help_about_version); + versionText.setText(getString(R.string.help_about_version) + " " + getVersion()); + + JellyBeanSpanFixTextView aboutTextView = (JellyBeanSpanFixTextView) view + .findViewById(R.id.help_about_text); + + // load html into textview + HtmlSpanner htmlSpanner = new HtmlSpanner(); + htmlSpanner.setStripExtraWhiteSpace(true); + try { + aboutTextView.setText(htmlSpanner.fromHtml(inputStreamText)); + } catch (IOException e) { + Log.e(Constants.TAG, "Error while reading raw resources as stream", e); + } + + // make links work + aboutTextView.setMovementMethod(LinkMovementMethod.getInstance()); + + // no flickering when clicking textview for Android < 4 + aboutTextView.setTextColor(getResources().getColor(android.R.color.black)); + + return view; + } + + /** + * Get the current package version. + * + * @return The current version. + */ + private String getVersion() { + String result = ""; + try { + PackageManager manager = getActivity().getPackageManager(); + PackageInfo info = manager.getPackageInfo(getActivity().getPackageName(), 0); + + result = String.format("%s (%s)", info.versionName, info.versionCode); + } catch (NameNotFoundException e) { + Log.w(Constants.TAG, "Unable to get application version: " + e.getMessage()); + result = "Unable to get application version."; + } + + return result; + } + +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java b/APG/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java new file mode 100644 index 000000000..e46e7b112 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import java.io.IOException; +import java.io.InputStream; + +import net.nightwhistler.htmlspanner.HtmlSpanner; +import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.helper.OtherHelper; +import org.thialfihar.android.apg.util.Log; + +import android.app.Activity; +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +import com.actionbarsherlock.app.SherlockFragment; + +public class HelpFragmentHtml extends SherlockFragment { + private Activity mActivity; + + private int htmlFile; + + public static final String ARG_HTML_FILE = "htmlFile"; + + /** + * Create a new instance of HelpFragmentHtml, providing "htmlFile" as an argument. + */ + static HelpFragmentHtml newInstance(int htmlFile) { + HelpFragmentHtml f = new HelpFragmentHtml(); + + // Supply html raw file input as an argument. + Bundle args = new Bundle(); + args.putInt(ARG_HTML_FILE, htmlFile); + f.setArguments(args); + + return f; + } + + /** + * Workaround for Android Bug. See + * http://stackoverflow.com/questions/8748064/starting-activity-from + * -fragment-causes-nullpointerexception + */ + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + setUserVisibleHint(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + htmlFile = getArguments().getInt(ARG_HTML_FILE); + + // load html from html file from /res/raw + InputStream inputStreamText = OtherHelper.getInputStreamFromResource(this.getActivity(), + htmlFile); + + mActivity = getActivity(); + + ScrollView scroller = new ScrollView(mActivity); + JellyBeanSpanFixTextView text = new JellyBeanSpanFixTextView(mActivity); + + // padding + int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, mActivity + .getResources().getDisplayMetrics()); + text.setPadding(padding, padding, padding, 0); + + scroller.addView(text); + + // load html into textview + HtmlSpanner htmlSpanner = new HtmlSpanner(); + htmlSpanner.setStripExtraWhiteSpace(true); + try { + text.setText(htmlSpanner.fromHtml(inputStreamText)); + } catch (IOException e) { + Log.e(Constants.TAG, "Error while reading raw resources as stream", e); + } + + // make links work + text.setMovementMethod(LinkMovementMethod.getInstance()); + + // no flickering when clicking textview for Android < 4 + text.setTextColor(getResources().getColor(android.R.color.black)); + + return scroller; + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/ImportFromQRCodeActivity.java b/APG/src/org/thialfihar/android/apg/ui/ImportFromQRCodeActivity.java new file mode 100644 index 000000000..aac1fd15b --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/ImportFromQRCodeActivity.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2011 Senecaso + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.helper.OtherHelper; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.service.ApgIntentService; +import org.thialfihar.android.apg.R; + +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; + +import org.thialfihar.android.apg.util.Log; + +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.MenuItem; +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; + +public class ImportFromQRCodeActivity extends SherlockFragmentActivity { + + // Not used in sourcode, but listed in AndroidManifest! + public static final String IMPORT_FROM_QR_CODE = Constants.INTENT_PREFIX + + "IMPORT_FROM_QR_CODE"; + + // public static final String EXTRA_KEY_ID = "keyId"; + + private TextView mContentView; + + private String mScannedContent; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.import_from_qr_code); + mContentView = (TextView) findViewById(R.id.import_from_qr_code_content); + + // set actionbar without home button if called from another app + OtherHelper.setActionBarBackButton(this); + + // start scanning + new IntentIntegrator(this).initiateScan(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: { + return super.onOptionsItemSelected(item); + } + } + } + + // private void importAndSignOld(final long keyId, final String expectedFingerprint) { + // if (expectedFingerprint != null && expectedFingerprint.length() > 0) { + // + // Thread t = new Thread() { + // @Override + // public void run() { + // try { + // // TODO: display some sort of spinner here while the user waits + // + // // TODO: there should be only 1 + // HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]); + // String encodedKey = server.get(keyId); + // + // PGPKeyRing keyring = PGPHelper.decodeKeyRing(new ByteArrayInputStream( + // encodedKey.getBytes())); + // if (keyring != null && keyring instanceof PGPPublicKeyRing) { + // PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring; + // + // // make sure the fingerprints match before we cache this thing + // String actualFingerprint = PGPHelper.convertToHex(publicKeyRing + // .getPublicKey().getFingerprint()); + // if (expectedFingerprint.equals(actualFingerprint)) { + // // store the signed key in our local cache + // int retval = PGPMain.storeKeyRingInCache(publicKeyRing); + // if (retval != Id.return_value.ok + // && retval != Id.return_value.updated) { + // status.putString(EXTRA_ERROR, + // "Failed to store signed key in local cache"); + // } else { + // Intent intent = new Intent(ImportFromQRCodeActivity.this, + // SignKeyActivity.class); + // intent.putExtra(EXTRA_KEY_ID, keyId); + // startActivityForResult(intent, Id.request.sign_key); + // } + // } else { + // status.putString( + // EXTRA_ERROR, + // "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key."); + // } + // } + // } catch (QueryException e) { + // Log.e(TAG, "Failed to query KeyServer", e); + // status.putString(EXTRA_ERROR, "Failed to query KeyServer"); + // status.putInt(Constants.extras.STATUS, Id.message.done); + // } catch (IOException e) { + // Log.e(TAG, "Failed to query KeyServer", e); + // status.putString(EXTRA_ERROR, "Failed to query KeyServer"); + // status.putInt(Constants.extras.STATUS, Id.message.done); + // } + // } + // }; + // + // t.setName("KeyExchange Download Thread"); + // t.setDaemon(true); + // t.start(); + // } + // } + + public void scanAgainOnClick(View view) { + new IntentIntegrator(this).initiateScan(); + } + + public void finishOnClick(View view) { + finish(); + } + + public void importOnClick(View view) { + Log.d(Constants.TAG, "import key started"); + + if (mScannedContent != null) { + // Send all information needed to service to import key in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_IMPORT_KEY); + + // fill values for this action + Bundle data = new Bundle(); + + data.putInt(ApgIntentService.IMPORT_KEY_TYPE, Id.type.public_key); + + data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); + data.putByteArray(ApgIntentService.IMPORT_BYTES, mScannedContent.getBytes()); + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after importing is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, + R.string.progress_importing, ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + int added = returnData.getInt(ApgIntentService.RESULT_IMPORT_ADDED); + int updated = returnData.getInt(ApgIntentService.RESULT_IMPORT_UPDATED); + int bad = returnData.getInt(ApgIntentService.RESULT_IMPORT_BAD); + String toastMessage; + if (added > 0 && updated > 0) { + toastMessage = getString(R.string.keysAddedAndUpdated, added, updated); + } else if (added > 0) { + toastMessage = getString(R.string.keysAdded, added); + } else if (updated > 0) { + toastMessage = getString(R.string.keysUpdated, updated); + } else { + toastMessage = getString(R.string.noKeysAddedOrUpdated); + } + Toast.makeText(ImportFromQRCodeActivity.this, toastMessage, + Toast.LENGTH_SHORT).show(); + if (bad > 0) { + AlertDialog.Builder alert = new AlertDialog.Builder( + ImportFromQRCodeActivity.this); + + alert.setIcon(android.R.drawable.ic_dialog_alert); + alert.setTitle(R.string.warning); + alert.setMessage(ImportFromQRCodeActivity.this.getString( + R.string.badKeysEncountered, bad)); + + alert.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + alert.setCancelable(true); + alert.create().show(); + } + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + } + + public void signAndUploadOnClick(View view) { + // first, import! + importOnClick(view); + + // TODO: implement sign and upload! + Toast.makeText(ImportFromQRCodeActivity.this, "Not implemented right now!", + Toast.LENGTH_SHORT).show(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case IntentIntegrator.REQUEST_CODE: { + IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, + data); + if (scanResult != null && scanResult.getFormatName() != null) { + + mScannedContent = scanResult.getContents(); + + mContentView.setText(mScannedContent); + // String[] bits = scanResult.getContents().split(","); + // if (bits.length != 2) { + // return; // dont know how to handle this. Not a valid code + // } + // + // long keyId = Long.parseLong(bits[0]); + // String expectedFingerprint = bits[1]; + + // importAndSign(keyId, expectedFingerprint); + } + + break; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + } + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyListActivity.java new file mode 100644 index 000000000..3adae94a9 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/KeyListActivity.java @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.service.ApgIntentService; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; +import org.thialfihar.android.apg.ui.dialog.DeleteKeyDialogFragment; +import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; +import org.thialfihar.android.apg.util.Log; + +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.app.SearchManager; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.widget.Toast; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +public class KeyListActivity extends SherlockFragmentActivity { + + public static final String ACTION_IMPORT = Constants.INTENT_PREFIX + "IMPORT"; + + public static final String EXTRA_TEXT = "text"; + + // protected View mFilterLayout; + // protected Button mClearFilterButton; + // protected TextView mFilterInfo; + + protected String mImportFilename = Constants.path.APP_DIR + "/"; + protected String mExportFilename = Constants.path.APP_DIR + "/"; + + protected String mImportData; + protected boolean mDeleteAfterImport = false; + + protected int mKeyType; + + FileDialogFragment mFileDialog; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + handleIntent(getIntent()); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + handleIntent(intent); + } + + protected void handleIntent(Intent intent) { + String searchString = null; + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + searchString = intent.getStringExtra(SearchManager.QUERY); + if (searchString != null && searchString.trim().length() == 0) { + searchString = null; + } + } + + // if (searchString == null) { + // mFilterLayout.setVisibility(View.GONE); + // } else { + // mFilterLayout.setVisibility(View.VISIBLE); + // mFilterInfo.setText(getString(R.string.filterInfo, searchString)); + // } + // + // if (mListAdapter != null) { + // mListAdapter.cleanup(); + // } + // mListAdapter = new KeyListAdapter(this, searchString); + // mList.setAdapter(mListAdapter); + + // Get intent, action + // Intent intent = getIntent(); + String action = intent.getAction(); + + if (Intent.ACTION_VIEW.equals(action)) { + // Android's Action when opening file associated to APG (see AndroidManifest.xml) + + handleActionImport(intent); + } else if (ACTION_IMPORT.equals(action)) { + // APG's own Actions + + handleActionImport(intent); + } + } + + /** + * Handles import action + * + * @param intent + */ + private void handleActionImport(Intent intent) { + if ("file".equals(intent.getScheme()) && intent.getDataString() != null) { + mImportFilename = intent.getData().getPath(); + } else { + mImportData = intent.getStringExtra(EXTRA_TEXT); + } + importKeys(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = data.getData().getPath(); + Log.d(Constants.TAG, "path=" + path); + + // set filename used in export/import dialogs + mFileDialog.setFilename(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!", e); + } + } + return; + } + + default: { + break; + } + } + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + menu.add(3, Id.menu.option.search, 0, R.string.menu_search) + .setIcon(R.drawable.ic_menu_search).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + menu.add(0, Id.menu.option.import_keys, 2, R.string.menu_importKeys).setShowAsAction( + MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(0, Id.menu.option.export_keys, 3, R.string.menu_exportKeys).setShowAsAction( + MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.import_keys: { + showImportKeysDialog(); + return true; + } + + case Id.menu.option.export_keys: { + showExportKeysDialog(-1); + return true; + } + + // case Id.menu.option.search: + // startSearch("", false, null, false); + // return true; + + default: { + return super.onOptionsItemSelected(item); + } + } + } + + /** + * Show to dialog from where to import keys + */ + public void showImportKeysDialog() { + // Message is received after file is selected + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == FileDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + mImportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); + + mDeleteAfterImport = data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED); + importKeys(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + mFileDialog = FileDialogFragment.newInstance(messenger, + getString(R.string.title_importKeys), getString(R.string.specifyFileToImportFrom), + mImportFilename, null, Id.request.filename); + + mFileDialog.show(getSupportFragmentManager(), "fileDialog"); + } + + /** + * Show dialog where to export keys + * + * @param keyRingId + * if -1 export all keys + */ + public void showExportKeysDialog(final long keyRingId) { + String title = null; + if (keyRingId != -1) { + // single key export + title = getString(R.string.title_exportKey); + } else { + title = getString(R.string.title_exportKeys); + } + + String message = null; + if (mKeyType == Id.type.public_key) { + message = getString(R.string.specifyFileToExportTo); + } else { + message = getString(R.string.specifyFileToExportSecretKeysTo); + } + + // Message is received after file is selected + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == FileDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); + + exportKeys(keyRingId); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + mFileDialog = FileDialogFragment.newInstance(messenger, title, message, mExportFilename, + null, Id.request.filename); + + mFileDialog.show(getSupportFragmentManager(), "fileDialog"); + } + + /** + * Show dialog to delete key + * + * @param keyRingId + */ + public void showDeleteKeyDialog(long keyRingId) { + // Message is received after key is deleted + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { + // refreshList(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, + keyRingId, mKeyType); + + deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog"); + } + + /** + * Import keys with mImportData + */ + public void importKeys() { + Log.d(Constants.TAG, "importKeys started"); + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_IMPORT_KEY); + + // fill values for this action + Bundle data = new Bundle(); + + data.putInt(ApgIntentService.IMPORT_KEY_TYPE, mKeyType); + + if (mImportData != null) { + data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); + data.putByteArray(ApgIntentService.IMPORT_BYTES, mImportData.getBytes()); + } else { + data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_FILE); + data.putString(ApgIntentService.IMPORT_FILENAME, mImportFilename); + } + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after importing is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_importing, + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + int added = returnData.getInt(ApgIntentService.RESULT_IMPORT_ADDED); + int updated = returnData.getInt(ApgIntentService.RESULT_IMPORT_UPDATED); + int bad = returnData.getInt(ApgIntentService.RESULT_IMPORT_BAD); + String toastMessage; + if (added > 0 && updated > 0) { + toastMessage = getString(R.string.keysAddedAndUpdated, added, updated); + } else if (added > 0) { + toastMessage = getString(R.string.keysAdded, added); + } else if (updated > 0) { + toastMessage = getString(R.string.keysUpdated, updated); + } else { + toastMessage = getString(R.string.noKeysAddedOrUpdated); + } + Toast.makeText(KeyListActivity.this, toastMessage, Toast.LENGTH_SHORT).show(); + if (bad > 0) { + AlertDialog.Builder alert = new AlertDialog.Builder(KeyListActivity.this); + + alert.setIcon(android.R.drawable.ic_dialog_alert); + alert.setTitle(R.string.warning); + alert.setMessage(KeyListActivity.this.getString( + R.string.badKeysEncountered, bad)); + + alert.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + alert.setCancelable(true); + alert.create().show(); + } else if (mDeleteAfterImport) { + // everything went well, so now delete, if that was turned on + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment + .newInstance(mImportFilename); + deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); + } + // refreshList(); + + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + /** + * Export keys + * + * @param keyRingId + * if -1 export all keys + */ + public void exportKeys(long keyRingId) { + Log.d(Constants.TAG, "exportKeys started"); + + // Send all information needed to service to export key in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_EXPORT_KEY); + + // fill values for this action + Bundle data = new Bundle(); + + data.putString(ApgIntentService.EXPORT_FILENAME, mExportFilename); + data.putInt(ApgIntentService.EXPORT_KEY_TYPE, mKeyType); + + if (keyRingId == -1) { + data.putBoolean(ApgIntentService.EXPORT_ALL, true); + } else { + data.putLong(ApgIntentService.EXPORT_KEY_RING_ID, keyRingId); + } + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after exporting is done in ApgService + ApgIntentServiceHandler exportHandler = new ApgIntentServiceHandler(this, R.string.progress_exporting, + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + int exported = returnData.getInt(ApgIntentService.RESULT_EXPORT); + String toastMessage; + if (exported == 1) { + toastMessage = getString(R.string.keyExported); + } else if (exported > 0) { + toastMessage = getString(R.string.keysExported, exported); + } else { + toastMessage = getString(R.string.noKeysExported); + } + Toast.makeText(KeyListActivity.this, toastMessage, Toast.LENGTH_SHORT).show(); + + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(exportHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + exportHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListFragment.java b/APG/src/org/thialfihar/android/apg/ui/KeyListFragment.java new file mode 100644 index 000000000..613f32ae9 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/KeyListFragment.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.ui.widget.ExpandableListFragment; + +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; + +public class KeyListFragment extends ExpandableListFragment { + protected KeyListActivity mKeyListActivity; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mKeyListActivity = (KeyListActivity) getActivity(); + + // register long press context menu + registerForContextMenu(getListView()); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText(getString(R.string.listEmpty)); + } + + /** + * Context Menu on Long Click + */ + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.add(0, Id.menu.export, 0, R.string.menu_exportKey); + menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey); + } + + @Override + public boolean onContextItemSelected(android.view.MenuItem item) { + ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); + + // expInfo.id would also return row id of childs, but we always want to get the row id of + // the group item, thus we are using the following way + int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); + long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); + + switch (item.getItemId()) { + case Id.menu.export: + mKeyListActivity.showExportKeysDialog(keyRingRowId); + return true; + + case Id.menu.delete: + mKeyListActivity.showDeleteKeyDialog(keyRingRowId); + return true; + + default: + return super.onContextItemSelected(item); + + } + } + +} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java new file mode 100644 index 000000000..73bb3790a --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; + +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; + +public class KeyListPublicActivity extends KeyListActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mKeyType = Id.type.public_key; + + setContentView(R.layout.key_list_public_activity); + + mExportFilename = Constants.path.APP_DIR + "/pubexport.asc"; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(1, Id.menu.option.key_server, 2, R.string.menu_keyServer) + .setIcon(R.drawable.ic_menu_search_list) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(1, Id.menu.option.scanQRCode, 1, R.string.menu_scanQRCode) + .setIcon(R.drawable.ic_menu_scan_qrcode) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case Id.menu.option.key_server: { + startActivityForResult(new Intent(this, KeyServerQueryActivity.class), 0); + + return true; + } + case Id.menu.option.scanQRCode: { + Intent intent = new Intent(this, ImportFromQRCodeActivity.class); + intent.setAction(ImportFromQRCodeActivity.IMPORT_FROM_QR_CODE); + startActivityForResult(intent, Id.request.import_from_qr_code); + + return true; + } + + default: { + return super.onOptionsItemSelected(item); + } + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.look_up_key_id: { + if (resultCode == RESULT_CANCELED || data == null + || data.getStringExtra(KeyServerQueryActivity.RESULT_EXTRA_TEXT) == null) { + return; + } + + Intent intent = new Intent(this, KeyListPublicActivity.class); + intent.setAction(KeyListPublicActivity.ACTION_IMPORT); + intent.putExtra(KeyListPublicActivity.EXTRA_TEXT, + data.getStringExtra(KeyListActivity.EXTRA_TEXT)); + handleIntent(intent); + break; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + break; + } + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java b/APG/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java new file mode 100644 index 000000000..e1a93602c --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.provider.ApgContract.KeyRings; +import org.thialfihar.android.apg.provider.ApgContract.UserIds; +import org.thialfihar.android.apg.ui.widget.KeyListAdapter; + +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.app.LoaderManager; +import android.view.ContextMenu; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; + +public class KeyListPublicFragment extends KeyListFragment implements + LoaderManager.LoaderCallbacks { + + private KeyListPublicActivity mKeyListPublicActivity; + + private KeyListAdapter mAdapter; + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mKeyListPublicActivity = (KeyListPublicActivity) getActivity(); + + mAdapter = new KeyListAdapter(mKeyListPublicActivity, null, Id.type.public_key); + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + // id is -1 as the child cursors are numbered 0,...,n + getLoaderManager().initLoader(-1, null, this); + } + + /** + * Context Menu on Long Click + */ + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.add(0, Id.menu.update, 1, R.string.menu_updateKey); + menu.add(0, Id.menu.exportToServer, 1, R.string.menu_exportKeyToServer); + menu.add(0, Id.menu.signKey, 1, R.string.menu_signKey); + } + + @Override + public boolean onContextItemSelected(android.view.MenuItem item) { + ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); + + // expInfo.id would also return row id of childs, but we always want to get the row id of + // the group item, thus we are using the following way + int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); + long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); + + switch (item.getItemId()) { + case Id.menu.update: + long updateKeyId = 0; + PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(mKeyListActivity, + keyRingRowId); + if (updateKeyRing != null) { + updateKeyId = PGPHelper.getMasterKey(updateKeyRing).getKeyID(); + } + if (updateKeyId == 0) { + // this shouldn't happen + return true; + } + + Intent queryIntent = new Intent(mKeyListActivity, KeyServerQueryActivity.class); + queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN); + queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId); + + // TODO: lookup?? + startActivityForResult(queryIntent, Id.request.look_up_key_id); + + return true; + + case Id.menu.exportToServer: + Intent uploadIntent = new Intent(mKeyListActivity, KeyServerUploadActivity.class); + uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER); + uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, keyRingRowId); + startActivityForResult(uploadIntent, Id.request.export_to_server); + + return true; + + case Id.menu.signKey: + long keyId = 0; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(mKeyListActivity, + keyRingRowId); + if (signKeyRing != null) { + keyId = PGPHelper.getMasterKey(signKeyRing).getKeyID(); + } + if (keyId == 0) { + // this shouldn't happen + return true; + } + + Intent signIntent = new Intent(mKeyListActivity, SignKeyActivity.class); + signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId); + startActivity(signIntent); + + return true; + + default: + return super.onContextItemSelected(item); + + } + } + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, + UserIds.USER_ID }; + + static final String SORT_ORDER = UserIds.USER_ID + " ASC"; + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildPublicKeyRingsUri(); + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.setGroupCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.setGroupCursor(null); + } + +} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java new file mode 100644 index 000000000..97abd2296 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.PGPMain; +import org.thialfihar.android.apg.service.PassphraseCacheService; +import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; +import org.thialfihar.android.apg.util.Log; + +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; + +public class KeyListSecretActivity extends KeyListActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mKeyType = Id.type.secret_key; + + setContentView(R.layout.key_list_secret_activity); + + mExportFilename = Constants.path.APP_DIR + "/secexport.asc"; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + menu.add(1, Id.menu.option.create, 1, R.string.menu_createKey).setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case Id.menu.option.create: { + createKey(); + return true; + } + + default: { + return super.onOptionsItemSelected(item); + } + } + } + + public void checkPassPhraseAndEdit(long keyId) { + String passPhrase = PassphraseCacheService.getCachedPassphrase(this, keyId); + if (passPhrase == null) { + showPassphraseDialog(keyId); + } else { + PGPMain.setEditPassPhrase(passPhrase); + editKey(keyId); + } + } + + private void showPassphraseDialog(final long secretKeyId) { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + String passPhrase = PassphraseCacheService.getCachedPassphrase( + KeyListSecretActivity.this, secretKeyId); + PGPMain.setEditPassPhrase(passPhrase); + editKey(secretKeyId); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + try { + PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( + KeyListSecretActivity.this, messenger, secretKeyId); + + passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); + } catch (PGPMain.ApgGeneralException e) { + Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); + // send message to handler to start encryption directly + returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); + } + } + + private void createKey() { + PGPMain.setEditPassPhrase(""); + Intent intent = new Intent(EditKeyActivity.ACTION_CREATE_KEY); + startActivityForResult(intent, 0); + } + + private void editKey(long keyId) { + Intent intent = new Intent(EditKeyActivity.ACTION_EDIT_KEY); + intent.putExtra(EditKeyActivity.EXTRA_KEY_ID, keyId); + startActivityForResult(intent, 0); + } + +} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java b/APG/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java new file mode 100644 index 000000000..12a0bb131 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.provider.ApgContract.KeyRings; +import org.thialfihar.android.apg.provider.ApgContract.UserIds; +import org.thialfihar.android.apg.ui.widget.KeyListAdapter; + +import com.google.zxing.integration.android.IntentIntegrator; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.app.LoaderManager; +import android.view.ContextMenu; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; + +public class KeyListSecretFragment extends KeyListFragment implements + LoaderManager.LoaderCallbacks { + + private KeyListSecretActivity mKeyListSecretActivity; + + private KeyListAdapter mAdapter; + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mKeyListSecretActivity = (KeyListSecretActivity) getActivity(); + + mAdapter = new KeyListAdapter(mKeyListSecretActivity, null, Id.type.secret_key); + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + // id is -1 as the child cursors are numbered 0,...,n + getLoaderManager().initLoader(-1, null, this); + } + + /** + * Context Menu on Long Click + */ + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + + menu.add(0, Id.menu.edit, 0, R.string.menu_editKey); + menu.add(0, Id.menu.share_qr_code, 2, R.string.menu_share); + } + + @Override + public boolean onContextItemSelected(android.view.MenuItem item) { + ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); + + // expInfo.id would also return row id of childs, but we always want to get the row id of + // the group item, thus we are using the following way + int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); + long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); + + switch (item.getItemId()) { + case Id.menu.edit: + // TODO: do it better directly with keyRingRowId? + long masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListSecretActivity, + keyRingRowId); + + mKeyListSecretActivity.checkPassPhraseAndEdit(masterKeyId); + return true; + + case Id.menu.share_qr_code: + // TODO: do it better directly with keyRingRowId? + long masterKeyId2 = ProviderHelper.getSecretMasterKeyId(mKeyListSecretActivity, + keyRingRowId); + + String msg = PGPHelper.getPubkeyAsArmoredString(mKeyListSecretActivity, masterKeyId2); + + new IntentIntegrator(mKeyListSecretActivity).shareText(msg); + return true; + + default: + return super.onContextItemSelected(item); + + } + } + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, + UserIds.USER_ID }; + + static final String SORT_ORDER = UserIds.USER_ID + " ASC"; + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildSecretKeyRingsUri(); + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.setGroupCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.setGroupCursor(null); + } + +} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java new file mode 100644 index 000000000..7cd54bf32 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import java.util.ArrayList; +import java.util.List; + +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.helper.Preferences; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.service.ApgIntentService; +import org.thialfihar.android.apg.util.Log; +import org.thialfihar.android.apg.util.KeyServer.KeyInfo; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.MenuItem; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; +import android.widget.ListView; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +public class KeyServerQueryActivity extends SherlockFragmentActivity { + + // possible intent actions for this activity + public static final String ACTION_LOOK_UP_KEY_ID = Constants.INTENT_PREFIX + "LOOK_UP_KEY_ID"; + public static final String ACTION_LOOK_UP_KEY_ID_AND_RETURN = Constants.INTENT_PREFIX + + "LOOK_UP_KEY_ID_AND_RETURN"; + + public static final String EXTRA_KEY_ID = "keyId"; + + public static final String RESULT_EXTRA_TEXT = "text"; + + private ListView mList; + private EditText mQuery; + private Button mSearch; + private KeyInfoListAdapter mAdapter; + private Spinner mKeyServer; + + private int mQueryType; + private String mQueryString; + private long mQueryId; + private volatile List mSearchResult; + private volatile String mKeyData; + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, KeyListPublicActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: + break; + + } + return false; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.key_server_query_layout); + + mQuery = (EditText) findViewById(R.id.query); + mSearch = (Button) findViewById(R.id.btn_search); + mList = (ListView) findViewById(R.id.list); + mAdapter = new KeyInfoListAdapter(this); + mList.setAdapter(mAdapter); + + mKeyServer = (Spinner) findViewById(R.id.keyServer); + ArrayAdapter adapter = new ArrayAdapter(this, + android.R.layout.simple_spinner_item, Preferences.getPreferences(this) + .getKeyServers()); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mKeyServer.setAdapter(adapter); + if (adapter.getCount() > 0) { + mKeyServer.setSelection(0); + } else { + mSearch.setEnabled(false); + } + + mList.setOnItemClickListener(new OnItemClickListener() { + public void onItemClick(AdapterView adapter, View view, int position, long keyId) { + get(keyId); + } + }); + + mSearch.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + String query = mQuery.getText().toString(); + search(query); + } + }); + + Intent intent = getIntent(); + String action = intent.getAction(); + if (ACTION_LOOK_UP_KEY_ID.equals(action) || ACTION_LOOK_UP_KEY_ID_AND_RETURN.equals(action)) { + long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0); + if (keyId != 0) { + String query = "0x" + PGPHelper.keyToHex(keyId); + mQuery.setText(query); + search(query); + } + } + } + + private void search(String query) { + mQueryType = Id.keyserver.search; + mQueryString = query; + mAdapter.setKeys(new ArrayList()); + + start(); + } + + private void get(long keyId) { + mQueryType = Id.keyserver.get; + mQueryId = keyId; + + start(); + } + + private void start() { + Log.d(Constants.TAG, "start search with service"); + + // Send all information needed to service to query keys in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_QUERY_KEY); + + // fill values for this action + Bundle data = new Bundle(); + + String server = (String) mKeyServer.getSelectedItem(); + data.putString(ApgIntentService.QUERY_KEY_SERVER, server); + + data.putInt(ApgIntentService.QUERY_KEY_TYPE, mQueryType); + + if (mQueryType == Id.keyserver.search) { + data.putString(ApgIntentService.QUERY_KEY_STRING, mQueryString); + } else if (mQueryType == Id.keyserver.get) { + data.putLong(ApgIntentService.QUERY_KEY_ID, mQueryId); + } + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after querying is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_querying, + ProgressDialog.STYLE_SPINNER) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + if (mQueryType == Id.keyserver.search) { + mSearchResult = returnData + .getParcelableArrayList(ApgIntentService.RESULT_QUERY_KEY_SEARCH_RESULT); + } else if (mQueryType == Id.keyserver.get) { + mKeyData = returnData.getString(ApgIntentService.RESULT_QUERY_KEY_KEY_DATA); + } + + // TODO: IMPROVE CODE!!! some global variables can be avoided!!! + if (mQueryType == Id.keyserver.search) { + if (mSearchResult != null) { + Toast.makeText(KeyServerQueryActivity.this, + getString(R.string.keysFound, mSearchResult.size()), + Toast.LENGTH_SHORT).show(); + mAdapter.setKeys(mSearchResult); + } + } else if (mQueryType == Id.keyserver.get) { + Intent orgIntent = getIntent(); + if (ACTION_LOOK_UP_KEY_ID_AND_RETURN.equals(orgIntent.getAction())) { + if (mKeyData != null) { + Intent intent = new Intent(); + intent.putExtra(RESULT_EXTRA_TEXT, mKeyData); + setResult(RESULT_OK, intent); + } else { + setResult(RESULT_CANCELED); + } + finish(); + } else { + if (mKeyData != null) { + Intent intent = new Intent(KeyServerQueryActivity.this, + KeyListPublicActivity.class); + intent.setAction(KeyListActivity.ACTION_IMPORT); + intent.putExtra(KeyListActivity.EXTRA_TEXT, mKeyData); + startActivity(intent); + } + } + } + + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + public class KeyInfoListAdapter extends BaseAdapter { + protected LayoutInflater mInflater; + protected Activity mActivity; + protected List mKeys; + + public KeyInfoListAdapter(Activity activity) { + mActivity = activity; + mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mKeys = new ArrayList(); + } + + public void setKeys(List keys) { + mKeys = keys; + notifyDataSetChanged(); + } + + @Override + public boolean hasStableIds() { + return true; + } + + public int getCount() { + return mKeys.size(); + } + + public Object getItem(int position) { + return mKeys.get(position); + } + + public long getItemId(int position) { + return mKeys.get(position).keyId; + } + + public View getView(int position, View convertView, ViewGroup parent) { + KeyInfo keyInfo = mKeys.get(position); + + View view = mInflater.inflate(R.layout.key_server_query_result_item, null); + + 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 algorithm = (TextView) view.findViewById(R.id.algorithm); + algorithm.setText(""); + TextView status = (TextView) view.findViewById(R.id.status); + status.setText(""); + + String userId = keyInfo.userIds.get(0); + if (userId != null) { + String chunks[] = userId.split(" <", 2); + userId = chunks[0]; + if (chunks.length > 1) { + mainUserIdRest.setText("<" + chunks[1]); + } + mainUserId.setText(userId); + } + + keyId.setText(PGPHelper.getSmallFingerPrint(keyInfo.keyId)); + + if (mainUserIdRest.getText().length() == 0) { + mainUserIdRest.setVisibility(View.GONE); + } + + algorithm.setText("" + keyInfo.size + "/" + keyInfo.algorithm); + + if (keyInfo.revoked != null) { + status.setText("revoked"); + } else { + status.setVisibility(View.GONE); + } + + LinearLayout ll = (LinearLayout) view.findViewById(R.id.list); + if (keyInfo.userIds.size() == 1) { + ll.setVisibility(View.GONE); + } else { + boolean first = true; + boolean second = true; + for (String uid : keyInfo.userIds) { + if (first) { + first = false; + continue; + } + if (!second) { + View sep = new View(mActivity); + sep.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 1)); + sep.setBackgroundResource(android.R.drawable.divider_horizontal_dark); + ll.addView(sep); + } + TextView uidView = (TextView) mInflater.inflate( + R.layout.key_server_query_result_user_id, null); + uidView.setText(uid); + ll.addView(uidView); + second = false; + } + } + + return view; + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java new file mode 100644 index 000000000..db907ed48 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2011 Senecaso + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.Preferences; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.service.ApgIntentService; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.MenuItem; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.Spinner; +import android.widget.Toast; + +/** + * gpg --send-key activity + * + * Sends the selected public key to a key server + */ +public class KeyServerUploadActivity extends SherlockFragmentActivity { + + // Not used in sourcode, but listed in AndroidManifest! + public static final String ACTION_EXPORT_KEY_TO_SERVER = Constants.INTENT_PREFIX + + "EXPORT_KEY_TO_SERVER"; + + public static final String EXTRA_KEYRING_ROW_ID = "keyId"; + + private Button export; + private Spinner keyServer; + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, KeyListPublicActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: + break; + + } + return false; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.key_server_export_layout); + + export = (Button) findViewById(R.id.btn_export_to_server); + keyServer = (Spinner) findViewById(R.id.keyServer); + + ArrayAdapter adapter = new ArrayAdapter(this, + android.R.layout.simple_spinner_item, Preferences.getPreferences(this) + .getKeyServers()); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + keyServer.setAdapter(adapter); + if (adapter.getCount() > 0) { + keyServer.setSelection(0); + } else { + export.setEnabled(false); + } + + export.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + uploadKey(); + } + }); + } + + private void uploadKey() { + // Send all information needed to service to upload key in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_UPLOAD_KEY); + + // fill values for this action + Bundle data = new Bundle(); + + int keyRingId = getIntent().getIntExtra(EXTRA_KEYRING_ROW_ID, -1); + data.putInt(ApgIntentService.UPLOAD_KEY_KEYRING_ROW_ID, keyRingId); + + String server = (String) keyServer.getSelectedItem(); + data.putString(ApgIntentService.UPLOAD_KEY_SERVER, server); + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after uploading is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_exporting, + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + + Toast.makeText(KeyServerUploadActivity.this, R.string.keySendSuccess, + Toast.LENGTH_SHORT).show(); + finish(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/MainActivity.java b/APG/src/org/thialfihar/android/apg/ui/MainActivity.java new file mode 100644 index 000000000..7c25e4533 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/MainActivity.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; + +public class MainActivity extends SherlockActivity { + + public void manageKeysOnClick(View view) { + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(new Intent(this, KeyListPublicActivity.class), 0); + } + + public void myKeysOnClick(View view) { + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(new Intent(this, KeyListSecretActivity.class), 0); + } + + public void encryptOnClick(View view) { + Intent intent = new Intent(MainActivity.this, EncryptActivity.class); + intent.setAction(EncryptActivity.ACTION_ENCRYPT); + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(intent, 0); + } + + public void decryptOnClick(View view) { + Intent intent = new Intent(MainActivity.this, DecryptActivity.class); + intent.setAction(DecryptActivity.ACTION_DECRYPT); + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(intent, 0); + } + + public void scanQrcodeOnClick(View view) { + Intent intent = new Intent(this, ImportFromQRCodeActivity.class); + intent.setAction(ImportFromQRCodeActivity.IMPORT_FROM_QR_CODE); + startActivityForResult(intent, Id.request.import_from_qr_code); + } + + public void helpOnClick(View view) { + startActivity(new Intent(this, HelpActivity.class)); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences) + .setIcon(R.drawable.ic_menu_settings) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case Id.menu.option.preferences: + startActivity(new Intent(this, PreferencesActivity.class)); + return true; + + default: + break; + + } + return false; + } + +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/PreferencesActivity.java b/APG/src/org/thialfihar/android/apg/ui/PreferencesActivity.java new file mode 100644 index 000000000..e28b385f2 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/PreferencesActivity.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.helper.Preferences; +import org.thialfihar.android.apg.ui.widget.IntegerListPreference; +import org.thialfihar.android.apg.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockPreferenceActivity; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.PreferenceScreen; + +public class PreferencesActivity extends SherlockPreferenceActivity { + private IntegerListPreference mPassPhraseCacheTtl = null; + private IntegerListPreference mEncryptionAlgorithm = null; + private IntegerListPreference mHashAlgorithm = null; + private IntegerListPreference mMessageCompression = null; + private IntegerListPreference mFileCompression = null; + private CheckBoxPreference mAsciiArmour = null; + private CheckBoxPreference mForceV3Signatures = null; + private PreferenceScreen mKeyServerPreference = null; + private Preferences mPreferences; + + @Override + protected void onCreate(Bundle savedInstanceState) { + mPreferences = Preferences.getPreferences(this); + super.onCreate(savedInstanceState); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + + addPreferencesFromResource(R.xml.apg_preferences); + + mPassPhraseCacheTtl = (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL); + mPassPhraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl()); + mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry()); + mPassPhraseCacheTtl + .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mPassPhraseCacheTtl.setValue(newValue.toString()); + mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry()); + mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString())); + return false; + } + }); + + mEncryptionAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM); + int valueIds[] = { PGPEncryptedData.AES_128, PGPEncryptedData.AES_192, + PGPEncryptedData.AES_256, PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH, + PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES, + PGPEncryptedData.IDEA, }; + String entries[] = { "AES-128", "AES-192", "AES-256", "Blowfish", "Twofish", "CAST5", + "DES", "Triple DES", "IDEA", }; + String values[] = new String[valueIds.length]; + for (int i = 0; i < values.length; ++i) { + values[i] = "" + valueIds[i]; + } + mEncryptionAlgorithm.setEntries(entries); + mEncryptionAlgorithm.setEntryValues(values); + mEncryptionAlgorithm.setValue("" + mPreferences.getDefaultEncryptionAlgorithm()); + mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry()); + mEncryptionAlgorithm + .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mEncryptionAlgorithm.setValue(newValue.toString()); + mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry()); + mPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue + .toString())); + return false; + } + }); + + mHashAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM); + valueIds = new int[] { HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160, + HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256, + HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512, }; + entries = new String[] { "MD5", "RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384", + "SHA-512", }; + values = new String[valueIds.length]; + for (int i = 0; i < values.length; ++i) { + values[i] = "" + valueIds[i]; + } + mHashAlgorithm.setEntries(entries); + mHashAlgorithm.setEntryValues(values); + mHashAlgorithm.setValue("" + mPreferences.getDefaultHashAlgorithm()); + mHashAlgorithm.setSummary(mHashAlgorithm.getEntry()); + mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mHashAlgorithm.setValue(newValue.toString()); + mHashAlgorithm.setSummary(mHashAlgorithm.getEntry()); + mPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString())); + return false; + } + }); + + mMessageCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION); + valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip, + Id.choice.compression.zlib, Id.choice.compression.bzip2, }; + entries = new String[] { + getString(R.string.choice_none) + " (" + getString(R.string.fast) + ")", + "ZIP (" + getString(R.string.fast) + ")", + "ZLIB (" + getString(R.string.fast) + ")", + "BZIP2 (" + getString(R.string.very_slow) + ")", }; + values = new String[valueIds.length]; + for (int i = 0; i < values.length; ++i) { + values[i] = "" + valueIds[i]; + } + mMessageCompression.setEntries(entries); + mMessageCompression.setEntryValues(values); + mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression()); + mMessageCompression.setSummary(mMessageCompression.getEntry()); + mMessageCompression + .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mMessageCompression.setValue(newValue.toString()); + mMessageCompression.setSummary(mMessageCompression.getEntry()); + mPreferences.setDefaultMessageCompression(Integer.parseInt(newValue + .toString())); + return false; + } + }); + + mFileCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION); + mFileCompression.setEntries(entries); + mFileCompression.setEntryValues(values); + mFileCompression.setValue("" + mPreferences.getDefaultFileCompression()); + mFileCompression.setSummary(mFileCompression.getEntry()); + mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mFileCompression.setValue(newValue.toString()); + mFileCompression.setSummary(mFileCompression.getEntry()); + mPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString())); + return false; + } + }); + + mAsciiArmour = (CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR); + mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour()); + mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mAsciiArmour.setChecked((Boolean) newValue); + mPreferences.setDefaultAsciiArmour((Boolean) newValue); + return false; + } + }); + + mForceV3Signatures = (CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES); + mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures()); + mForceV3Signatures + .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mForceV3Signatures.setChecked((Boolean) newValue); + mPreferences.setForceV3Signatures((Boolean) newValue); + return false; + } + }); + + mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS); + String servers[] = mPreferences.getKeyServers(); + mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, + servers.length)); + mKeyServerPreference + .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + public boolean onPreferenceClick(Preference preference) { + Intent intent = new Intent(PreferencesActivity.this, + PreferencesKeyServerActivity.class); + intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS, + mPreferences.getKeyServers()); + startActivityForResult(intent, Id.request.key_server_preference); + return false; + } + }); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.key_server_preference: { + if (resultCode == RESULT_CANCELED || data == null) { + return; + } + String servers[] = data + .getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS); + mPreferences.setKeyServers(servers); + mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, + servers.length)); + break; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + break; + } + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: + break; + + } + return false; + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java b/APG/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java new file mode 100644 index 000000000..b54cbde71 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import java.util.Vector; + +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.ui.widget.Editor; +import org.thialfihar.android.apg.ui.widget.KeyServerEditor; +import org.thialfihar.android.apg.ui.widget.Editor.EditorListener; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.TextView; + +public class PreferencesKeyServerActivity extends SherlockActivity implements OnClickListener, + EditorListener { + + public static final String EXTRA_KEY_SERVERS = "keyServers"; + + private LayoutInflater mInflater; + private ViewGroup mEditors; + private View mAdd; + private TextView mTitle; + private TextView mSummary; + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, PreferencesActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + + return true; + + case Id.menu.option.okay: + okClicked(); + + return true; + + case Id.menu.option.cancel: + cancelClicked(); + + return true; + + default: + break; + + } + return false; + } + + /** + * ActionBar menu is created based on class variables to change it at runtime + * + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + menu.add(1, Id.menu.option.cancel, 0, android.R.string.cancel).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + menu.add(1, Id.menu.option.okay, 1, android.R.string.ok).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return true; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.key_server_preference); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + + mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + mTitle = (TextView) findViewById(R.id.title); + mSummary = (TextView) findViewById(R.id.summary); + + mTitle.setText(R.string.label_keyServers); + + mEditors = (ViewGroup) findViewById(R.id.editors); + mAdd = findViewById(R.id.add); + mAdd.setOnClickListener(this); + + Intent intent = getIntent(); + String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS); + if (servers != null) { + for (int i = 0; i < servers.length; ++i) { + KeyServerEditor view = (KeyServerEditor) mInflater.inflate( + R.layout.key_server_editor, mEditors, false); + view.setEditorListener(this); + view.setValue(servers[i]); + mEditors.addView(view); + } + } + } + + public void onDeleted(Editor editor) { + // nothing to do + } + + public void onClick(View v) { + KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, + mEditors, false); + view.setEditorListener(this); + mEditors.addView(view); + } + + private void cancelClicked() { + setResult(RESULT_CANCELED, null); + finish(); + } + + private void okClicked() { + Intent data = new Intent(); + Vector servers = new Vector(); + for (int i = 0; i < mEditors.getChildCount(); ++i) { + KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i); + String tmp = editor.getValue(); + if (tmp.length() > 0) { + servers.add(tmp); + } + } + String[] dummy = new String[0]; + data.putExtra(EXTRA_KEY_SERVERS, servers.toArray(dummy)); + setResult(RESULT_OK, data); + finish(); + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java b/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java new file mode 100644 index 000000000..0da9e0ade --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; + +public class SelectPublicKeyActivity extends SherlockFragmentActivity { + + // Not used in sourcode, but listed in AndroidManifest! + public static final String ACTION_SELECT_PUBLIC_KEYS = Constants.INTENT_PREFIX + + "SELECT_PUBLIC_KEYS"; + + public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "masterKeyIds"; + + public static final String RESULT_EXTRA_MASTER_KEY_IDS = "masterKeyIds"; + public static final String RESULT_EXTRA_USER_IDS = "userIds"; + + SelectPublicKeyFragment mSelectFragment; + + long selectedMasterKeyIds[]; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.select_public_key_activity); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + + setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); + + mSelectFragment = (SelectPublicKeyFragment) getSupportFragmentManager().findFragmentById( + R.id.select_public_key_fragment); + + // + // mFilterLayout = findViewById(R.id.layout_filter); + // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo); + // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear); + // + // mClearFilterButton.setOnClickListener(new OnClickListener() { + // public void onClick(View v) { + // handleIntent(new Intent()); + // } + // }); + + handleIntent(getIntent()); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + handleIntent(intent); + } + + private void handleIntent(Intent intent) { + // String searchString = null; + // if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + // searchString = intent.getStringExtra(SearchManager.QUERY); + // if (searchString != null && searchString.trim().length() == 0) { + // searchString = null; + // } + // } + + // if (searchString == null) { + // mFilterLayout.setVisibility(View.GONE); + // } else { + // mFilterLayout.setVisibility(View.VISIBLE); + // mFilterInfo.setText(getString(R.string.filterInfo, searchString)); + // } + + // preselected master keys + selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); + } + + /** + * returns preselected key ids, this is used in the fragment + * + * @return + */ + public long[] getSelectedMasterKeyIds() { + return selectedMasterKeyIds; + } + + private void cancelClicked() { + setResult(RESULT_CANCELED, null); + finish(); + } + + private void okClicked() { + Intent data = new Intent(); + data.putExtra(RESULT_EXTRA_MASTER_KEY_IDS, mSelectFragment.getSelectedMasterKeyIds()); + data.putExtra(RESULT_EXTRA_USER_IDS, mSelectFragment.getSelectedUserIds()); + setResult(RESULT_OK, data); + finish(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( + android.R.drawable.ic_menu_search); + menu.add(1, Id.menu.option.cancel, 0, R.string.btn_doNotSave).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(1, Id.menu.option.okay, 1, R.string.btn_okay).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + return true; + } + + /** + * Menu Options + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.okay: + okClicked(); + return true; + + case Id.menu.option.cancel: + cancelClicked(); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java b/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java new file mode 100644 index 000000000..744c3fa81 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import java.util.Date; +import java.util.Vector; + +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.provider.ApgDatabase; +import org.thialfihar.android.apg.provider.ApgContract.KeyRings; +import org.thialfihar.android.apg.provider.ApgContract.Keys; +import org.thialfihar.android.apg.provider.ApgContract.UserIds; +import org.thialfihar.android.apg.provider.ApgDatabase.Tables; +import org.thialfihar.android.apg.ui.widget.SelectKeyCursorAdapter; + +import com.actionbarsherlock.app.SherlockListFragment; + +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.app.LoaderManager; +import android.view.View; +import android.widget.ListView; + +public class SelectPublicKeyFragment extends SherlockListFragment implements + LoaderManager.LoaderCallbacks { + + private SelectPublicKeyActivity mActivity; + private SelectKeyCursorAdapter mAdapter; + private ListView mListView; + + private long mSelectedMasterKeyIds[]; + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mActivity = (SelectPublicKeyActivity) getSherlockActivity(); + mListView = getListView(); + + // get selected master key ids, which are given to activity by intent + mSelectedMasterKeyIds = mActivity.getSelectedMasterKeyIds(); + + mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText(getString(R.string.listEmpty)); + + mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.public_key); + + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + /** + * Workaround for Android 4.1. Items are not checked in layout. See + * http://code.google.com/p/android/issues/detail?id=35885 + */ + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + l.setItemChecked(position, l.isItemChecked(position)); + } + + /** + * Selects items based on master key ids in list view + * + * @param masterKeyIds + */ + private void preselectMasterKeyIds(long[] masterKeyIds) { + if (masterKeyIds != null) { + for (int i = 0; i < mListView.getCount(); ++i) { + long keyId = mAdapter.getMasterKeyId(i); + for (int j = 0; j < masterKeyIds.length; ++j) { + if (keyId == masterKeyIds[j]) { + mListView.setItemChecked(i, true); + break; + } + } + } + } + } + + /** + * Returns all selected master key ids + * + * @return + */ + public long[] getSelectedMasterKeyIds() { + // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key + // ids! + Vector vector = new Vector(); + for (int i = 0; i < mListView.getCount(); ++i) { + if (mListView.isItemChecked(i)) { + vector.add(mAdapter.getMasterKeyId(i)); + } + } + + // convert to long array + long[] selectedMasterKeyIds = new long[vector.size()]; + for (int i = 0; i < vector.size(); ++i) { + selectedMasterKeyIds[i] = vector.get(i); + } + + return selectedMasterKeyIds; + } + + /** + * Returns all selected user ids + * + * @return + */ + public String[] getSelectedUserIds() { + Vector userIds = new Vector(); + for (int i = 0; i < mListView.getCount(); ++i) { + if (mListView.isItemChecked(i)) { + userIds.add((String) mAdapter.getUserId(i)); + } + } + + // make empty array to not return null + String userIdArray[] = new String[0]; + return userIds.toArray(userIdArray); + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildPublicKeyRingsUri(); + + // These are the rows that we will retrieve. + long now = new Date().getTime() / 1000; + String[] projection = new String[] { + KeyRings._ID, + KeyRings.MASTER_KEY_ID, + UserIds.USER_ID, + "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS + + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = " + + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys." + + Keys.CAN_ENCRYPT + " = '1') AS " + + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE, + "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS + + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = " + + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys." + + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_ENCRYPT + + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " + + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY + + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; + + String inMasterKeyList = null; + if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) { + inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN ("; + for (int i = 0; i < mSelectedMasterKeyIds.length; ++i) { + if (i != 0) { + inMasterKeyList += ", "; + } + inMasterKeyList += DatabaseUtils.sqlEscapeString("" + mSelectedMasterKeyIds[i]); + } + inMasterKeyList += ")"; + } + + // if (searchString != null && searchString.trim().length() > 0) { + // String[] chunks = searchString.trim().split(" +"); + // qb.appendWhere("(EXISTS (SELECT tmp." + UserIds._ID + " FROM " + UserIds.TABLE_NAME + // + " AS tmp WHERE " + "tmp." + UserIds.KEY_ID + " = " + Keys.TABLE_NAME + "." + // + Keys._ID); + // for (int i = 0; i < chunks.length; ++i) { + // qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE "); + // qb.appendWhereEscapeString("%" + chunks[i] + "%"); + // } + // qb.appendWhere("))"); + // + // if (inIdList != null) { + // qb.appendWhere(" OR (" + inIdList + ")"); + // } + // } + + String orderBy = UserIds.USER_ID + " ASC"; + if (inMasterKeyList != null) { + // sort by selected master keys + orderBy = inMasterKeyList + " DESC, " + orderBy; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + + // preselect given master keys + preselectMasterKeyIds(mSelectedMasterKeyIds); + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java b/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java new file mode 100644 index 000000000..9efadd291 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; + +public class SelectSecretKeyActivity extends SherlockFragmentActivity { + + // Not used in sourcode, but listed in AndroidManifest! + public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX + + "SELECT_SECRET_KEY"; + + public static final String RESULT_EXTRA_MASTER_KEY_ID = "masterKeyId"; + public static final String RESULT_EXTRA_USER_ID = "userId"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.select_secret_key_activity); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + + setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); + + // mFilterLayout = findViewById(R.id.layout_filter); + // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo); + // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear); + // + // mClearFilterButton.setOnClickListener(new OnClickListener() { + // public void onClick(View v) { + // handleIntent(new Intent()); + // } + // }); + + handleIntent(getIntent()); + } + + /** + * This is executed by SelectSecretKeyFragment after clicking on an item + * + * @param masterKeyId + * @param userId + */ + public void afterListSelection(long masterKeyId, String userId) { + Intent data = new Intent(); + data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId); + data.putExtra(RESULT_EXTRA_USER_ID, (String) userId); + setResult(RESULT_OK, data); + finish(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + handleIntent(intent); + } + + private void handleIntent(Intent intent) { + // String searchString = null; + // if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + // searchString = intent.getStringExtra(SearchManager.QUERY); + // if (searchString != null && searchString.trim().length() == 0) { + // searchString = null; + // } + // } + + // if (searchString == null) { + // mFilterLayout.setVisibility(View.GONE); + // } else { + // mFilterLayout.setVisibility(View.VISIBLE); + // mFilterInfo.setText(getString(R.string.filterInfo, searchString)); + // } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( + android.R.drawable.ic_menu_search); + return true; + } + + /** + * Menu Options + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java b/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java new file mode 100644 index 000000000..1f3fcb816 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import java.util.Date; + +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.provider.ApgDatabase; +import org.thialfihar.android.apg.provider.ApgContract.KeyRings; +import org.thialfihar.android.apg.provider.ApgContract.Keys; +import org.thialfihar.android.apg.provider.ApgContract.UserIds; +import org.thialfihar.android.apg.provider.ApgDatabase.Tables; +import org.thialfihar.android.apg.ui.widget.SelectKeyCursorAdapter; + +import com.actionbarsherlock.app.SherlockListFragment; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.app.LoaderManager; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListView; + +public class SelectSecretKeyFragment extends SherlockListFragment implements + LoaderManager.LoaderCallbacks { + + private SelectSecretKeyActivity mActivity; + private SelectKeyCursorAdapter mAdapter; + private ListView mListView; + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mActivity = (SelectSecretKeyActivity) getSherlockActivity(); + mListView = getListView(); + + mListView.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long id) { + long masterKeyId = mAdapter.getMasterKeyId(position); + String userId = mAdapter.getUserId(position); + + // return data to activity, which results in finishing it + mActivity.afterListSelection(masterKeyId, userId); + } + }); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText(getString(R.string.listEmpty)); + + mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.secret_key); + + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildPublicKeyRingsUri(); + + // These are the rows that we will retrieve. + long now = new Date().getTime() / 1000; + String[] projection = new String[] { + KeyRings._ID, + KeyRings.MASTER_KEY_ID, + UserIds.USER_ID, + "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS + + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = " + + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys." + + Keys.CAN_SIGN + " = '1') AS " + + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE, + "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS + + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = " + + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys." + + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_SIGN + + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " + + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY + + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; + + // if (searchString != null && searchString.trim().length() > 0) { + // String[] chunks = searchString.trim().split(" +"); + // qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " + UserIds.TABLE_NAME + // + " AS tmp WHERE " + "tmp." + UserIds.KEY_ID + " = " + Keys.TABLE_NAME + "." + // + Keys._ID); + // for (int i = 0; i < chunks.length; ++i) { + // qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE "); + // qb.appendWhereEscapeString("%" + chunks[i] + "%"); + // } + // qb.appendWhere(")"); + // } + + String orderBy = UserIds.USER_ID + " ASC"; + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/SignKeyActivity.java b/APG/src/org/thialfihar/android/apg/ui/SignKeyActivity.java new file mode 100644 index 000000000..b970cb3d3 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/SignKeyActivity.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2011 Senecaso + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui; + +import java.util.Iterator; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.PGPMain; +import org.thialfihar.android.apg.helper.Preferences; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.service.ApgIntentService; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.service.PassphraseCacheService; +import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; + +import org.thialfihar.android.apg.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.Spinner; +import android.widget.Toast; + +/** + * gpg --sign-key + * + * signs the specified public key with the specified secret master key + */ +public class SignKeyActivity extends SherlockFragmentActivity { + + public static final String EXTRA_KEY_ID = "keyId"; + + private long mPubKeyId = 0; + private long mMasterKeyId = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // check we havent already signed it + setContentView(R.layout.sign_key_layout); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + + final Spinner keyServer = (Spinner) findViewById(R.id.keyServer); + ArrayAdapter adapter = new ArrayAdapter(this, + android.R.layout.simple_spinner_item, Preferences.getPreferences(this) + .getKeyServers()); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + keyServer.setAdapter(adapter); + + final CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey); + if (!sendKey.isChecked()) { + keyServer.setEnabled(false); + } else { + keyServer.setEnabled(true); + } + + sendKey.setOnCheckedChangeListener(new OnCheckedChangeListener() { + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (!isChecked) { + keyServer.setEnabled(false); + } else { + keyServer.setEnabled(true); + } + } + }); + + Button sign = (Button) findViewById(R.id.sign); + sign.setEnabled(false); // disabled until the user selects a key to sign with + sign.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + if (mPubKeyId != 0) { + initiateSigning(); + } + } + }); + + mPubKeyId = getIntent().getLongExtra(EXTRA_KEY_ID, 0); + if (mPubKeyId == 0) { + finish(); // nothing to do if we dont know what key to sign + } else { + // kick off the SecretKey selection activity so the user chooses which key to sign with + // first + Intent intent = new Intent(this, SelectSecretKeyActivity.class); + startActivityForResult(intent, Id.request.secret_keys); + } + } + + private void showPassphraseDialog(final long secretKeyId) { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + startSigning(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + try { + PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, + messenger, secretKeyId); + + passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); + } catch (PGPMain.ApgGeneralException e) { + Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); + // send message to handler to start encryption directly + returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); + } + } + + /** + * handles the UI bits of the signing process on the UI thread + */ + private void initiateSigning() { + PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, mPubKeyId); + if (pubring != null) { + // if we have already signed this key, dont bother doing it again + boolean alreadySigned = false; + + @SuppressWarnings("unchecked") + Iterator itr = pubring.getPublicKey(mPubKeyId).getSignatures(); + while (itr.hasNext()) { + PGPSignature sig = itr.next(); + if (sig.getKeyID() == mMasterKeyId) { + alreadySigned = true; + break; + } + } + + if (!alreadySigned) { + /* + * get the user's passphrase for this key (if required) + */ + String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId); + if (passphrase == null) { + showPassphraseDialog(mMasterKeyId); + return; // bail out; need to wait until the user has entered the passphrase + // before trying again + } else { + startSigning(); + } + } else { + Toast.makeText(this, "Key has already been signed", Toast.LENGTH_SHORT).show(); + + setResult(RESULT_CANCELED); + finish(); + } + } + } + + /** + * kicks off the actual signing process on a background thread + */ + private void startSigning() { + // Send all information needed to service to sign key in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_SIGN_KEY); + + // fill values for this action + Bundle data = new Bundle(); + + data.putLong(ApgIntentService.SIGN_KEY_MASTER_KEY_ID, mMasterKeyId); + data.putLong(ApgIntentService.SIGN_KEY_PUB_KEY_ID, mPubKeyId); + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after signing is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_signing, + ProgressDialog.STYLE_SPINNER) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + + Toast.makeText(SignKeyActivity.this, R.string.keySignSuccess, + Toast.LENGTH_SHORT).show(); + + // check if we need to send the key to the server or not + CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey); + if (sendKey.isChecked()) { + /* + * upload the newly signed key to the key server + */ + uploadKey(); + } else { + finish(); + } + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + private void uploadKey() { + // Send all information needed to service to upload key in other thread + Intent intent = new Intent(this, ApgIntentService.class); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_UPLOAD_KEY); + + // fill values for this action + Bundle data = new Bundle(); + + data.putLong(ApgIntentService.UPLOAD_KEY_KEYRING_ROW_ID, mPubKeyId); + + Spinner keyServer = (Spinner) findViewById(R.id.keyServer); + String server = (String) keyServer.getSelectedItem(); + data.putString(ApgIntentService.UPLOAD_KEY_SERVER, server); + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // Message is received after uploading is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_exporting, + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + + Toast.makeText(SignKeyActivity.this, R.string.keySendSuccess, + Toast.LENGTH_SHORT).show(); + + finish(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.secret_keys: { + if (resultCode == RESULT_OK) { + mMasterKeyId = data.getLongExtra(EXTRA_KEY_ID, 0); + + // re-enable the sign button so the user can initiate the sign process + Button sign = (Button) findViewById(R.id.sign); + sign.setEnabled(true); + } + + break; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + } + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java new file mode 100644 index 000000000..311066389 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.dialog; + +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.service.ApgIntentService; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.widget.Toast; + +public class DeleteFileDialogFragment extends DialogFragment { + + private static final String ARG_DELETE_FILE = "delete_file"; + + /** + * Creates new instance of this delete file dialog fragment + */ + public static DeleteFileDialogFragment newInstance(String deleteFile) { + DeleteFileDialogFragment frag = new DeleteFileDialogFragment(); + Bundle args = new Bundle(); + + args.putString(ARG_DELETE_FILE, deleteFile); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final FragmentActivity activity = getActivity(); + + final String deleteFile = getArguments().getString(ARG_DELETE_FILE); + + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + + alert.setIcon(android.R.drawable.ic_dialog_alert); + alert.setTitle(R.string.warning); + alert.setMessage(this.getString(R.string.fileDeleteConfirmation, deleteFile)); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(activity, ApgIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_DELETE_FILE_SECURELY); + data.putString(ApgIntentService.DELETE_FILE, deleteFile); + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance( + R.string.progress_deletingSecurely, ProgressDialog.STYLE_HORIZONTAL); + + // Message is received after deleting is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(activity, deletingDialog) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + Toast.makeText(activity, R.string.fileDeleteSuccessful, + Toast.LENGTH_SHORT).show(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + deletingDialog.show(activity.getSupportFragmentManager(), "deletingDialog"); + + // start service with intent + activity.startService(intent); + } + }); + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + alert.setCancelable(true); + + return alert.create(); + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java new file mode 100644 index 000000000..bb5271ce7 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.dialog; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.util.Log; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; + +public class DeleteKeyDialogFragment extends DialogFragment { + + private Messenger mMessenger; + + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_DELETE_KEY_RING_ROW_ID = "delete_file"; + private static final String ARG_KEY_TYPE = "key_type"; + + public static final int MESSAGE_OKAY = 1; + + /** + * Creates new instance of this delete file dialog fragment + */ + public static DeleteKeyDialogFragment newInstance(Messenger messenger, long deleteKeyRingRowId, + int keyType) { + DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment(); + Bundle args = new Bundle(); + + args.putParcelable(ARG_MESSENGER, messenger); + args.putLong(ARG_DELETE_KEY_RING_ROW_ID, deleteKeyRingRowId); + args.putInt(ARG_KEY_TYPE, keyType); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final FragmentActivity activity = getActivity(); + + final long deleteKeyRingRowId = getArguments().getLong(ARG_DELETE_KEY_RING_ROW_ID); + final int keyType = getArguments().getInt(ARG_KEY_TYPE); + + // TODO: better way to do this? + String userId = activity.getString(R.string.unknownUserId); + + if (keyType == Id.type.public_key) { + PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity, + deleteKeyRingRowId); + userId = PGPHelper.getMainUserIdSafe(activity, PGPHelper.getMasterKey(keyRing)); + } else { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity, + deleteKeyRingRowId); + userId = PGPHelper.getMainUserIdSafe(activity, PGPHelper.getMasterKey(keyRing)); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(R.string.warning); + builder.setMessage(getString( + keyType == 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) { + if (keyType == Id.type.public_key) { + ProviderHelper.deletePublicKeyRing(activity, deleteKeyRingRowId); + } else { + ProviderHelper.deleteSecretKeyRing(activity, deleteKeyRingRowId); + } + + dismiss(); + + sendMessageToHandler(MESSAGE_OKAY); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + return builder.create(); + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what) { + Message msg = Message.obtain(); + msg.what = what; + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java new file mode 100644 index 000000000..7786d9228 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.dialog; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.FileHelper; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; +import org.thialfihar.android.apg.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageButton; + +public class FileDialogFragment extends DialogFragment { + + private Messenger mMessenger; + + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_TITLE = "title"; + private static final String ARG_MESSAGE = "message"; + private static final String ARG_DEFAULT_FILE = "default_file"; + private static final String ARG_CHECKBOX_TEXT = "checkbox_text"; + private static final String ARG_REQUEST_CODE = "request_code"; + + public static final int MESSAGE_OKAY = 1; + + public static final String MESSAGE_DATA_FILENAME = "filename"; + public static final String MESSAGE_DATA_CHECKED = "checked"; + + /** + * Creates new instance of this file dialog fragment + */ + public static FileDialogFragment newInstance(Messenger messenger, String title, String message, + String defaultFile, String checkboxText, int requestCode) { + FileDialogFragment frag = new FileDialogFragment(); + Bundle args = new Bundle(); + args.putParcelable(ARG_MESSENGER, messenger); + + args.putString(ARG_TITLE, title); + args.putString(ARG_MESSAGE, message); + args.putString(ARG_DEFAULT_FILE, defaultFile); + args.putString(ARG_CHECKBOX_TEXT, checkboxText); + args.putInt(ARG_REQUEST_CODE, requestCode); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + String title = getArguments().getString(ARG_TITLE); + String message = getArguments().getString(ARG_MESSAGE); + String defaultFile = getArguments().getString(ARG_DEFAULT_FILE); + String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT); + final int requestCode = getArguments().getInt(ARG_REQUEST_CODE); + + final EditText mFilename; + final ImageButton mBrowse; + final CheckBox mCheckBox; + + LayoutInflater inflater = (LayoutInflater) activity + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + + alert.setTitle(title); + alert.setMessage(message); + + View view = inflater.inflate(R.layout.file_dialog, null); + + mFilename = (EditText) view.findViewById(R.id.input); + mFilename.setText(defaultFile); + mBrowse = (ImageButton) view.findViewById(R.id.btn_browse); + mBrowse.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + // only .asc or .gpg files + FileHelper.openFile(activity, mFilename.getText().toString(), "text/plain", requestCode); + } + }); + + mCheckBox = (CheckBox) view.findViewById(R.id.checkbox); + if (checkboxText == null) { + mCheckBox.setEnabled(false); + mCheckBox.setVisibility(View.GONE); + } else { + mCheckBox.setEnabled(true); + mCheckBox.setVisibility(View.VISIBLE); + mCheckBox.setText(checkboxText); + } + + alert.setView(view); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + boolean checked = false; + if (mCheckBox.isEnabled()) { + checked = mCheckBox.isChecked(); + } + + // return resulting data back to activity + Bundle data = new Bundle(); + data.putString(MESSAGE_DATA_FILENAME, mFilename.getText().toString()); + data.putBoolean(MESSAGE_DATA_CHECKED, checked); + + sendMessageToHandler(MESSAGE_OKAY, data); + + dismiss(); + } + }); + + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + return alert.create(); + } + + /** + * Updates filename in dialog, normally called in onActivityResult in activity using the + * FileDialog + * + * @param messageId + * @param progress + * @param max + */ + public void setFilename(String filename) { + AlertDialog dialog = (AlertDialog) getDialog(); + EditText filenameEditText = (EditText) dialog.findViewById(R.id.input); + + if (filenameEditText != null) { + filenameEditText.setText(filename); + } + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what, Bundle data) { + Message msg = Message.obtain(); + msg.what = what; + if (data != null) { + msg.setData(data); + } + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java new file mode 100644 index 000000000..9797abc2a --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.dialog; + +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; + +import org.thialfihar.android.apg.ui.KeyServerQueryActivity; +import org.thialfihar.android.apg.util.Log; + +public class LookupUnknownKeyDialogFragment extends DialogFragment { + + private Messenger mMessenger; + + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_UNKNOWN_KEY_ID = "unknown_key_id"; + + public static final int MESSAGE_OKAY = 1; + public static final int MESSAGE_CANCEL = 2; + + /** + * Creates new instance of this dialog fragment + * + * @param messenger + * @param unknownKeyId + * @return + */ + public static LookupUnknownKeyDialogFragment newInstance(Messenger messenger, long unknownKeyId) { + LookupUnknownKeyDialogFragment frag = new LookupUnknownKeyDialogFragment(); + Bundle args = new Bundle(); + args.putLong(ARG_UNKNOWN_KEY_ID, unknownKeyId); + args.putParcelable(ARG_MESSENGER, messenger); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + final long unknownKeyId = getArguments().getLong(ARG_UNKNOWN_KEY_ID); + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + + alert.setIcon(android.R.drawable.ic_dialog_alert); + alert.setTitle(R.string.title_unknownSignatureKey); + alert.setMessage(getString(R.string.lookupUnknownKey, + PGPHelper.getSmallFingerPrint(unknownKeyId))); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + sendMessageToHandler(MESSAGE_OKAY); + + Intent intent = new Intent(activity, KeyServerQueryActivity.class); + intent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID); + intent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, unknownKeyId); + startActivityForResult(intent, Id.request.look_up_key_id); + } + }); + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + sendMessageToHandler(MESSAGE_CANCEL); + } + }); + alert.setCancelable(true); + alert.setOnCancelListener(new OnCancelListener() { + + @Override + public void onCancel(DialogInterface dialog) { + sendMessageToHandler(MESSAGE_CANCEL); + } + }); + + return alert.create(); + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what) { + Message msg = Message.obtain(); + msg.what = what; + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java new file mode 100644 index 000000000..fb8f4b6d1 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.dialog; + +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.helper.PGPMain; +import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; + +import org.thialfihar.android.apg.provider.ProviderHelper; +import org.thialfihar.android.apg.service.PassphraseCacheService; +import org.thialfihar.android.apg.util.Log; + +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; +import android.widget.Toast; + +public class PassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { + + private Messenger mMessenger; + + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_SECRET_KEY_ID = "secret_key_id"; + + public static final int MESSAGE_OKAY = 1; + + private EditText mPassphraseEditText; + + /** + * Creates new instance of this dialog fragment + * + * @param secretKeyId + * secret key id you want to use + * @param messenger + * to communicate back after caching the passphrase + * @return + * @throws ApgGeneralException + */ + public static PassphraseDialogFragment newInstance(Context context, Messenger messenger, + long secretKeyId) throws ApgGeneralException { + // check if secret key has a passphrase + if (!(secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none)) { + if (!hasPassphrase(context, secretKeyId)) { + throw new PGPMain.ApgGeneralException("No passphrase! No passphrase dialog needed!"); + } + } + + PassphraseDialogFragment frag = new PassphraseDialogFragment(); + Bundle args = new Bundle(); + args.putLong(ARG_SECRET_KEY_ID, secretKeyId); + args.putParcelable(ARG_MESSENGER, messenger); + + frag.setArguments(args); + + return frag; + } + + /** + * Checks if key has a passphrase + * + * @param secretKeyId + * @return true if it has a passphrase + */ + private static boolean hasPassphrase(Context context, long secretKeyId) { + // check if the key has no passphrase + try { + PGPSecretKey secretKey = PGPHelper.getMasterKey(ProviderHelper + .getPGPSecretKeyRingByKeyId(context, secretKeyId)); + // PGPSecretKey secretKey = + // PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); + + Log.d(Constants.TAG, "Check if key has no passphrase..."); + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + "SC").build("".toCharArray()); + PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); + if (testKey != null) { + Log.d(Constants.TAG, "Key has no passphrase! Caches empty passphrase!"); + + // cache empty passphrase + PassphraseCacheService.addCachedPassphrase(context, secretKey.getKeyID(), ""); + + return false; + } + } catch (PGPException e) { + // silently catch + } + + return true; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + long secretKeyId = getArguments().getLong(ARG_SECRET_KEY_ID); + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + + alert.setTitle(R.string.title_authentication); + + final PGPSecretKey secretKey; + + if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) { + secretKey = null; + alert.setMessage(R.string.passPhraseForSymmetricEncryption); + } else { + // TODO: by master key id??? + secretKey = PGPHelper.getMasterKey(ProviderHelper.getPGPSecretKeyRingByMasterKeyId( + activity, secretKeyId)); + // secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); + + if (secretKey == null) { + alert.setTitle(R.string.title_keyNotFound); + alert.setMessage(getString(R.string.keyNotFound, secretKeyId)); + alert.setPositiveButton(android.R.string.ok, new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dismiss(); + } + }); + alert.setCancelable(false); + return alert.create(); + } + String userId = PGPHelper.getMainUserIdSafe(activity, secretKey); + + Log.d(Constants.TAG, "User id: '" + userId + "'"); + alert.setMessage(getString(R.string.passPhraseFor, userId)); + } + + LayoutInflater inflater = activity.getLayoutInflater(); + View view = inflater.inflate(R.layout.passphrase, null); + alert.setView(view); + + mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + String passPhrase = mPassphraseEditText.getText().toString(); + long keyId; + if (secretKey != null) { + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(PGPMain.BOUNCY_CASTLE_PROVIDER_NAME).build( + passPhrase.toCharArray()); + PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); + if (testKey == null) { + Toast.makeText(activity, R.string.error_couldNotExtractPrivateKey, + Toast.LENGTH_SHORT).show(); + return; + } + } catch (PGPException e) { + Toast.makeText(activity, R.string.wrongPassPhrase, Toast.LENGTH_SHORT) + .show(); + return; + } + keyId = secretKey.getKeyID(); + } else { + keyId = Id.key.symmetric; + } + + // cache the new passphrase + Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); + PassphraseCacheService.addCachedPassphrase(activity, keyId, passPhrase); + + sendMessageToHandler(MESSAGE_OKAY); + } + }); + + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + + return alert.create(); + } + + @Override + public void onActivityCreated(Bundle arg0) { + super.onActivityCreated(arg0); + + // request focus and open soft keyboard + mPassphraseEditText.requestFocus(); + getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); + + mPassphraseEditText.setOnEditorActionListener(this); + } + + /** + * Associate the "done" button on the soft keyboard with the okay button in the view + */ + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (EditorInfo.IME_ACTION_DONE == actionId) { + AlertDialog dialog = ((AlertDialog) getDialog()); + Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + + bt.performClick(); + return true; + } + return false; + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what) { + Message msg = Message.obtain(); + msg.what = what; + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } + +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java new file mode 100644 index 000000000..2fb3b3256 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.dialog; + +import android.app.Activity; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnKeyListener; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.view.KeyEvent; + +public class ProgressDialogFragment extends DialogFragment { + + private static final String ARG_MESSAGE_ID = "message_id"; + private static final String ARG_STYLE = "style"; + + /** + * Creates new instance of this fragment + * + * @param id + * @return + */ + public static ProgressDialogFragment newInstance(int messageId, int style) { + ProgressDialogFragment frag = new ProgressDialogFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_MESSAGE_ID, messageId); + args.putInt(ARG_STYLE, style); + + frag.setArguments(args); + return frag; + } + + /** + * Updates progress of dialog + * + * @param messageId + * @param progress + * @param max + */ + public void setProgress(int messageId, int progress, int max) { + setProgress(getString(messageId), progress, max); + } + + /** + * Updates progress of dialog + * + * @param messageId + * @param progress + * @param max + */ + public void setProgress(int progress, int max) { + ProgressDialog dialog = (ProgressDialog) getDialog(); + + dialog.setProgress(progress); + dialog.setMax(max); + } + + /** + * Updates progress of dialog + * + * @param messageId + * @param progress + * @param max + */ + public void setProgress(String message, int progress, int max) { + ProgressDialog dialog = (ProgressDialog) getDialog(); + + dialog.setMessage(message); + dialog.setProgress(progress); + dialog.setMax(max); + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Activity activity = getActivity(); + + ProgressDialog dialog = new ProgressDialog(activity); + dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + dialog.setCancelable(false); + dialog.setCanceledOnTouchOutside(false); + + int messageId = getArguments().getInt(ARG_MESSAGE_ID); + int style = getArguments().getInt(ARG_STYLE); + + dialog.setMessage(getString(messageId)); + dialog.setProgressStyle(style); + + // Disable the back button + OnKeyListener keyListener = new OnKeyListener() { + + @Override + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + + if (keyCode == KeyEvent.KEYCODE_BACK) { + return true; + } + return false; + } + + }; + dialog.setOnKeyListener(keyListener); + + return dialog; + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java new file mode 100644 index 000000000..b23101a0a --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.dialog; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; +import org.thialfihar.android.apg.util.Log; + +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.TextView.OnEditorActionListener; + +public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { + private Messenger mMessenger; + + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_TITLE = "title"; + + public static final int MESSAGE_OKAY = 1; + + public static final String MESSAGE_NEW_PASSPHRASE = "new_passphrase"; + + private EditText mPassphraseEditText; + private EditText mPassphraseAgainEditText; + + /** + * Creates new instance of this dialog fragment + * + * @param title + * title of dialog + * @param messenger + * to communicate back after setting the passphrase + * @return + */ + public static SetPassphraseDialogFragment newInstance(Messenger messenger, int title) { + SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_TITLE, title); + args.putParcelable(ARG_MESSENGER, messenger); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + int title = getArguments().getInt(ARG_TITLE); + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + + alert.setTitle(title); + alert.setMessage(R.string.enterPassPhraseTwice); + + LayoutInflater inflater = activity.getLayoutInflater(); + View view = inflater.inflate(R.layout.passphrase_repeat, null); + alert.setView(view); + + mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); + mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + String passPhrase1 = mPassphraseEditText.getText().toString(); + String passPhrase2 = mPassphraseAgainEditText.getText().toString(); + if (!passPhrase1.equals(passPhrase2)) { + Toast.makeText( + activity, + getString(R.string.errorMessage, + getString(R.string.passPhrasesDoNotMatch)), Toast.LENGTH_SHORT) + .show(); + return; + } + + if (passPhrase1.equals("")) { + Toast.makeText( + activity, + getString(R.string.errorMessage, + getString(R.string.passPhraseMustNotBeEmpty)), + Toast.LENGTH_SHORT).show(); + return; + } + + // return resulting data back to activity + Bundle data = new Bundle(); + data.putString(MESSAGE_NEW_PASSPHRASE, passPhrase1); + + sendMessageToHandler(MESSAGE_OKAY, data); + } + }); + + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + + return alert.create(); + } + + @Override + public void onActivityCreated(Bundle arg0) { + super.onActivityCreated(arg0); + + // request focus and open soft keyboard + mPassphraseEditText.requestFocus(); + getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); + + mPassphraseAgainEditText.setOnEditorActionListener(this); + } + + /** + * Associate the "done" button on the soft keyboard with the okay button in the view + */ + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (EditorInfo.IME_ACTION_DONE == actionId) { + AlertDialog dialog = ((AlertDialog) getDialog()); + Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + + bt.performClick(); + return true; + } + return false; + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what, Bundle data) { + Message msg = Message.obtain(); + msg.what = what; + if (data != null) { + msg.setData(data); + } + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java b/APG/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java new file mode 100644 index 000000000..8a516ad41 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java @@ -0,0 +1,186 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +/** + * Custom layout that arranges children in a grid-like manner, optimizing for even horizontal and + * vertical whitespace. + */ +public class DashboardLayout extends ViewGroup { + private static final int UNEVEN_GRID_PENALTY_MULTIPLIER = 10; + + private int mMaxChildWidth = 0; + private int mMaxChildHeight = 0; + + public DashboardLayout(Context context) { + super(context, null); + } + + public DashboardLayout(Context context, AttributeSet attrs) { + super(context, attrs, 0); + } + + public DashboardLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + mMaxChildWidth = 0; + mMaxChildHeight = 0; + + // Measure once to find the maximum child size. + + int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST); + int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST); + + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + + mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth()); + mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight()); + } + + // Measure again for each child to be exactly the same size. + + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildWidth, MeasureSpec.EXACTLY); + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildHeight, MeasureSpec.EXACTLY); + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec), + resolveSize(mMaxChildHeight, heightMeasureSpec)); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int width = r - l; + int height = b - t; + + final int count = getChildCount(); + + // Calculate the number of visible children. + int visibleCount = 0; + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + ++visibleCount; + } + + if (visibleCount == 0) { + return; + } + + // Calculate what number of rows and columns will optimize for even horizontal and + // vertical whitespace between items. Start with a 1 x N grid, then try 2 x N, and so on. + int bestSpaceDifference = Integer.MAX_VALUE; + int spaceDifference; + + // Horizontal and vertical space between items + int hSpace = 0; + int vSpace = 0; + + int cols = 1; + int rows; + + while (true) { + rows = (visibleCount - 1) / cols + 1; + + hSpace = ((width - mMaxChildWidth * cols) / (cols + 1)); + vSpace = ((height - mMaxChildHeight * rows) / (rows + 1)); + + spaceDifference = Math.abs(vSpace - hSpace); + if (rows * cols != visibleCount) { + spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER; + } else if (rows * mMaxChildHeight > height || cols * mMaxChildWidth > width) { + spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER; + } + + if (spaceDifference < bestSpaceDifference) { + // Found a better whitespace squareness/ratio + bestSpaceDifference = spaceDifference; + + // If we found a better whitespace squareness and there's only 1 row, this is + // the best we can do. + if (rows == 1) { + break; + } + } else { + // This is a worse whitespace ratio, use the previous value of cols and exit. + --cols; + rows = (visibleCount - 1) / cols + 1; + hSpace = ((width - mMaxChildWidth * cols) / (cols + 1)); + vSpace = ((height - mMaxChildHeight * rows) / (rows + 1)); + break; + } + + ++cols; + } + + // Lay out children based on calculated best-fit number of rows and cols. + + // If we chose a layout that has negative horizontal or vertical space, force it to zero. + hSpace = Math.max(0, hSpace); + vSpace = Math.max(0, vSpace); + + // Re-use width/height variables to be child width/height. + width = (width - hSpace * (cols + 1)) / cols; + height = (height - vSpace * (rows + 1)) / rows; + + int left, top; + int col, row; + int visibleIndex = 0; + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + + row = visibleIndex / cols; + col = visibleIndex % cols; + + left = hSpace * (col + 1) + width * col; + top = vSpace * (row + 1) + height * row; + + child.layout(left, top, (hSpace == 0 && col == cols - 1) ? r : (left + width), + (vSpace == 0 && row == rows - 1) ? b : (top + height)); + ++visibleIndex; + } + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/Editor.java b/APG/src/org/thialfihar/android/apg/ui/widget/Editor.java new file mode 100644 index 000000000..a7200fad0 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/Editor.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +public interface Editor { + public interface EditorListener { + public void onDeleted(Editor editor); + } + + public void setEditorListener(EditorListener listener); +} diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java b/APG/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java new file mode 100644 index 000000000..01488909f --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.Fragment; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnCreateContextMenuListener; +import android.view.ViewGroup; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.ExpandableListAdapter; +import android.widget.ExpandableListView; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.ProgressBar; +import android.widget.TextView; + +/** + * @author Khoa Tran + * + * @see android.support.v4.app.ListFragment + * @see android.app.ExpandableListActivity + * + * ExpandableListFragment for Android < 3.0 + * + * from + * http://stackoverflow.com/questions/6051050/expandablelistfragment-with-loadermanager-for- + * compatibility-package + * + */ +public class ExpandableListFragment extends Fragment implements OnCreateContextMenuListener, + ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupCollapseListener, + ExpandableListView.OnGroupExpandListener { + + static final int INTERNAL_EMPTY_ID = 0x00ff0001; + static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002; + static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003; + + final private Handler mHandler = new Handler(); + + final private Runnable mRequestFocus = new Runnable() { + public void run() { + mExpandableList.focusableViewAvailable(mExpandableList); + } + }; + + final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { + public void onItemClick(AdapterView parent, View v, int position, long id) { + onListItemClick((ExpandableListView) parent, v, position, id); + } + }; + + ExpandableListAdapter mAdapter; + ExpandableListView mExpandableList; + boolean mFinishedStart = false; + View mEmptyView; + TextView mStandardEmptyView; + View mProgressContainer; + View mExpandableListContainer; + CharSequence mEmptyText; + boolean mExpandableListShown; + + public ExpandableListFragment() { + } + + /** + * Provide default implementation to return a simple list view. Subclasses can override to + * replace with their own layout. If doing so, the returned view hierarchy must have a + * ListView whose id is {@link android.R.id#list android.R.id.list} and can optionally have a + * sibling view id {@link android.R.id#empty android.R.id.empty} that is to be shown when the + * list is empty. + * + *

+ * If you are overriding this method with your own custom content, consider including the + * standard layout {@link android.R.layout#list_content} in your layout file, so that you + * continue to retain all of the standard behavior of ListFragment. In particular, this is + * currently the only way to have the built-in indeterminant progress state be shown. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final Context context = getActivity(); + + FrameLayout root = new FrameLayout(context); + + // ------------------------------------------------------------------ + + LinearLayout pframe = new LinearLayout(context); + pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); + pframe.setOrientation(LinearLayout.VERTICAL); + pframe.setVisibility(View.GONE); + pframe.setGravity(Gravity.CENTER); + + ProgressBar progress = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge); + pframe.addView(progress, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + + root.addView(pframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + // ------------------------------------------------------------------ + + FrameLayout lframe = new FrameLayout(context); + lframe.setId(INTERNAL_LIST_CONTAINER_ID); + + TextView tv = new TextView(getActivity()); + tv.setId(INTERNAL_EMPTY_ID); + tv.setGravity(Gravity.CENTER); + lframe.addView(tv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + ExpandableListView lv = new ExpandableListView(getActivity()); + lv.setId(android.R.id.list); + lv.setDrawSelectorOnTop(false); + lframe.addView(lv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + root.addView(lframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + // ------------------------------------------------------------------ + + root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + return root; + } + + /** + * Attach to list view once the view hierarchy has been created. + */ + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + ensureList(); + } + + /** + * Detach from list view. + */ + @Override + public void onDestroyView() { + mHandler.removeCallbacks(mRequestFocus); + mExpandableList = null; + mExpandableListShown = false; + mEmptyView = mProgressContainer = mExpandableListContainer = null; + mStandardEmptyView = null; + super.onDestroyView(); + } + + /** + * This method will be called when an item in the list is selected. Subclasses should override. + * Subclasses can call getListView().getItemAtPosition(position) if they need to access the data + * associated with the selected item. + * + * @param l + * The ListView where the click happened + * @param v + * The view that was clicked within the ListView + * @param position + * The position of the view in the list + * @param id + * The row id of the item that was clicked + */ + public void onListItemClick(ExpandableListView l, View v, int position, long id) { + } + + /** + * Provide the cursor for the list view. + */ + public void setListAdapter(ExpandableListAdapter adapter) { + boolean hadAdapter = mAdapter != null; + mAdapter = adapter; + if (mExpandableList != null) { + mExpandableList.setAdapter(adapter); + if (!mExpandableListShown && !hadAdapter) { + // The list was hidden, and previously didn't have an + // adapter. It is now time to show it. + setListShown(true, getView().getWindowToken() != null); + } + } + } + + /** + * Set the currently selected list item to the specified position with the adapter's data + * + * @param position + */ + public void setSelection(int position) { + ensureList(); + mExpandableList.setSelection(position); + } + + /** + * Get the position of the currently selected list item. + */ + public int getSelectedItemPosition() { + ensureList(); + return mExpandableList.getSelectedItemPosition(); + } + + /** + * Get the cursor row ID of the currently selected list item. + */ + public long getSelectedItemId() { + ensureList(); + return mExpandableList.getSelectedItemId(); + } + + /** + * Get the activity's list view widget. + */ + public ExpandableListView getListView() { + ensureList(); + return mExpandableList; + } + + /** + * The default content for a ListFragment has a TextView that can be shown when the list is + * empty. If you would like to have it shown, call this method to supply the text it should use. + */ + public void setEmptyText(CharSequence text) { + ensureList(); + if (mStandardEmptyView == null) { + throw new IllegalStateException("Can't be used with a custom content view"); + } + mStandardEmptyView.setText(text); + if (mEmptyText == null) { + mExpandableList.setEmptyView(mStandardEmptyView); + } + mEmptyText = text; + } + + /** + * Control whether the list is being displayed. You can make it not displayed if you are waiting + * for the initial data to show in it. During this time an indeterminant progress indicator will + * be shown instead. + * + *

+ * Applications do not normally need to use this themselves. The default behavior of + * ListFragment is to start with the list not being shown, only showing it once an adapter is + * given with {@link #setListAdapter(ListAdapter)}. If the list at that point had not been + * shown, when it does get shown it will be do without the user ever seeing the hidden state. + * + * @param shown + * If true, the list view is shown; if false, the progress indicator. The initial + * value is true. + */ + public void setListShown(boolean shown) { + setListShown(shown, true); + } + + /** + * Like {@link #setListShown(boolean)}, but no animation is used when transitioning from the + * previous state. + */ + public void setListShownNoAnimation(boolean shown) { + setListShown(shown, false); + } + + /** + * Control whether the list is being displayed. You can make it not displayed if you are waiting + * for the initial data to show in it. During this time an indeterminant progress indicator will + * be shown instead. + * + * @param shown + * If true, the list view is shown; if false, the progress indicator. The initial + * value is true. + * @param animate + * If true, an animation will be used to transition to the new state. + */ + private void setListShown(boolean shown, boolean animate) { + ensureList(); + if (mProgressContainer == null) { + throw new IllegalStateException("Can't be used with a custom content view"); + } + if (mExpandableListShown == shown) { + return; + } + mExpandableListShown = shown; + if (shown) { + if (animate) { + mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), + android.R.anim.fade_out)); + mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), + android.R.anim.fade_in)); + } else { + mProgressContainer.clearAnimation(); + mExpandableListContainer.clearAnimation(); + } + mProgressContainer.setVisibility(View.GONE); + mExpandableListContainer.setVisibility(View.VISIBLE); + } else { + if (animate) { + mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), + android.R.anim.fade_in)); + mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), + android.R.anim.fade_out)); + } else { + mProgressContainer.clearAnimation(); + mExpandableListContainer.clearAnimation(); + } + mProgressContainer.setVisibility(View.VISIBLE); + mExpandableListContainer.setVisibility(View.GONE); + } + } + + /** + * Get the ListAdapter associated with this activity's ListView. + */ + public ExpandableListAdapter getListAdapter() { + return mAdapter; + } + + private void ensureList() { + if (mExpandableList != null) { + return; + } + View root = getView(); + if (root == null) { + throw new IllegalStateException("Content view not yet created"); + } + if (root instanceof ExpandableListView) { + mExpandableList = (ExpandableListView) root; + } else { + mStandardEmptyView = (TextView) root.findViewById(INTERNAL_EMPTY_ID); + if (mStandardEmptyView == null) { + mEmptyView = root.findViewById(android.R.id.empty); + } else { + mStandardEmptyView.setVisibility(View.GONE); + } + mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID); + mExpandableListContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID); + View rawExpandableListView = root.findViewById(android.R.id.list); + if (!(rawExpandableListView instanceof ExpandableListView)) { + if (rawExpandableListView == null) { + throw new RuntimeException( + "Your content must have a ListView whose id attribute is " + + "'android.R.id.list'"); + } + throw new RuntimeException( + "Content has view with id attribute 'android.R.id.list' " + + "that is not a ListView class"); + } + mExpandableList = (ExpandableListView) rawExpandableListView; + if (mEmptyView != null) { + mExpandableList.setEmptyView(mEmptyView); + } else if (mEmptyText != null) { + mStandardEmptyView.setText(mEmptyText); + mExpandableList.setEmptyView(mStandardEmptyView); + } + } + mExpandableListShown = true; + mExpandableList.setOnItemClickListener(mOnClickListener); + if (mAdapter != null) { + ExpandableListAdapter adapter = mAdapter; + mAdapter = null; + setListAdapter(adapter); + } else { + // We are starting without an adapter, so assume we won't + // have our data right away and start with the progress indicator. + if (mProgressContainer != null) { + setListShown(false, false); + } + } + mHandler.post(mRequestFocus); + } + + /** + * Override this to populate the context menu when an item is long pressed. menuInfo will + * contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo} whose + * packedPosition is a packed position that should be used with + * {@link ExpandableListView#getPackedPositionType(long)} and the other similar methods. + *

+ * {@inheritDoc} + */ + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + } + + /** + * Override this for receiving callbacks when a child has been clicked. + *

+ * {@inheritDoc} + */ + public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, + int childPosition, long id) { + return false; + } + + /** + * Override this for receiving callbacks when a group has been collapsed. + */ + public void onGroupCollapse(int groupPosition) { + } + + /** + * Override this for receiving callbacks when a group has been expanded. + */ + public void onGroupExpand(int groupPosition) { + } + + // /** + // * Ensures the expandable list view has been created before Activity restores all + // * of the view states. + // * + // *@see Activity#onRestoreInstanceState(Bundle) + // */ + // @Override + // protected void onRestoreInstanceState(Bundle state) { + // ensureList(); + // super.onRestoreInstanceState(state); + // } + + /** + * Updates the screen state (current list and other views) when the content changes. + * + * @see Activity#onContentChanged() + */ + + public void onContentChanged() { + // super.onContentChanged(); + View emptyView = getView().findViewById(android.R.id.empty); + mExpandableList = (ExpandableListView) getView().findViewById(android.R.id.list); + if (mExpandableList == null) { + throw new RuntimeException( + "Your content must have a ExpandableListView whose id attribute is " + + "'android.R.id.list'"); + } + if (emptyView != null) { + mExpandableList.setEmptyView(emptyView); + } + mExpandableList.setOnChildClickListener(this); + mExpandableList.setOnGroupExpandListener(this); + mExpandableList.setOnGroupCollapseListener(this); + + if (mFinishedStart) { + setListAdapter(mAdapter); + } + mFinishedStart = true; + } + + /** + * Get the activity's expandable list view widget. This can be used to get the selection, set + * the selection, and many other useful functions. + * + * @see ExpandableListView + */ + public ExpandableListView getExpandableListView() { + ensureList(); + return mExpandableList; + } + + /** + * Get the ExpandableListAdapter associated with this activity's ExpandableListView. + */ + public ExpandableListAdapter getExpandableListAdapter() { + return mAdapter; + } + + /** + * Gets the ID of the currently selected group or child. + * + * @return The ID of the currently selected group or child. + */ + public long getSelectedId() { + return mExpandableList.getSelectedId(); + } + + /** + * Gets the position (in packed position representation) of the currently selected group or + * child. Use {@link ExpandableListView#getPackedPositionType}, + * {@link ExpandableListView#getPackedPositionGroup}, and + * {@link ExpandableListView#getPackedPositionChild} to unpack the returned packed position. + * + * @return A packed position representation containing the currently selected group or child's + * position and type. + */ + public long getSelectedPosition() { + return mExpandableList.getSelectedPosition(); + } + + /** + * Sets the selection to the specified child. If the child is in a collapsed group, the group + * will only be expanded and child subsequently selected if shouldExpandGroup is set to true, + * otherwise the method will return false. + * + * @param groupPosition + * The position of the group that contains the child. + * @param childPosition + * The position of the child within the group. + * @param shouldExpandGroup + * Whether the child's group should be expanded if it is collapsed. + * @return Whether the selection was successfully set on the child. + */ + public boolean setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup) { + return mExpandableList.setSelectedChild(groupPosition, childPosition, shouldExpandGroup); + } + + /** + * Sets the selection to the specified group. + * + * @param groupPosition + * The position of the group that should be selected. + */ + public void setSelectedGroup(int groupPosition) { + mExpandableList.setSelectedGroup(groupPosition); + } +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java b/APG/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java new file mode 100644 index 000000000..1d5f9e3d7 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java @@ -0,0 +1,95 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +import android.content.Context; +import android.preference.ListPreference; +import android.util.AttributeSet; + +/** + * A list preference which persists its values as integers instead of strings. + * Code reading the values should use + * {@link android.content.SharedPreferences#getInt}. + * When using XML-declared arrays for entry values, the arrays should be regular + * string arrays containing valid integer values. + * + * @author Rodrigo Damazio + */ +public class IntegerListPreference extends ListPreference { + + public IntegerListPreference(Context context) { + super(context); + + verifyEntryValues(null); + } + + public IntegerListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + verifyEntryValues(null); + } + + @Override + public void setEntryValues(CharSequence[] entryValues) { + CharSequence[] oldValues = getEntryValues(); + super.setEntryValues(entryValues); + verifyEntryValues(oldValues); + } + + @Override + public void setEntryValues(int entryValuesResId) { + CharSequence[] oldValues = getEntryValues(); + super.setEntryValues(entryValuesResId); + verifyEntryValues(oldValues); + } + + @Override + protected String getPersistedString(String defaultReturnValue) { + // During initial load, there's no known default value + int defaultIntegerValue = Integer.MIN_VALUE; + if (defaultReturnValue != null) { + defaultIntegerValue = Integer.parseInt(defaultReturnValue); + } + + // When the list preference asks us to read a string, instead read an + // integer. + int value = getPersistedInt(defaultIntegerValue); + return Integer.toString(value); + } + + @Override + protected boolean persistString(String value) { + // When asked to save a string, instead save an integer + return persistInt(Integer.parseInt(value)); + } + + private void verifyEntryValues(CharSequence[] oldValues) { + CharSequence[] entryValues = getEntryValues(); + if (entryValues == null) { + return; + } + + for (CharSequence entryValue : entryValues) { + try { + Integer.parseInt(entryValue.toString()); + } catch (NumberFormatException nfe) { + super.setEntryValues(oldValues); + throw nfe; + } + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java b/APG/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java new file mode 100644 index 000000000..f8adc3b72 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.util.Choice; +import org.thialfihar.android.apg.R; + +import android.app.DatePickerDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.DatePicker; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Vector; + +public class KeyEditor extends LinearLayout implements Editor, OnClickListener { + private PGPSecretKey mKey; + + private EditorListener mEditorListener = null; + + private boolean mIsMasterKey; + ImageButton mDeleteButton; + TextView mAlgorithm; + TextView mKeyId; + Spinner mUsage; + TextView mCreationDate; + Button mExpiryDateButton; + GregorianCalendar mExpiryDate; + + private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = new DatePickerDialog.OnDateSetListener() { + public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { + GregorianCalendar date = new GregorianCalendar(year, monthOfYear, dayOfMonth); + setExpiryDate(date); + } + }; + + public KeyEditor(Context context) { + super(context); + } + + public KeyEditor(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + setDrawingCacheEnabled(true); + setAlwaysDrawnWithCacheEnabled(true); + + mAlgorithm = (TextView) findViewById(R.id.algorithm); + mKeyId = (TextView) findViewById(R.id.keyId); + mCreationDate = (TextView) findViewById(R.id.creation); + mExpiryDateButton = (Button) findViewById(R.id.expiry); + mUsage = (Spinner) findViewById(R.id.usage); + Choice choices[] = { + new Choice(Id.choice.usage.sign_only, getResources().getString( + R.string.choice_signOnly)), + new Choice(Id.choice.usage.encrypt_only, getResources().getString( + R.string.choice_encryptOnly)), + new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( + R.string.choice_signAndEncrypt)), }; + ArrayAdapter adapter = new ArrayAdapter(getContext(), + android.R.layout.simple_spinner_item, choices); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mUsage.setAdapter(adapter); + + mDeleteButton = (ImageButton) findViewById(R.id.delete); + mDeleteButton.setOnClickListener(this); + + setExpiryDate(null); + + mExpiryDateButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + GregorianCalendar date = mExpiryDate; + if (date == null) { + date = new GregorianCalendar(); + } + + DatePickerDialog dialog = new DatePickerDialog(getContext(), + mExpiryDateSetListener, date.get(Calendar.YEAR), date.get(Calendar.MONTH), + date.get(Calendar.DAY_OF_MONTH)); + dialog.setCancelable(true); + dialog.setButton(Dialog.BUTTON_NEGATIVE, getContext() + .getString(R.string.btn_noDate), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + setExpiryDate(null); + } + }); + dialog.show(); + } + }); + + super.onFinishInflate(); + } + + public void setValue(PGPSecretKey key, boolean isMasterKey, int usage) { + mKey = key; + + mIsMasterKey = isMasterKey; + if (mIsMasterKey) { + mDeleteButton.setVisibility(View.INVISIBLE); + } + + mAlgorithm.setText(PGPHelper.getAlgorithmInfo(key)); + String keyId1Str = PGPHelper.getSmallFingerPrint(key.getKeyID()); + String keyId2Str = PGPHelper.getSmallFingerPrint(key.getKeyID() >> 32); + mKeyId.setText(keyId1Str + " " + keyId2Str); + + Vector choices = new Vector(); + boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT); + boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA); + if (!isElGamalKey) { + choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString( + R.string.choice_signOnly))); + } + if (!mIsMasterKey && !isDSAKey) { + choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString( + R.string.choice_encryptOnly))); + } + if (!isElGamalKey && !isDSAKey) { + choices.add(new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( + R.string.choice_signAndEncrypt))); + } + + ArrayAdapter adapter = new ArrayAdapter(getContext(), + android.R.layout.simple_spinner_item, choices); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mUsage.setAdapter(adapter); + + // Set value in choice dropdown to key + int selectId = 0; + if (PGPHelper.isEncryptionKey(key)) { + if (PGPHelper.isSigningKey(key)) { + selectId = Id.choice.usage.sign_and_encrypt; + } else { + selectId = Id.choice.usage.encrypt_only; + } + } else { + // set usage if it is predefined + if (usage != -1) { + selectId = usage; + } else { + selectId = Id.choice.usage.sign_only; + } + + } + + for (int i = 0; i < choices.size(); ++i) { + if (choices.get(i).getId() == selectId) { + mUsage.setSelection(i); + break; + } + } + + GregorianCalendar cal = new GregorianCalendar(); + cal.setTime(PGPHelper.getCreationDate(key)); + mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime())); + cal = new GregorianCalendar(); + Date date = PGPHelper.getExpiryDate(key); + if (date == null) { + setExpiryDate(null); + } else { + cal.setTime(PGPHelper.getExpiryDate(key)); + setExpiryDate(cal); + } + + } + + public PGPSecretKey getValue() { + return mKey; + } + + public void onClick(View v) { + final ViewGroup parent = (ViewGroup) getParent(); + if (v == mDeleteButton) { + parent.removeView(this); + if (mEditorListener != null) { + mEditorListener.onDeleted(this); + } + } + } + + public void setEditorListener(EditorListener listener) { + mEditorListener = listener; + } + + private void setExpiryDate(GregorianCalendar date) { + mExpiryDate = date; + if (date == null) { + mExpiryDateButton.setText(R.string.none); + } else { + mExpiryDateButton.setText(DateFormat.getDateInstance().format(date.getTime())); + } + } + + public GregorianCalendar getExpiryDate() { + return mExpiryDate; + } + + public int getUsage() { + return ((Choice) mUsage.getSelectedItem()).getId(); + } + +} diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java b/APG/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java new file mode 100644 index 000000000..1f5c3a97e --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.OtherHelper; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.provider.ApgContract.Keys; +import org.thialfihar.android.apg.provider.ApgContract.UserIds; +import org.thialfihar.android.apg.util.Log; + +import android.content.Context; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.MergeCursor; +import android.net.Uri; +import android.provider.BaseColumns; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorTreeAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class KeyListAdapter extends CursorTreeAdapter { + private Context mContext; + private LayoutInflater mInflater; + + protected int mKeyType; + + private static final int CHILD_KEY = 0; + private static final int CHILD_USER_ID = 1; + private static final int CHILD_FINGERPRINT = 2; + + public KeyListAdapter(Context context, Cursor groupCursor, int keyType) { + super(groupCursor, context); + mContext = context; + mInflater = LayoutInflater.from(context); + mKeyType = keyType; + } + + /** + * Inflate new view for group items + */ + @Override + public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) { + return mInflater.inflate(R.layout.key_list_group_item, null); + } + + /** + * Binds TextViews from group view to results from database group cursor. + */ + @Override + protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) { + int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID); + + TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); + mainUserId.setText(R.string.unknownUserId); + TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); + mainUserIdRest.setText(""); + + String userId = cursor.getString(userIdIndex); + if (userId != null) { + String[] userIdSplit = OtherHelper.splitUserId(userId); + + if (userIdSplit[1] != null) { + mainUserIdRest.setText(userIdSplit[1]); + } + mainUserId.setText(userIdSplit[0]); + } + + if (mainUserId.getText().length() == 0) { + mainUserId.setText(R.string.unknownUserId); + } + + if (mainUserIdRest.getText().length() == 0) { + mainUserIdRest.setVisibility(View.GONE); + } + } + + /** + * Inflate new view for child items + */ + @Override + public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) { + return mInflater.inflate(R.layout.key_list_child_item, null); + } + + /** + * Bind TextViews from view of childs based on query results + */ + @Override + protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { + LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout); + LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout); + + // first entry is fingerprint + if (cursor.getPosition() == 0) { + // show only userId layout + keyLayout.setVisibility(View.GONE); + userIdLayout.setVisibility(View.VISIBLE); + + String fingerprint = PGPHelper.getFingerPrint(context, + cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID))); + fingerprint = fingerprint.replace(" ", "\n"); + + TextView userId = (TextView) view.findViewById(R.id.userId); + if (userId == null) { + Log.d(Constants.TAG, "userId is null!"); + } + userId.setText(context.getString(R.string.fingerprint) + ":\n" + fingerprint); + } else { + // differentiate between keys and userIds in MergeCursor + if (cursor.getColumnIndex(Keys.KEY_ID) != -1) { + keyLayout.setVisibility(View.VISIBLE); + userIdLayout.setVisibility(View.GONE); + + String keyIdStr = PGPHelper.getSmallFingerPrint(cursor.getLong(cursor + .getColumnIndex(Keys.KEY_ID))); + String algorithmStr = PGPHelper.getAlgorithmInfo( + cursor.getInt(cursor.getColumnIndex(Keys.ALGORITHM)), + cursor.getInt(cursor.getColumnIndex(Keys.KEY_SIZE))); + + TextView keyId = (TextView) view.findViewById(R.id.keyId); + keyId.setText(keyIdStr); + + TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails); + keyDetails.setText("(" + algorithmStr + ")"); + + ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey); + if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) != 1) { + masterKeyIcon.setVisibility(View.INVISIBLE); + } else { + masterKeyIcon.setVisibility(View.VISIBLE); + } + + ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); + if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) { + encryptIcon.setVisibility(View.GONE); + } else { + encryptIcon.setVisibility(View.VISIBLE); + } + + ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); + if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) { + signIcon.setVisibility(View.GONE); + } else { + signIcon.setVisibility(View.VISIBLE); + } + } else { + keyLayout.setVisibility(View.GONE); + userIdLayout.setVisibility(View.VISIBLE); + + String userIdStr = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID)); + + TextView userId = (TextView) view.findViewById(R.id.userId); + userId.setText(userIdStr); + } + } + } + + /** + * Given the group cursor, we start cursors for a fingerprint, keys, and userIds, which are + * merged together and build the child cursor + */ + @Override + protected Cursor getChildrenCursor(Cursor groupCursor) { + final long keyRingRowId = groupCursor.getLong(groupCursor.getColumnIndex(BaseColumns._ID)); + + Cursor fingerprintCursor = getChildCursor(keyRingRowId, CHILD_FINGERPRINT); + Cursor keyCursor = getChildCursor(keyRingRowId, CHILD_KEY); + Cursor userIdCursor = getChildCursor(keyRingRowId, CHILD_USER_ID); + + MergeCursor mergeCursor = new MergeCursor(new Cursor[] { fingerprintCursor, keyCursor, + userIdCursor }); + Log.d(Constants.TAG, "mergeCursor:" + DatabaseUtils.dumpCursorToString(mergeCursor)); + + return mergeCursor; + } + + /** + * This builds a cursor for a specific type of children + * + * @param keyRingRowId + * foreign row id of the keyRing + * @param type + * @return + */ + private Cursor getChildCursor(long keyRingRowId, int type) { + Uri uri = null; + String[] projection = null; + String sortOrder = null; + String selection = null; + + switch (type) { + case CHILD_FINGERPRINT: + projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, + Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; + sortOrder = Keys.RANK + " ASC"; + + // use only master key for fingerprint + selection = Keys.IS_MASTER_KEY + " = 1 "; + + if (mKeyType == Id.type.public_key) { + uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId)); + } else { + uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId)); + } + break; + + case CHILD_KEY: + projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, + Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; + sortOrder = Keys.RANK + " ASC"; + + if (mKeyType == Id.type.public_key) { + uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId)); + } else { + uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId)); + } + + break; + + case CHILD_USER_ID: + projection = new String[] { UserIds._ID, UserIds.USER_ID, UserIds.RANK, }; + sortOrder = UserIds.RANK + " ASC"; + + // not the main user id + selection = UserIds.RANK + " > 0 "; + + if (mKeyType == Id.type.public_key) { + uri = UserIds.buildPublicUserIdsUri(String.valueOf(keyRingRowId)); + } else { + uri = UserIds.buildSecretUserIdsUri(String.valueOf(keyRingRowId)); + } + + break; + + default: + return null; + + } + + return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder); + } + +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java b/APG/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java new file mode 100644 index 000000000..cbea7f031 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +import org.thialfihar.android.apg.R; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener { + private EditorListener mEditorListener = null; + + ImageButton mDeleteButton; + TextView mServer; + + public KeyServerEditor(Context context) { + super(context); + } + + public KeyServerEditor(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + setDrawingCacheEnabled(true); + setAlwaysDrawnWithCacheEnabled(true); + + mServer = (TextView) findViewById(R.id.server); + + mDeleteButton = (ImageButton) findViewById(R.id.delete); + mDeleteButton.setOnClickListener(this); + + super.onFinishInflate(); + } + + public void setValue(String value) { + mServer.setText(value); + } + + public String getValue() { + return mServer.getText().toString().trim(); + } + + public void onClick(View v) { + final ViewGroup parent = (ViewGroup)getParent(); + if (v == mDeleteButton) { + parent.removeView(this); + if (mEditorListener != null) { + mEditorListener.onDeleted(this); + } + } + } + + public void setEditorListener(EditorListener listener) { + mEditorListener = listener; + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/SectionView.java b/APG/src/org/thialfihar/android/apg/ui/widget/SectionView.java new file mode 100644 index 000000000..45fbbaba1 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/SectionView.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.helper.PGPConversionHelper; +import org.thialfihar.android.apg.service.ApgIntentServiceHandler; +import org.thialfihar.android.apg.service.ApgIntentService; +import org.thialfihar.android.apg.service.PassphraseCacheService; +import org.thialfihar.android.apg.ui.dialog.ProgressDialogFragment; +import org.thialfihar.android.apg.ui.widget.Editor.EditorListener; +import org.thialfihar.android.apg.util.Choice; +import org.thialfihar.android.apg.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; + +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; + +import java.util.Iterator; +import java.util.Vector; + +public class SectionView extends LinearLayout implements OnClickListener, EditorListener { + private LayoutInflater mInflater; + private View mAdd; + private ViewGroup mEditors; + private TextView mTitle; + private int mType = 0; + + private Choice mNewKeyAlgorithmChoice; + private int mNewKeySize; + + private SherlockFragmentActivity mActivity; + + private ProgressDialogFragment mGeneratingDialog; + + public SectionView(Context context) { + super(context); + mActivity = (SherlockFragmentActivity) context; + } + + public SectionView(Context context, AttributeSet attrs) { + super(context, attrs); + mActivity = (SherlockFragmentActivity) context; + } + + public ViewGroup getEditors() { + return mEditors; + } + + public void setType(int type) { + mType = type; + switch (type) { + case Id.type.user_id: { + mTitle.setText(R.string.section_userIds); + break; + } + + case Id.type.key: { + mTitle.setText(R.string.section_keys); + break; + } + + default: { + break; + } + } + } + + /** {@inheritDoc} */ + @Override + protected void onFinishInflate() { + mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + setDrawingCacheEnabled(true); + setAlwaysDrawnWithCacheEnabled(true); + + mAdd = findViewById(R.id.header); + mAdd.setOnClickListener(this); + + mEditors = (ViewGroup) findViewById(R.id.editors); + mTitle = (TextView) findViewById(R.id.title); + + updateEditorsVisible(); + super.onFinishInflate(); + } + + /** {@inheritDoc} */ + public void onDeleted(Editor editor) { + this.updateEditorsVisible(); + } + + protected void updateEditorsVisible() { + final boolean hasChildren = mEditors.getChildCount() > 0; + mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE); + } + + /** {@inheritDoc} */ + public void onClick(View v) { + switch (mType) { + case Id.type.user_id: { + UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, + mEditors, false); + view.setEditorListener(this); + if (mEditors.getChildCount() == 0) { + view.setIsMainUserId(true); + } + mEditors.addView(view); + break; + } + + case Id.type.key: { + AlertDialog.Builder dialog = new AlertDialog.Builder(getContext()); + + View view = mInflater.inflate(R.layout.create_key, null); + dialog.setView(view); + dialog.setTitle(R.string.title_createKey); + + boolean wouldBeMasterKey = (mEditors.getChildCount() == 0); + + final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm); + Vector choices = new Vector(); + choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString(R.string.dsa))); + if (!wouldBeMasterKey) { + choices.add(new Choice(Id.choice.algorithm.elgamal, getResources().getString( + R.string.elgamal))); + } + + choices.add(new Choice(Id.choice.algorithm.rsa, getResources().getString(R.string.rsa))); + + ArrayAdapter adapter = new ArrayAdapter(getContext(), + android.R.layout.simple_spinner_item, choices); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + algorithm.setAdapter(adapter); + // make RSA the default + for (int i = 0; i < choices.size(); ++i) { + if (choices.get(i).getId() == Id.choice.algorithm.rsa) { + algorithm.setSelection(i); + break; + } + } + + final EditText keySize = (EditText) view.findViewById(R.id.create_key_size); + + dialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface di, int id) { + di.dismiss(); + try { + mNewKeySize = Integer.parseInt("" + keySize.getText()); + } catch (NumberFormatException e) { + mNewKeySize = 0; + } + + mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem(); + createKey(); + } + }); + + dialog.setCancelable(true); + dialog.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface di, int id) { + di.dismiss(); + } + }); + + dialog.create().show(); + break; + } + + default: { + break; + } + } + this.updateEditorsVisible(); + } + + public void setUserIds(Vector list) { + if (mType != Id.type.user_id) { + return; + } + + mEditors.removeAllViews(); + for (String userId : list) { + UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, + mEditors, false); + view.setEditorListener(this); + view.setValue(userId); + if (mEditors.getChildCount() == 0) { + view.setIsMainUserId(true); + } + mEditors.addView(view); + } + + this.updateEditorsVisible(); + } + + public void setKeys(Vector list, Vector usages) { + if (mType != Id.type.key) { + return; + } + + mEditors.removeAllViews(); + + // go through all keys and set view based on them + for (int i = 0; i < list.size(); i++) { + KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, + false); + view.setEditorListener(this); + boolean isMasterKey = (mEditors.getChildCount() == 0); + view.setValue(list.get(i), isMasterKey, usages.get(i)); + mEditors.addView(view); + } + + this.updateEditorsVisible(); + } + + private void createKey() { + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(mActivity, ApgIntentService.class); + + intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_GENERATE_KEY); + + // fill values for this action + Bundle data = new Bundle(); + + String passPhrase; + if (mEditors.getChildCount() > 0) { + PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue(); + passPhrase = PassphraseCacheService + .getCachedPassphrase(mActivity, masterKey.getKeyID()); + + data.putByteArray(ApgIntentService.MASTER_KEY, + PGPConversionHelper.PGPSecretKeyToBytes(masterKey)); + } else { + passPhrase = ""; + } + data.putString(ApgIntentService.SYMMETRIC_PASSPHRASE, passPhrase); + data.putInt(ApgIntentService.ALGORITHM, mNewKeyAlgorithmChoice.getId()); + data.putInt(ApgIntentService.KEY_SIZE, mNewKeySize); + + intent.putExtra(ApgIntentService.EXTRA_DATA, data); + + // show progress dialog + mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating, + ProgressDialog.STYLE_SPINNER); + + // Message is received after generating is done in ApgService + ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(mActivity, mGeneratingDialog) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { + // get new key from data bundle returned from service + Bundle data = message.getData(); + PGPSecretKeyRing newKeyRing = (PGPSecretKeyRing) PGPConversionHelper + .BytesToPGPKeyRing(data.getByteArray(ApgIntentService.RESULT_NEW_KEY)); + + boolean isMasterKey = (mEditors.getChildCount() == 0); + + // take only the key from this ring + PGPSecretKey newKey = null; + @SuppressWarnings("unchecked") + Iterator it = newKeyRing.getSecretKeys(); + + if (isMasterKey) { + newKey = it.next(); + } else { + // first one is the master key + it.next(); + newKey = it.next(); + } + + // add view with new key + KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, + mEditors, false); + view.setEditorListener(SectionView.this); + view.setValue(newKey, isMasterKey, -1); + mEditors.addView(view); + SectionView.this.updateEditorsVisible(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); + + mGeneratingDialog.show(mActivity.getSupportFragmentManager(), "dialog"); + + // start service with intent + mActivity.startService(intent); + } +} diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java b/APG/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java new file mode 100644 index 000000000..0f6bd5f11 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.helper.OtherHelper; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.provider.ApgContract.KeyRings; +import org.thialfihar.android.apg.provider.ApgContract.UserIds; + +import android.content.Context; +import android.database.Cursor; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ListView; +import android.widget.TextView; + +public class SelectKeyCursorAdapter extends CursorAdapter { + + protected int mKeyType; + + private LayoutInflater mInflater; + private ListView mListView; + + public final static String PROJECTION_ROW_AVAILABLE = "available"; + public final static String PROJECTION_ROW_VALID = "valid"; + + @SuppressWarnings("deprecation") + public SelectKeyCursorAdapter(Context context, ListView listView, Cursor c, int keyType) { + super(context, c); + + mInflater = LayoutInflater.from(context); + mListView = listView; + mKeyType = keyType; + } + + public String getUserId(int position) { + mCursor.moveToPosition(position); + return mCursor.getString(mCursor.getColumnIndex(UserIds.USER_ID)); + } + + public long getMasterKeyId(int position) { + mCursor.moveToPosition(position); + return mCursor.getLong(mCursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + boolean valid = cursor.getInt(cursor + .getColumnIndex(PROJECTION_ROW_VALID)) > 0; + + 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 = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID)); + if (userId != null) { + String[] userIdSplit = OtherHelper.splitUserId(userId); + + if (userIdSplit[1] != null) { + mainUserIdRest.setText(userIdSplit[1]); + } + mainUserId.setText(userIdSplit[0]); + } + + long masterKeyId = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); + keyId.setText(PGPHelper.getSmallFingerPrint(masterKeyId)); + + if (mainUserIdRest.getText().length() == 0) { + mainUserIdRest.setVisibility(View.GONE); + } + + if (valid) { + if (mKeyType == Id.type.public_key) { + status.setText(R.string.canEncrypt); + } else { + status.setText(R.string.canSign); + } + } else { + if (cursor.getInt(cursor + .getColumnIndex(PROJECTION_ROW_AVAILABLE)) > 0) { + // has some CAN_ENCRYPT keys, but col(ROW_VALID) = 0, so must be revoked or + // expired + status.setText(R.string.expired); + } else { + status.setText(R.string.noKey); + } + } + + CheckBox selected = (CheckBox) view.findViewById(R.id.selected); + if (mKeyType == Id.type.public_key) { + selected.setVisibility(View.VISIBLE); + + if (!valid) { + mListView.setItemChecked(cursor.getPosition(), false); + } + + selected.setChecked(mListView.isItemChecked(cursor.getPosition())); + selected.setEnabled(valid); + } else { + selected.setVisibility(View.GONE); + } + + status.setText(status.getText() + " "); + + view.setEnabled(valid); + mainUserId.setEnabled(valid); + mainUserIdRest.setEnabled(valid); + keyId.setEnabled(valid); + status.setEnabled(valid); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.select_key_item, null); + } + +} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java b/APG/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java new file mode 100644 index 000000000..2495a4f9d --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.ui.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.thialfihar.android.apg.R; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.RadioButton; + +public class UserIdEditor extends LinearLayout implements Editor, OnClickListener { + private EditorListener mEditorListener = null; + + private ImageButton mDeleteButton; + private RadioButton mIsMainUserId; + private EditText mName; + private EditText mEmail; + private EditText mComment; + + // see http://www.regular-expressions.info/email.html + // RFC 2822 if we omit the syntax using double quotes and square brackets + // android.util.Patterns.EMAIL_ADDRESS is only available as of Android 2.2+ + private static final Pattern EMAIL_PATTERN = Pattern + .compile( + "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", + Pattern.CASE_INSENSITIVE); + + public static class NoNameException extends Exception { + static final long serialVersionUID = 0xf812773343L; + + public NoNameException(String message) { + super(message); + } + } + + public static class NoEmailException extends Exception { + static final long serialVersionUID = 0xf812773344L; + + public NoEmailException(String message) { + super(message); + } + } + + public static class InvalidEmailException extends Exception { + static final long serialVersionUID = 0xf812773345L; + + public InvalidEmailException(String message) { + super(message); + } + } + + public UserIdEditor(Context context) { + super(context); + } + + public UserIdEditor(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + setDrawingCacheEnabled(true); + setAlwaysDrawnWithCacheEnabled(true); + + mDeleteButton = (ImageButton) findViewById(R.id.delete); + mDeleteButton.setOnClickListener(this); + mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId); + mIsMainUserId.setOnClickListener(this); + + mName = (EditText) findViewById(R.id.name); + mEmail = (EditText) findViewById(R.id.email); + mComment = (EditText) findViewById(R.id.comment); + + super.onFinishInflate(); + } + + public void setValue(String userId) { + mName.setText(""); + mComment.setText(""); + mEmail.setText(""); + + Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$"); + Matcher matcher = withComment.matcher(userId); + if (matcher.matches()) { + mName.setText(matcher.group(1)); + mComment.setText(matcher.group(2)); + mEmail.setText(matcher.group(3)); + return; + } + + Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$"); + matcher = withoutComment.matcher(userId); + if (matcher.matches()) { + mName.setText(matcher.group(1)); + mEmail.setText(matcher.group(2)); + return; + } + } + + public String getValue() throws NoNameException, NoEmailException, InvalidEmailException { + String name = ("" + mName.getText()).trim(); + String email = ("" + mEmail.getText()).trim(); + String comment = ("" + mComment.getText()).trim(); + + if (email.length() > 0) { + Matcher emailMatcher = EMAIL_PATTERN.matcher(email); + if (!emailMatcher.matches()) { + throw new InvalidEmailException(getContext().getString(R.string.error_invalidEmail, + email)); + } + } + + String userId = name; + if (comment.length() > 0) { + userId += " (" + comment + ")"; + } + if (email.length() > 0) { + userId += " <" + email + ">"; + } + + if (userId.equals("")) { + // ok, empty one... + return userId; + } + + // otherwise make sure that name and email exist + if (name.equals("")) { + throw new NoNameException("need a name"); + } + + if (email.equals("")) { + throw new NoEmailException("need an email"); + } + + return userId; + } + + public void onClick(View v) { + final ViewGroup parent = (ViewGroup) getParent(); + if (v == mDeleteButton) { + boolean wasMainUserId = mIsMainUserId.isChecked(); + parent.removeView(this); + if (mEditorListener != null) { + mEditorListener.onDeleted(this); + } + if (wasMainUserId && parent.getChildCount() > 0) { + UserIdEditor editor = (UserIdEditor) parent.getChildAt(0); + editor.setIsMainUserId(true); + } + } else if (v == mIsMainUserId) { + for (int i = 0; i < parent.getChildCount(); ++i) { + UserIdEditor editor = (UserIdEditor) parent.getChildAt(i); + if (editor == this) { + editor.setIsMainUserId(true); + } else { + editor.setIsMainUserId(false); + } + } + } + } + + public void setIsMainUserId(boolean value) { + mIsMainUserId.setChecked(value); + } + + public boolean isMainUserId() { + return mIsMainUserId.isChecked(); + } + + public void setEditorListener(EditorListener listener) { + mEditorListener = listener; + } +} diff --git a/APG/src/org/thialfihar/android/apg/util/Choice.java b/APG/src/org/thialfihar/android/apg/util/Choice.java new file mode 100644 index 000000000..94cc58f55 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/Choice.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * 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.util; + +public class Choice { + private String mName; + private int mId; + + public Choice() { + mId = -1; + mName = ""; + } + + public Choice(int id, String name) { + mId = id; + mName = name; + } + + public int getId() { + return mId; + } + + public String getName() { + return mName; + } + + @Override + public String toString() { + return mName; + } +} diff --git a/APG/src/org/thialfihar/android/apg/util/Compatibility.java b/APG/src/org/thialfihar/android/apg/util/Compatibility.java new file mode 100644 index 000000000..1597a01f4 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/Compatibility.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.util; + +import java.lang.reflect.Method; + +import android.content.Context; +import org.thialfihar.android.apg.util.Log; + +public class Compatibility { + + private static final String clipboardLabel = "APG"; + + /** + * Wrapper around ClipboardManager based on Android version using Reflection API + * + * @param context + * @param text + */ + public static void copyToClipboard(Context context, String text) { + Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE); + try { + if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) { + Method method = clipboard.getClass().getMethod("setText", CharSequence.class); + method.invoke(clipboard, text); + } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) { + Class clazz = Class.forName("android.content.ClipData"); + Method method = clazz.getMethod("newPlainText", CharSequence.class, + CharSequence.class); + Object clip = method.invoke(null, clipboardLabel, text); + method = clipboard.getClass().getMethod("setPrimaryClip", clazz); + method.invoke(clipboard, clip); + } + } catch (Exception e) { + Log.e("ProjectsException", "There was and error copying the text to the clipboard: " + + e.getMessage()); + } + } + + /** + * Wrapper around ClipboardManager based on Android version using Reflection API + * + * @param context + * @param text + */ + public static CharSequence getClipboardText(Context context) { + Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE); + try { + if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) { + // CharSequence text = clipboard.getText(); + Method method = clipboard.getClass().getMethod("getText"); + Object text = method.invoke(clipboard); + + return (CharSequence) text; + } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) { + // ClipData clipData = clipboard.getPrimaryClip(); + Method methodGetPrimaryClip = clipboard.getClass().getMethod("getPrimaryClip"); + Object clipData = methodGetPrimaryClip.invoke(clipboard); + + // ClipData.Item clipDataItem = clipData.getItemAt(0); + Method methodGetItemAt = clipData.getClass().getMethod("getItemAt", Integer.TYPE); + Object clipDataItem = methodGetItemAt.invoke(clipData, 0); + + // CharSequence text = clipDataItem.coerceToText(context); + Method methodGetString = clipDataItem.getClass().getMethod("coerceToText", + Context.class); + Object text = methodGetString.invoke(clipDataItem, context); + + return (CharSequence) text; + } else { + return null; + } + } catch (Exception e) { + Log.e("ProjectsException", "There was and error getting the text from the clipboard: " + + e.getMessage()); + + return null; + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/util/HkpKeyServer.java b/APG/src/org/thialfihar/android/apg/util/HkpKeyServer.java new file mode 100644 index 000000000..492e2e3d0 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/HkpKeyServer.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2011 Senecaso + * + * 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.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.thialfihar.android.apg.helper.PGPHelper; +import org.thialfihar.android.apg.helper.PGPMain; + +import android.text.Html; + +public class HkpKeyServer extends KeyServer { + private static class HttpError extends Exception { + private static final long serialVersionUID = 1718783705229428893L; + private int mCode; + private String mData; + + public HttpError(int code, String data) { + super("" + code + ": " + data); + mCode = code; + mData = data; + } + + public int getCode() { + return mCode; + } + + public String getData() { + return mData; + } + } + + private String mHost; + private short mPort = 11371; + + // example: + // pub 2048R/9F5C9090 2009-08-17 Jörg Runge + // <joerg@joergrunge.de> + public static Pattern PUB_KEY_LINE = Pattern + .compile( + "pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)", + Pattern.CASE_INSENSITIVE); + public static Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE + | Pattern.CASE_INSENSITIVE); + + public HkpKeyServer(String host) { + mHost = host; + } + + public HkpKeyServer(String host, short port) { + mHost = host; + mPort = port; + } + + static private String readAll(InputStream in, String encoding) throws IOException { + ByteArrayOutputStream raw = new ByteArrayOutputStream(); + + byte buffer[] = new byte[1 << 16]; + int n = 0; + while ((n = in.read(buffer)) != -1) { + raw.write(buffer, 0, n); + } + + if (encoding == null) { + encoding = "utf8"; + } + return raw.toString(encoding); + } + + private String query(String request) throws QueryException, HttpError { + InetAddress ips[]; + try { + ips = InetAddress.getAllByName(mHost); + } catch (UnknownHostException e) { + throw new QueryException(e.toString()); + } + for (int i = 0; i < ips.length; ++i) { + try { + String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request; + URL realUrl = new URL(url); + HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); + conn.setConnectTimeout(5000); + conn.setReadTimeout(25000); + conn.connect(); + int response = conn.getResponseCode(); + if (response >= 200 && response < 300) { + return readAll(conn.getInputStream(), conn.getContentEncoding()); + } else { + String data = readAll(conn.getErrorStream(), conn.getContentEncoding()); + throw new HttpError(response, data); + } + } catch (MalformedURLException e) { + // nothing to do, try next IP + } catch (IOException e) { + // nothing to do, try next IP + } + } + + throw new QueryException("querying server(s) for '" + mHost + "' failed"); + } + + @Override + public ArrayList search(String query) throws QueryException, TooManyResponses, + InsufficientQuery { + ArrayList results = new ArrayList(); + + if (query.length() < 3) { + throw new InsufficientQuery(); + } + + String encodedQuery; + try { + encodedQuery = URLEncoder.encode(query, "utf8"); + } catch (UnsupportedEncodingException e) { + return null; + } + String request = "/pks/lookup?op=index&search=" + encodedQuery; + + String data = null; + try { + data = query(request); + } catch (HttpError e) { + if (e.getCode() == 404) { + return results; + } else { + if (e.getData().toLowerCase().contains("no keys found")) { + return results; + } else if (e.getData().toLowerCase().contains("too many")) { + throw new TooManyResponses(); + } else if (e.getData().toLowerCase().contains("insufficient")) { + throw new InsufficientQuery(); + } + } + throw new QueryException("querying server(s) for '" + mHost + "' failed"); + } + + Matcher matcher = PUB_KEY_LINE.matcher(data); + while (matcher.find()) { + KeyInfo info = new KeyInfo(); + info.size = Integer.parseInt(matcher.group(1)); + info.algorithm = matcher.group(2); + info.keyId = PGPHelper.keyFromHex(matcher.group(3)); + info.fingerPrint = PGPHelper.getSmallFingerPrint(info.keyId); + String chunks[] = matcher.group(4).split("-"); + info.date = new GregorianCalendar(Integer.parseInt(chunks[0]), + Integer.parseInt(chunks[1]), Integer.parseInt(chunks[2])).getTime(); + info.userIds = new ArrayList(); + if (matcher.group(5).startsWith("*** KEY")) { + info.revoked = matcher.group(5); + } else { + String tmp = matcher.group(5).replaceAll("<.*?>", ""); + tmp = Html.fromHtml(tmp).toString(); + info.userIds.add(tmp); + } + if (matcher.group(6).length() > 0) { + Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6)); + while (matcher2.find()) { + String tmp = matcher2.group(1).replaceAll("<.*?>", ""); + tmp = Html.fromHtml(tmp).toString(); + info.userIds.add(tmp); + } + } + results.add(info); + } + + return results; + } + + @Override + public String get(long keyId) throws QueryException { + HttpClient client = new DefaultHttpClient(); + try { + HttpGet get = new HttpGet("http://" + mHost + ":" + mPort + + "/pks/lookup?op=get&search=0x" + PGPHelper.keyToHex(keyId)); + + HttpResponse response = client.execute(get); + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new QueryException("not found"); + } + + HttpEntity entity = response.getEntity(); + InputStream is = entity.getContent(); + String data = readAll(is, EntityUtils.getContentCharSet(entity)); + Matcher matcher = PGPMain.PGP_PUBLIC_KEY.matcher(data); + if (matcher.find()) { + return matcher.group(1); + } + } catch (IOException e) { + // nothing to do, better luck on the next keyserver + } finally { + client.getConnectionManager().shutdown(); + } + + return null; + } + + @Override + public void add(String armouredText) throws AddKeyException { + HttpClient client = new DefaultHttpClient(); + try { + HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add"); + + List nameValuePairs = new ArrayList(2); + nameValuePairs.add(new BasicNameValuePair("keytext", armouredText)); + post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); + + HttpResponse response = client.execute(post); + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new AddKeyException(); + } + } catch (IOException e) { + // nothing to do, better luck on the next keyserver + } finally { + client.getConnectionManager().shutdown(); + } + } +} diff --git a/APG/src/org/thialfihar/android/apg/util/InputData.java b/APG/src/org/thialfihar/android/apg/util/InputData.java new file mode 100644 index 000000000..6b357e6de --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/InputData.java @@ -0,0 +1,40 @@ +/* + * 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.util; + +import java.io.InputStream; + + +public class InputData { + private PositionAwareInputStream mInputStream; + private long mSize; + + public InputData(InputStream inputStream, long size) { + mInputStream = new PositionAwareInputStream(inputStream); + mSize = size; + } + + public InputStream getInputStream() { + return mInputStream; + } + + public long getSize() { + return mSize; + } + + public long getStreamPosition() { + return mInputStream.position(); + } +} diff --git a/APG/src/org/thialfihar/android/apg/util/IterableIterator.java b/APG/src/org/thialfihar/android/apg/util/IterableIterator.java new file mode 100644 index 000000000..1071cc81c --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/IterableIterator.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * 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.util; + +import java.util.Iterator; + +public class IterableIterator implements Iterable { + private Iterator mIter; + + public IterableIterator(Iterator iter) { + mIter = iter; + } + + public Iterator iterator() { + return mIter; + } +} diff --git a/APG/src/org/thialfihar/android/apg/util/KeyServer.java b/APG/src/org/thialfihar/android/apg/util/KeyServer.java new file mode 100644 index 000000000..97c6c7636 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/KeyServer.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2011 Senecaso + * + * 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.util; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import android.os.Parcel; +import android.os.Parcelable; + +public abstract class KeyServer { + static public class QueryException extends Exception { + private static final long serialVersionUID = 2703768928624654512L; + + public QueryException(String message) { + super(message); + } + } + + static public class TooManyResponses extends Exception { + private static final long serialVersionUID = 2703768928624654513L; + } + + static public class InsufficientQuery extends Exception { + private static final long serialVersionUID = 2703768928624654514L; + } + + static public class AddKeyException extends Exception { + private static final long serialVersionUID = -507574859137295530L; + } + + static public class KeyInfo implements Serializable, Parcelable { + private static final long serialVersionUID = -7797972113284992662L; + public ArrayList userIds; + public String revoked; + public Date date; + public String fingerPrint; + public long keyId; + public int size; + public String algorithm; + + public KeyInfo() { + userIds = new ArrayList(); + } + + public KeyInfo(Parcel in) { + this(); + + in.readStringList(this.userIds); + this.revoked = in.readString(); + this.date = (Date) in.readSerializable(); + this.fingerPrint = in.readString(); + this.keyId = in.readLong(); + this.size = in.readInt(); + this.algorithm = in.readString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStringList(userIds); + dest.writeString(revoked); + dest.writeSerializable(date); + dest.writeString(fingerPrint); + dest.writeLong(keyId); + dest.writeInt(size); + dest.writeString(algorithm); + } + } + + abstract List search(String query) throws QueryException, TooManyResponses, + InsufficientQuery; + + abstract String get(long keyId) throws QueryException; + + abstract void add(String armouredText) throws AddKeyException; +} diff --git a/APG/src/org/thialfihar/android/apg/util/Log.java b/APG/src/org/thialfihar/android/apg/util/Log.java new file mode 100644 index 000000000..c2e3cc7a5 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/Log.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.util; + +import org.thialfihar.android.apg.Constants; + +/** + * Wraps Android Logging to enable or disable debug output using Constants + * + */ +public final class Log { + + public static void v(String tag, String msg) { + if (Constants.DEBUG) { + android.util.Log.v(tag, msg); + } + } + + public static void v(String tag, String msg, Throwable tr) { + if (Constants.DEBUG) { + android.util.Log.v(tag, msg, tr); + } + } + + public static void d(String tag, String msg) { + if (Constants.DEBUG) { + android.util.Log.d(tag, msg); + } + } + + public static void d(String tag, String msg, Throwable tr) { + if (Constants.DEBUG) { + android.util.Log.d(tag, msg, tr); + } + } + + public static void i(String tag, String msg) { + if (Constants.DEBUG) { + android.util.Log.i(tag, msg); + } + } + + public static void i(String tag, String msg, Throwable tr) { + if (Constants.DEBUG) { + android.util.Log.i(tag, msg, tr); + } + } + + public static void w(String tag, String msg) { + android.util.Log.w(tag, msg); + } + + public static void w(String tag, String msg, Throwable tr) { + android.util.Log.w(tag, msg, tr); + } + + public static void w(String tag, Throwable tr) { + android.util.Log.w(tag, tr); + } + + public static void e(String tag, String msg) { + android.util.Log.e(tag, msg); + } + + public static void e(String tag, String msg, Throwable tr) { + android.util.Log.e(tag, msg, tr); + } + +} diff --git a/APG/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java b/APG/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java new file mode 100644 index 000000000..7850e2513 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java @@ -0,0 +1,81 @@ +/* + * 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.util; + +import java.io.IOException; +import java.io.InputStream; + +public class PositionAwareInputStream extends InputStream { + private InputStream mStream; + private long mPosition; + + public PositionAwareInputStream(InputStream in) { + mStream = in; + mPosition = 0; + } + + @Override + public int read() throws IOException { + int ch = mStream.read(); + ++mPosition; + return ch; + } + + @Override + public int available() throws IOException { + return mStream.available(); + } + + @Override + public void close() throws IOException { + mStream.close(); + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public int read(byte[] b) throws IOException { + int result = mStream.read(b); + mPosition += result; + return result; + } + + @Override + public int read(byte[] b, int offset, int length) throws IOException { + int result = mStream.read(b, offset, length); + mPosition += result; + return result; + } + + @Override + public synchronized void reset() throws IOException { + mStream.reset(); + mPosition = 0; + } + + @Override + public long skip(long n) throws IOException { + long result = mStream.skip(n); + mPosition += result; + return result; + } + + public long position() { + return mPosition; + } +} diff --git a/APG/src/org/thialfihar/android/apg/util/Primes.java b/APG/src/org/thialfihar/android/apg/util/Primes.java new file mode 100644 index 000000000..bc52927e8 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/Primes.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * 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.util; + +import java.math.BigInteger; + +/** + * Primes for ElGamal + */ +public final class Primes { + // taken from http://www.ietf.org/rfc/rfc3526.txt + public static final String P1536 = + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + + "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF"; + + public static final String P2048 = + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + + "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + + "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + + "15728E5A 8AACAA68 FFFFFFFF FFFFFFFF"; + + public static final String P3072 = + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + + "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + + "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + + "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" + + "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" + + "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" + + "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" + + "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" + + "43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF"; + + public static final String P4096 = + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + + "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + + "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + + "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" + + "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" + + "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" + + "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" + + "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" + + "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" + + "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" + + "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" + + "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" + + "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" + + "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199" + + "FFFFFFFF FFFFFFFF"; + + public static final String P6144 = + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + + "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + + "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + + "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" + + "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" + + "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" + + "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" + + "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" + + "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" + + "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" + + "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" + + "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" + + "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" + + "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" + + "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" + + "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" + + "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" + + "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" + + "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" + + "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" + + "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" + + "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" + + "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" + + "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" + + "12BF2D5B 0B7474D6 E694F91E 6DCC4024 FFFFFFFF FFFFFFFF"; + + public static final String P8192 = + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + + "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + + "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + + "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" + + "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" + + "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" + + "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" + + "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" + + "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" + + "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" + + "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" + + "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" + + "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" + + "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" + + "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" + + "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" + + "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" + + "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" + + "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" + + "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" + + "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" + + "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" + + "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" + + "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" + + "12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4" + + "38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300" + + "741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568" + + "3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9" + + "22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B" + + "4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A" + + "062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36" + + "4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1" + + "B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92" + + "4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47" + + "9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71" + + "60C980DD 98EDD3DF FFFFFFFF FFFFFFFF"; + + public static BigInteger getBestPrime(int keySize) { + String primeString; + if (keySize >= (8192 + 6144) / 2) { + primeString = P8192; + } else if (keySize >= (6144 + 4096) / 2) { + primeString = P6144; + } else if (keySize >= (4096 + 3072) / 2) { + primeString = P4096; + } else if (keySize >= (3072 + 2048) / 2) { + primeString = P3072; + } else if (keySize >= (2048 + 1536) / 2) { + primeString = P2048; + } else { + primeString = P1536; + } + + return new BigInteger(primeString.replaceAll(" ", ""), 16); + } +} diff --git a/APG/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java b/APG/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java new file mode 100644 index 000000000..1f76cb071 --- /dev/null +++ b/APG/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010 Thialfihar + * + * 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.util; + +public interface ProgressDialogUpdater { + void setProgress(String message, int current, int total); + + void setProgress(int resourceId, int current, int total); + + void setProgress(int current, int total); +} diff --git a/org_apg/.gitignore b/org_apg/.gitignore deleted file mode 100644 index 2e423e1a3..000000000 --- a/org_apg/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -#Android generated -bin -gen -obj -libs/armeabi -lint.xml -local.properties - -#Eclipse -.project -.classpath -.settings - -#IntelliJ IDEA -.idea -*.iml - -#Maven -target -release.properties - -#Mac -.DS_Store \ No newline at end of file diff --git a/org_apg/AndroidManifest.xml b/org_apg/AndroidManifest.xml deleted file mode 100644 index 1361d3318..000000000 --- a/org_apg/AndroidManifest.xml +++ /dev/null @@ -1,379 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org_apg/build.xml b/org_apg/build.xml deleted file mode 100644 index eb9b68df7..000000000 --- a/org_apg/build.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org_apg/libs/barcodescanner-android-integration-supportv4.jar b/org_apg/libs/barcodescanner-android-integration-supportv4.jar deleted file mode 100644 index 4a7f1a39c..000000000 Binary files a/org_apg/libs/barcodescanner-android-integration-supportv4.jar and /dev/null differ diff --git a/org_apg/libs/htmlcleaner-2.2.jar b/org_apg/libs/htmlcleaner-2.2.jar deleted file mode 100644 index cc922d06b..000000000 Binary files a/org_apg/libs/htmlcleaner-2.2.jar and /dev/null differ diff --git a/org_apg/libs/htmlspanner-0.2-fork.jar b/org_apg/libs/htmlspanner-0.2-fork.jar deleted file mode 100644 index 76223ccc6..000000000 Binary files a/org_apg/libs/htmlspanner-0.2-fork.jar and /dev/null differ diff --git a/org_apg/libs/sc-bzip2-1.47.0.2.jar b/org_apg/libs/sc-bzip2-1.47.0.2.jar deleted file mode 100644 index 3bc42f30e..000000000 Binary files a/org_apg/libs/sc-bzip2-1.47.0.2.jar and /dev/null differ diff --git a/org_apg/libs/sc-light-jdk15on-1.47.0.2.jar b/org_apg/libs/sc-light-jdk15on-1.47.0.2.jar deleted file mode 100644 index 7cdbf856e..000000000 Binary files a/org_apg/libs/sc-light-jdk15on-1.47.0.2.jar and /dev/null differ diff --git a/org_apg/libs/scpg-jdk15on-1.47.0.2.jar b/org_apg/libs/scpg-jdk15on-1.47.0.2.jar deleted file mode 100644 index f290334d8..000000000 Binary files a/org_apg/libs/scpg-jdk15on-1.47.0.2.jar and /dev/null differ diff --git a/org_apg/libs/scprov-jdk15on-1.47.0.2.jar b/org_apg/libs/scprov-jdk15on-1.47.0.2.jar deleted file mode 100644 index 221a36b29..000000000 Binary files a/org_apg/libs/scprov-jdk15on-1.47.0.2.jar and /dev/null differ diff --git a/org_apg/pom.xml b/org_apg/pom.xml deleted file mode 100644 index cadb2a38d..000000000 --- a/org_apg/pom.xml +++ /dev/null @@ -1,67 +0,0 @@ - - 4.0.0 - 1.0.9-SNAPSHOT - org.thialfihar.android - apg - apk - APG - - - http://code.google.com/p/android-privacy-guard/ - - - UTF-8 - 1.6_r2 - - - - - com.google.android - android - provided - 1.6_r2 - - - com.madgag - scprov-jdk15 - 1.46.99.4-UNOFFICIAL-ROBERTO-RELEASE-SNAPSHOT - jar - - - com.google.zxing - android-integration - 1.6-SNAPSHOT - jar - - - - src - test - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - 3.0.0-alpha-11 - - - ${user.home}/android-sdk-linux_x86/ - 4 - - true - true - - true - - - - diff --git a/org_apg/proguard-project.txt b/org_apg/proguard-project.txt deleted file mode 100644 index f2fe1559a..000000000 --- a/org_apg/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/org_apg/project.properties b/org_apg/project.properties deleted file mode 100644 index f353c649e..000000000 --- a/org_apg/project.properties +++ /dev/null @@ -1,12 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "ant.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-15 -android.library.reference.1=../com_actionbarsherlock diff --git a/org_apg/res/anim/push_left_in.xml b/org_apg/res/anim/push_left_in.xml deleted file mode 100644 index 45fb4875a..000000000 --- a/org_apg/res/anim/push_left_in.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - diff --git a/org_apg/res/anim/push_left_out.xml b/org_apg/res/anim/push_left_out.xml deleted file mode 100644 index 845679f16..000000000 --- a/org_apg/res/anim/push_left_out.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/org_apg/res/anim/push_right_in.xml b/org_apg/res/anim/push_right_in.xml deleted file mode 100644 index 09a244406..000000000 --- a/org_apg/res/anim/push_right_in.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - diff --git a/org_apg/res/anim/push_right_out.xml b/org_apg/res/anim/push_right_out.xml deleted file mode 100644 index e8893a69a..000000000 --- a/org_apg/res/anim/push_right_out.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/org_apg/res/drawable-finger/btn_circle.xml b/org_apg/res/drawable-finger/btn_circle.xml deleted file mode 100644 index 6c3c7fc1a..000000000 --- a/org_apg/res/drawable-finger/btn_circle.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_disable.png b/org_apg/res/drawable-hdpi-finger/btn_circle_disable.png deleted file mode 100644 index ae063b545..000000000 Binary files a/org_apg/res/drawable-hdpi-finger/btn_circle_disable.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_disable_focused.png b/org_apg/res/drawable-hdpi-finger/btn_circle_disable_focused.png deleted file mode 100644 index 7a5d4fe4e..000000000 Binary files a/org_apg/res/drawable-hdpi-finger/btn_circle_disable_focused.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_normal.png b/org_apg/res/drawable-hdpi-finger/btn_circle_normal.png deleted file mode 100644 index 5eda66883..000000000 Binary files a/org_apg/res/drawable-hdpi-finger/btn_circle_normal.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_pressed.png b/org_apg/res/drawable-hdpi-finger/btn_circle_pressed.png deleted file mode 100644 index 88848bac2..000000000 Binary files a/org_apg/res/drawable-hdpi-finger/btn_circle_pressed.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_selected.png b/org_apg/res/drawable-hdpi-finger/btn_circle_selected.png deleted file mode 100644 index 74690705f..000000000 Binary files a/org_apg/res/drawable-hdpi-finger/btn_circle_selected.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi-finger/ic_btn_round_minus.png b/org_apg/res/drawable-hdpi-finger/ic_btn_round_minus.png deleted file mode 100644 index 27af3faf4..000000000 Binary files a/org_apg/res/drawable-hdpi-finger/ic_btn_round_minus.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi-finger/ic_btn_round_plus.png b/org_apg/res/drawable-hdpi-finger/ic_btn_round_plus.png deleted file mode 100644 index b24168c32..000000000 Binary files a/org_apg/res/drawable-hdpi-finger/ic_btn_round_plus.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png b/org_apg/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png deleted file mode 100644 index 73050476e..000000000 Binary files a/org_apg/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_decrypt_default.png b/org_apg/res/drawable-hdpi/dashboard_decrypt_default.png deleted file mode 100644 index 0d51bcb68..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_decrypt_default.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_decrypt_pressed.png b/org_apg/res/drawable-hdpi/dashboard_decrypt_pressed.png deleted file mode 100644 index d4cc0f8ea..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_decrypt_pressed.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_encrypt_default.png b/org_apg/res/drawable-hdpi/dashboard_encrypt_default.png deleted file mode 100644 index 07617bb9d..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_encrypt_default.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_encrypt_pressed.png b/org_apg/res/drawable-hdpi/dashboard_encrypt_pressed.png deleted file mode 100644 index b8fe6e1d6..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_encrypt_pressed.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_help_default.png b/org_apg/res/drawable-hdpi/dashboard_help_default.png deleted file mode 100644 index 233fddffc..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_help_default.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_help_pressed.png b/org_apg/res/drawable-hdpi/dashboard_help_pressed.png deleted file mode 100644 index dad8694f8..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_help_pressed.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_manage_keys_default.png b/org_apg/res/drawable-hdpi/dashboard_manage_keys_default.png deleted file mode 100644 index de83398c2..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_manage_keys_default.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_manage_keys_pressed.png b/org_apg/res/drawable-hdpi/dashboard_manage_keys_pressed.png deleted file mode 100644 index a86bc1bf9..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_manage_keys_pressed.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_my_keys_default.png b/org_apg/res/drawable-hdpi/dashboard_my_keys_default.png deleted file mode 100644 index f8b54961e..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_my_keys_default.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_my_keys_pressed.png b/org_apg/res/drawable-hdpi/dashboard_my_keys_pressed.png deleted file mode 100644 index 6a5c92138..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_my_keys_pressed.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_scan_qrcode_default.png b/org_apg/res/drawable-hdpi/dashboard_scan_qrcode_default.png deleted file mode 100644 index 632a8ce62..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_scan_qrcode_default.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/dashboard_scan_qrcode_pressed.png b/org_apg/res/drawable-hdpi/dashboard_scan_qrcode_pressed.png deleted file mode 100644 index 62a7a05ab..000000000 Binary files a/org_apg/res/drawable-hdpi/dashboard_scan_qrcode_pressed.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/encrypted.png b/org_apg/res/drawable-hdpi/encrypted.png deleted file mode 100644 index 541781cd1..000000000 Binary files a/org_apg/res/drawable-hdpi/encrypted.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/encrypted_large.png b/org_apg/res/drawable-hdpi/encrypted_large.png deleted file mode 100644 index 209278377..000000000 Binary files a/org_apg/res/drawable-hdpi/encrypted_large.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/encrypted_small.png b/org_apg/res/drawable-hdpi/encrypted_small.png deleted file mode 100644 index 3ff8e9b97..000000000 Binary files a/org_apg/res/drawable-hdpi/encrypted_small.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_menu_about.png b/org_apg/res/drawable-hdpi/ic_menu_about.png deleted file mode 100644 index 8f39c428a..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_menu_about.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_menu_decrypt.png b/org_apg/res/drawable-hdpi/ic_menu_decrypt.png deleted file mode 100644 index 6b98ad50c..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_menu_decrypt.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_menu_encrypt.png b/org_apg/res/drawable-hdpi/ic_menu_encrypt.png deleted file mode 100644 index 58689b43a..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_menu_encrypt.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_menu_filebrowser.png b/org_apg/res/drawable-hdpi/ic_menu_filebrowser.png deleted file mode 100644 index 3db304fa8..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_menu_filebrowser.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_menu_scan_qrcode.png b/org_apg/res/drawable-hdpi/ic_menu_scan_qrcode.png deleted file mode 100644 index b119b1999..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_menu_scan_qrcode.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_menu_search.png b/org_apg/res/drawable-hdpi/ic_menu_search.png deleted file mode 100644 index 1cb61faf4..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_menu_search.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_menu_search_list.png b/org_apg/res/drawable-hdpi/ic_menu_search_list.png deleted file mode 100644 index efee6dfd2..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_menu_search_list.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_menu_settings.png b/org_apg/res/drawable-hdpi/ic_menu_settings.png deleted file mode 100644 index 577e05587..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_menu_settings.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_next.png b/org_apg/res/drawable-hdpi/ic_next.png deleted file mode 100644 index d71058055..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_next.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/ic_previous.png b/org_apg/res/drawable-hdpi/ic_previous.png deleted file mode 100644 index d610e4667..000000000 Binary files a/org_apg/res/drawable-hdpi/ic_previous.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/icon.png b/org_apg/res/drawable-hdpi/icon.png deleted file mode 100644 index 6b8cc3d73..000000000 Binary files a/org_apg/res/drawable-hdpi/icon.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/key.png b/org_apg/res/drawable-hdpi/key.png deleted file mode 100644 index af4742ec0..000000000 Binary files a/org_apg/res/drawable-hdpi/key.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/key_large.png b/org_apg/res/drawable-hdpi/key_large.png deleted file mode 100644 index 590f7d5a4..000000000 Binary files a/org_apg/res/drawable-hdpi/key_large.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/key_small.png b/org_apg/res/drawable-hdpi/key_small.png deleted file mode 100644 index 6966048a1..000000000 Binary files a/org_apg/res/drawable-hdpi/key_small.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/overlay_error.png b/org_apg/res/drawable-hdpi/overlay_error.png deleted file mode 100644 index e6d7e60ba..000000000 Binary files a/org_apg/res/drawable-hdpi/overlay_error.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/overlay_ok.png b/org_apg/res/drawable-hdpi/overlay_ok.png deleted file mode 100644 index 0672f869d..000000000 Binary files a/org_apg/res/drawable-hdpi/overlay_ok.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/signed.png b/org_apg/res/drawable-hdpi/signed.png deleted file mode 100644 index ab9495e7b..000000000 Binary files a/org_apg/res/drawable-hdpi/signed.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/signed_large.png b/org_apg/res/drawable-hdpi/signed_large.png deleted file mode 100644 index c209f4167..000000000 Binary files a/org_apg/res/drawable-hdpi/signed_large.png and /dev/null differ diff --git a/org_apg/res/drawable-hdpi/signed_small.png b/org_apg/res/drawable-hdpi/signed_small.png deleted file mode 100644 index 54c4906e8..000000000 Binary files a/org_apg/res/drawable-hdpi/signed_small.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/encrypted.png b/org_apg/res/drawable-ldpi/encrypted.png deleted file mode 100644 index bcd8cfc8e..000000000 Binary files a/org_apg/res/drawable-ldpi/encrypted.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/encrypted_large.png b/org_apg/res/drawable-ldpi/encrypted_large.png deleted file mode 100644 index 34c3d3f97..000000000 Binary files a/org_apg/res/drawable-ldpi/encrypted_large.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/encrypted_small.png b/org_apg/res/drawable-ldpi/encrypted_small.png deleted file mode 100644 index 5e7294a4b..000000000 Binary files a/org_apg/res/drawable-ldpi/encrypted_small.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/ic_next.png b/org_apg/res/drawable-ldpi/ic_next.png deleted file mode 100644 index 474ed8faa..000000000 Binary files a/org_apg/res/drawable-ldpi/ic_next.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/ic_previous.png b/org_apg/res/drawable-ldpi/ic_previous.png deleted file mode 100644 index 6fd885e6b..000000000 Binary files a/org_apg/res/drawable-ldpi/ic_previous.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/icon.png b/org_apg/res/drawable-ldpi/icon.png deleted file mode 100644 index a1adf6bcb..000000000 Binary files a/org_apg/res/drawable-ldpi/icon.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/key.png b/org_apg/res/drawable-ldpi/key.png deleted file mode 100644 index c806b6041..000000000 Binary files a/org_apg/res/drawable-ldpi/key.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/key_large.png b/org_apg/res/drawable-ldpi/key_large.png deleted file mode 100644 index aa499a5e1..000000000 Binary files a/org_apg/res/drawable-ldpi/key_large.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/key_small.png b/org_apg/res/drawable-ldpi/key_small.png deleted file mode 100644 index 073b95029..000000000 Binary files a/org_apg/res/drawable-ldpi/key_small.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/overlay_error.png b/org_apg/res/drawable-ldpi/overlay_error.png deleted file mode 100644 index e5a88e18f..000000000 Binary files a/org_apg/res/drawable-ldpi/overlay_error.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/overlay_ok.png b/org_apg/res/drawable-ldpi/overlay_ok.png deleted file mode 100644 index 63374d47f..000000000 Binary files a/org_apg/res/drawable-ldpi/overlay_ok.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/signed.png b/org_apg/res/drawable-ldpi/signed.png deleted file mode 100644 index 4202c3f97..000000000 Binary files a/org_apg/res/drawable-ldpi/signed.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/signed_large.png b/org_apg/res/drawable-ldpi/signed_large.png deleted file mode 100644 index d2917644c..000000000 Binary files a/org_apg/res/drawable-ldpi/signed_large.png and /dev/null differ diff --git a/org_apg/res/drawable-ldpi/signed_small.png b/org_apg/res/drawable-ldpi/signed_small.png deleted file mode 100644 index 19d45f8da..000000000 Binary files a/org_apg/res/drawable-ldpi/signed_small.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_disable.png b/org_apg/res/drawable-mdpi-finger/btn_circle_disable.png deleted file mode 100644 index 33b74a66c..000000000 Binary files a/org_apg/res/drawable-mdpi-finger/btn_circle_disable.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_disable_focused.png b/org_apg/res/drawable-mdpi-finger/btn_circle_disable_focused.png deleted file mode 100644 index 005ad8dca..000000000 Binary files a/org_apg/res/drawable-mdpi-finger/btn_circle_disable_focused.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_normal.png b/org_apg/res/drawable-mdpi-finger/btn_circle_normal.png deleted file mode 100644 index fc5af1c9f..000000000 Binary files a/org_apg/res/drawable-mdpi-finger/btn_circle_normal.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_pressed.png b/org_apg/res/drawable-mdpi-finger/btn_circle_pressed.png deleted file mode 100644 index 8f40afdfc..000000000 Binary files a/org_apg/res/drawable-mdpi-finger/btn_circle_pressed.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_selected.png b/org_apg/res/drawable-mdpi-finger/btn_circle_selected.png deleted file mode 100644 index c74fac227..000000000 Binary files a/org_apg/res/drawable-mdpi-finger/btn_circle_selected.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi-finger/ic_btn_round_minus.png b/org_apg/res/drawable-mdpi-finger/ic_btn_round_minus.png deleted file mode 100644 index 96dbb17d2..000000000 Binary files a/org_apg/res/drawable-mdpi-finger/ic_btn_round_minus.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi-finger/ic_btn_round_plus.png b/org_apg/res/drawable-mdpi-finger/ic_btn_round_plus.png deleted file mode 100644 index 1ec8a956a..000000000 Binary files a/org_apg/res/drawable-mdpi-finger/ic_btn_round_plus.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png b/org_apg/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png deleted file mode 100644 index 0706c8af6..000000000 Binary files a/org_apg/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/encrypted.png b/org_apg/res/drawable-mdpi/encrypted.png deleted file mode 100644 index 34c3d3f97..000000000 Binary files a/org_apg/res/drawable-mdpi/encrypted.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/encrypted_large.png b/org_apg/res/drawable-mdpi/encrypted_large.png deleted file mode 100644 index 541781cd1..000000000 Binary files a/org_apg/res/drawable-mdpi/encrypted_large.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/encrypted_small.png b/org_apg/res/drawable-mdpi/encrypted_small.png deleted file mode 100644 index bcd8cfc8e..000000000 Binary files a/org_apg/res/drawable-mdpi/encrypted_small.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_menu_about.png b/org_apg/res/drawable-mdpi/ic_menu_about.png deleted file mode 100644 index 7c57436fc..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_menu_about.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_menu_decrypt.png b/org_apg/res/drawable-mdpi/ic_menu_decrypt.png deleted file mode 100644 index 84557bbee..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_menu_decrypt.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_menu_encrypt.png b/org_apg/res/drawable-mdpi/ic_menu_encrypt.png deleted file mode 100644 index 4ee0de4af..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_menu_encrypt.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_menu_filebrowser.png b/org_apg/res/drawable-mdpi/ic_menu_filebrowser.png deleted file mode 100644 index fda13f1be..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_menu_filebrowser.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_menu_scan_qrcode.png b/org_apg/res/drawable-mdpi/ic_menu_scan_qrcode.png deleted file mode 100644 index 08fb51400..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_menu_scan_qrcode.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_menu_search.png b/org_apg/res/drawable-mdpi/ic_menu_search.png deleted file mode 100644 index 2369d03f3..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_menu_search.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_menu_search_list.png b/org_apg/res/drawable-mdpi/ic_menu_search_list.png deleted file mode 100644 index 9033f1ec2..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_menu_search_list.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_menu_settings.png b/org_apg/res/drawable-mdpi/ic_menu_settings.png deleted file mode 100644 index f32a37e44..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_menu_settings.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_next.png b/org_apg/res/drawable-mdpi/ic_next.png deleted file mode 100644 index 8271c1380..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_next.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/ic_previous.png b/org_apg/res/drawable-mdpi/ic_previous.png deleted file mode 100644 index ef90db972..000000000 Binary files a/org_apg/res/drawable-mdpi/ic_previous.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/icon.png b/org_apg/res/drawable-mdpi/icon.png deleted file mode 100644 index 6b10a2ad3..000000000 Binary files a/org_apg/res/drawable-mdpi/icon.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/key.png b/org_apg/res/drawable-mdpi/key.png deleted file mode 100644 index aa499a5e1..000000000 Binary files a/org_apg/res/drawable-mdpi/key.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/key_large.png b/org_apg/res/drawable-mdpi/key_large.png deleted file mode 100644 index af4742ec0..000000000 Binary files a/org_apg/res/drawable-mdpi/key_large.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/key_small.png b/org_apg/res/drawable-mdpi/key_small.png deleted file mode 100644 index c806b6041..000000000 Binary files a/org_apg/res/drawable-mdpi/key_small.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/overlay_error.png b/org_apg/res/drawable-mdpi/overlay_error.png deleted file mode 100644 index 5fe017433..000000000 Binary files a/org_apg/res/drawable-mdpi/overlay_error.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/overlay_ok.png b/org_apg/res/drawable-mdpi/overlay_ok.png deleted file mode 100644 index b4f332260..000000000 Binary files a/org_apg/res/drawable-mdpi/overlay_ok.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/signed.png b/org_apg/res/drawable-mdpi/signed.png deleted file mode 100644 index d2917644c..000000000 Binary files a/org_apg/res/drawable-mdpi/signed.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/signed_large.png b/org_apg/res/drawable-mdpi/signed_large.png deleted file mode 100644 index ab9495e7b..000000000 Binary files a/org_apg/res/drawable-mdpi/signed_large.png and /dev/null differ diff --git a/org_apg/res/drawable-mdpi/signed_small.png b/org_apg/res/drawable-mdpi/signed_small.png deleted file mode 100644 index 4202c3f97..000000000 Binary files a/org_apg/res/drawable-mdpi/signed_small.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png b/org_apg/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png deleted file mode 100644 index 8155fe840..000000000 Binary files a/org_apg/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/ic_menu_about.png b/org_apg/res/drawable-xhdpi/ic_menu_about.png deleted file mode 100644 index 2641f142a..000000000 Binary files a/org_apg/res/drawable-xhdpi/ic_menu_about.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/ic_menu_decrypt.png b/org_apg/res/drawable-xhdpi/ic_menu_decrypt.png deleted file mode 100644 index 23ee08bee..000000000 Binary files a/org_apg/res/drawable-xhdpi/ic_menu_decrypt.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/ic_menu_encrypt.png b/org_apg/res/drawable-xhdpi/ic_menu_encrypt.png deleted file mode 100644 index 04562f33a..000000000 Binary files a/org_apg/res/drawable-xhdpi/ic_menu_encrypt.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/ic_menu_filebrowser.png b/org_apg/res/drawable-xhdpi/ic_menu_filebrowser.png deleted file mode 100644 index d1324014d..000000000 Binary files a/org_apg/res/drawable-xhdpi/ic_menu_filebrowser.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/ic_menu_scan_qrcode.png b/org_apg/res/drawable-xhdpi/ic_menu_scan_qrcode.png deleted file mode 100644 index 015e6c6a1..000000000 Binary files a/org_apg/res/drawable-xhdpi/ic_menu_scan_qrcode.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/ic_menu_search.png b/org_apg/res/drawable-xhdpi/ic_menu_search.png deleted file mode 100644 index 578cb24eb..000000000 Binary files a/org_apg/res/drawable-xhdpi/ic_menu_search.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/ic_menu_search_list.png b/org_apg/res/drawable-xhdpi/ic_menu_search_list.png deleted file mode 100644 index de20fa0e7..000000000 Binary files a/org_apg/res/drawable-xhdpi/ic_menu_search_list.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/ic_menu_settings.png b/org_apg/res/drawable-xhdpi/ic_menu_settings.png deleted file mode 100644 index aa33c3880..000000000 Binary files a/org_apg/res/drawable-xhdpi/ic_menu_settings.png and /dev/null differ diff --git a/org_apg/res/drawable-xhdpi/icon.png b/org_apg/res/drawable-xhdpi/icon.png deleted file mode 100644 index 03ee31bbd..000000000 Binary files a/org_apg/res/drawable-xhdpi/icon.png and /dev/null differ diff --git a/org_apg/res/drawable/btn_circle_disable.png b/org_apg/res/drawable/btn_circle_disable.png deleted file mode 100644 index 33b74a66c..000000000 Binary files a/org_apg/res/drawable/btn_circle_disable.png and /dev/null differ diff --git a/org_apg/res/drawable/btn_circle_disable_focused.png b/org_apg/res/drawable/btn_circle_disable_focused.png deleted file mode 100644 index 005ad8dca..000000000 Binary files a/org_apg/res/drawable/btn_circle_disable_focused.png and /dev/null differ diff --git a/org_apg/res/drawable/btn_circle_normal.png b/org_apg/res/drawable/btn_circle_normal.png deleted file mode 100644 index fc5af1c9f..000000000 Binary files a/org_apg/res/drawable/btn_circle_normal.png and /dev/null differ diff --git a/org_apg/res/drawable/btn_circle_pressed.png b/org_apg/res/drawable/btn_circle_pressed.png deleted file mode 100644 index 8f40afdfc..000000000 Binary files a/org_apg/res/drawable/btn_circle_pressed.png and /dev/null differ diff --git a/org_apg/res/drawable/btn_circle_selected.png b/org_apg/res/drawable/btn_circle_selected.png deleted file mode 100644 index c74fac227..000000000 Binary files a/org_apg/res/drawable/btn_circle_selected.png and /dev/null differ diff --git a/org_apg/res/drawable/dashboard_decrypt.xml b/org_apg/res/drawable/dashboard_decrypt.xml deleted file mode 100644 index 981e38a0b..000000000 --- a/org_apg/res/drawable/dashboard_decrypt.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/org_apg/res/drawable/dashboard_encrypt.xml b/org_apg/res/drawable/dashboard_encrypt.xml deleted file mode 100644 index af812dc51..000000000 --- a/org_apg/res/drawable/dashboard_encrypt.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/org_apg/res/drawable/dashboard_help.xml b/org_apg/res/drawable/dashboard_help.xml deleted file mode 100644 index e121ea0d1..000000000 --- a/org_apg/res/drawable/dashboard_help.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/org_apg/res/drawable/dashboard_manage_keys.xml b/org_apg/res/drawable/dashboard_manage_keys.xml deleted file mode 100644 index ebc519253..000000000 --- a/org_apg/res/drawable/dashboard_manage_keys.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/org_apg/res/drawable/dashboard_my_keys.xml b/org_apg/res/drawable/dashboard_my_keys.xml deleted file mode 100644 index d4045db45..000000000 --- a/org_apg/res/drawable/dashboard_my_keys.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/org_apg/res/drawable/dashboard_scan_qrcode.xml b/org_apg/res/drawable/dashboard_scan_qrcode.xml deleted file mode 100644 index 400994820..000000000 --- a/org_apg/res/drawable/dashboard_scan_qrcode.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/org_apg/res/drawable/encrypted.png b/org_apg/res/drawable/encrypted.png deleted file mode 100644 index 2783804bc..000000000 Binary files a/org_apg/res/drawable/encrypted.png and /dev/null differ diff --git a/org_apg/res/drawable/encrypted_large.png b/org_apg/res/drawable/encrypted_large.png deleted file mode 100644 index 6d7c616a4..000000000 Binary files a/org_apg/res/drawable/encrypted_large.png and /dev/null differ diff --git a/org_apg/res/drawable/encrypted_small.png b/org_apg/res/drawable/encrypted_small.png deleted file mode 100644 index 7f4ab803f..000000000 Binary files a/org_apg/res/drawable/encrypted_small.png and /dev/null differ diff --git a/org_apg/res/drawable/ic_btn_round_minus.png b/org_apg/res/drawable/ic_btn_round_minus.png deleted file mode 100644 index 96dbb17d2..000000000 Binary files a/org_apg/res/drawable/ic_btn_round_minus.png and /dev/null differ diff --git a/org_apg/res/drawable/ic_btn_round_plus.png b/org_apg/res/drawable/ic_btn_round_plus.png deleted file mode 100644 index 1ec8a956a..000000000 Binary files a/org_apg/res/drawable/ic_btn_round_plus.png and /dev/null differ diff --git a/org_apg/res/drawable/ic_launcher_folder.png b/org_apg/res/drawable/ic_launcher_folder.png deleted file mode 100644 index ed31ba580..000000000 Binary files a/org_apg/res/drawable/ic_launcher_folder.png and /dev/null differ diff --git a/org_apg/res/drawable/ic_launcher_folder_small.png b/org_apg/res/drawable/ic_launcher_folder_small.png deleted file mode 100644 index 5df8d60f0..000000000 Binary files a/org_apg/res/drawable/ic_launcher_folder_small.png and /dev/null differ diff --git a/org_apg/res/drawable/ic_next.png b/org_apg/res/drawable/ic_next.png deleted file mode 100644 index 8271c1380..000000000 Binary files a/org_apg/res/drawable/ic_next.png and /dev/null differ diff --git a/org_apg/res/drawable/ic_previous.png b/org_apg/res/drawable/ic_previous.png deleted file mode 100644 index ef90db972..000000000 Binary files a/org_apg/res/drawable/ic_previous.png and /dev/null differ diff --git a/org_apg/res/drawable/key.png b/org_apg/res/drawable/key.png deleted file mode 100644 index de7e72524..000000000 Binary files a/org_apg/res/drawable/key.png and /dev/null differ diff --git a/org_apg/res/drawable/key_large.png b/org_apg/res/drawable/key_large.png deleted file mode 100644 index 6f18c0240..000000000 Binary files a/org_apg/res/drawable/key_large.png and /dev/null differ diff --git a/org_apg/res/drawable/key_small.png b/org_apg/res/drawable/key_small.png deleted file mode 100644 index 121803508..000000000 Binary files a/org_apg/res/drawable/key_small.png and /dev/null differ diff --git a/org_apg/res/drawable/overlay_error.png b/org_apg/res/drawable/overlay_error.png deleted file mode 100644 index 2372de59e..000000000 Binary files a/org_apg/res/drawable/overlay_error.png and /dev/null differ diff --git a/org_apg/res/drawable/overlay_ok.png b/org_apg/res/drawable/overlay_ok.png deleted file mode 100644 index 2f0005898..000000000 Binary files a/org_apg/res/drawable/overlay_ok.png and /dev/null differ diff --git a/org_apg/res/drawable/section_header.xml b/org_apg/res/drawable/section_header.xml deleted file mode 100644 index a4468484e..000000000 --- a/org_apg/res/drawable/section_header.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/org_apg/res/drawable/signed.png b/org_apg/res/drawable/signed.png deleted file mode 100644 index 490e94fbd..000000000 Binary files a/org_apg/res/drawable/signed.png and /dev/null differ diff --git a/org_apg/res/drawable/signed_large.png b/org_apg/res/drawable/signed_large.png deleted file mode 100644 index 92e64dc51..000000000 Binary files a/org_apg/res/drawable/signed_large.png and /dev/null differ diff --git a/org_apg/res/drawable/signed_small.png b/org_apg/res/drawable/signed_small.png deleted file mode 100644 index 590220281..000000000 Binary files a/org_apg/res/drawable/signed_small.png and /dev/null differ diff --git a/org_apg/res/layout/account_item.xml b/org_apg/res/layout/account_item.xml deleted file mode 100644 index 0aa76719a..000000000 --- a/org_apg/res/layout/account_item.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - diff --git a/org_apg/res/layout/create_key.xml b/org_apg/res/layout/create_key.xml deleted file mode 100644 index 235d33f88..000000000 --- a/org_apg/res/layout/create_key.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org_apg/res/layout/decrypt.xml b/org_apg/res/layout/decrypt.xml deleted file mode 100644 index 055436abd..000000000 --- a/org_apg/res/layout/decrypt.xml +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org_apg/res/layout/edit_key.xml b/org_apg/res/layout/edit_key.xml deleted file mode 100644 index 3ab5f1c52..000000000 --- a/org_apg/res/layout/edit_key.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - diff --git a/org_apg/res/raw/help_about.html b/org_apg/res/raw/help_about.html deleted file mode 100644 index 7781ca85a..000000000 --- a/org_apg/res/raw/help_about.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - -

https://github.com/dschuermann/apg

-

Android Privacy Guard (APG) is an OpenPGP implementation for Android.

-

License: Apache License v2

- -

Developer

-
    -
  • Thialfihar (Main developer v1.x)
  • -
  • Senecaso (QRCode, sign key, upload key)
  • -
  • Markus Doits (AIDL)
  • -
  • Oliver Runge
  • -
  • Dominik Schürmann (Developer v2.x)
  • -
- -

Libraries

- - - \ No newline at end of file diff --git a/org_apg/res/raw/help_changelog.html b/org_apg/res/raw/help_changelog.html deleted file mode 100644 index bf013342b..000000000 --- a/org_apg/res/raw/help_changelog.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - -

2.0

-
    -
  • Complete redesign
  • -
  • Integration of different branches:
  • -
  • Share public keys via qr codes
  • -
  • Sign keys
  • -
  • Upload keys to server
  • -
- -

1.08

-
    -
  • basic key server support
  • -
  • app2sd (untested, let me know if there are problems)
  • -
  • more choices for pass phrase cache: 1, 2, 4, 8, hours
  • -
  • translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
  • -
  • bugfixes
  • -
  • optimizations
  • -
- -

1.0.7

-
    -
  • clear sign problem with lacking trailing newline fixed
  • -
  • more options for pass phrase cache time to live (20, 40, 60 mins)
  • -
- -

1.0.6

-
    -
  • account adding crash on Froyo fixed
  • -
  • secure file deletion
  • -
  • option to delete key file after import
  • -
  • stream encryption/decryption (gallery, etc.)
  • -
  • new options (language, force v3 signatures)
  • -
  • interface changes
  • -
  • bugfixes
  • -
- -

1.0.5

-
    -
  • German and Italian translation
  • -
  • much smaller package, due to reduced BC sources
  • -
  • new preferences GUI
  • -
  • layout adjustment for localization
  • -
  • signature bugfix
  • -
- -

1.0.4

-
    -
  • fixed another crash caused by some SDK bug with query builder
  • -
- -

1.0.3

-
    -
  • fixed crashes during encryption/signing and possibly key export
  • -
- -

1.0.2

-
    -
  • filterable key lists
  • -
  • smarter preselection of encryption keys
  • -
  • new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
  • -
  • fixes and additional features (key preselection) for k9, new beta build available
  • -
- -

1.0.1

-
    -
  • GMail account listing was broken in 1.0.0, fixed again
  • -
- -

1.0.0

-
    -
  • k9mail integration, APG supporting beta build of k9mail
  • -
  • support of more file managers (including ASTRO)
  • -
  • Slovenian translation
  • -
  • new database, much faster, less memory usage
  • -
  • defined Intents and content provider for other apps
  • -
  • bugfixes
  • -
- - \ No newline at end of file diff --git a/org_apg/res/raw/help_start.html b/org_apg/res/raw/help_start.html deleted file mode 100644 index db7559632..000000000 --- a/org_apg/res/raw/help_start.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - -

Beta software

-

This is beta software. It contains many remaining bugs!

- -

APG

-

Android Privacy Guard (APG) is a OpenPGP implementation for Android. -
This is a fork based on the original APG to introduce more features and a new user interface.

- -

WARNING

-

Be careful editing your existing keys, as they WILL be stripped of certificates right now. -

Also: key cross-certification is NOT supported, so signing with those keys will get a warning when the signature is checked.

- -

Getting started

-

Install K-9 Mail for the best integration, it supports APG for PGP/INLINE and lets you directly encrypt/decrypt emails. -
It is recommended that you install OI File Manager to be able to use the browse button for file selection in APG. -
First you need some keys. Either import them via the option menus in "Manage Keys" and "My Keys" or create them in "My Keys".

- -

I found a bug in APG!

-

Please report it in the issue tracker of APG.

- - \ No newline at end of file diff --git a/org_apg/res/values/arrays.xml b/org_apg/res/values/arrays.xml deleted file mode 100644 index 05320c609..000000000 --- a/org_apg/res/values/arrays.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - @string/choice_15secs - @string/choice_1min - @string/choice_3mins - @string/choice_5mins - @string/choice_10mins - @string/choice_20mins - @string/choice_40mins - @string/choice_1hour - @string/choice_2hours - @string/choice_4hours - @string/choice_8hours - - - 15 - 60 - 180 - 300 - 600 - 1200 - 2400 - 3600 - 7200 - 14400 - 28800 - - - \ No newline at end of file diff --git a/org_apg/res/values/colors.xml b/org_apg/res/values/colors.xml deleted file mode 100644 index d1dc6c1f4..000000000 --- a/org_apg/res/values/colors.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - #31b6e7 - #cecbce - - \ No newline at end of file diff --git a/org_apg/res/values/static_strings.xml b/org_apg/res/values/static_strings.xml deleted file mode 100644 index c69cf8adf..000000000 --- a/org_apg/res/values/static_strings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - APG - https://github.com/dschuermann/apg - - \ No newline at end of file diff --git a/org_apg/res/values/strings.xml b/org_apg/res/values/strings.xml deleted file mode 100644 index d2858559d..000000000 --- a/org_apg/res/values/strings.xml +++ /dev/null @@ -1,352 +0,0 @@ - - - - - - - Mail Inbox - Manage Public Keys - Manage Secret Keys - Select Recipients - Select Signature - Encrypt - Decrypt - Passphrase - Create Key - Edit Key - Create Key - Preferences - Key Server Preference - Change Passphrase - Set Passphrase - "Send Mail…" - Encrypt To File - Decrypt To File - Import Keys - Export Key - Export Keys - Key Not Found - Getting Started - Query Key Server - Export to Key Server - Unknown Signature Key - Import from QR Code - Sign Key - About - Help - - - User IDs - Keys - General - Defaults - Advanced - - - Sign (Clipboard) - Encrypt (Clipboard) - Encrypt (Email) - Sign (Email) - Encrypt - Sign - Decrypt - Verify - Select Recipients - Reply - Encrypt Message - Decrypt Message - Encrypt File - Decrypt File - Save - Cancel - Delete - None - Okay - Clear Filter - Change Passphrase - Set Passphrase - Search - Export To Server - - - About - Delete Account - Manage Public Keys - Manage Secret Keys - Settings - Import Keys - Export All Keys - Export Key - Delete Key - Create Key - Edit Key - Search - Help - Key Server - Update from Server - Export To Server - Share public key with QR Code - Scan QR Code - Sign Key - - - Sign - Message - File - No Passphrase - Passphrase - Again - Algorithm - ASCII Armour - Public Key(s) - Delete After Encryption - Delete After Decryption - Delete After Import - Encryption Algorithm - Hash Algorithm - Public Key - Passphrase - Passphrase Cache - Message Compression - File Compression - Force V3 Signatures - Key Servers - Key ID - Creation - Expiry - Usage - Key Size - Main User ID - Name - Comment - Email - Send Key to Server? - Select - 1 Selected - Selected - <unknown> - <none> - <no key> - - - <no expiry> - - can encrypt - can sign - expired - not valid - %s key server(s) - fingerprint - - - None - Sign only - Encrypt only - Sign and Encrypt - 15 secs - 1 min - 3 mins - 5 mins - 10 mins - 20 mins - 40 mins - 1 hour - 2 hours - 4 hours - 8 hours - until quit - System default - DSA - ElGamal - RSA - Open… - Save As… - Select File To Encrypt… - Select File To Decrypt… - Open - Save - Warning - Error - Warning: %s - Error: %s - - - Wrong passphrase. - Using clipboard content. - Key saved. - Set a passphrase first. - No compatible file manager installed. - The passphrases didn\'t match. - Empty passphrases are not allowed. - Symmetric encryption. - Enter passphrase for \'%s\' - Are you sure you want to delete\n%s? - Successfully deleted. - Select a file first. - Successfully decrypted. - Successfully encrypted. - Successfully encrypted to clipboard. - Enter the passphrase twice. - Select at least one encryption key. - Select at least one encryption key or a signature key. - Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists. - Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists. - Please specify which file to import keys from. (.asc or .gpg) - Please specify which file to export to.\nWARNING! File will be overwritten if it exists. - Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists. - Do you really want to delete the key \'%s\'?\nYou can\'t undo this! - Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this! - Successfully added %1$s key(s) and updated %2$s key(s). - Successfully added %s key(s). - Successfully updated %s key(s). - No keys added or updated. - Successfully exported 1 key. - Successfully exported %s keys. - No keys exported. - Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used. - Couldn\'t find key %08X. - Found %s key(s). - Unknown signature, touch to look up key. - Key editing is still kind of beta. - %s bad secret key(s) ignored. Perhaps you exported with the option\n --export-secret-subkeys\nMake sure you export with\n --export-secret-keys\ninstead. - Unknown key %s, do you want to try finding it on a keyserver? - Successfully sent key to server - Successfully signed key - Successfully validated and imported key - Long press one entry in this list to show more options! - This list is empty! - - - deleting \'%s\' failed - file not found - no suitable secret key found - no known kind of encryption found - external storage not ready - adding account \'%s\' failed - invalid email \'%s\' - key size must be at least 512bit - the master key cannot be an ElGamal key - unknown algorithm choice - you need to specify a name - you need to specify an email address - need at least one user id - main user id must not be empty - need at least a master key - expiry date must come after creation date - no encryption key(s) or passphrase given - signature failed - no passphrase given - no signature key given - not valid encryption data - corrupt data - couldn\'t find a packet with symmetric encryption - wrong passphrase - error saving some key(s) - could not extract private key - Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_ENCRYPT_STREAM_AND_RETURN. - - - done. - initializing… - saving… - importing… - exporting… - generating key, this can take a while… - building key… - preparing master key… - certifying master key… - building master key ring… - adding sub keys… - saving key ring… - importing secret keys… - importing public keys… - reloading keys… - exporting key… - exporting keys… - extracting signature key… - extracting key… - preparing streams… - encrypting data… - decrypting data… - preparing signature… - generating signature… - processing signature… - verifying signature… - signing… - reading data… - finding key… - decompressing data… - verifying integrity… - deleting \'%s\' securely… - querying… - querying %s… - - - APG - Permissions to use APG - Read key details of public and secret keys (The keys themselves can NOT be read.) - Read key details of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read. - Encrypt/Sign/Decrypt/Create keys without user interaction - Encrypt/Sign/Decrypt/Create keys (by using Intents or Remote Service) without user interaction - - - Encrypt - Decrypt - Import Public Keys - Import Secret Keys - Search Public Keys - Search Secret Keys - Filter: \"%s\" - - - fast - slow - very slow - - - - - - Manage Keys - My Keys - Encrypt - Decrypt - Help - Scan QRCode - - - Start - Changelog - About - Version: - - - Import key (only locally) - Import, Sign, and upload key - Scan qr code again - Finish - - - APG: Decrypt File - APG: Import Key - APG: Encrypt - APG: Decrypt - - \ No newline at end of file diff --git a/org_apg/res/values/styles.xml b/org_apg/res/values/styles.xml deleted file mode 100644 index ef3ff63f6..000000000 --- a/org_apg/res/values/styles.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org_apg/res/xml/apg_preferences.xml b/org_apg/res/xml/apg_preferences.xml deleted file mode 100644 index 778f51375..000000000 --- a/org_apg/res/xml/apg_preferences.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org_apg/res/xml/searchable_public_keys.xml b/org_apg/res/xml/searchable_public_keys.xml deleted file mode 100644 index e9602b121..000000000 --- a/org_apg/res/xml/searchable_public_keys.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/org_apg/res/xml/searchable_secret_keys.xml b/org_apg/res/xml/searchable_secret_keys.xml deleted file mode 100644 index a7e8873d6..000000000 --- a/org_apg/res/xml/searchable_secret_keys.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ApgApplication.java b/org_apg/src/org/thialfihar/android/apg/ApgApplication.java deleted file mode 100644 index f89375079..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ApgApplication.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg; - -import java.io.File; -import java.security.Security; - -import org.spongycastle.jce.provider.BouncyCastleProvider; - -import android.app.Application; -import android.os.Environment; - -public class ApgApplication extends Application { - - static { - // Define Java Security Provider to be Bouncy Castle - Security.addProvider(new BouncyCastleProvider()); - } - - @Override - public void onCreate() { - super.onCreate(); - - // Create APG directory on sdcard if not existing - 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 - } - } - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/Constants.java b/org_apg/src/org/thialfihar/android/apg/Constants.java deleted file mode 100644 index 95ab8158d..000000000 --- a/org_apg/src/org/thialfihar/android/apg/Constants.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg; - -import android.os.Environment; - -public final class Constants { - - public static final boolean DEBUG = true; - - public static final String TAG = "APG"; - - public static final String PACKAGE_NAME = "org.thialfihar.android.apg"; - - public static final String PERMISSION_ACCESS_KEY_DATABASE = PACKAGE_NAME - + ".permission.ACCESS_KEY_DATABASE"; - public static final String PERMISSION_ACCESS_API = PACKAGE_NAME + ".permission.ACCESS_API"; - - /* - * TODO: - * - * this would require patching K9Mail - * - * better naming scheme would be: - * - * - x.action.DECRYPT (with action as preferred in Android) - * - * - even better and shorter (without .android.): org.apg.action.DECRYPT - */ - public static final String INTENT_PREFIX = PACKAGE_NAME + ".intent."; - - public static final class path { - public static final String APP_DIR = Environment.getExternalStorageDirectory() + "/APG"; - } - - public static final class pref { - public static final String DEFAULT_ENCRYPTION_ALGORITHM = "defaultEncryptionAlgorithm"; - public static final String DEFAULT_HASH_ALGORITHM = "defaultHashAlgorithm"; - public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour"; - public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression"; - public static final String DEFAULT_FILE_COMPRESSION = "defaultFileCompression"; - public static final String PASS_PHRASE_CACHE_TTL = "passPhraseCacheTtl"; - public static final String LANGUAGE = "language"; - public static final String FORCE_V3_SIGNATURES = "forceV3Signatures"; - public static final String KEY_SERVERS = "keyServers"; - } - - public static final class defaults { - public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu"; - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/Id.java b/org_apg/src/org/thialfihar/android/apg/Id.java deleted file mode 100644 index e683f43b4..000000000 --- a/org_apg/src/org/thialfihar/android/apg/Id.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg; - -import org.spongycastle.bcpg.CompressionAlgorithmTags; - -/** - * - * TODO: - * - * - refactor ids, some are not needed and can be done with xml - * - */ -public final class Id { - - public static final String TAG = "APG"; - - public static final class menu { - public static final int export = 0x21070001; - public static final int delete = 0x21070002; - public static final int edit = 0x21070003; - public static final int update = 0x21070004; - public static final int exportToServer = 0x21070005; - public static final int share_qr_code = 0x21070006; - public static final int signKey = 0x21070007; - - public static final class option { - public static final int new_pass_phrase = 0x21070001; - public static final int create = 0x21070002; - public static final int about = 0x21070003; - public static final int manage_public_keys = 0x21070004; - public static final int manage_secret_keys = 0x21070005; - public static final int import_keys = 0x21070006; - public static final int export_keys = 0x21070007; - public static final int preferences = 0x21070008; - public static final int search = 0x21070009; - public static final int help = 0x21070010; - public static final int key_server = 0x21070011; - public static final int scanQRCode = 0x21070012; - public static final int encrypt = 0x21070013; - public static final int encrypt_to_clipboard = 0x21070014; - public static final int decrypt = 0x21070015; - public static final int reply = 0x21070016; - public static final int cancel = 0x21070017; - public static final int save = 0x21070018; - public static final int okay = 0x21070019; - - } - } - - // use only lower 16 bits due to compatibility lib - public static final class message { - public static final int progress_update = 0x00006001; - public static final int done = 0x00006002; - public static final int import_keys = 0x00006003; - public static final int export_keys = 0x00006004; - public static final int import_done = 0x00006005; - public static final int export_done = 0x00006006; - public static final int create_key = 0x00006007; - public static final int edit_key = 0x00006008; - public static final int delete_done = 0x00006009; - public static final int query_done = 0x00006010; - public static final int unknown_signature_key = 0x00006011; - } - - // public static final class message { - // public static final int progress_update = 0x21070001; - // public static final int done = 0x21070002; - // public static final int import_keys = 0x21070003; - // public static final int export_keys = 0x21070004; - // public static final int import_done = 0x21070005; - // public static final int export_done = 0x21070006; - // public static final int create_key = 0x21070007; - // public static final int edit_key = 0x21070008; - // public static final int delete_done = 0x21070009; - // public static final int query_done = 0x21070010; - // public static final int unknown_signature_key = 0x21070011; - // } - - // use only lower 16 bits due to compatibility lib - public static final class request { - public static final int public_keys = 0x00007001; - public static final int secret_keys = 0x00007002; - public static final int filename = 0x00007003; - public static final int output_filename = 0x00007004; - public static final int key_server_preference = 0x00007005; - public static final int look_up_key_id = 0x00007006; - public static final int export_to_server = 0x00007007; - public static final int import_from_qr_code = 0x00007008; - public static final int sign_key = 0x00007009; - } - - // public static final class request { - // public static final int public_keys = 0x21070001; - // public static final int secret_keys = 0x21070002; - // public static final int filename = 0x21070003; - // public static final int output_filename = 0x21070004; - // public static final int key_server_preference = 0x21070005; - // public static final int look_up_key_id = 0x21070006; - // public static final int export_to_server = 0x21070007; - // public static final int import_from_qr_code = 0x21070008; - // public static final int sign_key = 0x21070009; - // } - - public static final class dialog { - public static final int pass_phrase = 0x21070001; - public static final int encrypting = 0x21070002; - public static final int decrypting = 0x21070003; - public static final int new_pass_phrase = 0x21070004; - public static final int pass_phrases_do_not_match = 0x21070005; - public static final int no_pass_phrase = 0x21070006; - public static final int saving = 0x21070007; - public static final int delete_key = 0x21070008; - public static final int import_keys = 0x21070009; - public static final int importing = 0x2107000a; - public static final int export_key = 0x2107000b; - public static final int export_keys = 0x2107000c; - public static final int exporting = 0x2107000d; - public static final int new_account = 0x2107000e; - // public static final int about = 0x2107000f; - public static final int change_log = 0x21070010; - public static final int output_filename = 0x21070011; - public static final int delete_file = 0x21070012; - public static final int deleting = 0x21070013; - public static final int help = 0x21070014; - public static final int querying = 0x21070015; - public static final int lookup_unknown_key = 0x21070016; - public static final int signing = 0x21070017; - } - - public static final class task { - public static final int import_keys = 0x21070001; - public static final int export_keys = 0x21070002; - } - -// public static final class database { -// public static final int type_public = 0; -// public static final int type_secret = 1; -// } - - public static final class type { - public static final int public_key = 0x21070001; - public static final int secret_key = 0x21070002; - public static final int user_id = 0x21070003; - public static final int key = 0x21070004; - } - - public static final class choice { - public static final class algorithm { - public static final int dsa = 0x21070001; - public static final int elgamal = 0x21070002; - public static final int rsa = 0x21070003; - } - - public static final class compression { - public static final int none = 0x21070001; - public static final int zlib = CompressionAlgorithmTags.ZLIB; - public static final int bzip2 = CompressionAlgorithmTags.BZIP2; - public static final int zip = CompressionAlgorithmTags.ZIP; - } - - public static final class usage { - public static final int sign_only = 0x21070001; - public static final int encrypt_only = 0x21070002; - public static final int sign_and_encrypt = 0x21070003; - } - - public static final class action { - public static final int encrypt = 0x21070001; - public static final int decrypt = 0x21070002; - public static final int import_public = 0x21070003; - public static final int import_secret = 0x21070004; - } - } - - public static final class return_value { - public static final int ok = 0; - public static final int error = -1; - public static final int no_master_key = -2; - public static final int updated = 1; - public static final int bad = -3; - } - - public static final class target { - public static final int clipboard = 0x21070001; - public static final int email = 0x21070002; - public static final int file = 0x21070003; - public static final int message = 0x21070004; - } - - public static final class mode { - public static final int undefined = 0x21070001; - public static final int byte_array = 0x21070002; - public static final int file = 0x21070003; - public static final int stream = 0x21070004; - } - - public static final class key { - public static final int none = 0; - public static final int symmetric = -1; - } - - public static final class content { - public static final int unknown = 0; - public static final int encrypted_data = 1; - public static final int keys = 2; - } - - public static final class keyserver { - public static final int search = 0x21070001; - public static final int get = 0x21070002; - public static final int add = 0x21070003; - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/helper/FileHelper.java b/org_apg/src/org/thialfihar/android/apg/helper/FileHelper.java deleted file mode 100644 index 6efee19c0..000000000 --- a/org_apg/src/org/thialfihar/android/apg/helper/FileHelper.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.helper; - -import java.net.URISyntaxException; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.util.Log; - -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Environment; -import android.widget.Toast; - -public class FileHelper { - - /** - * Checks if external storage is mounted if file is located on external storage - * - * @param file - * @return true if storage is mounted - */ - public static boolean isStorageMounted(String file) { - if (file.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - return false; - } - } - - return true; - } - - /** - * Opens the preferred installed file manager on Android and shows a toast if no manager is - * installed. - * - * @param activity - * @param filename - * default selected file, not supported by all file managers - * @param type - * can be text/plain for example - * @param requestCode - * requestCode used to identify the result coming back from file manager to - * onActivityResult() in your activity - */ - public static void openFile(Activity activity, String filename, String type, int requestCode) { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - - intent.setData(Uri.parse("file://" + filename)); - intent.setType(type); - - try { - activity.startActivityForResult(intent, requestCode); - } catch (ActivityNotFoundException e) { - // No compatible file manager was found. - Toast.makeText(activity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show(); - } - } - - /** - * Get a file path from a Uri. - * - * from https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/ - * afilechooser/utils/FileUtils.java - * - * @param context - * @param uri - * @return - * - * @author paulburke - */ - public static String getPath(Context context, Uri uri) { - - Log.d(Constants.TAG + " File -", - "Authority: " + uri.getAuthority() + ", Fragment: " + uri.getFragment() - + ", Port: " + uri.getPort() + ", Query: " + uri.getQuery() + ", Scheme: " - + uri.getScheme() + ", Host: " + uri.getHost() + ", Segments: " - + uri.getPathSegments().toString()); - - if ("content".equalsIgnoreCase(uri.getScheme())) { - String[] projection = { "_data" }; - Cursor cursor = null; - - try { - cursor = context.getContentResolver().query(uri, projection, null, null, null); - int column_index = cursor.getColumnIndexOrThrow("_data"); - if (cursor.moveToFirst()) { - return cursor.getString(column_index); - } - } catch (Exception e) { - // Eat it - } - } - - else if ("file".equalsIgnoreCase(uri.getScheme())) { - return uri.getPath(); - } - - return null; - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/helper/OtherHelper.java b/org_apg/src/org/thialfihar/android/apg/helper/OtherHelper.java deleted file mode 100644 index bd5db2410..000000000 --- a/org_apg/src/org/thialfihar/android/apg/helper/OtherHelper.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.helper; - -import java.io.InputStream; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.Iterator; -import java.util.Set; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.util.Log; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; - -import android.app.Activity; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.widget.Toast; - -public class OtherHelper { - - /** - * Gets input stream of raw resource - * - * @param context - * current context - * @param resourceID - * of html file to read - * @return input stream of resource - */ - public static InputStream getInputStreamFromResource(Context context, int resourceID) { - InputStream raw = context.getResources().openRawResource(resourceID); - return raw; - } - - /** - * Return the number if days between two dates - * - * @param first - * @param second - * @return number of days - */ - public static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) { - GregorianCalendar tmp = new GregorianCalendar(); - tmp.setTime(first.getTime()); - long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400; - tmp.add(Calendar.DAY_OF_MONTH, (int) numDays); - while (tmp.before(second)) { - tmp.add(Calendar.DAY_OF_MONTH, 1); - ++numDays; - } - return numDays; - } - - /** - * Logs bundle content to debug for inspecting the content - * - * @param bundle - * @param bundleName - */ - public static void logDebugBundle(Bundle bundle, String bundleName) { - if (Constants.DEBUG) { - if (bundle != null) { - Set ks = bundle.keySet(); - Iterator iterator = ks.iterator(); - - Log.d(Constants.TAG, "Bundle " + bundleName + ":"); - Log.d(Constants.TAG, "------------------------------"); - while (iterator.hasNext()) { - String key = iterator.next(); - Object value = bundle.get(key); - - if (value != null) { - Log.d(Constants.TAG, key + " : " + value.toString()); - } else { - Log.d(Constants.TAG, key + " : null"); - } - } - Log.d(Constants.TAG, "------------------------------"); - } else { - Log.d(Constants.TAG, "Bundle " + bundleName + ": null"); - } - } - } - - /** - * Set actionbar without home button if called from another app - * - * @param activity - */ - public static void setActionBarBackButton(SherlockFragmentActivity activity) { - // set actionbar without home button if called from another app - final ActionBar actionBar = activity.getSupportActionBar(); - Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)=" - + activity.getCallingPackage()); - if (activity.getCallingPackage() != null - && activity.getCallingPackage().equals(Constants.PACKAGE_NAME)) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - } else { - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - } - } - - /** - * Check if the calling package has the needed permission to invoke an intent with specific - * restricted actions. - * - * If pkgName is null, this will also deny the use of the given action - * - * @param activity - * @param pkgName - * @param permName - * @param action - * @param restrictedActions - */ - public static void checkPackagePermissionForActions(Activity activity, String pkgName, - String permName, String action, String[] restrictedActions) { - if (action != null) { - PackageManager pkgManager = activity.getPackageManager(); - - for (int i = 0; i < restrictedActions.length; i++) { - if (restrictedActions[i].equals(action)) { - if (pkgName != null - && (pkgManager.checkPermission(permName, pkgName) == PackageManager.PERMISSION_GRANTED || pkgName - .equals(Constants.PACKAGE_NAME))) { - Log.d(Constants.TAG, pkgName + " has permission " + permName + ". Action " - + action + " was granted!"); - } else { - String error = pkgName + " does NOT have permission " + permName - + ". Action " + action + " was NOT granted!"; - Log.e(Constants.TAG, error); - Toast.makeText(activity, activity.getString(R.string.errorMessage, error), - Toast.LENGTH_LONG).show(); - - // end activity - activity.setResult(Activity.RESULT_CANCELED, null); - activity.finish(); - } - } - } - } - } - - /** - * Splits userId string into naming part and email part - * - * @param userId - * @return array with naming (0) and email (1) - */ - public static String[] splitUserId(String userId) { - String[] output = new String[2]; - - String chunks[] = userId.split(" <", 2); - userId = chunks[0]; - if (chunks.length > 1) { - output[1] = "<" + chunks[1]; - } - output[0] = userId; - - return output; - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java b/org_apg/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java deleted file mode 100644 index 040ff2a9e..000000000 --- a/org_apg/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.helper; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; - -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPObjectFactory; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; - -import org.thialfihar.android.apg.util.Log; - -public class PGPConversionHelper { - - /** - * Convert from byte[] to PGPKeyRing - * - * @param keysBytes - * @return - */ - public static PGPKeyRing BytesToPGPKeyRing(byte[] keysBytes) { - PGPObjectFactory factory = new PGPObjectFactory(keysBytes); - PGPKeyRing keyRing = null; - try { - if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) { - Log.e(Constants.TAG, "No keys given!"); - } - } catch (IOException e) { - Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e); - } - - return keyRing; - } - - /** - * Convert from byte[] to ArrayList - * - * @param keysBytes - * @return - */ - public static ArrayList BytesToPGPSecretKeyList(byte[] keysBytes) { - PGPSecretKeyRing keyRing = (PGPSecretKeyRing) BytesToPGPKeyRing(keysBytes); - ArrayList keys = new ArrayList(); - - @SuppressWarnings("unchecked") - Iterator itr = keyRing.getSecretKeys(); - while (itr.hasNext()) { - keys.add(itr.next()); - } - - return keys; - } - - /** - * Convert from byte[] to PGPSecretKey - * - * Singles keys are encoded as keyRings with one single key in it by Bouncy Castle - * - * @param keysBytes - * @return - */ - public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) { - PGPSecretKey key = BytesToPGPSecretKeyList(keyBytes).get(0); - - return key; - } - - /** - * Convert from ArrayList to byte[] - * - * @param keys - * @return - */ - public static byte[] PGPSecretKeyArrayListToBytes(ArrayList keys) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - for (PGPSecretKey key : keys) { - try { - key.encode(os); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while converting ArrayList to byte[]!", e); - } - } - - return os.toByteArray(); - } - - /** - * Convert from PGPSecretKey to byte[] - * - * @param keysBytes - * @return - */ - public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) { - try { - return key.getEncoded(); - } catch (IOException e) { - Log.e(Constants.TAG, "Encoding failed", e); - - return null; - } - } - - /** - * Convert from PGPSecretKeyRing to byte[] - * - * @param keysBytes - * @return - */ - public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) { - try { - return keyRing.getEncoded(); - } catch (IOException e) { - Log.e(Constants.TAG, "Encoding failed", e); - - return null; - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/helper/PGPHelper.java b/org_apg/src/org/thialfihar/android/apg/helper/PGPHelper.java deleted file mode 100644 index b566cffa0..000000000 --- a/org_apg/src/org/thialfihar/android/apg/helper/PGPHelper.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * 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.helper; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Vector; - -import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.bcpg.sig.KeyFlags; -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPObjectFactory; -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.spongycastle.openpgp.PGPSignature; -import org.spongycastle.openpgp.PGPSignatureSubpacketVector; -import org.spongycastle.openpgp.PGPUtil; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.util.IterableIterator; -import org.thialfihar.android.apg.util.Log; - -import android.content.Context; - -public class PGPHelper { - public static PGPKeyRing decodeKeyRing(InputStream is) throws IOException { - InputStream in = PGPUtil.getDecoderStream(is); - PGPObjectFactory objectFactory = new PGPObjectFactory(in); - Object obj = objectFactory.nextObject(); - - if (obj instanceof PGPKeyRing) { - return (PGPKeyRing) obj; - } - - return null; - } - - public static Date getCreationDate(PGPPublicKey key) { - return key.getCreationTime(); - } - - public static Date getCreationDate(PGPSecretKey key) { - return key.getPublicKey().getCreationTime(); - } - - @SuppressWarnings("unchecked") - public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) { - if (keyRing == null) { - return null; - } - for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - if (key.isMasterKey()) { - return key; - } - } - - return null; - } - - @SuppressWarnings("unchecked") - public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) { - if (keyRing == null) { - return null; - } - for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { - if (key.isMasterKey()) { - return key; - } - } - - return null; - } - - @SuppressWarnings("unchecked") - public static Vector getEncryptKeys(PGPPublicKeyRing keyRing) { - Vector encryptKeys = new Vector(); - - for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - if (isEncryptionKey(key)) { - encryptKeys.add(key); - } - } - - return encryptKeys; - } - - @SuppressWarnings("unchecked") - public static Vector getSigningKeys(PGPSecretKeyRing keyRing) { - Vector signingKeys = new Vector(); - - for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { - if (isSigningKey(key)) { - signingKeys.add(key); - } - } - - return signingKeys; - } - - public static Vector getUsableEncryptKeys(PGPPublicKeyRing keyRing) { - Vector usableKeys = new Vector(); - Vector encryptKeys = getEncryptKeys(keyRing); - PGPPublicKey masterKey = null; - for (int i = 0; i < encryptKeys.size(); ++i) { - PGPPublicKey key = encryptKeys.get(i); - if (!isExpired(key)) { - if (key.isMasterKey()) { - masterKey = key; - } else { - usableKeys.add(key); - } - } - } - if (masterKey != null) { - usableKeys.add(masterKey); - } - return usableKeys; - } - - public static boolean isExpired(PGPPublicKey key) { - Date creationDate = getCreationDate(key); - Date expiryDate = getExpiryDate(key); - Date now = new Date(); - if (now.compareTo(creationDate) >= 0 - && (expiryDate == null || now.compareTo(expiryDate) <= 0)) { - return false; - } - return true; - } - - public static boolean isExpired(PGPSecretKey key) { - return isExpired(key.getPublicKey()); - } - - public static Vector getUsableSigningKeys(PGPSecretKeyRing keyRing) { - Vector usableKeys = new Vector(); - Vector signingKeys = getSigningKeys(keyRing); - PGPSecretKey masterKey = null; - for (int i = 0; i < signingKeys.size(); ++i) { - PGPSecretKey key = signingKeys.get(i); - if (key.isMasterKey()) { - masterKey = key; - } else { - usableKeys.add(key); - } - } - if (masterKey != null) { - usableKeys.add(masterKey); - } - return usableKeys; - } - - public static Date getExpiryDate(PGPPublicKey key) { - Date creationDate = getCreationDate(key); - if (key.getValidDays() == 0) { - // no expiry - return null; - } - Calendar calendar = GregorianCalendar.getInstance(); - calendar.setTime(creationDate); - calendar.add(Calendar.DATE, key.getValidDays()); - Date expiryDate = calendar.getTime(); - - return expiryDate; - } - - public static Date getExpiryDate(PGPSecretKey key) { - return getExpiryDate(key.getPublicKey()); - } - - public static PGPPublicKey getEncryptPublicKey(Context context, long masterKeyId) { - PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(context, masterKeyId); - if (keyRing == null) { - Log.e(Constants.TAG, "keyRing is null!"); - return null; - } - Vector encryptKeys = getUsableEncryptKeys(keyRing); - if (encryptKeys.size() == 0) { - Log.e(Constants.TAG, "encryptKeys is null!"); - return null; - } - return encryptKeys.get(0); - } - - public static PGPSecretKey getSigningKey(Context context, long masterKeyId) { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, masterKeyId); - if (keyRing == null) { - return null; - } - Vector signingKeys = getUsableSigningKeys(keyRing); - if (signingKeys.size() == 0) { - return null; - } - return signingKeys.get(0); - } - - @SuppressWarnings("unchecked") - public static String getMainUserId(PGPPublicKey key) { - for (String userId : new IterableIterator(key.getUserIDs())) { - return userId; - } - return null; - } - - @SuppressWarnings("unchecked") - public static String getMainUserId(PGPSecretKey key) { - for (String userId : new IterableIterator(key.getUserIDs())) { - return userId; - } - return null; - } - - public static String getMainUserIdSafe(Context context, PGPPublicKey key) { - String userId = getMainUserId(key); - if (userId == null || userId.equals("")) { - userId = context.getString(R.string.unknownUserId); - } - return userId; - } - - public static String getMainUserIdSafe(Context context, PGPSecretKey key) { - String userId = getMainUserId(key); - if (userId == null || userId.equals("")) { - userId = context.getString(R.string.unknownUserId); - } - return userId; - } - - @SuppressWarnings("unchecked") - public static boolean isEncryptionKey(PGPPublicKey key) { - if (!key.isEncryptionKey()) { - return false; - } - - if (key.getVersion() <= 3) { - // this must be true now - return key.isEncryptionKey(); - } - - // special cases - if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) { - return true; - } - - if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) { - return true; - } - - for (PGPSignature sig : new IterableIterator(key.getSignatures())) { - if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { - continue; - } - PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); - - if (hashed != null - && (hashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { - return true; - } - - PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); - - if (unhashed != null - && (unhashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { - return true; - } - } - return false; - } - - public static boolean isEncryptionKey(PGPSecretKey key) { - return isEncryptionKey(key.getPublicKey()); - } - - @SuppressWarnings("unchecked") - public static boolean isSigningKey(PGPPublicKey key) { - if (key.getVersion() <= 3) { - return true; - } - - // special case - if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) { - return true; - } - - for (PGPSignature sig : new IterableIterator(key.getSignatures())) { - if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { - continue; - } - PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); - - if (hashed != null && (hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) { - return true; - } - - PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); - - if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) { - return true; - } - } - - return false; - } - - public static boolean isSigningKey(PGPSecretKey key) { - return isSigningKey(key.getPublicKey()); - } - - public static String getAlgorithmInfo(PGPPublicKey key) { - return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength()); - } - - public static String getAlgorithmInfo(PGPSecretKey key) { - return getAlgorithmInfo(key.getPublicKey()); - } - - public static String getAlgorithmInfo(int algorithm, int keySize) { - String algorithmStr = null; - - switch (algorithm) { - case PGPPublicKey.RSA_ENCRYPT: - case PGPPublicKey.RSA_GENERAL: - case PGPPublicKey.RSA_SIGN: { - algorithmStr = "RSA"; - break; - } - - case PGPPublicKey.DSA: { - algorithmStr = "DSA"; - break; - } - - case PGPPublicKey.ELGAMAL_ENCRYPT: - case PGPPublicKey.ELGAMAL_GENERAL: { - algorithmStr = "ElGamal"; - break; - } - - default: { - algorithmStr = "???"; - break; - } - } - return algorithmStr + ", " + keySize + "bit"; - } - - public static String convertToHex(byte[] fp) { - String fingerPrint = ""; - for (int i = 0; i < fp.length; ++i) { - if (i != 0 && i % 10 == 0) { - fingerPrint += " "; - } else if (i != 0 && i % 2 == 0) { - fingerPrint += " "; - } - String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(); - while (chunk.length() < 2) { - chunk = "0" + chunk; - } - fingerPrint += chunk; - } - - return fingerPrint; - - } - - public static String getPubkeyAsArmoredString(Context context, long keyId) { - PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId); - // if it is no public key get it from your own keys... - if (key == null) { - PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); - if (secretKey == null) { - Log.e(Constants.TAG, "Key could not be found!"); - return null; - } - key = secretKey.getPublicKey(); - } - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ArmoredOutputStream aos = new ArmoredOutputStream(bos); - aos.setHeader("Version", PGPMain.getFullVersion(context)); - - String armouredKey = null; - try { - aos.write(key.getEncoded()); - aos.close(); - - armouredKey = bos.toString("UTF-8"); - } catch (IOException e) { - Log.e(Constants.TAG, "Problems while encoding key as armored string", e); - } - - Log.d(Constants.TAG, "key:" + armouredKey); - - return armouredKey; - } - - public static String getFingerPrint(Context context, long keyId) { - PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId); - // if it is no public key get it from your own keys... - if (key == null) { - PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); - if (secretKey == null) { - Log.e(Constants.TAG, "Key could not be found!"); - return null; - } - key = secretKey.getPublicKey(); - } - - return convertToHex(key.getFingerprint()); - } - - public static String getSmallFingerPrint(long keyId) { - String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(); - while (fingerPrint.length() < 8) { - fingerPrint = "0" + fingerPrint; - } - return fingerPrint; - } - - public static String keyToHex(long keyId) { - return getSmallFingerPrint(keyId >> 32) + getSmallFingerPrint(keyId); - } - - public static long keyFromHex(String data) { - int len = data.length(); - String s2 = data.substring(len - 8); - String s1 = data.substring(0, len - 8); - return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/helper/PGPMain.java b/org_apg/src/org/thialfihar/android/apg/helper/PGPMain.java deleted file mode 100644 index 0cde19ae5..000000000 --- a/org_apg/src/org/thialfihar/android/apg/helper/PGPMain.java +++ /dev/null @@ -1,1827 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * 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.helper; - -import org.spongycastle.bcpg.ArmoredInputStream; -import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.bcpg.BCPGOutputStream; -import org.spongycastle.bcpg.CompressionAlgorithmTags; -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; -import org.spongycastle.bcpg.sig.KeyFlags; -import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.spongycastle.jce.spec.ElGamalParameterSpec; -import org.spongycastle.openpgp.PGPCompressedData; -import org.spongycastle.openpgp.PGPCompressedDataGenerator; -import org.spongycastle.openpgp.PGPEncryptedData; -import org.spongycastle.openpgp.PGPEncryptedDataGenerator; -import org.spongycastle.openpgp.PGPEncryptedDataList; -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPKeyPair; -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPKeyRingGenerator; -import org.spongycastle.openpgp.PGPLiteralData; -import org.spongycastle.openpgp.PGPLiteralDataGenerator; -import org.spongycastle.openpgp.PGPObjectFactory; -import org.spongycastle.openpgp.PGPOnePassSignature; -import org.spongycastle.openpgp.PGPOnePassSignatureList; -import org.spongycastle.openpgp.PGPPBEEncryptedData; -import org.spongycastle.openpgp.PGPPrivateKey; -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.spongycastle.openpgp.PGPSignature; -import org.spongycastle.openpgp.PGPSignatureGenerator; -import org.spongycastle.openpgp.PGPSignatureList; -import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; -import org.spongycastle.openpgp.PGPSignatureSubpacketVector; -import org.spongycastle.openpgp.PGPUtil; -import org.spongycastle.openpgp.PGPV3SignatureGenerator; -import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; -import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; -import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; -import org.spongycastle.openpgp.operator.PGPDigestCalculator; -import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; -import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; -import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.util.HkpKeyServer; -import org.thialfihar.android.apg.util.InputData; -import org.thialfihar.android.apg.util.PositionAwareInputStream; -import org.thialfihar.android.apg.util.Primes; -import org.thialfihar.android.apg.util.ProgressDialogUpdater; -import org.thialfihar.android.apg.util.KeyServer.AddKeyException; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Bundle; -import android.os.Environment; -import org.thialfihar.android.apg.util.Log; - -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.math.BigInteger; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.security.Security; -import java.security.SignatureException; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.Vector; -import java.util.regex.Pattern; - -/** - * TODO: - * - * - Separate this file into different helpers - * - */ -public class PGPMain { - - static { - // register spongy castle provider - Security.addProvider(new BouncyCastleProvider()); - } - - // Not BC due to the use of Spongy Castle for Android - public static final String BOUNCY_CASTLE_PROVIDER_NAME = "SC"; - - private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] { - SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, - SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5, - SymmetricKeyAlgorithmTags.TRIPLE_DES }; - private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1, - HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160 }; - private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] { - CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2, - CompressionAlgorithmTags.ZIP }; - - public static Pattern PGP_MESSAGE = Pattern.compile( - ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL); - - public static Pattern PGP_SIGNED_MESSAGE = Pattern - .compile( - ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*", - Pattern.DOTALL); - - public static Pattern PGP_PUBLIC_KEY = Pattern.compile( - ".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*", - Pattern.DOTALL); - - private static String mEditPassPhrase = null; - - public static class ApgGeneralException extends Exception { - static final long serialVersionUID = 0xf812773342L; - - public ApgGeneralException(String message) { - super(message); - } - } - - public static class NoAsymmetricEncryptionException extends Exception { - static final long serialVersionUID = 0xf812773343L; - - public NoAsymmetricEncryptionException() { - super(); - } - } - - public static void setEditPassPhrase(String passPhrase) { - mEditPassPhrase = passPhrase; - } - - public static String getEditPassPhrase() { - return mEditPassPhrase; - } - - public static void updateProgress(ProgressDialogUpdater progress, int message, int current, - int total) { - if (progress != null) { - progress.setProgress(message, current, total); - } - } - - public static void updateProgress(ProgressDialogUpdater progress, int current, int total) { - if (progress != null) { - progress.setProgress(current, total); - } - } - - /** - * Creates new secret key. The returned PGPSecretKeyRing contains only one newly generated key - * when this key is the new masterkey. If a masterkey is supplied in the parameters - * PGPSecretKeyRing contains the masterkey and the new key as a subkey (certified by the - * masterkey). - * - * @param context - * @param algorithmChoice - * @param keySize - * @param passPhrase - * @param masterSecretKey - * @return - * @throws NoSuchAlgorithmException - * @throws PGPException - * @throws NoSuchProviderException - * @throws ApgGeneralException - * @throws InvalidAlgorithmParameterException - */ - public static PGPSecretKeyRing createKey(Context context, int algorithmChoice, int keySize, - String passPhrase, PGPSecretKey masterSecretKey) throws NoSuchAlgorithmException, - PGPException, NoSuchProviderException, ApgGeneralException, - InvalidAlgorithmParameterException { - - if (keySize < 512) { - throw new ApgGeneralException(context.getString(R.string.error_keySizeMinimum512bit)); - } - - if (passPhrase == null) { - passPhrase = ""; - } - - int algorithm = 0; - KeyPairGenerator keyGen = null; - - switch (algorithmChoice) { - case Id.choice.algorithm.dsa: { - keyGen = KeyPairGenerator.getInstance("DSA", BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - algorithm = PGPPublicKey.DSA; - break; - } - - case Id.choice.algorithm.elgamal: { - if (masterSecretKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_masterKeyMustNotBeElGamal)); - } - keyGen = KeyPairGenerator.getInstance("ElGamal", BOUNCY_CASTLE_PROVIDER_NAME); - BigInteger p = Primes.getBestPrime(keySize); - BigInteger g = new BigInteger("2"); - - ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); - - keyGen.initialize(elParams); - algorithm = PGPPublicKey.ELGAMAL_ENCRYPT; - break; - } - - case Id.choice.algorithm.rsa: { - keyGen = KeyPairGenerator.getInstance("RSA", BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - - algorithm = PGPPublicKey.RSA_GENERAL; - break; - } - - default: { - throw new ApgGeneralException(context.getString(R.string.error_unknownAlgorithmChoice)); - } - } - - // build new key pair - PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - - // Build key encrypter and decrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( - passPhrase.toCharArray()); - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(passPhrase.toCharArray()); - - PGPKeyRingGenerator ringGen = null; - PGPContentSignerBuilder certificationSignerBuilder = null; - if (masterSecretKey == null) { - certificationSignerBuilder = new JcaPGPContentSignerBuilder(keyPair.getPublicKey() - .getAlgorithm(), HashAlgorithmTags.SHA1); - - // build keyRing with only this one master key in it! - ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, keyPair, "", - sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); - } else { - PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); - PGPPrivateKey masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); - - certificationSignerBuilder = new JcaPGPContentSignerBuilder(masterKeyPair - .getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // build keyRing with master key and new key as subkey (certified by masterkey) - ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, - "", sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); - - ringGen.addSubKey(keyPair); - } - - PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing(); - - return secKeyRing; - } - - public static void buildSecretKey(Context context, ArrayList userIds, - ArrayList keys, ArrayList keysUsages, long masterKeyId, - String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) - throws ApgGeneralException, NoSuchProviderException, PGPException, - NoSuchAlgorithmException, SignatureException, IOException { - - Log.d(Constants.TAG, "userIds: " + userIds.toString()); - - updateProgress(progress, R.string.progress_buildingKey, 0, 100); - - if (oldPassPhrase == null) { - oldPassPhrase = ""; - } - if (newPassPhrase == null) { - newPassPhrase = ""; - } - - updateProgress(progress, R.string.progress_preparingMasterKey, 10, 100); - - int usageId = keysUsages.get(0); - boolean canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); - boolean canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); - - String mainUserId = userIds.get(0); - - PGPSecretKey masterKey = keys.get(0); - - // this removes all userIds and certifications previously attached to the masterPublicKey - PGPPublicKey tmpKey = masterKey.getPublicKey(); - PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(), - tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime()); - - // already done by code above: - // PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - // // Somehow, the PGPPublicKey already has an empty certification attached to it when the - // // keyRing is generated the first time, we remove that when it exists, before adding the - // new - // // ones - // PGPPublicKey masterPublicKeyRmCert = PGPPublicKey.removeCertification(masterPublicKey, - // ""); - // if (masterPublicKeyRmCert != null) { - // masterPublicKey = masterPublicKeyRmCert; - // } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); - PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); - - updateProgress(progress, R.string.progress_certifyingMasterKey, 20, 100); - - for (String userId : userIds) { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); - } - - // TODO: cross-certify the master key with every sub key (APG 1) - - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); - - PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA; - if (canEncrypt) { - keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; - } - hashedPacketsGen.setKeyFlags(true, keyFlags); - - hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); - hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); - hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); - - // TODO: this doesn't work quite right yet (APG 1) - // if (keyEditor.getExpiryDate() != null) { - // GregorianCalendar creationDate = new GregorianCalendar(); - // creationDate.setTime(getCreationDate(masterKey)); - // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); - // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); - // if (numDays <= 0) { - // throw new GeneralException( - // context.getString(R.string.error_expiryMustComeAfterCreation)); - // } - // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); - // } - - updateProgress(progress, R.string.progress_buildingMasterKeyRing, 30, 100); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( - masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // Build key encrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( - newPassPhrase.toCharArray()); - - PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, - masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), - unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - - updateProgress(progress, R.string.progress_addingSubKeys, 40, 100); - - for (int i = 1; i < keys.size(); ++i) { - updateProgress(progress, 40 + 50 * (i - 1) / (keys.size() - 1), 100); - - PGPSecretKey subKey = keys.get(i); - PGPPublicKey subPublicKey = subKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); - PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); - - // TODO: now used without algorithm and creation time?! (APG 1) - PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); - - hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - keyFlags = 0; - - usageId = keysUsages.get(i); - canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); - canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); - if (canSign) { - keyFlags |= KeyFlags.SIGN_DATA; - } - if (canEncrypt) { - keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; - } - hashedPacketsGen.setKeyFlags(true, keyFlags); - - // TODO: this doesn't work quite right yet (APG 1) - // if (keyEditor.getExpiryDate() != null) { - // GregorianCalendar creationDate = new GregorianCalendar(); - // creationDate.setTime(getCreationDate(masterKey)); - // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); - // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); - // if (numDays <= 0) { - // throw new GeneralException( - // context.getString(R.string.error_expiryMustComeAfterCreation)); - // } - // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); - // } - - keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); - } - - PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); - PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); - - updateProgress(progress, R.string.progress_savingKeyRing, 90, 100); - - ProviderHelper.saveKeyRing(context, secretKeyRing); - ProviderHelper.saveKeyRing(context, publicKeyRing); - - updateProgress(progress, R.string.progress_done, 100, 100); - } - - public static int storeKeyRingInCache(Context context, PGPKeyRing keyring) { - int status = Integer.MIN_VALUE; // out of bounds value (Id.retrun_value.*) - try { - if (keyring instanceof PGPSecretKeyRing) { - PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring; - boolean save = true; - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); - PGPPrivateKey testKey = secretKeyRing.getSecretKey().extractPrivateKey( - keyDecryptor); - if (testKey == null) { - // this is bad, something is very wrong... likely a --export-secret-subkeys - // export - save = false; - status = Id.return_value.bad; - } - } catch (PGPException e) { - // all good if this fails, we likely didn't use the right password - } - - if (save) { - ProviderHelper.saveKeyRing(context, secretKeyRing); - // TODO: remove status returns, use exceptions! - status = Id.return_value.ok; - } - } else if (keyring instanceof PGPPublicKeyRing) { - PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring; - ProviderHelper.saveKeyRing(context, publicKeyRing); - // TODO: remove status returns, use exceptions! - status = Id.return_value.ok; - } - } catch (IOException e) { - status = Id.return_value.error; - } - - return status; - } - - public static boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ArmoredOutputStream aos = new ArmoredOutputStream(bos); - try { - aos.write(keyring.getEncoded()); - aos.close(); - - String armouredKey = bos.toString("UTF-8"); - server.add(armouredKey); - - return true; - } catch (IOException e) { - return false; - } catch (AddKeyException e) { - // TODO: tell the user? - return false; - } finally { - try { - bos.close(); - } catch (IOException e) { - } - } - } - - public static Bundle importKeyRings(Context context, int type, InputData data, - ProgressDialogUpdater progress) throws ApgGeneralException, FileNotFoundException, - PGPException, IOException { - Bundle returnData = new Bundle(); - - if (type == Id.type.secret_key) { - if (progress != null) - progress.setProgress(R.string.progress_importingSecretKeys, 0, 100); - } else { - if (progress != null) - progress.setProgress(R.string.progress_importingPublicKeys, 0, 100); - } - - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new ApgGeneralException(context.getString(R.string.error_externalStorageNotReady)); - } - - PositionAwareInputStream progressIn = new PositionAwareInputStream(data.getInputStream()); - // need to have access to the bufferedInput, so we can reuse it for the possible - // PGPObject chunks after the first one, e.g. files with several consecutive ASCII - // armour blocks - BufferedInputStream bufferedInput = new BufferedInputStream(progressIn); - int newKeys = 0; - int oldKeys = 0; - int badKeys = 0; - try { - PGPKeyRing keyring = PGPHelper.decodeKeyRing(bufferedInput); - while (keyring != null) { - int status = Integer.MIN_VALUE; // out of bounds value - - // if this key is what we expect it to be, save it - if ((type == Id.type.secret_key && keyring instanceof PGPSecretKeyRing) - || (type == Id.type.public_key && keyring instanceof PGPPublicKeyRing)) { - status = storeKeyRingInCache(context, keyring); - } - - if (status == Id.return_value.error) { - throw new ApgGeneralException(context.getString(R.string.error_savingKeys)); - } - - // update the counts to display to the user at the end - if (status == Id.return_value.updated) { - ++oldKeys; - } else if (status == Id.return_value.ok) { - ++newKeys; - } else if (status == Id.return_value.bad) { - ++badKeys; - } - - updateProgress(progress, (int) (100 * progressIn.position() / data.getSize()), 100); - - // TODO: needed? - // obj = objectFactory.nextObject(); - - keyring = PGPHelper.decodeKeyRing(bufferedInput); - } - } catch (EOFException e) { - // nothing to do, we are done - } - - returnData.putInt(ApgIntentService.RESULT_IMPORT_ADDED, newKeys); - returnData.putInt(ApgIntentService.RESULT_IMPORT_UPDATED, oldKeys); - returnData.putInt(ApgIntentService.RESULT_IMPORT_BAD, badKeys); - - updateProgress(progress, R.string.progress_done, 100, 100); - - return returnData; - } - - public static Bundle exportKeyRings(Context context, Vector keyRingIds, - OutputStream outStream, ProgressDialogUpdater progress) throws ApgGeneralException, - FileNotFoundException, PGPException, IOException { - Bundle returnData = new Bundle(); - - if (keyRingIds.size() == 1) { - updateProgress(progress, R.string.progress_exportingKey, 0, 100); - } else { - updateProgress(progress, R.string.progress_exportingKeys, 0, 100); - } - - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new ApgGeneralException(context.getString(R.string.error_externalStorageNotReady)); - } - ArmoredOutputStream out = new ArmoredOutputStream(outStream); - out.setHeader("Version", getFullVersion(context)); - - int numKeys = 0; - for (int i = 0; i < keyRingIds.size(); ++i) { - updateProgress(progress, i * 100 / keyRingIds.size(), 100); - - // try to get it as a PGPPublicKeyRing, if that fails try to get it as a SecretKeyRing - PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId( - context, keyRingIds.get(i)); - if (publicKeyRing != null) { - publicKeyRing.encode(out); - } else { - PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( - context, keyRingIds.get(i)); - if (secretKeyRing != null) { - secretKeyRing.encode(out); - } else { - continue; - } - } - ++numKeys; - } - out.close(); - returnData.putInt(ApgIntentService.RESULT_EXPORT, numKeys); - - updateProgress(progress, R.string.progress_done, 100, 100); - - return returnData; - } - - /** - * Encrypt and Sign data - * - * @param context - * @param progress - * @param data - * @param outStream - * @param useAsciiArmor - * @param compression - * @param encryptionKeyIds - * @param symmetricEncryptionAlgorithm - * @param encryptionPassphrase - * @param signatureKeyId - * @param signatureHashAlgorithm - * @param signatureForceV3 - * @param signaturePassphrase - * @throws IOException - * @throws ApgGeneralException - * @throws PGPException - * @throws NoSuchProviderException - * @throws NoSuchAlgorithmException - * @throws SignatureException - */ - public static void encryptAndSign(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, boolean useAsciiArmor, int compression, - long encryptionKeyIds[], String encryptionPassphrase, int symmetricEncryptionAlgorithm, - long signatureKeyId, int signatureHashAlgorithm, boolean signatureForceV3, - String signaturePassphrase) throws IOException, ApgGeneralException, PGPException, - NoSuchProviderException, NoSuchAlgorithmException, SignatureException { - - if (encryptionKeyIds == null) { - encryptionKeyIds = new long[0]; - } - - ArmoredOutputStream armorOut = null; - OutputStream out = null; - OutputStream encryptOut = null; - if (useAsciiArmor) { - armorOut = new ArmoredOutputStream(outStream); - armorOut.setHeader("Version", getFullVersion(context)); - out = armorOut; - } else { - out = outStream; - } - PGPSecretKey signingKey = null; - PGPSecretKeyRing signingKeyRing = null; - PGPPrivateKey signaturePrivateKey = null; - - if (encryptionKeyIds.length == 0 && encryptionPassphrase == null) { - throw new ApgGeneralException( - context.getString(R.string.error_noEncryptionKeysOrPassPhrase)); - } - - if (signatureKeyId != Id.key.none) { - signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); - signingKey = PGPHelper.getSigningKey(context, signatureKeyId); - if (signingKey == null) { - throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); - } - - if (signaturePassphrase == null) { - throw new ApgGeneralException( - context.getString(R.string.error_noSignaturePassPhrase)); - } - - updateProgress(progress, R.string.progress_extractingSignatureKey, 0, 100); - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); - signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - } - updateProgress(progress, R.string.progress_preparingStreams, 5, 100); - - // encrypt and compress input file content - JcePGPDataEncryptorBuilder encryptorBuilder = new JcePGPDataEncryptorBuilder( - symmetricEncryptionAlgorithm).setProvider(BOUNCY_CASTLE_PROVIDER_NAME) - .setWithIntegrityPacket(true); - - PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(encryptorBuilder); - - if (encryptionKeyIds.length == 0) { - // Symmetric encryption - Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption"); - - JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = new JcePBEKeyEncryptionMethodGenerator( - encryptionPassphrase.toCharArray()); - cPk.addMethod(symmetricEncryptionGenerator); - } else { - // Asymmetric encryption - for (int i = 0; i < encryptionKeyIds.length; ++i) { - PGPPublicKey key = PGPHelper.getEncryptPublicKey(context, encryptionKeyIds[i]); - if (key != null) { - - JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator = new JcePublicKeyKeyEncryptionMethodGenerator( - key); - cPk.addMethod(pubKeyEncryptionGenerator); - } - } - } - encryptOut = cPk.open(out, new byte[1 << 16]); - - PGPSignatureGenerator signatureGenerator = null; - PGPV3SignatureGenerator signatureV3Generator = null; - - if (signatureKeyId != Id.key.none) { - updateProgress(progress, R.string.progress_preparingSignature, 10, 100); - - // content signer based on signing key algorithm and choosen hash algorithm - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - if (signatureForceV3) { - signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); - signatureV3Generator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); - } else { - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); - - String userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signingKeyRing)); - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - spGen.setSignerUserID(false, userId); - signatureGenerator.setHashedSubpackets(spGen.generate()); - } - } - - PGPCompressedDataGenerator compressGen = null; - BCPGOutputStream bcpgOut = null; - if (compression == Id.choice.compression.none) { - bcpgOut = new BCPGOutputStream(encryptOut); - } else { - compressGen = new PGPCompressedDataGenerator(compression); - bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut)); - } - if (signatureKeyId != Id.key.none) { - if (signatureForceV3) { - signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut); - } else { - signatureGenerator.generateOnePassVersion(false).encode(bcpgOut); - } - } - - PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator(); - // file name not needed, so empty string - OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(), - new byte[1 << 16]); - updateProgress(progress, R.string.progress_encrypting, 20, 100); - - long done = 0; - int n = 0; - byte[] buffer = new byte[1 << 16]; - InputStream in = data.getInputStream(); - while ((n = in.read(buffer)) > 0) { - pOut.write(buffer, 0, n); - if (signatureKeyId != Id.key.none) { - if (signatureForceV3) { - signatureV3Generator.update(buffer, 0, n); - } else { - signatureGenerator.update(buffer, 0, n); - } - } - done += n; - if (data.getSize() != 0) { - updateProgress(progress, (int) (20 + (95 - 20) * done / data.getSize()), 100); - } - } - - literalGen.close(); - - if (signatureKeyId != Id.key.none) { - updateProgress(progress, R.string.progress_generatingSignature, 95, 100); - if (signatureForceV3) { - signatureV3Generator.generate().encode(pOut); - } else { - signatureGenerator.generate().encode(pOut); - } - } - if (compressGen != null) { - compressGen.close(); - } - encryptOut.close(); - if (useAsciiArmor) { - armorOut.close(); - } - - updateProgress(progress, R.string.progress_done, 100, 100); - } - - public static void signText(Context context, ProgressDialogUpdater progress, InputData data, - OutputStream outStream, long signatureKeyId, String signaturePassphrase, - int signatureHashAlgorithm, boolean forceV3Signature) throws ApgGeneralException, - PGPException, IOException, NoSuchAlgorithmException, SignatureException { - - ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream); - armorOut.setHeader("Version", getFullVersion(context)); - - PGPSecretKey signingKey = null; - PGPSecretKeyRing signingKeyRing = null; - PGPPrivateKey signaturePrivateKey = null; - - if (signatureKeyId == 0) { - armorOut.close(); - throw new ApgGeneralException(context.getString(R.string.error_noSignatureKey)); - } - - signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); - signingKey = PGPHelper.getSigningKey(context, signatureKeyId); - if (signingKey == null) { - armorOut.close(); - throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); - } - - if (signaturePassphrase == null) { - armorOut.close(); - throw new ApgGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); - } - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); - signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - armorOut.close(); - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - updateProgress(progress, R.string.progress_preparingStreams, 0, 100); - - updateProgress(progress, R.string.progress_preparingSignature, 30, 100); - - PGPSignatureGenerator signatureGenerator = null; - PGPV3SignatureGenerator signatureV3Generator = null; - - // content signer based on signing key algorithm and choosen hash algorithm - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey - .getPublicKey().getAlgorithm(), signatureHashAlgorithm) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - if (forceV3Signature) { - signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); - signatureV3Generator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); - } else { - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); - - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - String userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signingKeyRing)); - spGen.setSignerUserID(false, userId); - signatureGenerator.setHashedSubpackets(spGen.generate()); - } - - updateProgress(progress, R.string.progress_signing, 40, 100); - - armorOut.beginClearText(signatureHashAlgorithm); - - InputStream inStream = data.getInputStream(); - final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream)); - - final byte[] newline = "\r\n".getBytes("UTF-8"); - - if (forceV3Signature) { - processLine(reader.readLine(), armorOut, signatureV3Generator); - } else { - processLine(reader.readLine(), armorOut, signatureGenerator); - } - - while (true) { - final String line = reader.readLine(); - - if (line == null) { - armorOut.write(newline); - break; - } - - armorOut.write(newline); - if (forceV3Signature) { - signatureV3Generator.update(newline); - processLine(line, armorOut, signatureV3Generator); - } else { - signatureGenerator.update(newline); - processLine(line, armorOut, signatureGenerator); - } - } - - armorOut.endClearText(); - - BCPGOutputStream bOut = new BCPGOutputStream(armorOut); - if (forceV3Signature) { - signatureV3Generator.generate().encode(bOut); - } else { - signatureGenerator.generate().encode(bOut); - } - armorOut.close(); - - updateProgress(progress, R.string.progress_done, 100, 100); - } - - public static void generateSignature(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, boolean armored, boolean binary, - long signatureKeyId, String signaturePassPhrase, int hashAlgorithm, - boolean forceV3Signature) throws ApgGeneralException, PGPException, IOException, - NoSuchAlgorithmException, SignatureException { - - OutputStream out = null; - - // Ascii Armor (Base64) - ArmoredOutputStream armorOut = null; - if (armored) { - armorOut = new ArmoredOutputStream(outStream); - armorOut.setHeader("Version", getFullVersion(context)); - out = armorOut; - } else { - out = outStream; - } - - PGPSecretKey signingKey = null; - PGPSecretKeyRing signingKeyRing = null; - PGPPrivateKey signaturePrivateKey = null; - - if (signatureKeyId == 0) { - throw new ApgGeneralException(context.getString(R.string.error_noSignatureKey)); - } - - signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); - signingKey = PGPHelper.getSigningKey(context, signatureKeyId); - if (signingKey == null) { - throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); - } - - if (signaturePassPhrase == null) { - throw new ApgGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); - } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassPhrase.toCharArray()); - signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - updateProgress(progress, R.string.progress_preparingStreams, 0, 100); - - updateProgress(progress, R.string.progress_preparingSignature, 30, 100); - - PGPSignatureGenerator signatureGenerator = null; - PGPV3SignatureGenerator signatureV3Generator = null; - - int type = PGPSignature.CANONICAL_TEXT_DOCUMENT; - if (binary) { - type = PGPSignature.BINARY_DOCUMENT; - } - - // content signer based on signing key algorithm and choosen hash algorithm - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey - .getPublicKey().getAlgorithm(), hashAlgorithm) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - if (forceV3Signature) { - signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); - signatureV3Generator.init(type, signaturePrivateKey); - } else { - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(type, signaturePrivateKey); - - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - String userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signingKeyRing)); - spGen.setSignerUserID(false, userId); - signatureGenerator.setHashedSubpackets(spGen.generate()); - } - - updateProgress(progress, R.string.progress_signing, 40, 100); - - InputStream inStream = data.getInputStream(); - if (binary) { - byte[] buffer = new byte[1 << 16]; - int n = 0; - while ((n = inStream.read(buffer)) > 0) { - if (forceV3Signature) { - signatureV3Generator.update(buffer, 0, n); - } else { - signatureGenerator.update(buffer, 0, n); - } - } - } else { - final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream)); - final byte[] newline = "\r\n".getBytes("UTF-8"); - - while (true) { - final String line = reader.readLine(); - - if (line == null) { - break; - } - - if (forceV3Signature) { - processLine(line, null, signatureV3Generator); - signatureV3Generator.update(newline); - } else { - processLine(line, null, signatureGenerator); - signatureGenerator.update(newline); - } - } - } - - BCPGOutputStream bOut = new BCPGOutputStream(out); - if (forceV3Signature) { - signatureV3Generator.generate().encode(bOut); - } else { - signatureGenerator.generate().encode(bOut); - } - out.close(); - outStream.close(); - - if (progress != null) - progress.setProgress(R.string.progress_done, 100, 100); - } - - public static PGPPublicKeyRing signKey(Context context, long masterKeyId, long pubKeyId, - String passphrase) throws ApgGeneralException, NoSuchAlgorithmException, - NoSuchProviderException, PGPException, SignatureException { - if (passphrase == null || passphrase.length() <= 0) { - throw new ApgGeneralException("Unable to obtain passphrase"); - } else { - PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByKeyId(context, pubKeyId); - - PGPSecretKey signingKey = PGPHelper.getSigningKey(context, masterKeyId); - if (signingKey == null) { - throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); - } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - - // TODO: SHA256 fixed? - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( - contentSignerBuilder); - - signatureGenerator.init(PGPSignature.DIRECT_KEY, signaturePrivateKey); - - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - - PGPSignatureSubpacketVector packetVector = spGen.generate(); - signatureGenerator.setHashedSubpackets(packetVector); - - PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId), - signatureGenerator.generate()); - pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey); - - return pubring; - } - } - - public static long getDecryptionKeyId(Context context, InputStream inputStream) - throws ApgGeneralException, NoAsymmetricEncryptionException, IOException { - InputStream in = PGPUtil.getDecoderStream(inputStream); - PGPObjectFactory pgpF = new PGPObjectFactory(in); - PGPEncryptedDataList enc; - Object o = pgpF.nextObject(); - - // the first object might be a PGP marker packet. - if (o instanceof PGPEncryptedDataList) { - enc = (PGPEncryptedDataList) o; - } else { - enc = (PGPEncryptedDataList) pgpF.nextObject(); - } - - if (enc == null) { - throw new ApgGeneralException(context.getString(R.string.error_invalidData)); - } - - // TODO: currently we always only look at the first known key - // find the secret key - PGPSecretKey secretKey = null; - Iterator it = enc.getEncryptedDataObjects(); - boolean gotAsymmetricEncryption = false; - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPublicKeyEncryptedData) { - gotAsymmetricEncryption = true; - PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj; - secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, pbe.getKeyID()); - if (secretKey != null) { - break; - } - } - } - - if (!gotAsymmetricEncryption) { - throw new NoAsymmetricEncryptionException(); - } - - if (secretKey == null) { - return Id.key.none; - } - - return secretKey.getKeyID(); - } - - public static boolean hasSymmetricEncryption(Context context, InputStream inputStream) - throws ApgGeneralException, IOException { - InputStream in = PGPUtil.getDecoderStream(inputStream); - PGPObjectFactory pgpF = new PGPObjectFactory(in); - PGPEncryptedDataList enc; - Object o = pgpF.nextObject(); - - // the first object might be a PGP marker packet. - if (o instanceof PGPEncryptedDataList) { - enc = (PGPEncryptedDataList) o; - } else { - enc = (PGPEncryptedDataList) pgpF.nextObject(); - } - - if (enc == null) { - throw new ApgGeneralException(context.getString(R.string.error_invalidData)); - } - - Iterator it = enc.getEncryptedDataObjects(); - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPBEEncryptedData) { - return true; - } - } - - return false; - } - - public static Bundle decryptAndVerify(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, String passphrase, boolean assumeSymmetric) - throws IOException, ApgGeneralException, PGPException, SignatureException { - if (passphrase == null) { - passphrase = ""; - } - - Bundle returnData = new Bundle(); - InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); - PGPObjectFactory pgpF = new PGPObjectFactory(in); - PGPEncryptedDataList enc; - Object o = pgpF.nextObject(); - long signatureKeyId = 0; - - int currentProgress = 0; - if (progress != null) - progress.setProgress(R.string.progress_readingData, currentProgress, 100); - - if (o instanceof PGPEncryptedDataList) { - enc = (PGPEncryptedDataList) o; - } else { - enc = (PGPEncryptedDataList) pgpF.nextObject(); - } - - if (enc == null) { - throw new ApgGeneralException(context.getString(R.string.error_invalidData)); - } - - InputStream clear = null; - PGPEncryptedData encryptedData = null; - - currentProgress += 5; - - // TODO: currently we always only look at the first known key or symmetric encryption, - // there might be more... - if (assumeSymmetric) { - PGPPBEEncryptedData pbe = null; - Iterator it = enc.getEncryptedDataObjects(); - // find secret key - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPBEEncryptedData) { - pbe = (PGPPBEEncryptedData) obj; - break; - } - } - - if (pbe == null) { - throw new ApgGeneralException( - context.getString(R.string.error_noSymmetricEncryptionPacket)); - } - - updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); - - PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(); - PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( - digestCalcProvider).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( - passphrase.toCharArray()); - - clear = pbe.getDataStream(decryptorFactory); - - encryptedData = pbe; - currentProgress += 5; - } else { - if (progress != null) - progress.setProgress(R.string.progress_findingKey, currentProgress, 100); - PGPPublicKeyEncryptedData pbe = null; - PGPSecretKey secretKey = null; - Iterator it = enc.getEncryptedDataObjects(); - // find secret key - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPublicKeyEncryptedData) { - PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; - secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID()); - if (secretKey != null) { - pbe = encData; - break; - } - } - } - - if (secretKey == null) { - throw new ApgGeneralException(context.getString(R.string.error_noSecretKeyFound)); - } - - currentProgress += 5; - updateProgress(progress, R.string.progress_extractingKey, currentProgress, 100); - PGPPrivateKey privateKey = null; - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - privateKey = secretKey.extractPrivateKey(keyDecryptor); - } catch (PGPException e) { - throw new PGPException(context.getString(R.string.error_wrongPassPhrase)); - } - if (privateKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - currentProgress += 5; - updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); - - PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey); - - clear = pbe.getDataStream(decryptorFactory); - - encryptedData = pbe; - currentProgress += 5; - } - - PGPObjectFactory plainFact = new PGPObjectFactory(clear); - Object dataChunk = plainFact.nextObject(); - PGPOnePassSignature signature = null; - PGPPublicKey signatureKey = null; - int signatureIndex = -1; - - if (dataChunk instanceof PGPCompressedData) { - if (progress != null) - progress.setProgress(R.string.progress_decompressingData, currentProgress, 100); - PGPObjectFactory fact = new PGPObjectFactory( - ((PGPCompressedData) dataChunk).getDataStream()); - dataChunk = fact.nextObject(); - plainFact = fact; - currentProgress += 10; - } - - if (dataChunk instanceof PGPOnePassSignatureList) { - if (progress != null) - progress.setProgress(R.string.progress_processingSignature, currentProgress, 100); - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE, true); - PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; - for (int i = 0; i < sigList.size(); ++i) { - signature = sigList.get(i); - signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); - if (signatureKeyId == 0) { - signatureKeyId = signature.getKeyID(); - } - if (signatureKey == null) { - signature = null; - } else { - signatureIndex = i; - signatureKeyId = signature.getKeyID(); - String userId = null; - PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId( - context, signatureKeyId); - if (signKeyRing != null) { - userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signKeyRing)); - } - returnData.putString(ApgIntentService.RESULT_SIGNATURE_USER_ID, userId); - break; - } - } - - returnData.putLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); - - if (signature != null) { - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - signature.init(contentVerifierBuilderProvider, signatureKey); - } else { - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN, true); - } - - dataChunk = plainFact.nextObject(); - currentProgress += 10; - } - - if (dataChunk instanceof PGPSignatureList) { - dataChunk = plainFact.nextObject(); - } - - if (dataChunk instanceof PGPLiteralData) { - if (progress != null) - progress.setProgress(R.string.progress_decrypting, currentProgress, 100); - PGPLiteralData literalData = (PGPLiteralData) dataChunk; - OutputStream out = outStream; - - byte[] buffer = new byte[1 << 16]; - InputStream dataIn = literalData.getInputStream(); - - int startProgress = currentProgress; - int endProgress = 100; - if (signature != null) { - endProgress = 90; - } else if (encryptedData.isIntegrityProtected()) { - endProgress = 95; - } - int n = 0; - int done = 0; - long startPos = data.getStreamPosition(); - while ((n = dataIn.read(buffer)) > 0) { - out.write(buffer, 0, n); - done += n; - if (signature != null) { - try { - signature.update(buffer, 0, n); - } catch (SignatureException e) { - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS, false); - signature = null; - } - } - // unknown size, but try to at least have a moving, slowing down progress bar - currentProgress = startProgress + (endProgress - startProgress) * done - / (done + 100000); - if (data.getSize() - startPos == 0) { - currentProgress = endProgress; - } else { - currentProgress = (int) (startProgress + (endProgress - startProgress) - * (data.getStreamPosition() - startPos) / (data.getSize() - startPos)); - } - updateProgress(progress, currentProgress, 100); - } - - if (signature != null) { - if (progress != null) - progress.setProgress(R.string.progress_verifyingSignature, 90, 100); - PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); - PGPSignature messageSignature = signatureList.get(signatureIndex); - if (signature.verify(messageSignature)) { - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS, true); - } else { - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS, false); - } - } - } - - // TODO: add integrity somewhere - if (encryptedData.isIntegrityProtected()) { - if (progress != null) - progress.setProgress(R.string.progress_verifyingIntegrity, 95, 100); - if (encryptedData.verify()) { - // passed - } else { - // failed - } - } else { - // no integrity check - } - - updateProgress(progress, R.string.progress_done, 100, 100); - return returnData; - } - - public static Bundle verifyText(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, boolean lookupUnknownKey) throws IOException, - ApgGeneralException, PGPException, SignatureException { - Bundle returnData = new Bundle(); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream()); - - updateProgress(progress, R.string.progress_done, 0, 100); - - // mostly taken from ClearSignedFileProcessor - ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); - int lookAhead = readInputLine(lineOut, aIn); - byte[] lineSep = getLineSeparator(); - - byte[] line = lineOut.toByteArray(); - out.write(line, 0, getLengthWithoutSeparator(line)); - out.write(lineSep); - - while (lookAhead != -1 && aIn.isClearText()) { - lookAhead = readInputLine(lineOut, lookAhead, aIn); - line = lineOut.toByteArray(); - out.write(line, 0, getLengthWithoutSeparator(line)); - out.write(lineSep); - } - - out.close(); - - byte[] clearText = out.toByteArray(); - outStream.write(clearText); - - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE, true); - - updateProgress(progress, R.string.progress_processingSignature, 60, 100); - PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); - - PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); - if (sigList == null) { - throw new ApgGeneralException(context.getString(R.string.error_corruptData)); - } - PGPSignature signature = null; - long signatureKeyId = 0; - PGPPublicKey signatureKey = null; - for (int i = 0; i < sigList.size(); ++i) { - signature = sigList.get(i); - signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); - if (signatureKeyId == 0) { - signatureKeyId = signature.getKeyID(); - } - // if key is not known and we want to lookup unknown ones... - if (signatureKey == null && lookupUnknownKey) { - - returnData = new Bundle(); - returnData.putLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_LOOKUP_KEY, true); - - // return directly now, decrypt will be done again after importing unknown key - return returnData; - } - - if (signatureKey == null) { - signature = null; - } else { - signatureKeyId = signature.getKeyID(); - String userId = null; - PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context, - signatureKeyId); - if (signKeyRing != null) { - userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signKeyRing)); - } - returnData.putString(ApgIntentService.RESULT_SIGNATURE_USER_ID, userId); - break; - } - } - - returnData.putLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); - - if (signature == null) { - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN, true); - if (progress != null) - progress.setProgress(R.string.progress_done, 100, 100); - return returnData; - } - - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - signature.init(contentVerifierBuilderProvider, signatureKey); - - InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText)); - - lookAhead = readInputLine(lineOut, sigIn); - - processLine(signature, lineOut.toByteArray()); - - if (lookAhead != -1) { - do { - lookAhead = readInputLine(lineOut, lookAhead, sigIn); - - signature.update((byte) '\r'); - signature.update((byte) '\n'); - - processLine(signature, lineOut.toByteArray()); - } while (lookAhead != -1); - } - - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS, signature.verify()); - - updateProgress(progress, R.string.progress_done, 100, 100); - return returnData; - } - - public static int getStreamContent(Context context, InputStream inStream) throws IOException { - InputStream in = PGPUtil.getDecoderStream(inStream); - PGPObjectFactory pgpF = new PGPObjectFactory(in); - Object object = pgpF.nextObject(); - while (object != null) { - if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) { - return Id.content.keys; - } else if (object instanceof PGPEncryptedDataList) { - return Id.content.encrypted_data; - } - object = pgpF.nextObject(); - } - - return Id.content.unknown; - } - - private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, - final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException { - - if (pLine == null) { - return; - } - - final char[] chars = pLine.toCharArray(); - int len = chars.length; - - while (len > 0) { - if (!Character.isWhitespace(chars[len - 1])) { - break; - } - len--; - } - - final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); - - if (pArmoredOutput != null) { - pArmoredOutput.write(data); - } - pSignatureGenerator.update(data); - } - - private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, - final PGPV3SignatureGenerator pSignatureGenerator) throws IOException, - SignatureException { - - if (pLine == null) { - return; - } - - final char[] chars = pLine.toCharArray(); - int len = chars.length; - - while (len > 0) { - if (!Character.isWhitespace(chars[len - 1])) { - break; - } - len--; - } - - final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); - - if (pArmoredOutput != null) { - pArmoredOutput.write(data); - } - pSignatureGenerator.update(data); - } - - // taken from ClearSignedFileProcessor in BC - private static void processLine(PGPSignature sig, byte[] line) throws SignatureException, - IOException { - int length = getLengthWithoutWhiteSpace(line); - if (length > 0) { - sig.update(line, 0, length); - } - } - - private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) - throws IOException { - bOut.reset(); - - int lookAhead = -1; - int ch; - - while ((ch = fIn.read()) >= 0) { - bOut.write(ch); - if (ch == '\r' || ch == '\n') { - lookAhead = readPassedEOL(bOut, ch, fIn); - break; - } - } - - return lookAhead; - } - - private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn) - throws IOException { - bOut.reset(); - - int ch = lookAhead; - - do { - bOut.write(ch); - if (ch == '\r' || ch == '\n') { - lookAhead = readPassedEOL(bOut, ch, fIn); - break; - } - } while ((ch = fIn.read()) >= 0); - - if (ch < 0) { - lookAhead = -1; - } - - return lookAhead; - } - - private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn) - throws IOException { - int lookAhead = fIn.read(); - - if (lastCh == '\r' && lookAhead == '\n') { - bOut.write(lookAhead); - lookAhead = fIn.read(); - } - - return lookAhead; - } - - private static int getLengthWithoutSeparator(byte[] line) { - int end = line.length - 1; - - while (end >= 0 && isLineEnding(line[end])) { - end--; - } - - return end + 1; - } - - private static boolean isLineEnding(byte b) { - return b == '\r' || b == '\n'; - } - - private static int getLengthWithoutWhiteSpace(byte[] line) { - int end = line.length - 1; - - while (end >= 0 && isWhiteSpace(line[end])) { - end--; - } - - return end + 1; - } - - private static boolean isWhiteSpace(byte b) { - return b == '\r' || b == '\n' || b == '\t' || b == ' '; - } - - private static byte[] getLineSeparator() { - String nl = System.getProperty("line.separator"); - byte[] nlBytes = new byte[nl.length()]; - - for (int i = 0; i != nlBytes.length; i++) { - nlBytes[i] = (byte) nl.charAt(i); - } - - return nlBytes; - } - - public static boolean isReleaseVersion(Context context) { - try { - PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); - if (pi.versionCode % 100 == 99) { - return true; - } else { - return false; - } - } catch (NameNotFoundException e) { - // impossible! - return false; - } - } - - public static String getVersion(Context context) { - String version = null; - try { - PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); - version = pi.versionName; - return version; - } catch (NameNotFoundException e) { - Log.e(Constants.TAG, "Version could not be retrieved!", e); - return "0.0.0"; - } - } - - public static String getFullVersion(Context context) { - return "APG v" + getVersion(context); - } - - /** - * Generate a random filename - * - * @param length - * @return - */ - public static String generateRandomFilename(int length) { - SecureRandom random = new SecureRandom(); - - byte bytes[] = new byte[length]; - random.nextBytes(bytes); - String result = ""; - for (int i = 0; i < length; ++i) { - int v = (bytes[i] + 256) % 64; - if (v < 10) { - result += (char) ('0' + v); - } else if (v < 36) { - result += (char) ('A' + v - 10); - } else if (v < 62) { - result += (char) ('a' + v - 36); - } else if (v == 62) { - result += '_'; - } else if (v == 63) { - result += '.'; - } - } - return result; - } - - /** - * Go once through stream to get length of stream. The length is later used to display progress - * when encrypting/decrypting - * - * @param in - * @return - * @throws IOException - */ - public static long getLengthOfStream(InputStream in) throws IOException { - long size = 0; - long n = 0; - byte dummy[] = new byte[0x10000]; - while ((n = in.read(dummy)) > 0) { - size += n; - } - return size; - } - - /** - * Deletes file securely by overwriting it with random data before deleting it. - * - * TODO: Does this really help on flash storage? - * - * @param context - * @param progress - * @param file - * @throws FileNotFoundException - * @throws IOException - */ - public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file) - throws FileNotFoundException, IOException { - long length = file.length(); - SecureRandom random = new SecureRandom(); - RandomAccessFile raf = new RandomAccessFile(file, "rws"); - raf.seek(0); - raf.getFilePointer(); - byte[] data = new byte[1 << 16]; - int pos = 0; - String msg = context.getString(R.string.progress_deletingSecurely, file.getName()); - while (pos < length) { - if (progress != null) - progress.setProgress(msg, (int) (100 * pos / length), 100); - random.nextBytes(data); - raf.write(data); - pos += data.length; - } - raf.close(); - file.delete(); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/helper/Preferences.java b/org_apg/src/org/thialfihar/android/apg/helper/Preferences.java deleted file mode 100644 index 14adf357a..000000000 --- a/org_apg/src/org/thialfihar/android/apg/helper/Preferences.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * 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.helper; - -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.openpgp.PGPEncryptedData; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; - -import android.content.Context; -import android.content.SharedPreferences; - -import java.util.Vector; - -/** - * Singleton Implementation of a Preference Helper - */ -public class Preferences { - private static Preferences mPreferences; - private SharedPreferences mSharedPreferences; - - public static synchronized Preferences getPreferences(Context context) { - return getPreferences(context, false); - } - - public static synchronized Preferences getPreferences(Context context, boolean force_new) { - if (mPreferences == null || force_new) { - mPreferences = new Preferences(context); - } - return mPreferences; - } - - private Preferences(Context context) { - mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE); - } - - public String getLanguage() { - return mSharedPreferences.getString(Constants.pref.LANGUAGE, ""); - } - - public void setLanguage(String value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - editor.putString(Constants.pref.LANGUAGE, value); - editor.commit(); - } - - public long getPassPhraseCacheTtl() { - int ttl = mSharedPreferences.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; - } - return (long) ttl; - } - - public void setPassPhraseCacheTtl(int value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - editor.putInt(Constants.pref.PASS_PHRASE_CACHE_TTL, value); - editor.commit(); - } - - public int getDefaultEncryptionAlgorithm() { - return mSharedPreferences.getInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM, - PGPEncryptedData.AES_256); - } - - public void setDefaultEncryptionAlgorithm(int value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - editor.putInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM, value); - editor.commit(); - } - - public int getDefaultHashAlgorithm() { - return mSharedPreferences.getInt(Constants.pref.DEFAULT_HASH_ALGORITHM, - HashAlgorithmTags.SHA256); - } - - public void setDefaultHashAlgorithm(int value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - editor.putInt(Constants.pref.DEFAULT_HASH_ALGORITHM, value); - editor.commit(); - } - - public int getDefaultMessageCompression() { - return mSharedPreferences.getInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION, - Id.choice.compression.zlib); - } - - public void setDefaultMessageCompression(int value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - editor.putInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION, value); - editor.commit(); - } - - public int getDefaultFileCompression() { - return mSharedPreferences.getInt(Constants.pref.DEFAULT_FILE_COMPRESSION, - Id.choice.compression.none); - } - - public void setDefaultFileCompression(int value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - editor.putInt(Constants.pref.DEFAULT_FILE_COMPRESSION, value); - editor.commit(); - } - - public boolean getDefaultAsciiArmour() { - return mSharedPreferences.getBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, false); - } - - public void setDefaultAsciiArmour(boolean value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - editor.putBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, value); - editor.commit(); - } - - public boolean getForceV3Signatures() { - return mSharedPreferences.getBoolean(Constants.pref.FORCE_V3_SIGNATURES, false); - } - - public void setForceV3Signatures(boolean value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - editor.putBoolean(Constants.pref.FORCE_V3_SIGNATURES, value); - editor.commit(); - } - - public String[] getKeyServers() { - String rawData = mSharedPreferences.getString(Constants.pref.KEY_SERVERS, - Constants.defaults.KEY_SERVERS); - Vector servers = new Vector(); - String chunks[] = rawData.split(","); - for (int i = 0; i < chunks.length; ++i) { - String tmp = chunks[i].trim(); - if (tmp.length() > 0) { - servers.add(tmp); - } - } - return servers.toArray(chunks); - } - - public void setKeyServers(String[] value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - String rawData = ""; - for (int i = 0; i < value.length; ++i) { - String tmp = value[i].trim(); - if (tmp.length() == 0) { - continue; - } - if (!"".equals(rawData)) { - rawData += ","; - } - rawData += tmp; - } - editor.putString(Constants.pref.KEY_SERVERS, rawData); - editor.commit(); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgContract.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgContract.java deleted file mode 100644 index c68eec750..000000000 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgContract.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * 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 org.thialfihar.android.apg.Constants; - -import android.net.Uri; -import android.provider.BaseColumns; - -public class ApgContract { - - interface KeyRingsColumns { - String MASTER_KEY_ID = "master_key_id"; // not a database id - String TYPE = "type"; // see KeyTypes - String KEY_RING_DATA = "key_ring_data"; // PGPPublicKeyRing / PGPSecretKeyRing blob - } - - interface KeysColumns { - String KEY_ID = "key_id"; // not a database id - String TYPE = "type"; // see KeyTypes - String IS_MASTER_KEY = "is_master_key"; - String ALGORITHM = "algorithm"; - String KEY_SIZE = "key_size"; - String CAN_SIGN = "can_sign"; - String CAN_ENCRYPT = "can_encrypt"; - String IS_REVOKED = "is_revoked"; - String CREATION = "creation"; - String EXPIRY = "expiry"; - String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID - String KEY_DATA = "key_data"; // PGPPublicKey / PGPSecretKey blob - String RANK = "rank"; - } - - interface UserIdsColumns { - String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID - String USER_ID = "user_id"; // not a database id - String RANK = "rank"; - } - - public static final class KeyTypes { - public static final int PUBLIC = 0; - public static final int SECRET = 1; - } - - public static final String CONTENT_AUTHORITY_EXTERNAL = Constants.PACKAGE_NAME; - public static final String CONTENT_AUTHORITY_INTERNAL = Constants.PACKAGE_NAME + ".internal"; - - private static final Uri BASE_CONTENT_URI_INTERNAL = Uri.parse("content://" - + CONTENT_AUTHORITY_INTERNAL); - - public static final String BASE_KEY_RINGS = "key_rings"; - public static final String BASE_DATA = "data"; - - public static final String PATH_PUBLIC = "public"; - public static final String PATH_SECRET = "secret"; - - public static final String PATH_BY_MASTER_KEY_ID = "master_key_id"; - public static final String PATH_BY_KEY_ID = "key_id"; - public static final String PATH_BY_EMAILS = "emails"; - - public static final String PATH_USER_IDS = "user_ids"; - public static final String PATH_KEYS = "keys"; - - public static class KeyRings implements KeyRingsColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_KEY_RINGS).build(); - - /** Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring"; - - /** Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring"; - - public static Uri buildPublicKeyRingsUri() { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build(); - } - - public static Uri buildPublicKeyRingsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId).build(); - } - - public static Uri buildPublicKeyRingsByMasterKeyIdUri(String masterKeyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC) - .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); - } - - public static Uri buildPublicKeyRingsByKeyIdUri(String keyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_KEY_ID) - .appendPath(keyId).build(); - } - - public static Uri buildPublicKeyRingsByEmailsUri(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_EMAILS) - .appendPath(emails).build(); - } - - public static Uri buildSecretKeyRingsUri() { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build(); - } - - public static Uri buildSecretKeyRingsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId).build(); - } - - public static Uri buildSecretKeyRingsByMasterKeyIdUri(String masterKeyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET) - .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); - } - - public static Uri buildSecretKeyRingsByKeyIdUri(String keyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_KEY_ID) - .appendPath(keyId).build(); - } - - public static Uri buildSecretKeyRingsByEmailsUri(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS) - .appendPath(emails).build(); - } - } - - public static class Keys implements KeysColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_KEY_RINGS).build(); - - /** Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key"; - - /** Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key"; - - public static Uri buildPublicKeysUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).build(); - } - - public static Uri buildPublicKeysUri(String keyRingRowId, String keyRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).appendPath(keyRowId).build(); - } - - public static Uri buildSecretKeysUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).build(); - } - - public static Uri buildSecretKeysUri(String keyRingRowId, String keyRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).appendPath(keyRowId).build(); - } - } - - public static class UserIds implements UserIdsColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_KEY_RINGS).build(); - - /** Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id"; - - /** Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id"; - - public static Uri buildPublicUserIdsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).build(); - } - - public static Uri buildPublicUserIdsUri(String keyRingRowId, String userIdRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); - } - - public static Uri buildSecretUserIdsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).build(); - } - - public static Uri buildSecretUserIdsUri(String keyRingRowId, String userIdRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); - } - } - - public static class DataStream { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_DATA).build(); - - public static Uri buildDataStreamUri(String streamFilename) { - return CONTENT_URI.buildUpon().appendPath(streamFilename).build(); - } - } - - private ApgContract() { - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgDatabase.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgDatabase.java deleted file mode 100644 index e25fce3f0..000000000 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgDatabase.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * 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 org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.provider.ApgContract.KeyRingsColumns; -import org.thialfihar.android.apg.provider.ApgContract.KeysColumns; -import org.thialfihar.android.apg.provider.ApgContract.UserIdsColumns; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.provider.BaseColumns; - -import org.thialfihar.android.apg.util.Log; - -public class ApgDatabase extends SQLiteOpenHelper { - private static final String DATABASE_NAME = "apg.db"; - private static final int DATABASE_VERSION = 3; - - public interface Tables { - String KEY_RINGS = "key_rings"; - String KEYS = "keys"; - String USER_IDS = "user_ids"; - } - - private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS - + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + KeyRingsColumns.MASTER_KEY_ID + " INT64, " + KeyRingsColumns.TYPE + " INTEGER, " - + KeyRingsColumns.KEY_RING_DATA + " BLOB)"; - - private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " (" - + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + KeysColumns.KEY_ID - + " INT64, " + KeysColumns.TYPE + " INTEGER, " + KeysColumns.IS_MASTER_KEY - + " INTEGER, " + KeysColumns.ALGORITHM + " INTEGER, " + KeysColumns.KEY_SIZE - + " INTEGER, " + KeysColumns.CAN_SIGN + " INTEGER, " + KeysColumns.CAN_ENCRYPT - + " INTEGER, " + KeysColumns.IS_REVOKED + " INTEGER, " + KeysColumns.CREATION - + " INTEGER, " + KeysColumns.EXPIRY + " INTEGER, " + KeysColumns.KEY_DATA + " BLOB," - + KeysColumns.RANK + " INTEGER, " + KeysColumns.KEY_RING_ROW_ID - + " INTEGER NOT NULL, FOREIGN KEY(" + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " - + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)"; - - private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS - + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + UserIdsColumns.USER_ID + " TEXT, " + UserIdsColumns.RANK + " INTEGER, " - + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" - + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" - + BaseColumns._ID + ") ON DELETE CASCADE)"; - - ApgDatabase(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - Log.w(Constants.TAG, "Creating database..."); - - db.execSQL(CREATE_KEY_RINGS); - db.execSQL(CREATE_KEYS); - db.execSQL(CREATE_USER_IDS); - } - - @Override - public void onOpen(SQLiteDatabase db) { - super.onOpen(db); - if (!db.isReadOnly()) { - // Enable foreign key constraints - db.execSQL("PRAGMA foreign_keys=ON;"); - } - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); - - // Upgrade from oldVersion through all methods to newest one - for (int version = oldVersion; version < newVersion; ++version) { - Log.w(Constants.TAG, "Upgrading database to version " + version); - - switch (version) { - - default: - break; - - } - } - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgProvider.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgProvider.java deleted file mode 100644 index a7ec253a3..000000000 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgProvider.java +++ /dev/null @@ -1,825 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * 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 java.io.File; -import java.io.FileNotFoundException; -import java.util.Arrays; -import java.util.HashMap; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.KeyRingsColumns; -import org.thialfihar.android.apg.provider.ApgContract.KeyTypes; -import org.thialfihar.android.apg.provider.ApgContract.KeysColumns; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.provider.ApgContract.UserIdsColumns; -import org.thialfihar.android.apg.provider.ApgDatabase.Tables; -import org.thialfihar.android.apg.util.Log; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.UriMatcher; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteConstraintException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteQueryBuilder; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.provider.BaseColumns; -import android.text.TextUtils; - -public class ApgProvider extends ContentProvider { - private static final int PUBLIC_KEY_RING = 101; - private static final int PUBLIC_KEY_RING_BY_ROW_ID = 102; - private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID = 103; - private static final int PUBLIC_KEY_RING_BY_KEY_ID = 104; - private static final int PUBLIC_KEY_RING_BY_EMAILS = 105; - - private static final int PUBLIC_KEY_RING_KEY = 111; - private static final int PUBLIC_KEY_RING_KEY_BY_ROW_ID = 112; - - private static final int PUBLIC_KEY_RING_USER_ID = 121; - private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122; - - private static final int SECRET_KEY_RING = 201; - private static final int SECRET_KEY_RING_BY_ROW_ID = 202; - private static final int SECRET_KEY_RING_BY_MASTER_KEY_ID = 203; - private static final int SECRET_KEY_RING_BY_KEY_ID = 204; - private static final int SECRET_KEY_RING_BY_EMAILS = 205; - - private static final int SECRET_KEY_RING_KEY = 211; - private static final int SECRET_KEY_RING_KEY_BY_ROW_ID = 212; - - private static final int SECRET_KEY_RING_USER_ID = 221; - private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; - - private static final int DATA_STREAM = 301; - - protected static boolean sInternalProvider; - protected static UriMatcher sUriMatcher; - - /** - * Build and return a {@link UriMatcher} that catches all {@link Uri} variations supported by - * this {@link ContentProvider}. - */ - protected static UriMatcher buildUriMatcher(boolean internalProvider) { - final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); - - String authority; - if (internalProvider) { - authority = ApgContract.CONTENT_AUTHORITY_INTERNAL; - } else { - authority = ApgContract.CONTENT_AUTHORITY_EXTERNAL; - } - - /** - * public key rings - * - *
-         * key_rings/public
-         * key_rings/public/#
-         * key_rings/public/master_key_id/_
-         * key_rings/public/key_id/_
-         * key_rings/public/emails/_
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC, - PUBLIC_KEY_RING); - matcher.addURI(authority, - ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/#", - PUBLIC_KEY_RING_BY_ROW_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" - + ApgContract.PATH_BY_MASTER_KEY_ID + "/*", PUBLIC_KEY_RING_BY_MASTER_KEY_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" - + ApgContract.PATH_BY_KEY_ID + "/*", PUBLIC_KEY_RING_BY_KEY_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" - + ApgContract.PATH_BY_EMAILS + "/*", PUBLIC_KEY_RING_BY_EMAILS); - - /** - * public keys - * - *
-         * key_rings/public/#/keys
-         * key_rings/public/#/keys/#
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC - + "/#/" + ApgContract.PATH_KEYS, PUBLIC_KEY_RING_KEY); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC - + "/#/" + ApgContract.PATH_KEYS + "/#", PUBLIC_KEY_RING_KEY_BY_ROW_ID); - - /** - * public user ids - * - *
-         * key_rings/public/#/user_ids
-         * key_rings/public/#/user_ids/#
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC - + "/#/" + ApgContract.PATH_USER_IDS, PUBLIC_KEY_RING_USER_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC - + "/#/" + ApgContract.PATH_USER_IDS + "/#", PUBLIC_KEY_RING_USER_ID_BY_ROW_ID); - - /** - * secret key rings - * - *
-         * key_rings/secret
-         * key_rings/secret/#
-         * key_rings/secret/master_key_id/_
-         * key_rings/secret/key_id/_
-         * key_rings/secret/emails/_
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET, - SECRET_KEY_RING); - matcher.addURI(authority, - ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/#", - SECRET_KEY_RING_BY_ROW_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" - + ApgContract.PATH_BY_MASTER_KEY_ID + "/*", SECRET_KEY_RING_BY_MASTER_KEY_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" - + ApgContract.PATH_BY_KEY_ID + "/*", SECRET_KEY_RING_BY_KEY_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" - + ApgContract.PATH_BY_EMAILS + "/*", SECRET_KEY_RING_BY_EMAILS); - - /** - * secret keys - * - *
-         * key_rings/secret/#/keys
-         * key_rings/secret/#/keys/#
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET - + "/#/" + ApgContract.PATH_KEYS, SECRET_KEY_RING_KEY); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET - + "/#/" + ApgContract.PATH_KEYS + "/#", SECRET_KEY_RING_KEY_BY_ROW_ID); - - /** - * secret user ids - * - *
-         * key_rings/secret/#/user_ids
-         * key_rings/secret/#/user_ids/#
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET - + "/#/" + ApgContract.PATH_USER_IDS, SECRET_KEY_RING_USER_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET - + "/#/" + ApgContract.PATH_USER_IDS + "/#", SECRET_KEY_RING_USER_ID_BY_ROW_ID); - - /** - * data stream - * - *
-         * data / _
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_DATA + "/*", DATA_STREAM); - - return matcher; - } - - private ApgDatabase mApgDatabase; - - /** {@inheritDoc} */ - @Override - public boolean onCreate() { - mApgDatabase = new ApgDatabase(getContext()); - return true; - } - - /** {@inheritDoc} */ - @Override - public String getType(Uri uri) { - final int match = sUriMatcher.match(uri); - switch (match) { - case PUBLIC_KEY_RING: - case PUBLIC_KEY_RING_BY_EMAILS: - case SECRET_KEY_RING: - case SECRET_KEY_RING_BY_EMAILS: - return KeyRings.CONTENT_TYPE; - - case PUBLIC_KEY_RING_BY_ROW_ID: - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case PUBLIC_KEY_RING_BY_KEY_ID: - case SECRET_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_KEY_ID: - return KeyRings.CONTENT_ITEM_TYPE; - - case PUBLIC_KEY_RING_KEY: - case SECRET_KEY_RING_KEY: - return Keys.CONTENT_TYPE; - - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - return Keys.CONTENT_ITEM_TYPE; - - case PUBLIC_KEY_RING_USER_ID: - case SECRET_KEY_RING_USER_ID: - return UserIds.CONTENT_TYPE; - - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - return UserIds.CONTENT_ITEM_TYPE; - - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } - } - - /** - * Returns type of the query (secret/public) - * - * @param uri - * @return - */ - private int getKeyType(int match) { - int type; - switch (match) { - case PUBLIC_KEY_RING: - case PUBLIC_KEY_RING_BY_ROW_ID: - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case PUBLIC_KEY_RING_BY_KEY_ID: - case PUBLIC_KEY_RING_BY_EMAILS: - case PUBLIC_KEY_RING_KEY: - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case PUBLIC_KEY_RING_USER_ID: - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - type = KeyTypes.PUBLIC; - break; - - case SECRET_KEY_RING: - case SECRET_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_KEY_ID: - case SECRET_KEY_RING_BY_EMAILS: - case SECRET_KEY_RING_KEY: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - type = KeyTypes.SECRET; - break; - - default: - throw new IllegalArgumentException("Unknown match " + match); - - } - - return type; - } - - /** - * Set result of query to specific columns, don't show blob column for external content provider - * - * @return - */ - private HashMap getProjectionMapForKeyRings() { - HashMap projectionMap = new HashMap(); - - projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID); - projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." - + KeyRingsColumns.MASTER_KEY_ID); - // only give out keyRing blob when we are using the internal content provider - if (sInternalProvider) { - projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "." - + KeyRingsColumns.KEY_RING_DATA); - } - projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID); - - return projectionMap; - } - - /** - * Set result of query to specific columns, don't show blob column for external content provider - * - * @return - */ - private HashMap getProjectionMapForKeys() { - HashMap projectionMap = new HashMap(); - - projectionMap.put(BaseColumns._ID, BaseColumns._ID); - projectionMap.put(KeysColumns.KEY_ID, KeysColumns.KEY_ID); - projectionMap.put(KeysColumns.IS_MASTER_KEY, KeysColumns.IS_MASTER_KEY); - projectionMap.put(KeysColumns.ALGORITHM, KeysColumns.ALGORITHM); - projectionMap.put(KeysColumns.KEY_SIZE, KeysColumns.KEY_SIZE); - projectionMap.put(KeysColumns.CAN_SIGN, KeysColumns.CAN_SIGN); - projectionMap.put(KeysColumns.CAN_ENCRYPT, KeysColumns.CAN_ENCRYPT); - projectionMap.put(KeysColumns.IS_REVOKED, KeysColumns.IS_REVOKED); - projectionMap.put(KeysColumns.CREATION, KeysColumns.CREATION); - projectionMap.put(KeysColumns.EXPIRY, KeysColumns.EXPIRY); - projectionMap.put(KeysColumns.KEY_RING_ROW_ID, KeysColumns.KEY_RING_ROW_ID); - // only give out keyRing blob when we are using the internal content provider - if (sInternalProvider) { - projectionMap.put(KeysColumns.KEY_DATA, KeysColumns.KEY_DATA); - } - projectionMap.put(KeysColumns.RANK, KeysColumns.RANK); - - return projectionMap; - } - - /** - * Builds default query for keyRings: KeyRings table is joined with Keys and UserIds - * - * @param qb - * @param match - * @param isMasterKey - * @param sortOrder - * @return - */ - private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match, String sortOrder) { - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.USER_IDS + " ON " + "(" - + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." - + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." - + UserIdsColumns.RANK + " = '0')"); - - qb.setProjectionMap(getProjectionMapForKeyRings()); - - return qb; - } - - /** - * Builds default query for keyRings: KeyRings table is joined with Keys and UserIds - * - * @param qb - * @param match - * @param isMasterKey - * @param sortOrder - * @return - */ - private SQLiteQueryBuilder buildKeyRingQueryWithKeys(SQLiteQueryBuilder qb, int match, - String sortOrder) { - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "(" - + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "." - + KeysColumns.KEY_RING_ROW_ID + " AND " + Tables.KEYS + "." - + KeysColumns.IS_MASTER_KEY + " = '1') " + " INNER JOIN " + Tables.USER_IDS - + " ON " + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS - + "." + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." - + UserIdsColumns.RANK + " = '0')"); - - qb.setProjectionMap(getProjectionMapForKeyRings()); - - return qb; - } - - /** {@inheritDoc} */ - @SuppressWarnings("deprecation") - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")"); - - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - SQLiteDatabase db = mApgDatabase.getReadableDatabase(); - - int match = sUriMatcher.match(uri); - - switch (match) { - case PUBLIC_KEY_RING: - case SECRET_KEY_RING: - qb = buildKeyRingQuery(qb, match, sortOrder); - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } - - break; - - case PUBLIC_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_ROW_ID: - qb = buildKeyRingQuery(qb, match, sortOrder); - - qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } - - break; - - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - qb = buildKeyRingQuery(qb, match, sortOrder); - - qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } - - break; - - case SECRET_KEY_RING_BY_KEY_ID: - case PUBLIC_KEY_RING_BY_KEY_ID: - qb = buildKeyRingQueryWithKeys(qb, match, sortOrder); - - qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } - - break; - - case SECRET_KEY_RING_BY_EMAILS: - case PUBLIC_KEY_RING_BY_EMAILS: - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "(" - + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "." - + KeysColumns.KEY_RING_ROW_ID + " AND " + Tables.KEYS + "." - + KeysColumns.IS_MASTER_KEY + " = '1'" + ") " + " INNER JOIN " - + Tables.USER_IDS + " ON " + "(" + Tables.KEYS + "." + BaseColumns._ID + " = " - + Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " AND " - + Tables.USER_IDS + "." + UserIdsColumns.RANK + " = '0')"); - - qb.setProjectionMap(getProjectionMapForKeyRings()); - - String emails = uri.getLastPathSegment(); - String chunks[] = emails.split(" *, *"); - boolean gotCondition = false; - String emailWhere = ""; - for (int i = 0; i < chunks.length; ++i) { - if (chunks[i].length() == 0) { - continue; - } - if (i != 0) { - emailWhere += " OR "; - } - emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE "; - // match '*', so it has to be at the *end* of the user id - emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">"); - gotCondition = true; - } - - if (gotCondition) { - qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " - + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID - + " = " + Tables.KEYS + "." + BaseColumns._ID + " AND (" + emailWhere - + "))"); - } - - break; - - case PUBLIC_KEY_RING_KEY: - case SECRET_KEY_RING_KEY: - qb.setTables(Tables.KEYS); - qb.appendWhere(KeysColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - qb.setProjectionMap(getProjectionMapForKeys()); - - break; - - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - qb.setTables(Tables.KEYS); - qb.appendWhere(KeysColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - qb.appendWhere(" AND " + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - qb.setProjectionMap(getProjectionMapForKeys()); - - break; - - case PUBLIC_KEY_RING_USER_ID: - case SECRET_KEY_RING_USER_ID: - qb.setTables(Tables.USER_IDS); - qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - break; - - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - qb.setTables(Tables.USER_IDS); - qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - qb.appendWhere(" AND " + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - break; - - default: - throw new IllegalArgumentException("Unknown URI " + uri); - - } - - // If no sort order is specified use the default - String orderBy; - if (TextUtils.isEmpty(sortOrder)) { - orderBy = null; - } else { - orderBy = sortOrder; - } - - 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 changes - c.setNotificationUri(getContext().getContentResolver(), uri); - - if (Constants.DEBUG) { - Log.d(Constants.TAG, - "Query: " - + qb.buildQuery(projection, selection, selectionArgs, null, null, - orderBy, null)); - Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(c)); - } - - return c; - } - - /** {@inheritDoc} */ - @Override - public Uri insert(Uri uri, ContentValues values) { - Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")"); - - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); - - Uri rowUri = null; - long rowId = -1; - try { - final int match = sUriMatcher.match(uri); - - switch (match) { - case PUBLIC_KEY_RING: - values.put(KeyRings.TYPE, KeyTypes.PUBLIC); - - rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); - rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); - - break; - case PUBLIC_KEY_RING_KEY: - values.put(Keys.TYPE, KeyTypes.PUBLIC); - - rowId = db.insertOrThrow(Tables.KEYS, null, values); - rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); - - break; - case PUBLIC_KEY_RING_USER_ID: - rowId = db.insertOrThrow(Tables.USER_IDS, null, values); - rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); - - break; - case SECRET_KEY_RING: - values.put(KeyRings.TYPE, KeyTypes.SECRET); - - rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); - rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); - - break; - case SECRET_KEY_RING_KEY: - values.put(Keys.TYPE, KeyTypes.SECRET); - - rowId = db.insertOrThrow(Tables.KEYS, null, values); - rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); - - break; - case SECRET_KEY_RING_USER_ID: - rowId = db.insertOrThrow(Tables.USER_IDS, null, values); - rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); - - break; - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } - } catch (SQLiteConstraintException e) { - Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?"); - } - - // notify of changes in db - getContext().getContentResolver().notifyChange(uri, null); - - return rowUri; - } - - /** {@inheritDoc} */ - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - Log.v(Constants.TAG, "delete(uri=" + uri + ")"); - - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); - - int count; - final int match = sUriMatcher.match(uri); - - String defaultSelection = null; - switch (match) { - case PUBLIC_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_ROW_ID: - defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); - // corresponding keys and userIds are deleted by ON DELETE CASCADE - count = db.delete(Tables.KEY_RINGS, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), - selectionArgs); - break; - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); - // corresponding keys and userIds are deleted by ON DELETE CASCADE - count = db.delete(Tables.KEY_RINGS, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), - selectionArgs); - break; - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - count = db.delete(Tables.KEYS, - buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs); - break; - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection), - selectionArgs); - break; - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } - - // notify of changes in db - getContext().getContentResolver().notifyChange(uri, null); - - return count; - } - - /** {@inheritDoc} */ - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")"); - - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); - - String defaultSelection = null; - int count = 0; - try { - final int match = sUriMatcher.match(uri); - switch (match) { - case PUBLIC_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_ROW_ID: - defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); - - count = db.update( - Tables.KEY_RINGS, - values, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), - selection), selectionArgs); - break; - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); - - count = db.update( - Tables.KEY_RINGS, - values, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), - selection), selectionArgs); - break; - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - count = db - .update(Tables.KEYS, values, - buildDefaultKeysSelection(uri, getKeyType(match), selection), - selectionArgs); - break; - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - count = db.update(Tables.USER_IDS, values, - buildDefaultUserIdsSelection(uri, selection), selectionArgs); - break; - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } - } catch (SQLiteConstraintException e) { - Log.e(Constants.TAG, "Constraint exception on update! Entry already existing?"); - } - - // notify of changes in db - getContext().getContentResolver().notifyChange(uri, null); - - return count; - } - - /** - * Build default selection statement for KeyRings. If no extra selection is specified only build - * where clause with rowId - * - * @param uri - * @param selection - * @return - */ - private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType, - String selection) { - String andType = ""; - if (keyType != null) { - andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType; - } - - String andSelection = ""; - if (!TextUtils.isEmpty(selection)) { - andSelection = " AND (" + selection + ")"; - } - - return defaultSelection + andType + andSelection; - } - - /** - * Build default selection statement for Keys. If no extra selection is specified only build - * where clause with rowId - * - * @param uri - * @param selection - * @return - */ - private String buildDefaultKeysSelection(Uri uri, Integer keyType, String selection) { - String rowId = uri.getLastPathSegment(); - - String foreignKeyRingRowId = uri.getPathSegments().get(2); - String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " - + foreignKeyRingRowId; - - String andType = ""; - if (keyType != null) { - andType = " AND " + KeysColumns.TYPE + "=" + keyType; - } - - String andSelection = ""; - if (!TextUtils.isEmpty(selection)) { - andSelection = " AND (" + selection + ")"; - } - - return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andType + andSelection; - } - - /** - * Build default selection statement for UserIds. If no extra selection is specified only build - * where clause with rowId - * - * @param uri - * @param selection - * @return - */ - private String buildDefaultUserIdsSelection(Uri uri, String selection) { - String rowId = uri.getLastPathSegment(); - - String foreignKeyRingRowId = uri.getPathSegments().get(2); - String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " - + foreignKeyRingRowId; - - String andSelection = ""; - if (!TextUtils.isEmpty(selection)) { - andSelection = " AND (" + selection + ")"; - } - - return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andSelection; - } - - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - int match = sUriMatcher.match(uri); - if (match != DATA_STREAM) { - throw new FileNotFoundException(); - } - String fileName = uri.getLastPathSegment(); - File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName); - return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java deleted file mode 100644 index 61cca8bed..000000000 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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; - -/** - * The same content provider as ApgProviderInternal except that it does not give out keyRing and key - * blob data when querying. - * - * This provider is exported with a readPermission in AndroidManifest.xml - */ -public class ApgProviderExternal extends ApgProvider { - - static { - sInternalProvider = false; - sUriMatcher = buildUriMatcher(sInternalProvider); - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java deleted file mode 100644 index e2226a0fa..000000000 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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; - -/** - * This provider is NOT exported in AndroidManifest.xml as it also return the actual secret keys - * from the database - */ -public class ApgProviderInternal extends ApgProvider { - - static { - sInternalProvider = true; - sUriMatcher = buildUriMatcher(sInternalProvider); - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java deleted file mode 100644 index 5bf1dd329..000000000 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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 org.thialfihar.android.apg.Constants; - -import android.net.Uri; -import android.provider.BaseColumns; - -public class ApgServiceBlobContract { - - interface BlobsColumns { - String KEY = "key"; - } - - public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME + ".blobs"; - - private static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); - - public static class Blobs implements BlobsColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI; - } - - private ApgServiceBlobContract() { - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java deleted file mode 100644 index 417486e33..000000000 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2011 Markus Doits - * - * 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.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.provider.BaseColumns; - -import org.thialfihar.android.apg.provider.ApgServiceBlobContract.BlobsColumns; - -public class ApgServiceBlobDatabase extends SQLiteOpenHelper { - private static final String DATABASE_NAME = "apg_blob.db"; - private static final int DATABASE_VERSION = 2; - - public static final String TABLE = "data"; - - public ApgServiceBlobDatabase(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE + " ( " + BaseColumns._ID - + " INTEGER PRIMARY KEY AUTOINCREMENT, " + BlobsColumns.KEY + " TEXT NOT NULL)"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // no upgrade necessary yet - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java deleted file mode 100644 index 8053aecbf..000000000 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2011 Markus Doits - * - * 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 org.thialfihar.android.apg.Constants; - -import android.content.ContentProvider; -import android.content.ContentUris; -import android.content.ContentValues; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.provider.BaseColumns; - -import org.thialfihar.android.apg.provider.ApgServiceBlobContract.Blobs; -import org.thialfihar.android.apg.provider.ApgServiceBlobContract.BlobsColumns; -import org.thialfihar.android.apg.util.Log; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.List; -import java.util.UUID; - -public class ApgServiceBlobProvider extends ContentProvider { - private static final String STORE_PATH = Constants.path.APP_DIR + "/ApgBlobs"; - - private ApgServiceBlobDatabase mBlobDatabase = null; - - public ApgServiceBlobProvider() { - File dir = new File(STORE_PATH); - dir.mkdirs(); - } - - @Override - public boolean onCreate() { - mBlobDatabase = new ApgServiceBlobDatabase(getContext()); - return true; - } - - /** {@inheritDoc} */ - @Override - public Uri insert(Uri uri, ContentValues ignored) { - // ContentValues are actually ignored, because we want to store a blob with no more - // information but have to create an record with the password generated here first - ContentValues vals = new ContentValues(); - - // Insert a random key in the database. This has to provided by the caller when updating or - // getting the blob - String password = UUID.randomUUID().toString(); - vals.put(BlobsColumns.KEY, password); - - SQLiteDatabase db = mBlobDatabase.getWritableDatabase(); - long newRowId = db.insert(ApgServiceBlobDatabase.TABLE, null, vals); - Uri insertedUri = ContentUris.withAppendedId(Blobs.CONTENT_URI, newRowId); - - return Uri.withAppendedPath(insertedUri, password); - } - - /** {@inheritDoc} */ - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException, - FileNotFoundException { - Log.d(Constants.TAG, "openFile() called with uri: " + uri.toString() + " and mode: " + mode); - - List segments = uri.getPathSegments(); - if (segments.size() < 2) { - throw new SecurityException("Password not found in URI"); - } - String id = segments.get(0); - String key = segments.get(1); - - Log.d(Constants.TAG, "Got id: " + id + " and key: " + key); - - // get the data - SQLiteDatabase db = mBlobDatabase.getReadableDatabase(); - Cursor result = db.query(ApgServiceBlobDatabase.TABLE, new String[] { BaseColumns._ID }, - BaseColumns._ID + " = ? and " + BlobsColumns.KEY + " = ?", - new String[] { id, key }, null, null, null); - - if (result.getCount() == 0) { - // either the key is wrong or no id exists - throw new FileNotFoundException("No file found with that ID and/or password"); - } - - File targetFile = new File(STORE_PATH, id); - if (mode.equals("w")) { - Log.d(Constants.TAG, "Try to open file w"); - if (!targetFile.exists()) { - try { - targetFile.createNewFile(); - } catch (IOException e) { - Log.e(Constants.TAG, "Got IEOException on creating new file", e); - throw new FileNotFoundException("Could not create file to write to"); - } - } - return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_WRITE_ONLY - | ParcelFileDescriptor.MODE_TRUNCATE); - } else if (mode.equals("r")) { - Log.d(Constants.TAG, "Try to open file r"); - if (!targetFile.exists()) { - throw new FileNotFoundException("Error: Could not find the file requested"); - } - return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY); - } - - return null; - } - - /** {@inheritDoc} */ - @Override - public String getType(Uri uri) { - return null; - } - - /** {@inheritDoc} */ - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - return null; - } - - /** {@inheritDoc} */ - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - return 0; - } - - /** {@inheritDoc} */ - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - return 0; - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ProviderHelper.java b/org_apg/src/org/thialfihar/android/apg/provider/ProviderHelper.java deleted file mode 100644 index 915c0aea5..000000000 --- a/org_apg/src/org/thialfihar/android/apg/provider/ProviderHelper.java +++ /dev/null @@ -1,501 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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 java.io.IOException; -import java.util.ArrayList; -import java.util.Date; -import java.util.Vector; - -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.helper.PGPConversionHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.util.IterableIterator; -import org.thialfihar.android.apg.util.Log; - -import android.content.ContentProviderOperation; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.net.Uri; -import android.os.RemoteException; - -public class ProviderHelper { - - /** - * Private helper method to get PGPKeyRing from database - * - * @param context - * @param queryUri - * @return - */ - private static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) { - Cursor cursor = context.getContentResolver().query(queryUri, - new String[] { KeyRings._ID, KeyRings.KEY_RING_DATA }, null, null, null); - - PGPKeyRing keyRing = null; - if (cursor != null && cursor.moveToFirst()) { - int keyRingDataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); - - byte[] data = cursor.getBlob(keyRingDataCol); - if (data != null) { - keyRing = PGPConversionHelper.BytesToPGPKeyRing(data); - } - } - - if (cursor != null) { - cursor.close(); - } - - return keyRing; - } - - /** - * Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId - * - * @param context - * @param rowId - * @return - */ - public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); - return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId - * - * @param context - * @param masterKeyId - * @return - */ - public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context, - long masterKeyId) { - Uri queryUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key - * with this keyId - * - * @param context - * @param keyId - * @return - */ - public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) { - Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId)); - return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPPublicKey object from the database blob associated with a key with - * this keyId - * - * @param context - * @param keyId - * @return - */ - public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) { - PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId); - if (keyRing == null) { - return null; - } - - return keyRing.getPublicKey(keyId); - } - - /** - * Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId - * - * @param context - * @param rowId - * @return - */ - public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); - return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId - * - * @param context - * @param masterKeyId - * @return - */ - public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context, - long masterKeyId) { - Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key - * with this keyId - * - * @param context - * @param keyId - * @return - */ - public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) { - Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId)); - return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPSecretKey object from the database blob associated with a key with - * this keyId - * - * @param context - * @param keyId - * @return - */ - public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) { - PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId); - if (keyRing == null) { - return null; - } - - return keyRing.getSecretKey(keyId); - } - - /** - * Saves PGPPublicKeyRing with its keys and userIds in DB - * - * @param context - * @param keyRing - * @return - * @throws IOException - * @throws GeneralException - */ - @SuppressWarnings("unchecked") - public static void saveKeyRing(Context context, PGPPublicKeyRing keyRing) throws IOException { - PGPPublicKey masterKey = keyRing.getPublicKey(); - long masterKeyId = masterKey.getKeyID(); - - // delete old version of this keyRing, which also deletes all keys and userIds on cascade - Uri deleteUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - - try { - context.getContentResolver().delete(deleteUri, null, null); - } catch (UnsupportedOperationException e) { - Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); - } - - ContentValues values = new ContentValues(); - values.put(KeyRings.MASTER_KEY_ID, masterKeyId); - values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); - - // insert new version of this keyRing - Uri uri = KeyRings.buildPublicKeyRingsUri(); - Uri insertedUri = context.getContentResolver().insert(uri, values); - long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); - - // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); - - int rank = 0; - for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank)); - ++rank; - } - - int userIdRank = 0; - for (String userId : new IterableIterator(masterKey.getUserIDs())) { - operations.add(buildPublicUserIdOperations(context, keyRingRowId, userId, userIdRank)); - ++userIdRank; - } - - try { - context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY_INTERNAL, operations); - } catch (RemoteException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } catch (OperationApplicationException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } - } - - /** - * Saves PGPSecretKeyRing with its keys and userIds in DB - * - * @param context - * @param keyRing - * @return - * @throws IOException - * @throws GeneralException - */ - @SuppressWarnings("unchecked") - public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException { - PGPSecretKey masterKey = keyRing.getSecretKey(); - long masterKeyId = masterKey.getKeyID(); - - // delete old version of this keyRing, which also deletes all keys and userIds on cascade - Uri deleteUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - - try { - context.getContentResolver().delete(deleteUri, null, null); - } catch (UnsupportedOperationException e) { - Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); - } - - ContentValues values = new ContentValues(); - values.put(KeyRings.MASTER_KEY_ID, masterKeyId); - values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); - - // insert new version of this keyRing - Uri uri = KeyRings.buildSecretKeyRingsUri(); - Uri insertedUri = context.getContentResolver().insert(uri, values); - long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); - - // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); - - int rank = 0; - for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { - operations.add(buildSecretKeyOperations(context, keyRingRowId, key, rank)); - ++rank; - } - - int userIdRank = 0; - for (String userId : new IterableIterator(masterKey.getUserIDs())) { - operations.add(buildSecretUserIdOperations(context, keyRingRowId, userId, userIdRank)); - ++userIdRank; - } - - try { - context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY_INTERNAL, operations); - } catch (RemoteException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } catch (OperationApplicationException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } - } - - /** - * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing - * - * @param context - * @param keyRingRowId - * @param key - * @param rank - * @return - * @throws IOException - */ - private static ContentProviderOperation buildPublicKeyOperations(Context context, - long keyRingRowId, PGPPublicKey key, int rank) throws IOException { - ContentValues values = new ContentValues(); - values.put(Keys.KEY_ID, key.getKeyID()); - 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, PGPHelper.isSigningKey(key)); - values.put(Keys.CAN_ENCRYPT, PGPHelper.isEncryptionKey(key)); - values.put(Keys.IS_REVOKED, key.isRevoked()); - values.put(Keys.CREATION, PGPHelper.getCreationDate(key).getTime() / 1000); - Date expiryDate = PGPHelper.getExpiryDate(key); - if (expiryDate != null) { - values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); - } - values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); - values.put(Keys.KEY_DATA, key.getEncoded()); - values.put(Keys.RANK, rank); - - Uri uri = Keys.buildPublicKeysUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing - * - * @param context - * @param keyRingRowId - * @param key - * @param rank - * @return - * @throws IOException - */ - private static ContentProviderOperation buildPublicUserIdOperations(Context context, - long keyRingRowId, String userId, int rank) { - ContentValues values = new ContentValues(); - values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); - values.put(UserIds.USER_ID, userId); - values.put(UserIds.RANK, rank); - - Uri uri = UserIds.buildPublicUserIdsUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing - * - * @param context - * @param keyRingRowId - * @param key - * @param rank - * @return - * @throws IOException - */ - private static ContentProviderOperation buildSecretKeyOperations(Context context, - long keyRingRowId, PGPSecretKey key, int rank) throws IOException { - ContentValues values = new ContentValues(); - values.put(Keys.KEY_ID, key.getKeyID()); - 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, PGPHelper.isSigningKey(key)); - values.put(Keys.CAN_ENCRYPT, PGPHelper.isEncryptionKey(key)); - values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked()); - values.put(Keys.CREATION, PGPHelper.getCreationDate(key).getTime() / 1000); - Date expiryDate = PGPHelper.getExpiryDate(key); - if (expiryDate != null) { - values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); - } - values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); - values.put(Keys.KEY_DATA, key.getEncoded()); - values.put(Keys.RANK, rank); - - Uri uri = Keys.buildSecretKeysUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing - * - * @param context - * @param keyRingRowId - * @param key - * @param rank - * @return - * @throws IOException - */ - private static ContentProviderOperation buildSecretUserIdOperations(Context context, - long keyRingRowId, String userId, int rank) { - ContentValues values = new ContentValues(); - values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); - values.put(UserIds.USER_ID, userId); - values.put(UserIds.RANK, rank); - - Uri uri = UserIds.buildSecretUserIdsUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Private helper method - * - * @param context - * @param queryUri - * @return - */ - private static Vector getKeyRingsRowIds(Context context, Uri queryUri) { - Cursor cursor = context.getContentResolver().query(queryUri, new String[] { KeyRings._ID }, - null, null, null); - - Vector keyIds = new Vector(); - if (cursor != null) { - int idCol = cursor.getColumnIndex(KeyRings._ID); - if (cursor.moveToFirst()) { - do { - keyIds.add(cursor.getInt(idCol)); - } while (cursor.moveToNext()); - } - } - - if (cursor != null) { - cursor.close(); - } - - return keyIds; - } - - /** - * Retrieves ids of all SecretKeyRings - * - * @param context - * @return - */ - public static Vector getSecretKeyRingsRowIds(Context context) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(); - return getKeyRingsRowIds(context, queryUri); - } - - /** - * Retrieves ids of all PublicKeyRings - * - * @param context - * @return - */ - public static Vector getPublicKeyRingsRowIds(Context context) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(); - return getKeyRingsRowIds(context, queryUri); - } - - public static void deletePublicKeyRing(Context context, long rowId) { - ContentResolver cr = context.getContentResolver(); - cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null); - } - - public static void deleteSecretKeyRing(Context context, long rowId) { - ContentResolver cr = context.getContentResolver(); - cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null); - } - - public static long getPublicMasterKeyId(Context context, long keyRingRowId) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId)); - return getMasterKeyId(context, queryUri, keyRingRowId); - } - - public static long getSecretMasterKeyId(Context context, long keyRingRowId) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); - return getMasterKeyId(context, queryUri, keyRingRowId); - } - - private static long getMasterKeyId(Context context, Uri queryUri, long keyRingRowId) { - String[] projection = new String[] { KeyRings.MASTER_KEY_ID }; - - ContentResolver cr = context.getContentResolver(); - Cursor cursor = cr.query(queryUri, projection, null, null, null); - - long masterKeyId = -1; - if (cursor != null && cursor.moveToFirst()) { - int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - - masterKeyId = cursor.getLong(masterKeyIdCol); - } - - if (cursor != null) { - cursor.close(); - } - - return masterKeyId; - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/service/ApgIntentService.java b/org_apg/src/org/thialfihar/android/apg/service/ApgIntentService.java deleted file mode 100644 index 8a6ca6b4c..000000000 --- a/org_apg/src/org/thialfihar/android/apg/service/ApgIntentService.java +++ /dev/null @@ -1,881 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.service; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Vector; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.FileHelper; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; -import org.thialfihar.android.apg.helper.PGPConversionHelper; -import org.thialfihar.android.apg.provider.ApgContract.DataStream; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.util.HkpKeyServer; -import org.thialfihar.android.apg.util.InputData; -import org.thialfihar.android.apg.util.KeyServer.KeyInfo; -import org.thialfihar.android.apg.util.ProgressDialogUpdater; - -import android.app.IntentService; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; - -import org.thialfihar.android.apg.util.Log; - -/** - * This Service contains all important long lasting operations for APG. It receives Intents with - * data from the activities or other apps, queues these intents, executes them, and stops itself - * after doing them. - */ -public class ApgIntentService extends IntentService implements ProgressDialogUpdater { - - /* extras that can be given by intent */ - public static final String EXTRA_MESSENGER = "messenger"; - public static final String EXTRA_ACTION = "action"; - public static final String EXTRA_DATA = "data"; - - /* possible EXTRA_ACTIONs */ - public static final int ACTION_ENCRYPT_SIGN = 10; - - public static final int ACTION_DECRYPT_VERIFY = 20; - - public static final int ACTION_SAVE_KEYRING = 30; - public static final int ACTION_GENERATE_KEY = 31; - public static final int ACTION_GENERATE_DEFAULT_RSA_KEYS = 32; - - public static final int ACTION_DELETE_FILE_SECURELY = 40; - - public static final int ACTION_IMPORT_KEY = 50; - public static final int ACTION_EXPORT_KEY = 51; - - public static final int ACTION_UPLOAD_KEY = 60; - public static final int ACTION_QUERY_KEY = 61; - - public static final int ACTION_SIGN_KEY = 70; - - /* keys for data bundle */ - - // encrypt, decrypt, import export - public static final String TARGET = "target"; - // possible targets: - public static final int TARGET_BYTES = 1; - public static final int TARGET_FILE = 2; - public static final int TARGET_STREAM = 3; - - // encrypt - public static final String SECRET_KEY_ID = "secretKeyId"; - public static final String USE_ASCII_AMOR = "useAsciiAmor"; - public static final String ENCRYPTION_KEYS_IDS = "encryptionKeysIds"; - public static final String COMPRESSION_ID = "compressionId"; - public static final String GENERATE_SIGNATURE = "generateSignature"; - public static final String SIGN_ONLY = "signOnly"; - public static final String MESSAGE_BYTES = "messageBytes"; - public static final String INPUT_FILE = "inputFile"; - public static final String OUTPUT_FILE = "outputFile"; - public static final String PROVIDER_URI = "providerUri"; - - // decrypt/verify - public static final String SIGNED_ONLY = "signedOnly"; - public static final String RETURN_BYTES = "returnBinary"; - public static final String CIPHERTEXT_BYTES = "ciphertextBytes"; - public static final String ASSUME_SYMMETRIC = "assumeSymmetric"; - public static final String LOOKUP_UNKNOWN_KEY = "lookupUnknownKey"; - - // edit keys - public static final String NEW_PASSPHRASE = "newPassphrase"; - public static final String CURRENT_PASSPHRASE = "currentPassphrase"; - public static final String USER_IDS = "userIds"; - public static final String KEYS = "keys"; - public static final String KEYS_USAGES = "keysUsages"; - public static final String MASTER_KEY_ID = "masterKeyId"; - - // generate key - public static final String ALGORITHM = "algorithm"; - public static final String KEY_SIZE = "keySize"; - public static final String SYMMETRIC_PASSPHRASE = "passphrase"; - public static final String MASTER_KEY = "masterKey"; - - // delete file securely - public static final String DELETE_FILE = "deleteFile"; - - // import key - public static final String IMPORT_INPUT_STREAM = "importInputStream"; - public static final String IMPORT_FILENAME = "importFilename"; - public static final String IMPORT_BYTES = "importBytes"; - public static final String IMPORT_KEY_TYPE = "importKeyType"; - - // export key - public static final String EXPORT_OUTPUT_STREAM = "exportOutputStream"; - public static final String EXPORT_FILENAME = "exportFilename"; - public static final String EXPORT_KEY_TYPE = "exportKeyType"; - public static final String EXPORT_ALL = "exportAll"; - public static final String EXPORT_KEY_RING_ID = "exportKeyRingId"; - - // upload key - public static final String UPLOAD_KEY_SERVER = "uploadKeyServer"; - public static final String UPLOAD_KEY_KEYRING_ROW_ID = "uploadKeyRingId"; - - // query key - public static final String QUERY_KEY_SERVER = "queryKeyServer"; - public static final String QUERY_KEY_TYPE = "queryKeyType"; - public static final String QUERY_KEY_STRING = "queryKeyString"; - public static final String QUERY_KEY_ID = "queryKeyId"; - - // sign key - public static final String SIGN_KEY_MASTER_KEY_ID = "signKeyMasterKeyId"; - public static final String SIGN_KEY_PUB_KEY_ID = "signKeyPubKeyId"; - - /* - * possible data keys as result send over messenger - */ - // keys - public static final String RESULT_NEW_KEY = "newKey"; - public static final String RESULT_NEW_KEY2 = "newKey2"; - - // encrypt - public static final String RESULT_SIGNATURE_BYTES = "signatureData"; - public static final String RESULT_SIGNATURE_STRING = "signatureText"; - public static final String RESULT_ENCRYPTED_STRING = "encryptedMessage"; - public static final String RESULT_ENCRYPTED_BYTES = "encryptedData"; - public static final String RESULT_URI = "resultUri"; - - // decrypt/verify - public static final String RESULT_DECRYPTED_STRING = "decryptedMessage"; - public static final String RESULT_DECRYPTED_BYTES = "decryptedData"; - public static final String RESULT_SIGNATURE = "signature"; - public static final String RESULT_SIGNATURE_KEY_ID = "signatureKeyId"; - public static final String RESULT_SIGNATURE_USER_ID = "signatureUserId"; - - public static final String RESULT_SIGNATURE_SUCCESS = "signatureSuccess"; - public static final String RESULT_SIGNATURE_UNKNOWN = "signatureUnknown"; - public static final String RESULT_SIGNATURE_LOOKUP_KEY = "lookupKey"; - - // import - public static final String RESULT_IMPORT_ADDED = "added"; - public static final String RESULT_IMPORT_UPDATED = "updated"; - public static final String RESULT_IMPORT_BAD = "bad"; - - // export - public static final String RESULT_EXPORT = "exported"; - - // query - public static final String RESULT_QUERY_KEY_KEY_DATA = "queryKeyKeyData"; - public static final String RESULT_QUERY_KEY_SEARCH_RESULT = "queryKeySearchResult"; - - Messenger mMessenger; - - public ApgIntentService() { - super("ApgService"); - } - - /** - * The IntentService calls this method from the default worker thread with the intent that - * started the service. When this method returns, IntentService stops the service, as - * appropriate. - */ - @Override - protected void onHandleIntent(Intent intent) { - Bundle extras = intent.getExtras(); - if (extras == null) { - Log.e(Constants.TAG, "Extras bundle is null!"); - return; - } - - if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || extras - .containsKey(EXTRA_ACTION))) { - Log.e(Constants.TAG, - "Extra bundle must contain a messenger, a data bundle, and an action!"); - return; - } - - mMessenger = (Messenger) extras.get(EXTRA_MESSENGER); - Bundle data = extras.getBundle(EXTRA_DATA); - - OtherHelper.logDebugBundle(data, "EXTRA_DATA"); - - int action = extras.getInt(EXTRA_ACTION); - - // execute action from extra bundle - switch (action) { - case ACTION_ENCRYPT_SIGN: - - try { - /* Input */ - int target = data.getInt(TARGET); - - long secretKeyId = data.getLong(SECRET_KEY_ID); - String encryptionPassphrase = data.getString(SYMMETRIC_PASSPHRASE); - - boolean useAsciiArmor = data.getBoolean(USE_ASCII_AMOR); - long encryptionKeyIds[] = data.getLongArray(ENCRYPTION_KEYS_IDS); - int compressionId = data.getInt(COMPRESSION_ID); - boolean generateSignature = data.getBoolean(GENERATE_SIGNATURE); - boolean signOnly = data.getBoolean(SIGN_ONLY); - - InputStream inStream = null; - long inLength = -1; - InputData inputData = null; - OutputStream outStream = null; - String streamFilename = null; - switch (target) { - case TARGET_BYTES: /* encrypting bytes directly */ - byte[] bytes = data.getByteArray(MESSAGE_BYTES); - - inStream = new ByteArrayInputStream(bytes); - inLength = bytes.length; - - inputData = new InputData(inStream, inLength); - outStream = new ByteArrayOutputStream(); - - break; - case TARGET_FILE: /* encrypting file */ - String inputFile = data.getString(INPUT_FILE); - String outputFile = data.getString(OUTPUT_FILE); - - // check if storage is ready - if (!FileHelper.isStorageMounted(inputFile) - || !FileHelper.isStorageMounted(outputFile)) { - throw new ApgGeneralException( - getString(R.string.error_externalStorageNotReady)); - } - - inStream = new FileInputStream(inputFile); - File file = new File(inputFile); - inLength = file.length(); - inputData = new InputData(inStream, inLength); - - outStream = new FileOutputStream(outputFile); - - break; - - case TARGET_STREAM: /* Encrypting stream from content uri */ - Uri providerUri = (Uri) data.getParcelable(PROVIDER_URI); - - // InputStream - InputStream in = getContentResolver().openInputStream(providerUri); - inLength = PGPMain.getLengthOfStream(in); - inputData = new InputData(in, inLength); - - // OutputStream - try { - while (true) { - streamFilename = PGPMain.generateRandomFilename(32); - if (streamFilename == null) { - throw new PGPMain.ApgGeneralException( - "couldn't generate random file name"); - } - openFileInput(streamFilename).close(); - } - } catch (FileNotFoundException e) { - // found a name that isn't used yet - } - outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE); - - break; - - default: - throw new PGPMain.ApgGeneralException("No target choosen!"); - - } - - /* Operation */ - - if (generateSignature) { - Log.d(Constants.TAG, "generating signature..."); - PGPMain.generateSignature(this, this, inputData, outStream, useAsciiArmor, - false, secretKeyId, PassphraseCacheService.getCachedPassphrase(this, - secretKeyId), Preferences.getPreferences(this) - .getDefaultHashAlgorithm(), Preferences.getPreferences(this) - .getForceV3Signatures()); - } else if (signOnly) { - Log.d(Constants.TAG, "sign only..."); - PGPMain.signText(this, this, inputData, outStream, secretKeyId, - PassphraseCacheService.getCachedPassphrase(this, secretKeyId), - Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences - .getPreferences(this).getForceV3Signatures()); - } else { - Log.d(Constants.TAG, "encrypt..."); - PGPMain.encryptAndSign(this, this, inputData, outStream, useAsciiArmor, - compressionId, encryptionKeyIds, encryptionPassphrase, Preferences - .getPreferences(this).getDefaultEncryptionAlgorithm(), - secretKeyId, - Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences - .getPreferences(this).getForceV3Signatures(), - PassphraseCacheService.getCachedPassphrase(this, secretKeyId)); - } - - outStream.close(); - - /* Output */ - - Bundle resultData = new Bundle(); - - switch (target) { - case TARGET_BYTES: - if (useAsciiArmor) { - String output = new String( - ((ByteArrayOutputStream) outStream).toByteArray()); - if (generateSignature) { - resultData.putString(RESULT_SIGNATURE_STRING, output); - } else { - resultData.putString(RESULT_ENCRYPTED_STRING, output); - } - } else { - byte output[] = ((ByteArrayOutputStream) outStream).toByteArray(); - if (generateSignature) { - resultData.putByteArray(RESULT_SIGNATURE_BYTES, output); - } else { - resultData.putByteArray(RESULT_ENCRYPTED_BYTES, output); - } - } - - break; - case TARGET_FILE: - // nothing, file was written, just send okay - - break; - case TARGET_STREAM: - String uri = DataStream.buildDataStreamUri(streamFilename).toString(); - resultData.putString(RESULT_URI, uri); - - break; - } - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_DECRYPT_VERIFY: - try { - /* Input */ - int target = data.getInt(TARGET); - - long secretKeyId = data.getLong(SECRET_KEY_ID); - byte[] bytes = data.getByteArray(CIPHERTEXT_BYTES); - boolean signedOnly = data.getBoolean(SIGNED_ONLY); - boolean returnBytes = data.getBoolean(RETURN_BYTES); - boolean assumeSymmetricEncryption = data.getBoolean(ASSUME_SYMMETRIC); - - boolean lookupUnknownKey = data.getBoolean(LOOKUP_UNKNOWN_KEY); - - InputStream inStream = null; - long inLength = -1; - InputData inputData = null; - OutputStream outStream = null; - String streamFilename = null; - switch (target) { - case TARGET_BYTES: /* decrypting bytes directly */ - inStream = new ByteArrayInputStream(bytes); - inLength = bytes.length; - - inputData = new InputData(inStream, inLength); - outStream = new ByteArrayOutputStream(); - - break; - - case TARGET_FILE: /* decrypting file */ - String inputFile = data.getString(INPUT_FILE); - String outputFile = data.getString(OUTPUT_FILE); - - // check if storage is ready - if (!FileHelper.isStorageMounted(inputFile) - || !FileHelper.isStorageMounted(outputFile)) { - throw new ApgGeneralException( - getString(R.string.error_externalStorageNotReady)); - } - - // InputStream - inLength = -1; - inStream = new FileInputStream(inputFile); - File file = new File(inputFile); - inLength = file.length(); - inputData = new InputData(inStream, inLength); - - // OutputStream - outStream = new FileOutputStream(outputFile); - - break; - - case TARGET_STREAM: /* decrypting stream from content uri */ - Uri providerUri = (Uri) data.getParcelable(PROVIDER_URI); - - // InputStream - InputStream in = getContentResolver().openInputStream(providerUri); - inLength = PGPMain.getLengthOfStream(in); - inputData = new InputData(in, inLength); - - // OutputStream - try { - while (true) { - streamFilename = PGPMain.generateRandomFilename(32); - if (streamFilename == null) { - throw new PGPMain.ApgGeneralException( - "couldn't generate random file name"); - } - openFileInput(streamFilename).close(); - } - } catch (FileNotFoundException e) { - // found a name that isn't used yet - } - outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE); - - break; - - default: - throw new PGPMain.ApgGeneralException("No target choosen!"); - - } - - /* Operation */ - - Bundle resultData = new Bundle(); - - // verifyText and decrypt returning additional resultData values for the - // verification of signatures - if (signedOnly) { - resultData = PGPMain.verifyText(this, this, inputData, outStream, - lookupUnknownKey); - } else { - resultData = PGPMain.decryptAndVerify(this, this, inputData, outStream, - PassphraseCacheService.getCachedPassphrase(this, secretKeyId), - assumeSymmetricEncryption); - } - - outStream.close(); - - /* Output */ - - switch (target) { - case TARGET_BYTES: - if (returnBytes) { - byte output[] = ((ByteArrayOutputStream) outStream).toByteArray(); - resultData.putByteArray(RESULT_DECRYPTED_BYTES, output); - } else { - String output = new String( - ((ByteArrayOutputStream) outStream).toByteArray()); - resultData.putString(RESULT_DECRYPTED_STRING, output); - } - - break; - case TARGET_FILE: - // nothing, file was written, just send okay and verification bundle - - break; - case TARGET_STREAM: - String uri = DataStream.buildDataStreamUri(streamFilename).toString(); - resultData.putString(RESULT_URI, uri); - - break; - } - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_SAVE_KEYRING: - - try { - /* Input */ - String oldPassPhrase = data.getString(CURRENT_PASSPHRASE); - String newPassPhrase = data.getString(NEW_PASSPHRASE); - if (newPassPhrase == null) { - newPassPhrase = oldPassPhrase; - } - ArrayList userIds = data.getStringArrayList(USER_IDS); - ArrayList keys = PGPConversionHelper.BytesToPGPSecretKeyList(data - .getByteArray(KEYS)); - ArrayList keysUsages = data.getIntegerArrayList(KEYS_USAGES); - long masterKeyId = data.getLong(MASTER_KEY_ID); - - /* Operation */ - PGPMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId, oldPassPhrase, - newPassPhrase, this); - PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); - - /* Output */ - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_GENERATE_KEY: - - try { - /* Input */ - int algorithm = data.getInt(ALGORITHM); - String passphrase = data.getString(SYMMETRIC_PASSPHRASE); - int keysize = data.getInt(KEY_SIZE); - PGPSecretKey masterKey = null; - if (data.containsKey(MASTER_KEY)) { - masterKey = PGPConversionHelper.BytesToPGPSecretKey(data - .getByteArray(MASTER_KEY)); - } - - /* Operation */ - PGPSecretKeyRing newKeyRing = PGPMain.createKey(this, algorithm, keysize, - passphrase, masterKey); - - /* Output */ - Bundle resultData = new Bundle(); - resultData.putByteArray(RESULT_NEW_KEY, - PGPConversionHelper.PGPSecretKeyRingToBytes(newKeyRing)); - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_GENERATE_DEFAULT_RSA_KEYS: - // generate one RSA 2048 key for signing and one subkey for encrypting! - try { - /* Input */ - String passphrase = data.getString(SYMMETRIC_PASSPHRASE); - - /* Operation */ - PGPSecretKeyRing masterKeyRing = PGPMain.createKey(this, Id.choice.algorithm.rsa, - 2048, passphrase, null); - - PGPSecretKeyRing subKeyRing = PGPMain.createKey(this, Id.choice.algorithm.rsa, - 2048, passphrase, masterKeyRing.getSecretKey()); - - /* Output */ - Bundle resultData = new Bundle(); - resultData.putByteArray(RESULT_NEW_KEY, - PGPConversionHelper.PGPSecretKeyRingToBytes(masterKeyRing)); - resultData.putByteArray(RESULT_NEW_KEY2, - PGPConversionHelper.PGPSecretKeyRingToBytes(subKeyRing)); - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_DELETE_FILE_SECURELY: - try { - /* Input */ - String deleteFile = data.getString(DELETE_FILE); - - /* Operation */ - try { - PGPMain.deleteFileSecurely(this, this, new File(deleteFile)); - } catch (FileNotFoundException e) { - throw new PGPMain.ApgGeneralException(getString(R.string.error_fileNotFound, - deleteFile)); - } catch (IOException e) { - throw new PGPMain.ApgGeneralException(getString( - R.string.error_fileDeleteFailed, deleteFile)); - } - - /* Output */ - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_IMPORT_KEY: - try { - - /* Input */ - int target = data.getInt(TARGET); - - int keyType = Id.type.public_key; - if (data.containsKey(IMPORT_KEY_TYPE)) { - keyType = data.getInt(IMPORT_KEY_TYPE); - } - - /* Operation */ - InputStream inStream = null; - long inLength = -1; - InputData inputData = null; - switch (target) { - case TARGET_BYTES: /* import key from bytes directly */ - byte[] bytes = data.getByteArray(IMPORT_BYTES); - - inStream = new ByteArrayInputStream(bytes); - inLength = bytes.length; - - inputData = new InputData(inStream, inLength); - - break; - case TARGET_FILE: /* import key from file */ - String inputFile = data.getString(IMPORT_FILENAME); - - inStream = new FileInputStream(inputFile); - File file = new File(inputFile); - inLength = file.length(); - inputData = new InputData(inStream, inLength); - - break; - - case TARGET_STREAM: - // TODO: not implemented - break; - } - - Bundle resultData = new Bundle(); - resultData = PGPMain.importKeyRings(this, keyType, inputData, this); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_EXPORT_KEY: - try { - - /* Input */ - int keyType = Id.type.public_key; - if (data.containsKey(EXPORT_KEY_TYPE)) { - keyType = data.getInt(EXPORT_KEY_TYPE); - } - - String outputFile = data.getString(EXPORT_FILENAME); - - boolean exportAll = data.getBoolean(EXPORT_ALL); - int keyRingId = -1; - if (!exportAll) { - keyRingId = data.getInt(EXPORT_KEY_RING_ID); - } - - /* Operation */ - - // check if storage is ready - if (!FileHelper.isStorageMounted(outputFile)) { - throw new ApgGeneralException(getString(R.string.error_externalStorageNotReady)); - } - - // OutputStream - FileOutputStream outStream = new FileOutputStream(outputFile); - - Vector keyRingIds = new Vector(); - if (exportAll) { - if (keyType == Id.type.public_key) { - keyRingIds = ProviderHelper.getPublicKeyRingsRowIds(this); - } else { - keyRingIds = ProviderHelper.getSecretKeyRingsRowIds(this); - } - } else { - keyRingIds.add(keyRingId); - } - - Bundle resultData = new Bundle(); - resultData = PGPMain.exportKeyRings(this, keyRingIds, outStream, this); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_UPLOAD_KEY: - try { - - /* Input */ - int keyRingRowId = data.getInt(UPLOAD_KEY_KEYRING_ROW_ID); - String keyServer = data.getString(UPLOAD_KEY_SERVER); - - /* Operation */ - HkpKeyServer server = new HkpKeyServer(keyServer); - - PGPPublicKeyRing keyring = ProviderHelper.getPGPPublicKeyRingByRowId(this, keyRingRowId); - if (keyring != null) { - boolean uploaded = PGPMain.uploadKeyRingToServer(server, - (PGPPublicKeyRing) keyring); - if (!uploaded) { - throw new ApgGeneralException("Unable to export key to selected server"); - } - } - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_QUERY_KEY: - try { - - /* Input */ - int queryType = data.getInt(QUERY_KEY_TYPE); - String keyServer = data.getString(QUERY_KEY_SERVER); - - String queryString = data.getString(QUERY_KEY_STRING); - long keyId = data.getLong(QUERY_KEY_ID); - - /* Operation */ - Bundle resultData = new Bundle(); - - HkpKeyServer server = new HkpKeyServer(keyServer); - if (queryType == Id.keyserver.search) { - ArrayList searchResult = server.search(queryString); - - resultData.putParcelableArrayList(RESULT_QUERY_KEY_SEARCH_RESULT, searchResult); - } else if (queryType == Id.keyserver.get) { - String keyData = server.get(keyId); - - resultData.putString(RESULT_QUERY_KEY_KEY_DATA, keyData); - } - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_SIGN_KEY: - try { - - /* Input */ - long masterKeyId = data.getLong(SIGN_KEY_MASTER_KEY_ID); - long pubKeyId = data.getLong(SIGN_KEY_PUB_KEY_ID); - - /* Operation */ - String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this, - masterKeyId); - - PGPPublicKeyRing signedPubKeyRing = PGPMain.signKey(this, masterKeyId, pubKeyId, - signaturePassPhrase); - - // store the signed key in our local cache - int retval = PGPMain.storeKeyRingInCache(this, signedPubKeyRing); - if (retval != Id.return_value.ok && retval != Id.return_value.updated) { - throw new ApgGeneralException("Failed to store signed key in local cache"); - } - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - default: - break; - } - - } - - private void sendErrorToHandler(Exception e) { - Log.e(Constants.TAG, "ApgService Exception: ", e); - e.printStackTrace(); - - Bundle data = new Bundle(); - data.putString(ApgIntentServiceHandler.DATA_ERROR, e.getMessage()); - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_EXCEPTION, null, data); - } - - private void sendMessageToHandler(Integer arg1, Integer arg2, Bundle data) { - Message msg = Message.obtain(); - msg.arg1 = arg1; - if (arg2 != null) { - msg.arg2 = arg2; - } - if (data != null) { - msg.setData(data); - } - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } - - private void sendMessageToHandler(Integer arg1, Bundle data) { - sendMessageToHandler(arg1, null, data); - } - - private void sendMessageToHandler(Integer arg1) { - sendMessageToHandler(arg1, null, null); - } - - /** - * Set progress of ProgressDialog by sending message to handler on UI thread - */ - public void setProgress(String message, int progress, int max) { - Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max=" - + max); - - Bundle data = new Bundle(); - if (message != null) { - data.putString(ApgIntentServiceHandler.DATA_MESSAGE, message); - } - data.putInt(ApgIntentServiceHandler.DATA_PROGRESS, progress); - data.putInt(ApgIntentServiceHandler.DATA_PROGRESS_MAX, max); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_UPDATE_PROGRESS, null, data); - } - - public void setProgress(int resourceId, int progress, int max) { - setProgress(getString(resourceId), progress, max); - } - - public void setProgress(int progress, int max) { - setProgress(null, progress, max); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java b/org_apg/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java deleted file mode 100644 index 8494cf035..000000000 --- a/org_apg/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.service; - -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.ui.dialog.ProgressDialogFragment; - -import android.app.Activity; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.support.v4.app.FragmentActivity; -import android.widget.Toast; - -public class ApgIntentServiceHandler extends Handler { - - // possible messages send from this service to handler on ui - public static final int MESSAGE_OKAY = 1; - public static final int MESSAGE_EXCEPTION = 2; - public static final int MESSAGE_UPDATE_PROGRESS = 3; - - // possible data keys for messages - public static final String DATA_ERROR = "error"; - public static final String DATA_PROGRESS = "progress"; - public static final String DATA_PROGRESS_MAX = "max"; - public static final String DATA_MESSAGE = "message"; - public static final String DATA_MESSAGE_ID = "message_id"; - - Activity mActivity; - ProgressDialogFragment mProgressDialogFragment; - - public ApgIntentServiceHandler(Activity activity) { - this.mActivity = activity; - } - - public ApgIntentServiceHandler(Activity activity, ProgressDialogFragment progressDialogFragment) { - this.mActivity = activity; - this.mProgressDialogFragment = progressDialogFragment; - } - - public ApgIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) { - this.mActivity = activity; - this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId, - progressDialogStyle); - } - - public void showProgressDialog(FragmentActivity activity) { - mProgressDialogFragment.show(activity.getSupportFragmentManager(), "progressDialog"); - } - - @Override - public void handleMessage(Message message) { - Bundle data = message.getData(); - - switch (message.arg1) { - case MESSAGE_OKAY: - mProgressDialogFragment.dismiss(); - - break; - - case MESSAGE_EXCEPTION: - mProgressDialogFragment.dismiss(); - - // show error from service - if (data.containsKey(DATA_ERROR)) { - Toast.makeText(mActivity, - mActivity.getString(R.string.errorMessage, data.getString(DATA_ERROR)), - Toast.LENGTH_SHORT).show(); - } - - break; - - case MESSAGE_UPDATE_PROGRESS: - if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) { - - // update progress from service - if (data.containsKey(DATA_MESSAGE)) { - mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE), - data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX)); - } else if (data.containsKey(DATA_MESSAGE_ID)) { - mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID), - data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX)); - } else { - mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS), - data.getInt(DATA_PROGRESS_MAX)); - } - } - - break; - - default: - break; - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/service/ApgService.java b/org_apg/src/org/thialfihar/android/apg/service/ApgService.java deleted file mode 100644 index 9e618b545..000000000 --- a/org_apg/src/org/thialfihar/android/apg/service/ApgService.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.service; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SignatureException; - -import org.spongycastle.openpgp.PGPException; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; -import org.thialfihar.android.apg.util.InputData; -import org.thialfihar.android.apg.util.Log; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; - -/** - * TODO: - * - * - is this service thread safe? Probably not! - * - */ -public class ApgService extends Service { - Context mContext; - - @Override - public void onCreate() { - super.onCreate(); - mContext = this; - Log.d(Constants.TAG, "ApgService, onCreate()"); - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.d(Constants.TAG, "ApgService, onDestroy()"); - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException { - byte[] buffer = new byte[8]; - int len = 0; - while ((len = is.read(buffer)) != -1) { - os.write(buffer, 0, len); - } - } - - void encryptAndSignImplementation(byte[] inputBytes, String inputUri, boolean useAsciiArmor, - int compression, long[] encryptionKeyIds, String encryptionPassphrase, - int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, - boolean signatureForceV3, String signaturePassphrase, IApgEncryptDecryptHandler handler) - throws RemoteException { - - try { - // TODO use inputUri - -// InputStream inStream = null; -// if (isBlob) { -// ContentResolver cr = getContentResolver(); -// try { -// inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); -// } catch (Exception e) { -// Log.e(TAG, "... exception on opening blob", e); -// } -// } else { -// inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes()); -// } -// InputData in = new InputData(inStream, 0); // XXX Size second param? - - // build InputData and write into OutputStream - InputStream inputStream = new ByteArrayInputStream(inputBytes); - long inputLength = inputBytes.length; - InputData input = new InputData(inputStream, inputLength); - - OutputStream output = new ByteArrayOutputStream(); - - PGPMain.encryptAndSign(mContext, null, input, output, useAsciiArmor, compression, - encryptionKeyIds, encryptionPassphrase, symmetricEncryptionAlgorithm, - signatureKeyId, signatureHashAlgorithm, signatureForceV3, signaturePassphrase); - - output.close(); - -// if (isBlob) { -// ContentResolver cr = getContentResolver(); -// try { -// OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB -// .name()))); -// writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream); -// outStream.close(); -// } catch (Exception e) { -// Log.e(TAG, "... exception on writing blob", e); -// } -// } else { -// pReturn.putString(ret.RESULT.name(), out.toString()); -// } - - byte[] outputBytes = ((ByteArrayOutputStream) output).toByteArray(); - - // return over handler on client side - handler.onSuccessEncrypt(outputBytes, null); - } catch (Exception e) { - Log.e(Constants.TAG, "ApgService, Exception!", e); - - try { - handler.onException(getExceptionId(e), e.getMessage()); - } catch (Exception t) { - Log.e(Constants.TAG, "Error returning exception to client", t); - } - } - } - - public void decryptAndVerifyImplementation(byte[] inputBytes, String inputUri, - String passphrase, boolean assumeSymmetric, IApgEncryptDecryptHandler handler) - throws RemoteException { - - try { - // build InputData and write into OutputStream - InputStream inputStream = new ByteArrayInputStream(inputBytes); - long inputLength = inputBytes.length; - InputData inputData = new InputData(inputStream, inputLength); - - OutputStream outputStream = new ByteArrayOutputStream(); - - Bundle outputBundle = PGPMain.decryptAndVerify(mContext, null, inputData, outputStream, - passphrase, assumeSymmetric); - - outputStream.close(); - - byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); - - // get signature informations from bundle - boolean signature = outputBundle.getBoolean(ApgIntentService.RESULT_SIGNATURE); - long signatureKeyId = outputBundle.getLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID); - String signatureUserId = outputBundle - .getString(ApgIntentService.RESULT_SIGNATURE_USER_ID); - boolean signatureSuccess = outputBundle - .getBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS); - boolean signatureUnknown = outputBundle - .getBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN); - - // return over handler on client side - handler.onSuccessDecrypt(outputBytes, null, signature, signatureKeyId, signatureUserId, - signatureSuccess, signatureUnknown); - } catch (Exception e) { - Log.e(Constants.TAG, "ApgService, Exception!", e); - - try { - handler.onException(getExceptionId(e), e.getMessage()); - } catch (Exception t) { - Log.e(Constants.TAG, "Error returning exception to client", t); - } - } - } - - private void getDecryptionKeyImplementation(byte[] inputBytes, String inputUri, - IApgHelperHandler handler) { - - // TODO: implement inputUri - - try { - InputStream inputStream = new ByteArrayInputStream(inputBytes); - - long secretKeyId = Id.key.none; - boolean symmetric; - - try { - secretKeyId = PGPMain.getDecryptionKeyId(ApgService.this, inputStream); - if (secretKeyId == Id.key.none) { - throw new ApgGeneralException(getString(R.string.error_noSecretKeyFound)); - } - symmetric = false; - } catch (PGPMain.NoAsymmetricEncryptionException e) { - secretKeyId = Id.key.symmetric; - if (!PGPMain.hasSymmetricEncryption(ApgService.this, inputStream)) { - throw new ApgGeneralException(getString(R.string.error_noKnownEncryptionFound)); - } - symmetric = true; - } - - handler.onSuccessGetDecryptionKey(secretKeyId, symmetric); - - } catch (Exception e) { - Log.e(Constants.TAG, "ApgService, Exception!", e); - - try { - handler.onException(getExceptionId(e), e.getMessage()); - } catch (Exception t) { - Log.e(Constants.TAG, "Error returning exception to client", t); - } - } - } - - /** - * This is the implementation of the interface IApgService. All methods are oneway, meaning - * asynchronous and return to the client using IApgHandler. - * - * The real PGP code is located in PGPMain. - */ - private final IApgService.Stub mBinder = new IApgService.Stub() { - - @Override - public void encryptAsymmetric(byte[] inputBytes, String inputUri, boolean useAsciiArmor, - int compression, long[] encryptionKeyIds, int symmetricEncryptionAlgorithm, - IApgEncryptDecryptHandler handler) throws RemoteException { - - encryptAndSignImplementation(inputBytes, inputUri, useAsciiArmor, compression, - encryptionKeyIds, null, symmetricEncryptionAlgorithm, Id.key.none, 0, false, - null, handler); - } - - @Override - public void encryptSymmetric(byte[] inputBytes, String inputUri, boolean useAsciiArmor, - int compression, String encryptionPassphrase, int symmetricEncryptionAlgorithm, - IApgEncryptDecryptHandler handler) throws RemoteException { - - encryptAndSignImplementation(inputBytes, inputUri, useAsciiArmor, compression, null, - encryptionPassphrase, symmetricEncryptionAlgorithm, Id.key.none, 0, false, - null, handler); - } - - @Override - public void encryptAndSignAsymmetric(byte[] inputBytes, String inputUri, - boolean useAsciiArmor, int compression, long[] encryptionKeyIds, - int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, - boolean signatureForceV3, String signaturePassphrase, - IApgEncryptDecryptHandler handler) throws RemoteException { - - encryptAndSignImplementation(inputBytes, inputUri, useAsciiArmor, compression, - encryptionKeyIds, null, symmetricEncryptionAlgorithm, signatureKeyId, - signatureHashAlgorithm, signatureForceV3, signaturePassphrase, handler); - } - - @Override - public void encryptAndSignSymmetric(byte[] inputBytes, String inputUri, - boolean useAsciiArmor, int compression, String encryptionPassphrase, - int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, - boolean signatureForceV3, String signaturePassphrase, - IApgEncryptDecryptHandler handler) throws RemoteException { - - encryptAndSignImplementation(inputBytes, inputUri, useAsciiArmor, compression, null, - encryptionPassphrase, symmetricEncryptionAlgorithm, signatureKeyId, - signatureHashAlgorithm, signatureForceV3, signaturePassphrase, handler); - } - - @Override - public void decryptAndVerifyAsymmetric(byte[] inputBytes, String inputUri, - String keyPassphrase, IApgEncryptDecryptHandler handler) throws RemoteException { - - decryptAndVerifyImplementation(inputBytes, inputUri, keyPassphrase, false, handler); - } - - @Override - public void decryptAndVerifySymmetric(byte[] inputBytes, String inputUri, - String encryptionPassphrase, IApgEncryptDecryptHandler handler) - throws RemoteException { - - decryptAndVerifyImplementation(inputBytes, inputUri, encryptionPassphrase, true, - handler); - } - - @Override - public void getDecryptionKey(byte[] inputBytes, String inputUri, IApgHelperHandler handler) - throws RemoteException { - - getDecryptionKeyImplementation(inputBytes, inputUri, handler); - } - - }; - - /** - * As we can not throw an exception through Android RPC, we assign identifiers to the exception - * types. - * - * @param e - * @return - */ - private int getExceptionId(Exception e) { - if (e instanceof NoSuchProviderException) { - return 0; - } else if (e instanceof NoSuchAlgorithmException) { - return 1; - } else if (e instanceof SignatureException) { - return 2; - } else if (e instanceof IOException) { - return 3; - } else if (e instanceof ApgGeneralException) { - return 4; - } else if (e instanceof PGPException) { - return 5; - } else { - return -1; - } - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl b/org_apg/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl deleted file mode 100644 index 318e5f344..000000000 --- a/org_apg/src/org/thialfihar/android/apg/service/IApgEncryptDecryptHandler.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.service; - -interface IApgEncryptDecryptHandler { - /** - * Either output or streamUri is given. One of them is null - * - */ - oneway void onSuccessEncrypt(in byte[] outputBytes, in String outputUri); - - oneway void onSuccessDecrypt(in byte[] outputBytes, in String outputUri, in boolean signature, - in long signatureKeyId, in String signatureUserId, in boolean signatureSuccess, - in boolean signatureUnknown); - - - oneway void onException(in int exceptionNumber, in String message); -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl b/org_apg/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl deleted file mode 100644 index 29ff2dd3e..000000000 --- a/org_apg/src/org/thialfihar/android/apg/service/IApgHelperHandler.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.service; - -interface IApgHelperHandler { - - oneway void onSuccessGetDecryptionKey(in long secretKeyId, in boolean symmetric); - - - oneway void onException(in int exceptionNumber, in String message); -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/service/IApgService.aidl b/org_apg/src/org/thialfihar/android/apg/service/IApgService.aidl deleted file mode 100644 index 7753d1e3d..000000000 --- a/org_apg/src/org/thialfihar/android/apg/service/IApgService.aidl +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.service; - -import org.thialfihar.android.apg.service.IApgEncryptDecryptHandler; -import org.thialfihar.android.apg.service.IApgSignVerifyHandler; -import org.thialfihar.android.apg.service.IApgHelperHandler; - -/** - * All methods are oneway, which means they are asynchronous and non-blocking. - * Results are returned into given Handler, which has to be implemented on client side. - */ -interface IApgService { - - /** - * Encrypt - * - * Either inputBytes or inputUri is given, the other should be null. - * - * @param inputBytes - * Byte array you want to encrypt - * @param inputUri - * Blob in ContentProvider you want to encrypt - * @param useAsciiArmor - * Convert bytes to ascii armored text to guard against encoding problems - * @param compression - * Compression: 0x21070001: none, 1: Zip, 2: Zlib, 3: BZip2 - * @param encryptionKeyIds - * Ids of public keys used for encryption - * @param symmetricEncryptionAlgorithm - * 7: AES-128, 8: AES-192, 9: AES-256, 4: Blowfish, 10: Twofish, 3: CAST5, - * 6: DES, 2: Triple DES, 1: IDEA - * @param handler - * Results are returned to this IApgEncryptDecryptHandler Handler - * to onSuccessEncrypt(in byte[] output), after successful encryption - */ - oneway void encryptAsymmetric(in byte[] inputBytes, in String inputUri, in boolean useAsciiArmor, - in int compression, in long[] encryptionKeyIds, in int symmetricEncryptionAlgorithm, - in IApgEncryptDecryptHandler handler); - - /** - * Same as encryptAsymmetric but using a passphrase for symmetric encryption - * - * @param encryptionPassphrase - * Passphrase for direct symmetric encryption using symmetricEncryptionAlgorithm - */ - oneway void encryptSymmetric(in byte[] inputBytes, in String inputUri, in boolean useAsciiArmor, - in int compression, in String encryptionPassphrase, in int symmetricEncryptionAlgorithm, - in IApgEncryptDecryptHandler handler); - - /** - * Encrypt and sign - * - * Either inputBytes or inputUri is given, the other should be null. - * - * @param inputBytes - * Byte array you want to encrypt - * @param inputUri - * Blob in ContentProvider you want to encrypt - * @param useAsciiArmor - * Convert bytes to ascii armored text to guard against encoding problems - * @param compression - * Compression: 0x21070001: none, 1: Zip, 2: Zlib, 3: BZip2 - * @param encryptionKeyIds - * Ids of public keys used for encryption - * @param symmetricEncryptionAlgorithm - * 7: AES-128, 8: AES-192, 9: AES-256, 4: Blowfish, 10: Twofish, 3: CAST5, - * 6: DES, 2: Triple DES, 1: IDEA - * @param signatureKeyId - * Key id of key to sign with - * @param signatureHashAlgorithm - * 1: MD5, 3: RIPEMD-160, 2: SHA-1, 11: SHA-224, 8: SHA-256, 9: SHA-384, - * 10: SHA-512 - * @param signatureForceV3 - * Force V3 signatures - * @param signaturePassphrase - * Passphrase to unlock signature key - * @param handler - * Results are returned to this IApgEncryptDecryptHandler Handler - * to onSuccessEncrypt(in byte[] output), after successful encryption and signing - */ - oneway void encryptAndSignAsymmetric(in byte[] inputBytes, in String inputUri, - in boolean useAsciiArmor, in int compression, in long[] encryptionKeyIds, - in int symmetricEncryptionAlgorithm, in long signatureKeyId, in int signatureHashAlgorithm, - in boolean signatureForceV3, in String signaturePassphrase, - in IApgEncryptDecryptHandler handler); - - /** - * Same as encryptAndSignAsymmetric but using a passphrase for symmetric encryption - * - * @param encryptionPassphrase - * Passphrase for direct symmetric encryption using symmetricEncryptionAlgorithm - */ - oneway void encryptAndSignSymmetric(in byte[] inputBytes, in String inputUri, - in boolean useAsciiArmor, in int compression, in String encryptionPassphrase, - in int symmetricEncryptionAlgorithm, in long signatureKeyId, in int signatureHashAlgorithm, - in boolean signatureForceV3, in String signaturePassphrase, - in IApgEncryptDecryptHandler handler); - - /** - * Decrypts and verifies given input bytes. If no signature is present this method - * will only decrypt. - * - * @param inputBytes - * Byte array you want to decrypt and verify - * @param inputUri - * Blob in ContentProvider you want to decrypt and verify - * @param keyPassphrase - * Passphrase to unlock secret key for decryption. - * @param handler - * Handler where to return results to after successful encryption - */ - oneway void decryptAndVerifyAsymmetric(in byte[] inputBytes, in String inputUri, - in String keyPassphrase, in IApgEncryptDecryptHandler handler); - - /** - * Same as decryptAndVerifyAsymmetric but for symmetric decryption. - * - * @param encryptionPassphrase - * Passphrase to decrypt - */ - oneway void decryptAndVerifySymmetric(in byte[] inputBytes, in String inputUri, - in String encryptionPassphrase, in IApgEncryptDecryptHandler handler); - - /** - * - */ - oneway void getDecryptionKey(in byte[] inputBytes, in String inputUri, - in IApgHelperHandler handler); - - -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl b/org_apg/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl deleted file mode 100644 index cc854e540..000000000 --- a/org_apg/src/org/thialfihar/android/apg/service/IApgSignVerifyHandler.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.service; - -interface IApgSignVerifyHandler { - oneway void onSuccessSign(in byte[] outputBytes, in String outputUri); - - oneway void onSuccessVerify(in boolean signature, in long signatureKeyId, - in String signatureUserId, in boolean signatureSuccess, in boolean signatureUnknown); - - - oneway void onException(in int exceptionNumber, in String message); -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/service/PassphraseCacheService.java b/org_apg/src/org/thialfihar/android/apg/service/PassphraseCacheService.java deleted file mode 100644 index c2bc48aa7..000000000 --- a/org_apg/src/org/thialfihar/android/apg/service/PassphraseCacheService.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.service; - -import java.util.Date; -import java.util.HashMap; - -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.provider.ProviderHelper; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Binder; -import android.os.IBinder; -import android.util.Log; - -public class PassphraseCacheService extends Service { - public static final String TAG = Constants.TAG + ": PassphraseCacheService"; - - public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX - + "PASSPHRASE_CACHE_SERVICE"; - - public static final String EXTRA_TTL = "ttl"; - public static final String EXTRA_KEY_ID = "keyId"; - public static final String EXTRA_PASSPHRASE = "passphrase"; - - private static final int REQUEST_ID = 0; - private static final long DEFAULT_TTL = 15; - - private BroadcastReceiver mIntentReceiver; - - // This is static to be easily retrieved by getCachedPassphrase() without the need of callback - // functions - private static HashMap mPassphraseCache = new HashMap(); - - /** - * This caches a new passphrase by sending a new command to the service. An android service is - * only run once. Thus, when the service is already started, new commands just add new events to - * the alarm manager for new passphrases to let them timeout in the future. - * - * @param context - * @param keyId - * @param passphrase - */ - public static void addCachedPassphrase(Context context, long keyId, String passphrase) { - Log.d(TAG, "cacheNewPassphrase() for " + keyId); - - Intent intent = new Intent(context, PassphraseCacheService.class); - intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassPhraseCacheTtl()); - intent.putExtra(EXTRA_PASSPHRASE, passphrase); - intent.putExtra(EXTRA_KEY_ID, keyId); - - context.startService(intent); - } - - /** - * Gets a cached passphrase from memory - * - * @param context - * @param keyId - * @return - */ - public static String getCachedPassphrase(Context context, long keyId) { - // try to get master key id which is used as an identifier for cached passphrases - long masterKeyId = keyId; - if (masterKeyId != Id.key.symmetric) { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, keyId); - if (keyRing == null) { - return null; - } - PGPSecretKey masterKey = PGPHelper.getMasterKey(keyRing); - if (masterKey == null) { - return null; - } - masterKeyId = masterKey.getKeyID(); - } - - // get cached passphrase - String cachedPassphrase = mPassphraseCache.get(masterKeyId); - if (cachedPassphrase == null) { - return null; - } - // set it again to reset the cache life cycle - Log.d(TAG, "Cache passphrase again when getting it!"); - addCachedPassphrase(context, masterKeyId, cachedPassphrase); - - return cachedPassphrase; - } - - /** - * Register BroadcastReceiver that is unregistered when service is destroyed. This - * BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to then timeout - * specific passphrases in memory. - */ - private void registerReceiver() { - if (mIntentReceiver == null) { - mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - - Log.d(TAG, "Received broadcast..."); - - if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) { - long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); - timeout(context, keyId); - } - } - }; - - IntentFilter filter = new IntentFilter(); - filter.addAction(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); - registerReceiver(mIntentReceiver, filter); - } - } - - /** - * Build pending intent that is executed by alarm manager to time out a specific passphrase - * - * @param context - * @param keyId - * @return - */ - private static PendingIntent buildIntent(Context context, long keyId) { - Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); - intent.putExtra(EXTRA_KEY_ID, keyId); - PendingIntent sender = PendingIntent.getBroadcast(context, REQUEST_ID, intent, - PendingIntent.FLAG_CANCEL_CURRENT); - - return sender; - } - - @Override - public void onCreate() { - Log.d(TAG, "onCreate()"); - } - - /** - * Executed when service is started by intent - */ - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - Log.d(TAG, "onStartCommand()"); - - // register broadcastreceiver - registerReceiver(); - - if (intent != null) { - long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL); - long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); - String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE); - - Log.d(TAG, "Received intent in onStartCommand() with keyId: " + keyId + ", ttl: " + ttl); - - // add keyId and passphrase to memory - mPassphraseCache.put(keyId, passphrase); - - // register new alarm with keyId for this passphrase - long triggerTime = new Date().getTime() + (ttl * 1000); - AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); - am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId)); - } - - return START_STICKY; - } - - /** - * Called when one specific passphrase for keyId timed out - * - * @param context - * @param keyId - */ - private void timeout(Context context, long keyId) { - // remove passphrase corresponding to keyId from memory - mPassphraseCache.remove(keyId); - - Log.d(TAG, "Timeout of " + keyId + ", removed from memory!"); - - // stop whole service if no cached passphrases remaining - if (mPassphraseCache.isEmpty()) { - Log.d(TAG, "No passphrases remaining in memory, stopping service!"); - stopSelf(); - } - } - - @Override - public void onDestroy() { - Log.d(TAG, "onDestroy()"); - - unregisterReceiver(mIntentReceiver); - } - - public class PassphraseCacheBinder extends Binder { - public PassphraseCacheService getService() { - return PassphraseCacheService.this; - } - } - - private final IBinder mBinder = new PassphraseCacheBinder(); - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/DecryptActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/DecryptActivity.java deleted file mode 100644 index f1de44312..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/DecryptActivity.java +++ /dev/null @@ -1,940 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.FileHelper; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.LookupUnknownKeyDialogFragment; -import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; -import org.thialfihar.android.apg.util.Compatibility; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import org.thialfihar.android.apg.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.animation.AnimationUtils; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ViewFlipper; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.regex.Matcher; - -public class DecryptActivity extends SherlockFragmentActivity { - - // possible intent actions for this activity - public static final String ACTION_DECRYPT = Constants.INTENT_PREFIX + "DECRYPT"; - public static final String ACTION_DECRYPT_FILE = Constants.INTENT_PREFIX + "DECRYPT_FILE"; - public static final String ACTION_DECRYPT_AND_RETURN = Constants.INTENT_PREFIX - + "DECRYPT_AND_RETURN"; - - // possible extra keys - public static final String EXTRA_TEXT = "text"; - public static final String EXTRA_DATA = "data"; - public static final String EXTRA_REPLY_TO = "replyTo"; - public static final String EXTRA_SUBJECT = "subject"; - public static final String EXTRA_BINARY = "binary"; - - private long mSignatureKeyId = 0; - - private boolean mReturnResult = false; - private String mReplyTo = null; - private String mSubject = null; - private boolean mSignedOnly = false; - private boolean mAssumeSymmetricEncryption = false; - - private EditText mMessage = null; - private LinearLayout mSignatureLayout = null; - private ImageView mSignatureStatusImage = null; - private TextView mUserId = null; - private TextView mUserIdRest = null; - - private ViewFlipper mSource = null; - private TextView mSourceLabel = null; - private ImageView mSourcePrevious = null; - private ImageView mSourceNext = null; - - private boolean mDecryptEnabled = true; - private String mDecryptString = ""; - private boolean mReplyEnabled = true; - private String mReplyString = ""; - - private int mDecryptTarget; - - private EditText mFilename = null; - private CheckBox mDeleteAfter = null; - private ImageButton mBrowse = null; - - private String mInputFilename = null; - private String mOutputFilename = null; - - private Uri mContentUri = null; - private byte[] mDataBytes = null; - private boolean mReturnBinary = false; - - private long mUnknownSignatureKeyId = 0; - - private long mSecretKeyId = Id.key.none; - - private FileDialogFragment mFileDialog; - - private boolean mLookupUnknownKey = true; - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - if (mDecryptEnabled) { - menu.add(1, Id.menu.option.decrypt, 0, mDecryptString).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } - if (mReplyEnabled) { - menu.add(1, Id.menu.option.reply, 1, mReplyString).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.decrypt: { - decryptClicked(); - - return true; - } - case Id.menu.option.reply: { - replyClicked(); - - return true; - } - - default: { - return super.onOptionsItemSelected(item); - } - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // check permissions for intent actions without user interaction - String[] restrictedActions = new String[] { ACTION_DECRYPT_AND_RETURN }; - OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), - Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); - - setContentView(R.layout.decrypt); - - // set actionbar without home button if called from another app - OtherHelper.setActionBarBackButton(this); - - mSource = (ViewFlipper) findViewById(R.id.source); - mSourceLabel = (TextView) findViewById(R.id.sourceLabel); - mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious); - mSourceNext = (ImageView) findViewById(R.id.sourceNext); - - mSourcePrevious.setClickable(true); - mSourcePrevious.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this, - R.anim.push_right_in)); - mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this, - R.anim.push_right_out)); - mSource.showPrevious(); - updateSource(); - } - }); - - mSourceNext.setClickable(true); - OnClickListener nextSourceClickListener = new OnClickListener() { - public void onClick(View v) { - mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this, - R.anim.push_left_in)); - mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this, - R.anim.push_left_out)); - mSource.showNext(); - updateSource(); - } - }; - mSourceNext.setOnClickListener(nextSourceClickListener); - - mSourceLabel.setClickable(true); - mSourceLabel.setOnClickListener(nextSourceClickListener); - - mMessage = (EditText) findViewById(R.id.message); - mSignatureLayout = (LinearLayout) findViewById(R.id.signature); - mSignatureStatusImage = (ImageView) findViewById(R.id.ic_signature_status); - mUserId = (TextView) findViewById(R.id.mainUserId); - mUserIdRest = (TextView) findViewById(R.id.mainUserIdRest); - - // measure the height of the source_file view and set the message view's min height to that, - // so it fills mSource fully... bit of a hack. - View tmp = findViewById(R.id.sourceFile); - tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - int height = tmp.getMeasuredHeight(); - mMessage.setMinimumHeight(height); - - mFilename = (EditText) findViewById(R.id.filename); - mBrowse = (ImageButton) findViewById(R.id.btn_browse); - mBrowse.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*", - Id.request.filename); - } - }); - - mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterDecryption); - - // default: message source - mSource.setInAnimation(null); - mSource.setOutAnimation(null); - while (mSource.getCurrentView().getId() != R.id.sourceMessage) { - mSource.showNext(); - } - - boolean decryptImmediately = false; - - // Get intent, action and MIME type - Intent intent = getIntent(); - String action = intent.getAction(); - String type = intent.getType(); - - if (Intent.ACTION_VIEW.equals(action)) { - // Android's Action when opening file associated to APG (see AndroidManifest.xml) - - // This gets the Uri, where an inputStream can be opened from - mContentUri = intent.getData(); - - // TODO: old implementation of ACTION_VIEW. Is this used in K9? - // Uri uri = mIntent.getData(); - // try { - // InputStream attachment = getContentResolver().openInputStream(uri); - // ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - // byte bytes[] = new byte[1 << 16]; - // int length; - // while ((length = attachment.read(bytes)) > 0) { - // byteOut.write(bytes, 0, length); - // } - // byteOut.close(); - // String data = new String(byteOut.toByteArray()); - // mMessage.setText(data); - // } catch (FileNotFoundException e) { - // // ignore, then - // } catch (IOException e) { - // // ignore, then - // } - - // same as ACTION_DECRYPT_FILE but decrypt it immediately - handleActionDecryptFile(intent); - decryptImmediately = true; - } else if (Intent.ACTION_SEND.equals(action) && type != null) { - // Android's Action when sending to APG Decrypt - - if ("text/plain".equals(type)) { - // plain text - String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); - if (sharedText != null) { - intent.putExtra(EXTRA_TEXT, sharedText); - handleActionDecrypt(intent); - decryptImmediately = true; - } - } else { - // binary via content provider (could also be files) - Uri uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); - if (uri != null) { - mContentUri = uri; - } - } - } else if (ACTION_DECRYPT.equals(action)) { - handleActionDecrypt(intent); - } else if (ACTION_DECRYPT_FILE.equals(action)) { - handleActionDecryptFile(intent); - } else if (ACTION_DECRYPT_AND_RETURN.equals(action)) { - handleActionDecryptAndReturn(intent); - } - - if (mSource.getCurrentView().getId() == R.id.sourceMessage - && mMessage.getText().length() == 0) { - - CharSequence clipboardText = Compatibility.getClipboardText(this); - - String data = ""; - if (clipboardText != null) { - Matcher matcher = PGPMain.PGP_MESSAGE.matcher(clipboardText); - if (!matcher.matches()) { - matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(clipboardText); - } - if (matcher.matches()) { - data = matcher.group(1); - mMessage.setText(data); - Toast.makeText(this, R.string.usingClipboardContent, Toast.LENGTH_SHORT).show(); - } - } - } - - mSignatureLayout.setVisibility(View.GONE); - mSignatureLayout.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - if (mSignatureKeyId == 0) { - return; - } - PGPPublicKeyRing key = ProviderHelper.getPGPPublicKeyRingByKeyId( - DecryptActivity.this, mSignatureKeyId); - if (key != null) { - Intent intent = new Intent(DecryptActivity.this, KeyServerQueryActivity.class); - intent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID); - intent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, mSignatureKeyId); - startActivity(intent); - } - } - }); - - mReplyEnabled = false; - - // build new actionbar - invalidateOptionsMenu(); - - 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 (decryptImmediately - || (mSource.getCurrentView().getId() == R.id.sourceMessage && (mMessage.getText() - .length() > 0 || mDataBytes != null || mContentUri != null))) { - decryptClicked(); - } - } - - /** - * Handles activity intent with ACTION_DECRYPT - * - * @param intent - */ - private void handleActionDecrypt(Intent intent) { - Log.d(Constants.TAG, "Apg Intent DECRYPT startet"); - - Bundle extras = intent.getExtras(); - if (extras == null) { - Log.d(Constants.TAG, "extra bundle was null"); - extras = new Bundle(); - } else { - Log.d(Constants.TAG, "got extras"); - } - - mDataBytes = extras.getByteArray(EXTRA_DATA); - String textData = null; - if (mDataBytes == null) { - Log.d(Constants.TAG, "EXTRA_DATA was null"); - textData = extras.getString(EXTRA_TEXT); - } else { - Log.d(Constants.TAG, "Got data from EXTRA_DATA"); - } - if (textData != null) { - Log.d(Constants.TAG, "textData null, matching text ..."); - Matcher matcher = PGPMain.PGP_MESSAGE.matcher(textData); - if (matcher.matches()) { - Log.d(Constants.TAG, "PGP_MESSAGE matched"); - textData = matcher.group(1); - // replace non breakable spaces - textData = textData.replaceAll("\\xa0", " "); - mMessage.setText(textData); - } else { - matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(textData); - if (matcher.matches()) { - Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched"); - textData = matcher.group(1); - // replace non breakable spaces - textData = textData.replaceAll("\\xa0", " "); - mMessage.setText(textData); - - mDecryptString = getString(R.string.btn_verify); - // build new action bar - invalidateOptionsMenu(); - } else { - Log.d(Constants.TAG, "Nothing matched!"); - } - } - } - mReplyTo = extras.getString(EXTRA_REPLY_TO); - mSubject = extras.getString(EXTRA_SUBJECT); - } - - /** - * Handles activity intent with ACTION_DECRYPT_FILE - * - * @param intent - */ - private void handleActionDecryptFile(Intent intent) { - mInputFilename = intent.getData().getPath(); - mFilename.setText(mInputFilename); - guessOutputFilename(); - mSource.setInAnimation(null); - mSource.setOutAnimation(null); - while (mSource.getCurrentView().getId() != R.id.sourceFile) { - mSource.showNext(); - } - } - - /** - * Handles activity intent with ACTION_DECRYPT_AND_RETURN - * - * @param intent - */ - private void handleActionDecryptAndReturn(Intent intent) { - Bundle extras = intent.getExtras(); - if (extras == null) { - extras = new Bundle(); - } - - mReturnBinary = extras.getBoolean(EXTRA_BINARY, false); - - if (mContentUri == null) { - mDataBytes = extras.getByteArray(EXTRA_DATA); - String data = extras.getString(EXTRA_TEXT); - if (data != null) { - Matcher matcher = PGPMain.PGP_MESSAGE.matcher(data); - if (matcher.matches()) { - data = matcher.group(1); - // replace non breakable spaces - data = data.replaceAll("\\xa0", " "); - mMessage.setText(data); - } else { - matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(data); - if (matcher.matches()) { - data = matcher.group(1); - // replace non breakable spaces - data = data.replaceAll("\\xa0", " "); - mMessage.setText(data); - mDecryptString = getString(R.string.btn_verify); - - // build new action bar - invalidateOptionsMenu(); - } - } - } - } - mReturnResult = true; - } - - private void guessOutputFilename() { - mInputFilename = mFilename.getText().toString(); - File file = new File(mInputFilename); - String filename = file.getName(); - if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) { - filename = filename.substring(0, filename.length() - 4); - } - mOutputFilename = Constants.path.APP_DIR + "/" + filename; - } - - private void updateSource() { - switch (mSource.getCurrentView().getId()) { - case R.id.sourceFile: { - mSourceLabel.setText(R.string.label_file); - mDecryptString = getString(R.string.btn_decrypt); - - // build new action bar - invalidateOptionsMenu(); - break; - } - - case R.id.sourceMessage: { - mSourceLabel.setText(R.string.label_message); - mDecryptString = getString(R.string.btn_decrypt); - - // build new action bar - invalidateOptionsMenu(); - break; - } - - default: { - break; - } - } - } - - private void decryptClicked() { - if (mSource.getCurrentView().getId() == R.id.sourceFile) { - mDecryptTarget = Id.target.file; - } else { - mDecryptTarget = Id.target.message; - } - initiateDecryption(); - } - - private void initiateDecryption() { - if (mDecryptTarget == Id.target.file) { - String currentFilename = mFilename.getText().toString(); - if (mInputFilename == null || !mInputFilename.equals(currentFilename)) { - guessOutputFilename(); - } - - if (mInputFilename.equals("")) { - Toast.makeText(this, R.string.noFileSelected, Toast.LENGTH_SHORT).show(); - return; - } - - if (mInputFilename.startsWith("file")) { - File file = new File(mInputFilename); - if (!file.exists() || !file.isFile()) { - Toast.makeText( - this, - getString(R.string.errorMessage, getString(R.string.error_fileNotFound)), - Toast.LENGTH_SHORT).show(); - return; - } - } - } - - if (mDecryptTarget == Id.target.message) { - String messageData = mMessage.getText().toString(); - Matcher matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(messageData); - if (matcher.matches()) { - mSignedOnly = true; - decryptStart(); - return; - } - } - - // else treat it as an decrypted message/file - mSignedOnly = false; - - getDecryptionKeyFromInputStream(); - - // if we need a symmetric passphrase or a passphrase to use a secret key ask for it - if (mSecretKeyId == Id.key.symmetric - || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { - showPassphraseDialog(); - } else { - if (mDecryptTarget == Id.target.file) { - askForOutputFilename(); - } else { - decryptStart(); - } - } - } - - /** - * Shows passphrase dialog to cache a new passphrase the user enters for using it later for - * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks - * for a symmetric passphrase - */ - private void showPassphraseDialog() { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - if (mDecryptTarget == Id.target.file) { - askForOutputFilename(); - } else { - decryptStart(); - } - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - try { - PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, - messenger, mSecretKeyId); - - passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); - } catch (PGPMain.ApgGeneralException e) { - Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); - // send message to handler to start encryption directly - returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); - } - } - - /** - * TODO: Rework function, remove global variables - */ - private void getDecryptionKeyFromInputStream() { - InputStream inStream = null; - if (mContentUri != null) { - try { - inStream = getContentResolver().openInputStream(mContentUri); - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "File not found!", e); - Toast.makeText(this, getString(R.string.error_fileNotFound, e.getMessage()), - Toast.LENGTH_SHORT).show(); - } - } else if (mDecryptTarget == Id.target.file) { - // check if storage is ready - if (!FileHelper.isStorageMounted(mInputFilename)) { - Toast.makeText(this, getString(R.string.error_externalStorageNotReady), - Toast.LENGTH_SHORT).show(); - return; - } - - try { - inStream = new FileInputStream(mInputFilename); - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "File not found!", e); - Toast.makeText(this, getString(R.string.error_fileNotFound, e.getMessage()), - Toast.LENGTH_SHORT).show(); - } - } else { - if (mDataBytes != null) { - inStream = new ByteArrayInputStream(mDataBytes); - } else { - inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes()); - } - } - - // get decryption key for this inStream - try { - try { - mSecretKeyId = PGPMain.getDecryptionKeyId(this, inStream); - if (mSecretKeyId == Id.key.none) { - throw new PGPMain.ApgGeneralException( - getString(R.string.error_noSecretKeyFound)); - } - mAssumeSymmetricEncryption = false; - } catch (PGPMain.NoAsymmetricEncryptionException e) { - mSecretKeyId = Id.key.symmetric; - if (!PGPMain.hasSymmetricEncryption(this, inStream)) { - throw new PGPMain.ApgGeneralException( - getString(R.string.error_noKnownEncryptionFound)); - } - mAssumeSymmetricEncryption = true; - } - } catch (Exception e) { - Toast.makeText(this, getString(R.string.errorMessage, e.getMessage()), - Toast.LENGTH_SHORT).show(); - } - } - - private void replyClicked() { - Intent intent = new Intent(this, EncryptActivity.class); - intent.setAction(EncryptActivity.ACTION_ENCRYPT); - String data = mMessage.getText().toString(); - data = data.replaceAll("(?m)^", "> "); - data = "\n\n" + data; - intent.putExtra(EncryptActivity.EXTRA_TEXT, data); - intent.putExtra(EncryptActivity.EXTRA_SUBJECT, "Re: " + mSubject); - intent.putExtra(EncryptActivity.EXTRA_SEND_TO, mReplyTo); - intent.putExtra(EncryptActivity.EXTRA_SIGNATURE_KEY_ID, mSecretKeyId); - intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId }); - startActivity(intent); - } - - private void askForOutputFilename() { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == FileDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - decryptStart(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - mFileDialog = FileDialogFragment.newInstance(messenger, - getString(R.string.title_decryptToFile), - getString(R.string.specifyFileToDecryptTo), mOutputFilename, null, - Id.request.output_filename); - - mFileDialog.show(getSupportFragmentManager(), "fileDialog"); - } - - private void lookupUnknownKey(long unknownKeyId) { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_OKAY) { - // the result is handled by onActivityResult() as LookupUnknownKeyDialogFragment - // starts a new Intent which then returns data - } else if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_CANCEL) { - // decrypt again, but don't lookup unknown keys! - mLookupUnknownKey = false; - decryptStart(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - LookupUnknownKeyDialogFragment lookupKeyDialog = LookupUnknownKeyDialogFragment - .newInstance(messenger, unknownKeyId); - - lookupKeyDialog.show(getSupportFragmentManager(), "unknownKeyDialog"); - } - - private void decryptStart() { - Log.d(Constants.TAG, "decryptStart"); - - // Send all information needed to service to decrypt in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - // fill values for this action - Bundle data = new Bundle(); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_DECRYPT_VERIFY); - - // choose action based on input: decrypt stream, file or bytes - if (mContentUri != null) { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_STREAM); - - data.putParcelable(ApgIntentService.PROVIDER_URI, mContentUri); - } else if (mDecryptTarget == Id.target.file) { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_FILE); - - Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" - + mOutputFilename); - - data.putString(ApgIntentService.INPUT_FILE, mInputFilename); - data.putString(ApgIntentService.OUTPUT_FILE, mOutputFilename); - } else { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); - - if (mDataBytes != null) { - data.putByteArray(ApgIntentService.CIPHERTEXT_BYTES, mDataBytes); - } else { - String message = mMessage.getText().toString(); - data.putByteArray(ApgIntentService.CIPHERTEXT_BYTES, message.getBytes()); - } - } - - data.putLong(ApgIntentService.SECRET_KEY_ID, mSecretKeyId); - - data.putBoolean(ApgIntentService.SIGNED_ONLY, mSignedOnly); - data.putBoolean(ApgIntentService.LOOKUP_UNKNOWN_KEY, mLookupUnknownKey); - data.putBoolean(ApgIntentService.RETURN_BYTES, mReturnBinary); - data.putBoolean(ApgIntentService.ASSUME_SYMMETRIC, mAssumeSymmetricEncryption); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after encrypting is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_decrypting, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - // if key is unknown show lookup dialog - if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE_LOOKUP_KEY) - && mLookupUnknownKey) { - mUnknownSignatureKeyId = returnData - .getLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID); - lookupUnknownKey(mUnknownSignatureKeyId); - return; - } - - mSignatureKeyId = 0; - mSignatureLayout.setVisibility(View.GONE); - mReplyEnabled = false; - - // build new action bar - invalidateOptionsMenu(); - - Toast.makeText(DecryptActivity.this, R.string.decryptionSuccessful, - Toast.LENGTH_SHORT).show(); - if (mReturnResult) { - Intent intent = new Intent(); - intent.putExtras(returnData); - setResult(RESULT_OK, intent); - finish(); - return; - } - - switch (mDecryptTarget) { - case Id.target.message: - String decryptedMessage = returnData - .getString(ApgIntentService.RESULT_DECRYPTED_STRING); - mMessage.setText(decryptedMessage); - mMessage.setHorizontallyScrolling(false); - mReplyEnabled = false; - - // build new action bar - invalidateOptionsMenu(); - break; - - case Id.target.file: - if (mDeleteAfter.isChecked()) { - // Create and show dialog to delete original file - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment - .newInstance(mInputFilename); - deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); - } - break; - - default: - // shouldn't happen - break; - - } - - if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE)) { - String userId = returnData - .getString(ApgIntentService.RESULT_SIGNATURE_USER_ID); - mSignatureKeyId = returnData - .getLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID); - mUserIdRest - .setText("id: " + PGPHelper.getSmallFingerPrint(mSignatureKeyId)); - if (userId == null) { - userId = getResources().getString(R.string.unknownUserId); - } - String chunks[] = userId.split(" <", 2); - userId = chunks[0]; - if (chunks.length > 1) { - mUserIdRest.setText("<" + chunks[1]); - } - mUserId.setText(userId); - - if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS)) { - mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); - } else if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN)) { - mSignatureStatusImage.setImageResource(R.drawable.overlay_error); - Toast.makeText(DecryptActivity.this, - R.string.unknownSignatureKeyTouchToLookUp, Toast.LENGTH_LONG) - .show(); - } else { - mSignatureStatusImage.setImageResource(R.drawable.overlay_error); - } - mSignatureLayout.setVisibility(View.VISIBLE); - } - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = FileHelper.getPath(this, data.getData()); - Log.d(Constants.TAG, "path=" + path); - - mFilename.setText(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!"); - } - } - return; - } - - case Id.request.output_filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = data.getData().getPath(); - Log.d(Constants.TAG, "path=" + path); - - mFileDialog.setFilename(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!"); - } - } - return; - } - - // this request is returned after LookupUnknownKeyDialogFragment started - // KeyServerQueryActivity and user looked uo key - case Id.request.look_up_key_id: { - Log.d(Constants.TAG, "Returning from Lookup Key..."); - // decrypt again without lookup - mLookupUnknownKey = false; - decryptStart(); - return; - } - - default: { - break; - } - } - - super.onActivityResult(requestCode, resultCode, data); - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/EditKeyActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/EditKeyActivity.java deleted file mode 100644 index 0e480a3ae..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/EditKeyActivity.java +++ /dev/null @@ -1,569 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.PGPConversionHelper; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.ui.dialog.SetPassphraseDialogFragment; -import org.thialfihar.android.apg.ui.widget.KeyEditor; -import org.thialfihar.android.apg.ui.widget.SectionView; -import org.thialfihar.android.apg.ui.widget.UserIdEditor; -import org.thialfihar.android.apg.util.IterableIterator; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import org.thialfihar.android.apg.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.LinearLayout; -import android.widget.Toast; -import android.widget.CompoundButton.OnCheckedChangeListener; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Vector; - -public class EditKeyActivity extends SherlockFragmentActivity { - - // possible intent actions for this activity - public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY"; - public static final String ACTION_EDIT_KEY = Constants.INTENT_PREFIX + "EDIT_KEY"; - - // possible extra keys - public static final String EXTRA_USER_IDS = "userIds"; - public static final String EXTRA_NO_PASSPHRASE = "noPassphrase"; - public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generateDefaultKeys"; - public static final String EXTRA_KEY_ID = "keyId"; - - private ActionBar mActionBar; - - private PGPSecretKeyRing mKeyRing = null; - - private SectionView mUserIdsView; - private SectionView mKeysView; - - private String mCurrentPassPhrase = null; - private String mNewPassPhrase = null; - - private Button mChangePassPhrase; - - private CheckBox mNoPassphrase; - - Vector mUserIds; - Vector mKeys; - Vector mKeysUsages; - - // will be set to false to build layout later in handler - private boolean mBuildLayout = true; - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(1, Id.menu.option.cancel, 0, R.string.btn_doNotSave).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(1, Id.menu.option.save, 1, R.string.btn_save).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, KeyListSecretActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.save: - saveClicked(); - return true; - - case Id.menu.option.cancel: - finish(); - return true; - - default: - break; - - } - return false; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // check permissions for intent actions without user interaction - String[] restrictedActions = new String[] { ACTION_CREATE_KEY }; - OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), - Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); - - setContentView(R.layout.edit_key); - - mActionBar = getSupportActionBar(); - mActionBar.setDisplayShowTitleEnabled(true); - - // set actionbar without home button if called from another app - OtherHelper.setActionBarBackButton(this); - - // find views - mChangePassPhrase = (Button) findViewById(R.id.edit_key_btn_change_pass_phrase); - mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase); - - mUserIds = new Vector(); - mKeys = new Vector(); - mKeysUsages = new Vector(); - - // Catch Intents opened from other apps - Intent intent = getIntent(); - String action = intent.getAction(); - if (ACTION_CREATE_KEY.equals(action)) { - handleActionCreateKey(intent); - } else if (ACTION_EDIT_KEY.equals(action)) { - handleActionEditKey(intent); - } - - mChangePassPhrase.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - showSetPassphraseDialog(); - } - }); - - // disable passphrase when no passphrase checkobox is checked! - mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() { - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - // remove passphrase - mNewPassPhrase = null; - - mChangePassPhrase.setVisibility(View.GONE); - } else { - mChangePassPhrase.setVisibility(View.VISIBLE); - } - } - }); - - if (mBuildLayout) { - buildLayout(); - } - } - - /** - * Handle intent action to create new key - * - * @param intent - */ - private void handleActionCreateKey(Intent intent) { - Bundle extras = intent.getExtras(); - - mActionBar.setTitle(R.string.title_createKey); - - mCurrentPassPhrase = ""; - - if (extras != null) { - // if userId is given, prefill the fields - if (extras.containsKey(EXTRA_USER_IDS)) { - Log.d(Constants.TAG, "UserIds are given!"); - mUserIds.add(extras.getString(EXTRA_USER_IDS)); - } - - // if no passphrase is given - if (extras.containsKey(EXTRA_NO_PASSPHRASE)) { - boolean noPassphrase = extras.getBoolean(EXTRA_NO_PASSPHRASE); - if (noPassphrase) { - // check "no passphrase" checkbox and remove button - mNoPassphrase.setChecked(true); - mChangePassPhrase.setVisibility(View.GONE); - } - } - - // generate key - if (extras.containsKey(EXTRA_GENERATE_DEFAULT_KEYS)) { - boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS); - if (generateDefaultKeys) { - - // build layout in handler after generating keys not directly in onCreate - mBuildLayout = false; - - // Send all information needed to service generate keys in other thread - Intent serviceIntent = new Intent(this, ApgIntentService.class); - serviceIntent.putExtra(ApgIntentService.EXTRA_ACTION, - ApgIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS); - - // fill values for this action - Bundle data = new Bundle(); - data.putString(ApgIntentService.SYMMETRIC_PASSPHRASE, mCurrentPassPhrase); - - serviceIntent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after generating is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_generating, ProgressDialog.STYLE_SPINNER) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get new key from data bundle returned from service - Bundle data = message.getData(); - PGPSecretKeyRing masterKeyRing = (PGPSecretKeyRing) PGPConversionHelper - .BytesToPGPKeyRing(data - .getByteArray(ApgIntentService.RESULT_NEW_KEY)); - PGPSecretKeyRing subKeyRing = (PGPSecretKeyRing) PGPConversionHelper - .BytesToPGPKeyRing(data - .getByteArray(ApgIntentService.RESULT_NEW_KEY2)); - - // add master key - @SuppressWarnings("unchecked") - Iterator masterIt = masterKeyRing.getSecretKeys(); - mKeys.add(masterIt.next()); - mKeysUsages.add(Id.choice.usage.sign_only); - - // add sub key - @SuppressWarnings("unchecked") - Iterator subIt = subKeyRing.getSecretKeys(); - subIt.next(); // masterkey - mKeys.add(subIt.next()); - mKeysUsages.add(Id.choice.usage.encrypt_only); - - buildLayout(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - serviceIntent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - saveHandler.showProgressDialog(this); - - // start service with intent - startService(serviceIntent); - } - } - } - } - - /** - * Handle intent action to edit existing key - * - * @param intent - */ - private void handleActionEditKey(Intent intent) { - Bundle extras = intent.getExtras(); - - mActionBar.setTitle(R.string.title_editKey); - - mCurrentPassPhrase = PGPMain.getEditPassPhrase(); - if (mCurrentPassPhrase == null) { - mCurrentPassPhrase = ""; - } - - if (mCurrentPassPhrase.equals("")) { - // check "no passphrase" checkbox and remove button - mNoPassphrase.setChecked(true); - mChangePassPhrase.setVisibility(View.GONE); - } - - if (extras != null) { - if (extras.containsKey(EXTRA_KEY_ID)) { - long keyId = extras.getLong(EXTRA_KEY_ID); - - if (keyId != 0) { - PGPSecretKey masterKey = null; - mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, keyId); - if (mKeyRing != null) { - masterKey = PGPHelper.getMasterKey(mKeyRing); - for (PGPSecretKey key : new IterableIterator( - mKeyRing.getSecretKeys())) { - mKeys.add(key); - mKeysUsages.add(-1); // get usage when view is created - } - } - if (masterKey != null) { - for (String userId : new IterableIterator(masterKey.getUserIDs())) { - Log.d(Constants.TAG, "Added userId " + userId); - mUserIds.add(userId); - } - } - } - } - } - } - - /** - * Shows the dialog to set a new passphrase - */ - private void showSetPassphraseDialog() { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - - // set new returned passphrase! - mNewPassPhrase = data - .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE); - - updatePassPhraseButtonText(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - // set title based on isPassphraseSet() - int title = -1; - if (isPassphraseSet()) { - title = R.string.title_changePassPhrase; - } else { - title = R.string.title_setPassPhrase; - } - - SetPassphraseDialogFragment setPassphraseDialog = SetPassphraseDialogFragment.newInstance( - messenger, title); - - setPassphraseDialog.show(getSupportFragmentManager(), "setPassphraseDialog"); - } - - /** - * Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user - * id and key. - */ - private void buildLayout() { - // Build layout based on given userIds and keys - LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container); - mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); - mUserIdsView.setType(Id.type.user_id); - mUserIdsView.setUserIds(mUserIds); - container.addView(mUserIdsView); - mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); - mKeysView.setType(Id.type.key); - mKeysView.setKeys(mKeys, mKeysUsages); - container.addView(mKeysView); - - updatePassPhraseButtonText(); - } - - private long getMasterKeyId() { - if (mKeysView.getEditors().getChildCount() == 0) { - return 0; - } - return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyID(); - } - - public boolean isPassphraseSet() { - if (mNoPassphrase.isChecked()) { - return true; - } else if ((!mCurrentPassPhrase.equals("")) - || (mNewPassPhrase != null && !mNewPassPhrase.equals(""))) { - return true; - } else { - return false; - } - } - - private void saveClicked() { - try { - if (!isPassphraseSet()) { - throw new PGPMain.ApgGeneralException(this.getString(R.string.setAPassPhrase)); - } - - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_SAVE_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - data.putString(ApgIntentService.CURRENT_PASSPHRASE, mCurrentPassPhrase); - data.putString(ApgIntentService.NEW_PASSPHRASE, mNewPassPhrase); - data.putStringArrayList(ApgIntentService.USER_IDS, getUserIds(mUserIdsView)); - ArrayList keys = getKeys(mKeysView); - data.putByteArray(ApgIntentService.KEYS, - PGPConversionHelper.PGPSecretKeyArrayListToBytes(keys)); - data.putIntegerArrayList(ApgIntentService.KEYS_USAGES, getKeysUsages(mKeysView)); - data.putLong(ApgIntentService.MASTER_KEY_ID, getMasterKeyId()); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after saving is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_saving, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - finish(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } catch (PGPMain.ApgGeneralException e) { - Toast.makeText(this, getString(R.string.errorMessage, e.getMessage()), - Toast.LENGTH_SHORT).show(); - } - } - - /** - * Returns user ids from the SectionView - * - * @param userIdsView - * @return - */ - private ArrayList getUserIds(SectionView userIdsView) - throws PGPMain.ApgGeneralException { - ArrayList userIds = new ArrayList(); - - ViewGroup userIdEditors = userIdsView.getEditors(); - - boolean gotMainUserId = false; - for (int i = 0; i < userIdEditors.getChildCount(); ++i) { - UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i); - String userId = null; - try { - userId = editor.getValue(); - } catch (UserIdEditor.NoNameException e) { - throw new PGPMain.ApgGeneralException( - this.getString(R.string.error_userIdNeedsAName)); - } catch (UserIdEditor.NoEmailException e) { - throw new PGPMain.ApgGeneralException( - this.getString(R.string.error_userIdNeedsAnEmailAddress)); - } catch (UserIdEditor.InvalidEmailException e) { - throw new PGPMain.ApgGeneralException(e.getMessage()); - } - - if (userId.equals("")) { - continue; - } - - if (editor.isMainUserId()) { - userIds.add(0, userId); - gotMainUserId = true; - } else { - userIds.add(userId); - } - } - - if (userIds.size() == 0) { - throw new PGPMain.ApgGeneralException(getString(R.string.error_keyNeedsAUserId)); - } - - if (!gotMainUserId) { - throw new PGPMain.ApgGeneralException( - getString(R.string.error_mainUserIdMustNotBeEmpty)); - } - - return userIds; - } - - /** - * Returns keys from the SectionView - * - * @param keysView - * @return - */ - private ArrayList getKeys(SectionView keysView) - throws PGPMain.ApgGeneralException { - ArrayList keys = new ArrayList(); - - ViewGroup keyEditors = keysView.getEditors(); - - if (keyEditors.getChildCount() == 0) { - throw new PGPMain.ApgGeneralException(getString(R.string.error_keyNeedsMasterKey)); - } - - for (int i = 0; i < keyEditors.getChildCount(); ++i) { - KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); - keys.add(editor.getValue()); - } - - return keys; - } - - /** - * Returns usage selections of keys from the SectionView - * - * @param keysView - * @return - */ - private ArrayList getKeysUsages(SectionView keysView) - throws PGPMain.ApgGeneralException { - ArrayList getKeysUsages = new ArrayList(); - - ViewGroup keyEditors = keysView.getEditors(); - - if (keyEditors.getChildCount() == 0) { - throw new PGPMain.ApgGeneralException(getString(R.string.error_keyNeedsMasterKey)); - } - - for (int i = 0; i < keyEditors.getChildCount(); ++i) { - KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); - getKeysUsages.add(editor.getUsage()); - } - - return getKeysUsages; - } - - private void updatePassPhraseButtonText() { - mChangePassPhrase.setText(isPassphraseSet() ? R.string.btn_changePassPhrase - : R.string.btn_setPassPhrase); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/EncryptActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/EncryptActivity.java deleted file mode 100644 index 482372c51..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/EncryptActivity.java +++ /dev/null @@ -1,1099 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.FileHelper; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; -import org.thialfihar.android.apg.util.Choice; -import org.thialfihar.android.apg.util.Compatibility; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import org.thialfihar.android.apg.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.animation.AnimationUtils; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ViewFlipper; - -import java.io.File; -import java.util.Vector; - -public class EncryptActivity extends SherlockFragmentActivity { - - // possible intent actions for this activity - public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT"; - public static final String ACTION_ENCRYPT_AND_RETURN = Constants.INTENT_PREFIX - + "ENCRYPT_AND_RETURN"; - public static final String ACTION_GENERATE_SIGNATURE_AND_RETURN = Constants.INTENT_PREFIX - + "GENERATE_SIGNATURE"; - - public static final String ACTION_ENCRYPT_FILE = Constants.INTENT_PREFIX + "ENCRYPT_FILE"; - public static final String ACTION_ENCRYPT_STREAM_AND_RETURN = Constants.INTENT_PREFIX - + "ENCRYPT_STREAM_AND_RETURN"; - - // possible extra keys - public static final String EXTRA_TEXT = "text"; - public static final String EXTRA_DATA = "data"; - public static final String EXTRA_ASCII_ARMOUR = "asciiArmour"; - public static final String EXTRA_SEND_TO = "sendTo"; - public static final String EXTRA_SUBJECT = "subject"; - public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId"; - public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds"; - - private String mSubject = null; - private String mSendTo = null; - - private long mEncryptionKeyIds[] = null; - - private boolean mReturnResult = false; - private EditText mMessage = null; - private Button mSelectKeysButton = null; - - private boolean mEncryptEnabled = false; - private String mEncryptString = ""; - private boolean mEncryptToClipboardEnabled = false; - private String mEncryptToClipboardString = ""; - - private CheckBox mSign = null; - private TextView mMainUserId = null; - private TextView mMainUserIdRest = null; - - private ViewFlipper mSource = null; - private TextView mSourceLabel = null; - private ImageView mSourcePrevious = null; - private ImageView mSourceNext = null; - - private ViewFlipper mMode = null; - private TextView mModeLabel = null; - private ImageView mModePrevious = null; - private ImageView mModeNext = null; - - private int mEncryptTarget; - - private EditText mPassPhrase = null; - private EditText mPassPhraseAgain = null; - private CheckBox mAsciiArmour = null; - private Spinner mFileCompression = null; - - private EditText mFilename = null; - private CheckBox mDeleteAfter = null; - private ImageButton mBrowse = null; - - private String mInputFilename = null; - private String mOutputFilename = null; - - private boolean mAsciiArmorDemand = false; - private boolean mOverrideAsciiArmour = false; - private Uri mStreamAndReturnUri = null; - private byte[] mData = null; - - private boolean mGenerateSignature = false; - - private long mSecretKeyId = Id.key.none; - - private FileDialogFragment mFileDialog; - - /** - * ActionBar menu is created based on class variables to change it at runtime - * - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (mEncryptToClipboardEnabled) { - menu.add(1, Id.menu.option.encrypt_to_clipboard, 0, mEncryptToClipboardString) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } - if (mEncryptEnabled) { - menu.add(1, Id.menu.option.encrypt, 1, mEncryptString).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.encrypt_to_clipboard: - encryptToClipboardClicked(); - - return true; - - case Id.menu.option.encrypt: - encryptClicked(); - - return true; - - default: - return super.onOptionsItemSelected(item); - - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // check permissions for intent actions without user interaction - String[] restrictedActions = new String[] { ACTION_ENCRYPT_AND_RETURN, - ACTION_GENERATE_SIGNATURE_AND_RETURN, ACTION_ENCRYPT_STREAM_AND_RETURN }; - OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), - Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); - - setContentView(R.layout.encrypt); - - // set actionbar without home button if called from another app - OtherHelper.setActionBarBackButton(this); - - initView(); - - // Handle intent actions - handleActions(getIntent()); - - updateView(); - updateSource(); - updateMode(); - - if (mReturnResult) { - mSourcePrevious.setClickable(false); - mSourcePrevious.setEnabled(false); - mSourcePrevious.setVisibility(View.INVISIBLE); - - mSourceNext.setClickable(false); - mSourceNext.setEnabled(false); - mSourceNext.setVisibility(View.INVISIBLE); - - mSourceLabel.setClickable(false); - mSourceLabel.setEnabled(false); - } - - updateActionBarButtons(); - - if (mReturnResult - && (mMessage.getText().length() > 0 || mData != null) - && ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) || mSecretKeyId != 0)) { - encryptClicked(); - } - } - - /** - * Handles all actions with this intent - * - * @param intent - */ - private void handleActions(Intent intent) { - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - String type = intent.getType(); - Uri uri = intent.getData(); - - if (extras == null) { - extras = new Bundle(); - } - - if (Intent.ACTION_SEND.equals(action) && type != null) { - // Android's Action when sending to APG Encrypt - - if ("text/plain".equals(type)) { - // plain text - String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); - if (sharedText != null) { - // handle like normal text encryption, override action and extras - action = ACTION_ENCRYPT; - extras.putString(EXTRA_TEXT, sharedText); - extras.putBoolean(EXTRA_ASCII_ARMOUR, true); - } - } else { - // files via content provider, override uri and action - uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); - action = ACTION_ENCRYPT_FILE; - } - } - - if (ACTION_ENCRYPT_AND_RETURN.equals(action) - || ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { - mReturnResult = true; - } - - if (ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { - mGenerateSignature = true; - mOverrideAsciiArmour = true; - mAsciiArmorDemand = false; - } - - if (extras.containsKey(EXTRA_ASCII_ARMOUR)) { - mAsciiArmorDemand = extras.getBoolean(EXTRA_ASCII_ARMOUR, true); - mOverrideAsciiArmour = true; - mAsciiArmour.setChecked(mAsciiArmorDemand); - } - - mData = extras.getByteArray(EXTRA_DATA); - String textData = null; - if (mData == null) { - textData = extras.getString(EXTRA_TEXT); - } - mSendTo = extras.getString(EXTRA_SEND_TO); - mSubject = extras.getString(EXTRA_SUBJECT); - long signatureKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); - long[] encryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); - - // preselect keys given by intent - preselectKeys(signatureKeyId, encryptionKeyIds); - - if (ACTION_ENCRYPT.equals(action) || ACTION_ENCRYPT_AND_RETURN.equals(action) - || ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { - if (textData != null) { - mMessage.setText(textData); - } - mSource.setInAnimation(null); - mSource.setOutAnimation(null); - while (mSource.getCurrentView().getId() != R.id.sourceMessage) { - mSource.showNext(); - } - } else if (ACTION_ENCRYPT_FILE.equals(action)) { - // get file path from uri - String path = FileHelper.getPath(this, uri); - - if (path != null) { - mInputFilename = path; - mFilename.setText(mInputFilename); - - mSource.setInAnimation(null); - mSource.setOutAnimation(null); - while (mSource.getCurrentView().getId() != R.id.sourceFile) { - mSource.showNext(); - } - } else { - Log.e(Constants.TAG, - "Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_ENCRYPT_STREAM_AND_RETURN."); - Toast.makeText(this, R.string.error_onlyFilesAreSupported, Toast.LENGTH_LONG) - .show(); - // end activity - finish(); - } - - } else if (ACTION_ENCRYPT_STREAM_AND_RETURN.equals(action)) { - // use mIntentDataUri to encrypt any stream and return - // TODO - - mStreamAndReturnUri = null; - } - } - - /** - * If an Intent gives a signatureKeyId and/or encryptionKeyIds, preselect those! - * - * @param preselectedSignatureKeyId - * @param preselectedEncryptionKeyIds - */ - private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds) { - if (preselectedSignatureKeyId != 0) { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, - preselectedSignatureKeyId); - PGPSecretKey masterKey = null; - if (keyRing != null) { - masterKey = PGPHelper.getMasterKey(keyRing); - if (masterKey != null) { - Vector signKeys = PGPHelper.getUsableSigningKeys(keyRing); - if (signKeys.size() > 0) { - mSecretKeyId = masterKey.getKeyID(); - } - } - } - } - - if (preselectedEncryptionKeyIds != null) { - Vector goodIds = new Vector(); - for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) { - PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, - preselectedEncryptionKeyIds[i]); - PGPPublicKey masterKey = null; - if (keyRing == null) { - continue; - } - masterKey = PGPHelper.getMasterKey(keyRing); - if (masterKey == null) { - continue; - } - Vector encryptKeys = PGPHelper.getUsableEncryptKeys(keyRing); - if (encryptKeys.size() == 0) { - continue; - } - goodIds.add(masterKey.getKeyID()); - } - if (goodIds.size() > 0) { - mEncryptionKeyIds = new long[goodIds.size()]; - for (int i = 0; i < goodIds.size(); ++i) { - mEncryptionKeyIds[i] = goodIds.get(i); - } - } - } - } - - /** - * Guess output filename based on input path - * - * @param path - * @return Suggestion for output filename - */ - private String guessOutputFilename(String path) { - // output in the same directory but with additional ending - File file = new File(path); - String ending = (mAsciiArmour.isChecked() ? ".asc" : ".gpg"); - String outputFilename = file.getParent() + File.separator + file.getName() + ending; - - return outputFilename; - } - - private void updateSource() { - switch (mSource.getCurrentView().getId()) { - case R.id.sourceFile: { - mSourceLabel.setText(R.string.label_file); - break; - } - - case R.id.sourceMessage: { - mSourceLabel.setText(R.string.label_message); - break; - } - - default: { - break; - } - } - updateActionBarButtons(); - } - - /** - * Set ActionBar buttons based on parameters - * - * @param encryptEnabled - * @param encryptStringRes - * @param encryptToClipboardEnabled - * @param encryptToClipboardStringRes - */ - private void setActionbarButtons(boolean encryptEnabled, int encryptStringRes, - boolean encryptToClipboardEnabled, int encryptToClipboardStringRes) { - mEncryptEnabled = encryptEnabled; - if (encryptEnabled) { - mEncryptString = getString(encryptStringRes); - } - mEncryptToClipboardEnabled = encryptToClipboardEnabled; - if (encryptToClipboardEnabled) { - mEncryptToClipboardString = getString(encryptToClipboardStringRes); - } - - // build new action bar based on these class variables - invalidateOptionsMenu(); - } - - /** - * Update ActionBar buttons based on current selection in view - */ - private void updateActionBarButtons() { - switch (mSource.getCurrentView().getId()) { - case R.id.sourceFile: { - setActionbarButtons(true, R.string.btn_encryptFile, false, 0); - - break; - } - - case R.id.sourceMessage: { - mSourceLabel.setText(R.string.label_message); - - if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { - if (mReturnResult) { - setActionbarButtons(true, R.string.btn_encrypt, false, 0); - } else { - setActionbarButtons(true, R.string.btn_encryptAndEmail, true, - R.string.btn_encryptToClipboard); - } - } else { - if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) { - if (mSecretKeyId == 0) { - setActionbarButtons(false, 0, false, 0); - } else { - if (mReturnResult) { - setActionbarButtons(true, R.string.btn_sign, false, 0); - } else { - setActionbarButtons(true, R.string.btn_signAndEmail, true, - R.string.btn_signToClipboard); - } - } - } else { - if (mReturnResult) { - setActionbarButtons(true, R.string.btn_encrypt, false, 0); - } else { - setActionbarButtons(true, R.string.btn_encryptAndEmail, true, - R.string.btn_encryptToClipboard); - } - } - } - break; - } - - default: { - break; - } - } - - } - - private void updateMode() { - switch (mMode.getCurrentView().getId()) { - case R.id.modeAsymmetric: { - mModeLabel.setText(R.string.label_asymmetric); - break; - } - - case R.id.modeSymmetric: { - mModeLabel.setText(R.string.label_symmetric); - break; - } - - default: { - break; - } - } - updateActionBarButtons(); - } - - private void encryptToClipboardClicked() { - mEncryptTarget = Id.target.clipboard; - initiateEncryption(); - } - - private void encryptClicked() { - Log.d(Constants.TAG, "encryptClicked invoked!"); - - if (mSource.getCurrentView().getId() == R.id.sourceFile) { - mEncryptTarget = Id.target.file; - } else { - mEncryptTarget = Id.target.email; - } - initiateEncryption(); - } - - private void initiateEncryption() { - if (mEncryptTarget == Id.target.file) { - String currentFilename = mFilename.getText().toString(); - if (mInputFilename == null || !mInputFilename.equals(currentFilename)) { - mInputFilename = mFilename.getText().toString(); - } - - mOutputFilename = guessOutputFilename(mInputFilename); - - if (mInputFilename.equals("")) { - Toast.makeText(this, R.string.noFileSelected, Toast.LENGTH_SHORT).show(); - return; - } - - if (!mInputFilename.startsWith("content")) { - File file = new File(mInputFilename); - if (!file.exists() || !file.isFile()) { - Toast.makeText( - this, - getString(R.string.errorMessage, getString(R.string.error_fileNotFound)), - Toast.LENGTH_SHORT).show(); - return; - } - } - } - - // symmetric encryption - if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { - boolean gotPassPhrase = false; - String passPhrase = mPassPhrase.getText().toString(); - String passPhraseAgain = mPassPhraseAgain.getText().toString(); - if (!passPhrase.equals(passPhraseAgain)) { - Toast.makeText(this, R.string.passPhrasesDoNotMatch, Toast.LENGTH_SHORT).show(); - return; - } - - gotPassPhrase = (passPhrase.length() != 0); - if (!gotPassPhrase) { - Toast.makeText(this, R.string.passPhraseMustNotBeEmpty, Toast.LENGTH_SHORT).show(); - return; - } - } else { - boolean encryptIt = (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0); - // for now require at least one form of encryption for files - if (!encryptIt && mEncryptTarget == Id.target.file) { - Toast.makeText(this, R.string.selectEncryptionKey, Toast.LENGTH_SHORT).show(); - return; - } - - if (!encryptIt && mSecretKeyId == 0) { - Toast.makeText(this, R.string.selectEncryptionOrSignatureKey, Toast.LENGTH_SHORT) - .show(); - return; - } - - if (mSecretKeyId != 0 - && PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { - showPassphraseDialog(); - - return; - } - } - - if (mEncryptTarget == Id.target.file) { - showOutputFileDialog(); - } else { - encryptStart(); - } - } - - /** - * Shows passphrase dialog to cache a new passphrase the user enters for using it later for - * encryption - */ - private void showPassphraseDialog() { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - if (mEncryptTarget == Id.target.file) { - showOutputFileDialog(); - } else { - encryptStart(); - } - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - try { - PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( - EncryptActivity.this, messenger, mSecretKeyId); - - passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); - } catch (PGPMain.ApgGeneralException e) { - Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); - // send message to handler to start encryption directly - returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); - } - } - - private void showOutputFileDialog() { - // Message is received after file is selected - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == FileDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - encryptStart(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - mFileDialog = FileDialogFragment.newInstance(messenger, - getString(R.string.title_encryptToFile), - getString(R.string.specifyFileToEncryptTo), mOutputFilename, null, - Id.request.output_filename); - - mFileDialog.show(getSupportFragmentManager(), "fileDialog"); - } - - private void encryptStart() { - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - // fill values for this action - Bundle data = new Bundle(); - - boolean useAsciiArmor = true; - long encryptionKeyIds[] = null; - int compressionId = 0; - boolean signOnly = false; - - if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { - Log.d(Constants.TAG, "Symmetric encryption enabled!"); - String passPhrase = mPassPhrase.getText().toString(); - if (passPhrase.length() == 0) { - passPhrase = null; - } - - data.putString(ApgIntentService.SYMMETRIC_PASSPHRASE, passPhrase); - } else { - encryptionKeyIds = mEncryptionKeyIds; - signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0); - } - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_ENCRYPT_SIGN); - - // choose default settings, target and data bundle by target - if (mStreamAndReturnUri != null) { - // mIntentDataUri is only defined when ACTION_ENCRYPT_STREAM_AND_RETURN is used - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_STREAM); - data.putParcelable(ApgIntentService.PROVIDER_URI, mStreamAndReturnUri); - - } else if (mEncryptTarget == Id.target.file) { - useAsciiArmor = mAsciiArmour.isChecked(); - compressionId = ((Choice) mFileCompression.getSelectedItem()).getId(); - - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_FILE); - - Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" - + mOutputFilename); - - data.putString(ApgIntentService.INPUT_FILE, mInputFilename); - data.putString(ApgIntentService.OUTPUT_FILE, mOutputFilename); - - } else { - useAsciiArmor = true; - compressionId = Preferences.getPreferences(this).getDefaultMessageCompression(); - - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); - - if (mData != null) { - data.putByteArray(ApgIntentService.MESSAGE_BYTES, mData); - } else { - String message = mMessage.getText().toString(); - if (signOnly && !mReturnResult) { - fixBadCharactersForGmail(message); - } - data.putByteArray(ApgIntentService.MESSAGE_BYTES, message.getBytes()); - } - } - - if (mOverrideAsciiArmour) { - useAsciiArmor = mAsciiArmorDemand; - } - - data.putLong(ApgIntentService.SECRET_KEY_ID, mSecretKeyId); - data.putBoolean(ApgIntentService.USE_ASCII_AMOR, useAsciiArmor); - data.putLongArray(ApgIntentService.ENCRYPTION_KEYS_IDS, encryptionKeyIds); - data.putInt(ApgIntentService.COMPRESSION_ID, compressionId); - data.putBoolean(ApgIntentService.GENERATE_SIGNATURE, mGenerateSignature); - data.putBoolean(ApgIntentService.SIGN_ONLY, signOnly); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after encrypting is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_encrypting, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle data = message.getData(); - - String output; - switch (mEncryptTarget) { - case Id.target.clipboard: - output = data.getString(ApgIntentService.RESULT_ENCRYPTED_STRING); - Log.d(Constants.TAG, "output: " + output); - Compatibility.copyToClipboard(EncryptActivity.this, output); - Toast.makeText(EncryptActivity.this, - R.string.encryptionToClipboardSuccessful, Toast.LENGTH_SHORT) - .show(); - break; - - case Id.target.email: - if (mReturnResult) { - Intent intent = new Intent(); - intent.putExtras(data); - setResult(RESULT_OK, intent); - finish(); - return; - } - - output = data.getString(ApgIntentService.RESULT_ENCRYPTED_STRING); - Log.d(Constants.TAG, "output: " + output); - - Intent emailIntent = new Intent(Intent.ACTION_SEND); - emailIntent.setType("text/plain; charset=utf-8"); - emailIntent.putExtra(Intent.EXTRA_TEXT, output); - if (mSubject != null) { - emailIntent.putExtra(Intent.EXTRA_SUBJECT, mSubject); - } - if (mSendTo != null) { - emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { mSendTo }); - } - startActivity(Intent.createChooser(emailIntent, - getString(R.string.title_sendEmail))); - break; - - case Id.target.file: - Toast.makeText(EncryptActivity.this, R.string.encryptionSuccessful, - Toast.LENGTH_SHORT).show(); - - if (mDeleteAfter.isChecked()) { - // Create and show dialog to delete original file - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment - .newInstance(mInputFilename); - deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); - } - break; - - default: - // shouldn't happen - break; - - } - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - /** - * Fixes bad message characters for gmail - * - * @param message - * @return - */ - private String fixBadCharactersForGmail(String message) { - // fix the message a bit, trailing spaces and newlines break stuff, - // because GMail sends as HTML and such things fuck up the - // signature, - // TODO: things like "<" and ">" also fuck up the signature - message = message.replaceAll(" +\n", "\n"); - message = message.replaceAll("\n\n+", "\n\n"); - message = message.replaceFirst("^\n+", ""); - // make sure there'll be exactly one newline at the end - message = message.replaceFirst("\n*$", "\n"); - - return message; - } - - private void initView() { - mSource = (ViewFlipper) findViewById(R.id.source); - mSourceLabel = (TextView) findViewById(R.id.sourceLabel); - mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious); - mSourceNext = (ImageView) findViewById(R.id.sourceNext); - - mSourcePrevious.setClickable(true); - mSourcePrevious.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, - R.anim.push_right_in)); - mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, - R.anim.push_right_out)); - mSource.showPrevious(); - updateSource(); - } - }); - - mSourceNext.setClickable(true); - OnClickListener nextSourceClickListener = new OnClickListener() { - public void onClick(View v) { - mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, - R.anim.push_left_in)); - mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, - R.anim.push_left_out)); - mSource.showNext(); - updateSource(); - } - }; - mSourceNext.setOnClickListener(nextSourceClickListener); - - mSourceLabel.setClickable(true); - mSourceLabel.setOnClickListener(nextSourceClickListener); - - mMode = (ViewFlipper) findViewById(R.id.mode); - mModeLabel = (TextView) findViewById(R.id.modeLabel); - mModePrevious = (ImageView) findViewById(R.id.modePrevious); - mModeNext = (ImageView) findViewById(R.id.modeNext); - - mModePrevious.setClickable(true); - mModePrevious.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, - R.anim.push_right_in)); - mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, - R.anim.push_right_out)); - mMode.showPrevious(); - updateMode(); - } - }); - - OnClickListener nextModeClickListener = new OnClickListener() { - public void onClick(View v) { - mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, - R.anim.push_left_in)); - mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, - R.anim.push_left_out)); - mMode.showNext(); - updateMode(); - } - }; - mModeNext.setOnClickListener(nextModeClickListener); - - mModeLabel.setClickable(true); - mModeLabel.setOnClickListener(nextModeClickListener); - - mMessage = (EditText) findViewById(R.id.message); - mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys); - mSign = (CheckBox) findViewById(R.id.sign); - mMainUserId = (TextView) findViewById(R.id.mainUserId); - mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest); - - mPassPhrase = (EditText) findViewById(R.id.passPhrase); - mPassPhraseAgain = (EditText) findViewById(R.id.passPhraseAgain); - - // measure the height of the source_file view and set the message view's min height to that, - // so it fills mSource fully... bit of a hack. - View tmp = findViewById(R.id.sourceFile); - tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - int height = tmp.getMeasuredHeight(); - mMessage.setMinimumHeight(height); - - mFilename = (EditText) findViewById(R.id.filename); - mBrowse = (ImageButton) findViewById(R.id.btn_browse); - mBrowse.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - FileHelper.openFile(EncryptActivity.this, mFilename.getText().toString(), "*/*", - Id.request.filename); - } - }); - - mFileCompression = (Spinner) findViewById(R.id.fileCompression); - Choice[] choices = new Choice[] { - new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " (" - + getString(R.string.fast) + ")"), - new Choice(Id.choice.compression.zip, "ZIP (" + getString(R.string.fast) + ")"), - new Choice(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.fast) + ")"), - new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow) - + ")"), }; - ArrayAdapter adapter = new ArrayAdapter(this, - android.R.layout.simple_spinner_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mFileCompression.setAdapter(adapter); - - int defaultFileCompression = Preferences.getPreferences(this).getDefaultFileCompression(); - for (int i = 0; i < choices.length; ++i) { - if (choices[i].getId() == defaultFileCompression) { - mFileCompression.setSelection(i); - break; - } - } - - mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption); - - mAsciiArmour = (CheckBox) findViewById(R.id.asciiArmour); - mAsciiArmour.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour()); - - mSelectKeysButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - selectPublicKeys(); - } - }); - - mSign.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - CheckBox checkBox = (CheckBox) v; - if (checkBox.isChecked()) { - selectSecretKey(); - } else { - mSecretKeyId = Id.key.none; - updateView(); - } - } - }); - } - - private void updateView() { - if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) { - mSelectKeysButton.setText(R.string.noKeysSelected); - } else if (mEncryptionKeyIds.length == 1) { - mSelectKeysButton.setText(R.string.oneKeySelected); - } else { - mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " " - + getResources().getString(R.string.nKeysSelected)); - } - - if (mSecretKeyId == Id.key.none) { - mSign.setChecked(false); - mMainUserId.setText(""); - mMainUserIdRest.setText(""); - } else { - String uid = getResources().getString(R.string.unknownUserId); - String uidExtra = ""; - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, - mSecretKeyId); - if (keyRing != null) { - PGPSecretKey key = PGPHelper.getMasterKey(keyRing); - if (key != null) { - String userId = PGPHelper.getMainUserIdSafe(this, key); - String chunks[] = userId.split(" <", 2); - uid = chunks[0]; - if (chunks.length > 1) { - uidExtra = "<" + chunks[1]; - } - } - } - mMainUserId.setText(uid); - mMainUserIdRest.setText(uidExtra); - mSign.setChecked(true); - } - - updateActionBarButtons(); - } - - private void selectPublicKeys() { - Intent intent = new Intent(this, SelectPublicKeyActivity.class); - Vector keyIds = new Vector(); - if (mSecretKeyId != 0) { - keyIds.add(mSecretKeyId); - } - if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) { - for (int i = 0; i < mEncryptionKeyIds.length; ++i) { - keyIds.add(mEncryptionKeyIds[i]); - } - } - long[] initialKeyIds = null; - if (keyIds.size() > 0) { - initialKeyIds = new long[keyIds.size()]; - for (int i = 0; i < keyIds.size(); ++i) { - initialKeyIds[i] = keyIds.get(i); - } - } - intent.putExtra(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds); - startActivityForResult(intent, Id.request.public_keys); - } - - private void selectSecretKey() { - Intent intent = new Intent(this, SelectSecretKeyActivity.class); - startActivityForResult(intent, Id.request.secret_keys); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = FileHelper.getPath(this, data.getData()); - Log.d(Constants.TAG, "path=" + path); - - mFilename.setText(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!"); - } - } - return; - } - - case Id.request.output_filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = data.getData().getPath(); - Log.d(Constants.TAG, "path=" + path); - - mFileDialog.setFilename(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!"); - } - } - return; - } - - case Id.request.public_keys: { - if (resultCode == RESULT_OK) { - Bundle bundle = data.getExtras(); - mEncryptionKeyIds = bundle - .getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS); - } - updateView(); - break; - } - - case Id.request.secret_keys: { - if (resultCode == RESULT_OK) { - Bundle bundle = data.getExtras(); - mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); - } else { - mSecretKeyId = Id.key.none; - } - updateView(); - break; - } - - default: { - break; - } - } - - super.onActivityResult(requestCode, resultCode, data); - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/HelpActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/HelpActivity.java deleted file mode 100644 index bd9d68ce7..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/HelpActivity.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.R; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.app.FragmentTransaction; -import android.widget.TextView; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.ActionBar.Tab; -import com.actionbarsherlock.view.MenuItem; - -import java.util.ArrayList; - -import android.content.Context; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; - -import com.actionbarsherlock.app.SherlockFragmentActivity; - -public class HelpActivity extends SherlockFragmentActivity { - ViewPager mViewPager; - TabsAdapter mTabsAdapter; - TextView tabCenter; - TextView tabText; - - /** - * Menu Items - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mViewPager = new ViewPager(this); - mViewPager.setId(R.id.pager); - - setContentView(mViewPager); - ActionBar bar = getSupportActionBar(); - bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - bar.setDisplayShowTitleEnabled(true); - bar.setDisplayHomeAsUpEnabled(true); - - mTabsAdapter = new TabsAdapter(this, mViewPager); - - Bundle startBundle = new Bundle(); - startBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_start); - mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_start)), - HelpFragmentHtml.class, startBundle); - - Bundle changelogBundle = new Bundle(); - changelogBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_changelog); - mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_changelog)), - HelpFragmentHtml.class, changelogBundle); - - mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_about)), - HelpFragmentAbout.class, null); - } - - public static class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, - ViewPager.OnPageChangeListener { - private final Context mContext; - private final ActionBar mActionBar; - private final ViewPager mViewPager; - private final ArrayList mTabs = new ArrayList(); - - static final class TabInfo { - private final Class clss; - private final Bundle args; - - TabInfo(Class _class, Bundle _args) { - clss = _class; - args = _args; - } - } - - public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) { - super(activity.getSupportFragmentManager()); - mContext = activity; - mActionBar = activity.getSupportActionBar(); - mViewPager = pager; - mViewPager.setAdapter(this); - mViewPager.setOnPageChangeListener(this); - } - - public void addTab(ActionBar.Tab tab, Class clss, Bundle args) { - TabInfo info = new TabInfo(clss, args); - tab.setTag(info); - tab.setTabListener(this); - mTabs.add(info); - mActionBar.addTab(tab); - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return mTabs.size(); - } - - @Override - public Fragment getItem(int position) { - TabInfo info = mTabs.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), info.args); - } - - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - } - - public void onPageSelected(int position) { - mActionBar.setSelectedNavigationItem(position); - } - - public void onPageScrollStateChanged(int state) { - } - - public void onTabSelected(Tab tab, FragmentTransaction ft) { - Object tag = tab.getTag(); - for (int i = 0; i < mTabs.size(); i++) { - if (mTabs.get(i) == tag) { - mViewPager.setCurrentItem(i); - } - } - } - - public void onTabUnselected(Tab tab, FragmentTransaction ft) { - } - - public void onTabReselected(Tab tab, FragmentTransaction ft) { - } - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java b/org_apg/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java deleted file mode 100644 index 1b80db66b..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.io.IOException; -import java.io.InputStream; - -import net.nightwhistler.htmlspanner.HtmlSpanner; -import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.OtherHelper; - -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Bundle; -import android.text.method.LinkMovementMethod; -import org.thialfihar.android.apg.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.actionbarsherlock.app.SherlockFragment; - -public class HelpFragmentAbout extends SherlockFragment { - - /** - * Workaround for Android Bug. See - * http://stackoverflow.com/questions/8748064/starting-activity-from - * -fragment-causes-nullpointerexception - */ - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - setUserVisibleHint(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.help_fragment_about, container, false); - - // load html from html file from /res/raw - InputStream inputStreamText = OtherHelper.getInputStreamFromResource(this.getActivity(), - R.raw.help_about); - - TextView versionText = (TextView) view.findViewById(R.id.help_about_version); - versionText.setText(getString(R.string.help_about_version) + " " + getVersion()); - - JellyBeanSpanFixTextView aboutTextView = (JellyBeanSpanFixTextView) view - .findViewById(R.id.help_about_text); - - // load html into textview - HtmlSpanner htmlSpanner = new HtmlSpanner(); - htmlSpanner.setStripExtraWhiteSpace(true); - try { - aboutTextView.setText(htmlSpanner.fromHtml(inputStreamText)); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while reading raw resources as stream", e); - } - - // make links work - aboutTextView.setMovementMethod(LinkMovementMethod.getInstance()); - - // no flickering when clicking textview for Android < 4 - aboutTextView.setTextColor(getResources().getColor(android.R.color.black)); - - return view; - } - - /** - * Get the current package version. - * - * @return The current version. - */ - private String getVersion() { - String result = ""; - try { - PackageManager manager = getActivity().getPackageManager(); - PackageInfo info = manager.getPackageInfo(getActivity().getPackageName(), 0); - - result = String.format("%s (%s)", info.versionName, info.versionCode); - } catch (NameNotFoundException e) { - Log.w(Constants.TAG, "Unable to get application version: " + e.getMessage()); - result = "Unable to get application version."; - } - - return result; - } - -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java b/org_apg/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java deleted file mode 100644 index e46e7b112..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.io.IOException; -import java.io.InputStream; - -import net.nightwhistler.htmlspanner.HtmlSpanner; -import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.util.Log; - -import android.app.Activity; -import android.os.Bundle; -import android.text.method.LinkMovementMethod; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ScrollView; - -import com.actionbarsherlock.app.SherlockFragment; - -public class HelpFragmentHtml extends SherlockFragment { - private Activity mActivity; - - private int htmlFile; - - public static final String ARG_HTML_FILE = "htmlFile"; - - /** - * Create a new instance of HelpFragmentHtml, providing "htmlFile" as an argument. - */ - static HelpFragmentHtml newInstance(int htmlFile) { - HelpFragmentHtml f = new HelpFragmentHtml(); - - // Supply html raw file input as an argument. - Bundle args = new Bundle(); - args.putInt(ARG_HTML_FILE, htmlFile); - f.setArguments(args); - - return f; - } - - /** - * Workaround for Android Bug. See - * http://stackoverflow.com/questions/8748064/starting-activity-from - * -fragment-causes-nullpointerexception - */ - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - setUserVisibleHint(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - htmlFile = getArguments().getInt(ARG_HTML_FILE); - - // load html from html file from /res/raw - InputStream inputStreamText = OtherHelper.getInputStreamFromResource(this.getActivity(), - htmlFile); - - mActivity = getActivity(); - - ScrollView scroller = new ScrollView(mActivity); - JellyBeanSpanFixTextView text = new JellyBeanSpanFixTextView(mActivity); - - // padding - int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, mActivity - .getResources().getDisplayMetrics()); - text.setPadding(padding, padding, padding, 0); - - scroller.addView(text); - - // load html into textview - HtmlSpanner htmlSpanner = new HtmlSpanner(); - htmlSpanner.setStripExtraWhiteSpace(true); - try { - text.setText(htmlSpanner.fromHtml(inputStreamText)); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while reading raw resources as stream", e); - } - - // make links work - text.setMovementMethod(LinkMovementMethod.getInstance()); - - // no flickering when clicking textview for Android < 4 - text.setTextColor(getResources().getColor(android.R.color.black)); - - return scroller; - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/ImportFromQRCodeActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/ImportFromQRCodeActivity.java deleted file mode 100644 index aac1fd15b..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/ImportFromQRCodeActivity.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2011 Senecaso - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.R; - -import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; - -import org.thialfihar.android.apg.util.Log; - -import android.view.View; -import android.widget.TextView; -import android.widget.Toast; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.MenuItem; -import com.google.zxing.integration.android.IntentIntegrator; -import com.google.zxing.integration.android.IntentResult; - -public class ImportFromQRCodeActivity extends SherlockFragmentActivity { - - // Not used in sourcode, but listed in AndroidManifest! - public static final String IMPORT_FROM_QR_CODE = Constants.INTENT_PREFIX - + "IMPORT_FROM_QR_CODE"; - - // public static final String EXTRA_KEY_ID = "keyId"; - - private TextView mContentView; - - private String mScannedContent; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.import_from_qr_code); - mContentView = (TextView) findViewById(R.id.import_from_qr_code_content); - - // set actionbar without home button if called from another app - OtherHelper.setActionBarBackButton(this); - - // start scanning - new IntentIntegrator(this).initiateScan(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - default: { - return super.onOptionsItemSelected(item); - } - } - } - - // private void importAndSignOld(final long keyId, final String expectedFingerprint) { - // if (expectedFingerprint != null && expectedFingerprint.length() > 0) { - // - // Thread t = new Thread() { - // @Override - // public void run() { - // try { - // // TODO: display some sort of spinner here while the user waits - // - // // TODO: there should be only 1 - // HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]); - // String encodedKey = server.get(keyId); - // - // PGPKeyRing keyring = PGPHelper.decodeKeyRing(new ByteArrayInputStream( - // encodedKey.getBytes())); - // if (keyring != null && keyring instanceof PGPPublicKeyRing) { - // PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring; - // - // // make sure the fingerprints match before we cache this thing - // String actualFingerprint = PGPHelper.convertToHex(publicKeyRing - // .getPublicKey().getFingerprint()); - // if (expectedFingerprint.equals(actualFingerprint)) { - // // store the signed key in our local cache - // int retval = PGPMain.storeKeyRingInCache(publicKeyRing); - // if (retval != Id.return_value.ok - // && retval != Id.return_value.updated) { - // status.putString(EXTRA_ERROR, - // "Failed to store signed key in local cache"); - // } else { - // Intent intent = new Intent(ImportFromQRCodeActivity.this, - // SignKeyActivity.class); - // intent.putExtra(EXTRA_KEY_ID, keyId); - // startActivityForResult(intent, Id.request.sign_key); - // } - // } else { - // status.putString( - // EXTRA_ERROR, - // "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key."); - // } - // } - // } catch (QueryException e) { - // Log.e(TAG, "Failed to query KeyServer", e); - // status.putString(EXTRA_ERROR, "Failed to query KeyServer"); - // status.putInt(Constants.extras.STATUS, Id.message.done); - // } catch (IOException e) { - // Log.e(TAG, "Failed to query KeyServer", e); - // status.putString(EXTRA_ERROR, "Failed to query KeyServer"); - // status.putInt(Constants.extras.STATUS, Id.message.done); - // } - // } - // }; - // - // t.setName("KeyExchange Download Thread"); - // t.setDaemon(true); - // t.start(); - // } - // } - - public void scanAgainOnClick(View view) { - new IntentIntegrator(this).initiateScan(); - } - - public void finishOnClick(View view) { - finish(); - } - - public void importOnClick(View view) { - Log.d(Constants.TAG, "import key started"); - - if (mScannedContent != null) { - // Send all information needed to service to import key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_IMPORT_KEY); - - // fill values for this action - Bundle data = new Bundle(); - - data.putInt(ApgIntentService.IMPORT_KEY_TYPE, Id.type.public_key); - - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); - data.putByteArray(ApgIntentService.IMPORT_BYTES, mScannedContent.getBytes()); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after importing is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_importing, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - int added = returnData.getInt(ApgIntentService.RESULT_IMPORT_ADDED); - int updated = returnData.getInt(ApgIntentService.RESULT_IMPORT_UPDATED); - int bad = returnData.getInt(ApgIntentService.RESULT_IMPORT_BAD); - String toastMessage; - if (added > 0 && updated > 0) { - toastMessage = getString(R.string.keysAddedAndUpdated, added, updated); - } else if (added > 0) { - toastMessage = getString(R.string.keysAdded, added); - } else if (updated > 0) { - toastMessage = getString(R.string.keysUpdated, updated); - } else { - toastMessage = getString(R.string.noKeysAddedOrUpdated); - } - Toast.makeText(ImportFromQRCodeActivity.this, toastMessage, - Toast.LENGTH_SHORT).show(); - if (bad > 0) { - AlertDialog.Builder alert = new AlertDialog.Builder( - ImportFromQRCodeActivity.this); - - alert.setIcon(android.R.drawable.ic_dialog_alert); - alert.setTitle(R.string.warning); - alert.setMessage(ImportFromQRCodeActivity.this.getString( - R.string.badKeysEncountered, bad)); - - alert.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - alert.setCancelable(true); - alert.create().show(); - } - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - } - - public void signAndUploadOnClick(View view) { - // first, import! - importOnClick(view); - - // TODO: implement sign and upload! - Toast.makeText(ImportFromQRCodeActivity.this, "Not implemented right now!", - Toast.LENGTH_SHORT).show(); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case IntentIntegrator.REQUEST_CODE: { - IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, - data); - if (scanResult != null && scanResult.getFormatName() != null) { - - mScannedContent = scanResult.getContents(); - - mContentView.setText(mScannedContent); - // String[] bits = scanResult.getContents().split(","); - // if (bits.length != 2) { - // return; // dont know how to handle this. Not a valid code - // } - // - // long keyId = Long.parseLong(bits[0]); - // String expectedFingerprint = bits[1]; - - // importAndSign(keyId, expectedFingerprint); - } - - break; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - } - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/KeyListActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/KeyListActivity.java deleted file mode 100644 index 3adae94a9..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/KeyListActivity.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.DeleteKeyDialogFragment; -import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; -import org.thialfihar.android.apg.util.Log; - -import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.app.SearchManager; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.widget.Toast; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -public class KeyListActivity extends SherlockFragmentActivity { - - public static final String ACTION_IMPORT = Constants.INTENT_PREFIX + "IMPORT"; - - public static final String EXTRA_TEXT = "text"; - - // protected View mFilterLayout; - // protected Button mClearFilterButton; - // protected TextView mFilterInfo; - - protected String mImportFilename = Constants.path.APP_DIR + "/"; - protected String mExportFilename = Constants.path.APP_DIR + "/"; - - protected String mImportData; - protected boolean mDeleteAfterImport = false; - - protected int mKeyType; - - FileDialogFragment mFileDialog; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - - handleIntent(getIntent()); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - handleIntent(intent); - } - - protected void handleIntent(Intent intent) { - String searchString = null; - if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - searchString = intent.getStringExtra(SearchManager.QUERY); - if (searchString != null && searchString.trim().length() == 0) { - searchString = null; - } - } - - // if (searchString == null) { - // mFilterLayout.setVisibility(View.GONE); - // } else { - // mFilterLayout.setVisibility(View.VISIBLE); - // mFilterInfo.setText(getString(R.string.filterInfo, searchString)); - // } - // - // if (mListAdapter != null) { - // mListAdapter.cleanup(); - // } - // mListAdapter = new KeyListAdapter(this, searchString); - // mList.setAdapter(mListAdapter); - - // Get intent, action - // Intent intent = getIntent(); - String action = intent.getAction(); - - if (Intent.ACTION_VIEW.equals(action)) { - // Android's Action when opening file associated to APG (see AndroidManifest.xml) - - handleActionImport(intent); - } else if (ACTION_IMPORT.equals(action)) { - // APG's own Actions - - handleActionImport(intent); - } - } - - /** - * Handles import action - * - * @param intent - */ - private void handleActionImport(Intent intent) { - if ("file".equals(intent.getScheme()) && intent.getDataString() != null) { - mImportFilename = intent.getData().getPath(); - } else { - mImportData = intent.getStringExtra(EXTRA_TEXT); - } - importKeys(); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = data.getData().getPath(); - Log.d(Constants.TAG, "path=" + path); - - // set filename used in export/import dialogs - mFileDialog.setFilename(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!", e); - } - } - return; - } - - default: { - break; - } - } - super.onActivityResult(requestCode, resultCode, data); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(3, Id.menu.option.search, 0, R.string.menu_search) - .setIcon(R.drawable.ic_menu_search).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - menu.add(0, Id.menu.option.import_keys, 2, R.string.menu_importKeys).setShowAsAction( - MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(0, Id.menu.option.export_keys, 3, R.string.menu_exportKeys).setShowAsAction( - MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.import_keys: { - showImportKeysDialog(); - return true; - } - - case Id.menu.option.export_keys: { - showExportKeysDialog(-1); - return true; - } - - // case Id.menu.option.search: - // startSearch("", false, null, false); - // return true; - - default: { - return super.onOptionsItemSelected(item); - } - } - } - - /** - * Show to dialog from where to import keys - */ - public void showImportKeysDialog() { - // Message is received after file is selected - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == FileDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - mImportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - - mDeleteAfterImport = data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED); - importKeys(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - mFileDialog = FileDialogFragment.newInstance(messenger, - getString(R.string.title_importKeys), getString(R.string.specifyFileToImportFrom), - mImportFilename, null, Id.request.filename); - - mFileDialog.show(getSupportFragmentManager(), "fileDialog"); - } - - /** - * Show dialog where to export keys - * - * @param keyRingId - * if -1 export all keys - */ - public void showExportKeysDialog(final long keyRingId) { - String title = null; - if (keyRingId != -1) { - // single key export - title = getString(R.string.title_exportKey); - } else { - title = getString(R.string.title_exportKeys); - } - - String message = null; - if (mKeyType == Id.type.public_key) { - message = getString(R.string.specifyFileToExportTo); - } else { - message = getString(R.string.specifyFileToExportSecretKeysTo); - } - - // Message is received after file is selected - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == FileDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - - exportKeys(keyRingId); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - mFileDialog = FileDialogFragment.newInstance(messenger, title, message, mExportFilename, - null, Id.request.filename); - - mFileDialog.show(getSupportFragmentManager(), "fileDialog"); - } - - /** - * Show dialog to delete key - * - * @param keyRingId - */ - public void showDeleteKeyDialog(long keyRingId) { - // Message is received after key is deleted - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { - // refreshList(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - keyRingId, mKeyType); - - deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog"); - } - - /** - * Import keys with mImportData - */ - public void importKeys() { - Log.d(Constants.TAG, "importKeys started"); - - // Send all information needed to service to import key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_IMPORT_KEY); - - // fill values for this action - Bundle data = new Bundle(); - - data.putInt(ApgIntentService.IMPORT_KEY_TYPE, mKeyType); - - if (mImportData != null) { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); - data.putByteArray(ApgIntentService.IMPORT_BYTES, mImportData.getBytes()); - } else { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_FILE); - data.putString(ApgIntentService.IMPORT_FILENAME, mImportFilename); - } - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after importing is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_importing, - ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - int added = returnData.getInt(ApgIntentService.RESULT_IMPORT_ADDED); - int updated = returnData.getInt(ApgIntentService.RESULT_IMPORT_UPDATED); - int bad = returnData.getInt(ApgIntentService.RESULT_IMPORT_BAD); - String toastMessage; - if (added > 0 && updated > 0) { - toastMessage = getString(R.string.keysAddedAndUpdated, added, updated); - } else if (added > 0) { - toastMessage = getString(R.string.keysAdded, added); - } else if (updated > 0) { - toastMessage = getString(R.string.keysUpdated, updated); - } else { - toastMessage = getString(R.string.noKeysAddedOrUpdated); - } - Toast.makeText(KeyListActivity.this, toastMessage, Toast.LENGTH_SHORT).show(); - if (bad > 0) { - AlertDialog.Builder alert = new AlertDialog.Builder(KeyListActivity.this); - - alert.setIcon(android.R.drawable.ic_dialog_alert); - alert.setTitle(R.string.warning); - alert.setMessage(KeyListActivity.this.getString( - R.string.badKeysEncountered, bad)); - - alert.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - alert.setCancelable(true); - alert.create().show(); - } else if (mDeleteAfterImport) { - // everything went well, so now delete, if that was turned on - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment - .newInstance(mImportFilename); - deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); - } - // refreshList(); - - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - /** - * Export keys - * - * @param keyRingId - * if -1 export all keys - */ - public void exportKeys(long keyRingId) { - Log.d(Constants.TAG, "exportKeys started"); - - // Send all information needed to service to export key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_EXPORT_KEY); - - // fill values for this action - Bundle data = new Bundle(); - - data.putString(ApgIntentService.EXPORT_FILENAME, mExportFilename); - data.putInt(ApgIntentService.EXPORT_KEY_TYPE, mKeyType); - - if (keyRingId == -1) { - data.putBoolean(ApgIntentService.EXPORT_ALL, true); - } else { - data.putLong(ApgIntentService.EXPORT_KEY_RING_ID, keyRingId); - } - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after exporting is done in ApgService - ApgIntentServiceHandler exportHandler = new ApgIntentServiceHandler(this, R.string.progress_exporting, - ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - int exported = returnData.getInt(ApgIntentService.RESULT_EXPORT); - String toastMessage; - if (exported == 1) { - toastMessage = getString(R.string.keyExported); - } else if (exported > 0) { - toastMessage = getString(R.string.keysExported, exported); - } else { - toastMessage = getString(R.string.noKeysExported); - } - Toast.makeText(KeyListActivity.this, toastMessage, Toast.LENGTH_SHORT).show(); - - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(exportHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - exportHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/KeyListFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/KeyListFragment.java deleted file mode 100644 index 613f32ae9..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/KeyListFragment.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.ui.widget.ExpandableListFragment; - -import android.os.Bundle; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.View; -import android.widget.ExpandableListView; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; - -public class KeyListFragment extends ExpandableListFragment { - protected KeyListActivity mKeyListActivity; - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mKeyListActivity = (KeyListActivity) getActivity(); - - // register long press context menu - registerForContextMenu(getListView()); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(getString(R.string.listEmpty)); - } - - /** - * Context Menu on Long Click - */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - menu.add(0, Id.menu.export, 0, R.string.menu_exportKey); - menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey); - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem item) { - ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); - - // expInfo.id would also return row id of childs, but we always want to get the row id of - // the group item, thus we are using the following way - int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); - long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); - - switch (item.getItemId()) { - case Id.menu.export: - mKeyListActivity.showExportKeysDialog(keyRingRowId); - return true; - - case Id.menu.delete: - mKeyListActivity.showDeleteKeyDialog(keyRingRowId); - return true; - - default: - return super.onContextItemSelected(item); - - } - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java deleted file mode 100644 index 73bb3790a..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; - -public class KeyListPublicActivity extends KeyListActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mKeyType = Id.type.public_key; - - setContentView(R.layout.key_list_public_activity); - - mExportFilename = Constants.path.APP_DIR + "/pubexport.asc"; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(1, Id.menu.option.key_server, 2, R.string.menu_keyServer) - .setIcon(R.drawable.ic_menu_search_list) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(1, Id.menu.option.scanQRCode, 1, R.string.menu_scanQRCode) - .setIcon(R.drawable.ic_menu_scan_qrcode) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case Id.menu.option.key_server: { - startActivityForResult(new Intent(this, KeyServerQueryActivity.class), 0); - - return true; - } - case Id.menu.option.scanQRCode: { - Intent intent = new Intent(this, ImportFromQRCodeActivity.class); - intent.setAction(ImportFromQRCodeActivity.IMPORT_FROM_QR_CODE); - startActivityForResult(intent, Id.request.import_from_qr_code); - - return true; - } - - default: { - return super.onOptionsItemSelected(item); - } - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.look_up_key_id: { - if (resultCode == RESULT_CANCELED || data == null - || data.getStringExtra(KeyServerQueryActivity.RESULT_EXTRA_TEXT) == null) { - return; - } - - Intent intent = new Intent(this, KeyListPublicActivity.class); - intent.setAction(KeyListPublicActivity.ACTION_IMPORT); - intent.putExtra(KeyListPublicActivity.EXTRA_TEXT, - data.getStringExtra(KeyListActivity.EXTRA_TEXT)); - handleIntent(intent); - break; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - break; - } - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java deleted file mode 100644 index e1a93602c..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.ui.widget.KeyListAdapter; - -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.view.ContextMenu; -import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; -import android.widget.ExpandableListView; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; - -public class KeyListPublicFragment extends KeyListFragment implements - LoaderManager.LoaderCallbacks { - - private KeyListPublicActivity mKeyListPublicActivity; - - private KeyListAdapter mAdapter; - - /** - * Define Adapter and Loader on create of Activity - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mKeyListPublicActivity = (KeyListPublicActivity) getActivity(); - - mAdapter = new KeyListAdapter(mKeyListPublicActivity, null, Id.type.public_key); - setListAdapter(mAdapter); - - // Start out with a progress indicator. - setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - // id is -1 as the child cursors are numbered 0,...,n - getLoaderManager().initLoader(-1, null, this); - } - - /** - * Context Menu on Long Click - */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - menu.add(0, Id.menu.update, 1, R.string.menu_updateKey); - menu.add(0, Id.menu.exportToServer, 1, R.string.menu_exportKeyToServer); - menu.add(0, Id.menu.signKey, 1, R.string.menu_signKey); - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem item) { - ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); - - // expInfo.id would also return row id of childs, but we always want to get the row id of - // the group item, thus we are using the following way - int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); - long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); - - switch (item.getItemId()) { - case Id.menu.update: - long updateKeyId = 0; - PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(mKeyListActivity, - keyRingRowId); - if (updateKeyRing != null) { - updateKeyId = PGPHelper.getMasterKey(updateKeyRing).getKeyID(); - } - if (updateKeyId == 0) { - // this shouldn't happen - return true; - } - - Intent queryIntent = new Intent(mKeyListActivity, KeyServerQueryActivity.class); - queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN); - queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId); - - // TODO: lookup?? - startActivityForResult(queryIntent, Id.request.look_up_key_id); - - return true; - - case Id.menu.exportToServer: - Intent uploadIntent = new Intent(mKeyListActivity, KeyServerUploadActivity.class); - uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER); - uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, keyRingRowId); - startActivityForResult(uploadIntent, Id.request.export_to_server); - - return true; - - case Id.menu.signKey: - long keyId = 0; - PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(mKeyListActivity, - keyRingRowId); - if (signKeyRing != null) { - keyId = PGPHelper.getMasterKey(signKeyRing).getKeyID(); - } - if (keyId == 0) { - // this shouldn't happen - return true; - } - - Intent signIntent = new Intent(mKeyListActivity, SignKeyActivity.class); - signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId); - startActivity(signIntent); - - return true; - - default: - return super.onContextItemSelected(item); - - } - } - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, - UserIds.USER_ID }; - - static final String SORT_ORDER = UserIds.USER_ID + " ASC"; - - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.setGroupCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - } - - @Override - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.setGroupCursor(null); - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java deleted file mode 100644 index 97abd2296..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; -import org.thialfihar.android.apg.util.Log; - -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; - -public class KeyListSecretActivity extends KeyListActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mKeyType = Id.type.secret_key; - - setContentView(R.layout.key_list_secret_activity); - - mExportFilename = Constants.path.APP_DIR + "/secexport.asc"; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(1, Id.menu.option.create, 1, R.string.menu_createKey).setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case Id.menu.option.create: { - createKey(); - return true; - } - - default: { - return super.onOptionsItemSelected(item); - } - } - } - - public void checkPassPhraseAndEdit(long keyId) { - String passPhrase = PassphraseCacheService.getCachedPassphrase(this, keyId); - if (passPhrase == null) { - showPassphraseDialog(keyId); - } else { - PGPMain.setEditPassPhrase(passPhrase); - editKey(keyId); - } - } - - private void showPassphraseDialog(final long secretKeyId) { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - String passPhrase = PassphraseCacheService.getCachedPassphrase( - KeyListSecretActivity.this, secretKeyId); - PGPMain.setEditPassPhrase(passPhrase); - editKey(secretKeyId); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - try { - PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( - KeyListSecretActivity.this, messenger, secretKeyId); - - passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); - } catch (PGPMain.ApgGeneralException e) { - Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); - // send message to handler to start encryption directly - returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); - } - } - - private void createKey() { - PGPMain.setEditPassPhrase(""); - Intent intent = new Intent(EditKeyActivity.ACTION_CREATE_KEY); - startActivityForResult(intent, 0); - } - - private void editKey(long keyId) { - Intent intent = new Intent(EditKeyActivity.ACTION_EDIT_KEY); - intent.putExtra(EditKeyActivity.EXTRA_KEY_ID, keyId); - startActivityForResult(intent, 0); - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java deleted file mode 100644 index 12a0bb131..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.ui.widget.KeyListAdapter; - -import com.google.zxing.integration.android.IntentIntegrator; - -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.view.ContextMenu; -import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; -import android.widget.ExpandableListView; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; - -public class KeyListSecretFragment extends KeyListFragment implements - LoaderManager.LoaderCallbacks { - - private KeyListSecretActivity mKeyListSecretActivity; - - private KeyListAdapter mAdapter; - - /** - * Define Adapter and Loader on create of Activity - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mKeyListSecretActivity = (KeyListSecretActivity) getActivity(); - - mAdapter = new KeyListAdapter(mKeyListSecretActivity, null, Id.type.secret_key); - setListAdapter(mAdapter); - - // Start out with a progress indicator. - setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - // id is -1 as the child cursors are numbered 0,...,n - getLoaderManager().initLoader(-1, null, this); - } - - /** - * Context Menu on Long Click - */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - - menu.add(0, Id.menu.edit, 0, R.string.menu_editKey); - menu.add(0, Id.menu.share_qr_code, 2, R.string.menu_share); - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem item) { - ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); - - // expInfo.id would also return row id of childs, but we always want to get the row id of - // the group item, thus we are using the following way - int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); - long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); - - switch (item.getItemId()) { - case Id.menu.edit: - // TODO: do it better directly with keyRingRowId? - long masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListSecretActivity, - keyRingRowId); - - mKeyListSecretActivity.checkPassPhraseAndEdit(masterKeyId); - return true; - - case Id.menu.share_qr_code: - // TODO: do it better directly with keyRingRowId? - long masterKeyId2 = ProviderHelper.getSecretMasterKeyId(mKeyListSecretActivity, - keyRingRowId); - - String msg = PGPHelper.getPubkeyAsArmoredString(mKeyListSecretActivity, masterKeyId2); - - new IntentIntegrator(mKeyListSecretActivity).shareText(msg); - return true; - - default: - return super.onContextItemSelected(item); - - } - } - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, - UserIds.USER_ID }; - - static final String SORT_ORDER = UserIds.USER_ID + " ASC"; - - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildSecretKeyRingsUri(); - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.setGroupCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - } - - @Override - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.setGroupCursor(null); - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java deleted file mode 100644 index 7cd54bf32..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.util.ArrayList; -import java.util.List; - -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.util.Log; -import org.thialfihar.android.apg.util.KeyServer.KeyInfo; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.MenuItem; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.BaseAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.LinearLayout.LayoutParams; -import android.widget.ListView; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -public class KeyServerQueryActivity extends SherlockFragmentActivity { - - // possible intent actions for this activity - public static final String ACTION_LOOK_UP_KEY_ID = Constants.INTENT_PREFIX + "LOOK_UP_KEY_ID"; - public static final String ACTION_LOOK_UP_KEY_ID_AND_RETURN = Constants.INTENT_PREFIX - + "LOOK_UP_KEY_ID_AND_RETURN"; - - public static final String EXTRA_KEY_ID = "keyId"; - - public static final String RESULT_EXTRA_TEXT = "text"; - - private ListView mList; - private EditText mQuery; - private Button mSearch; - private KeyInfoListAdapter mAdapter; - private Spinner mKeyServer; - - private int mQueryType; - private String mQueryString; - private long mQueryId; - private volatile List mSearchResult; - private volatile String mKeyData; - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, KeyListPublicActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - default: - break; - - } - return false; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.key_server_query_layout); - - mQuery = (EditText) findViewById(R.id.query); - mSearch = (Button) findViewById(R.id.btn_search); - mList = (ListView) findViewById(R.id.list); - mAdapter = new KeyInfoListAdapter(this); - mList.setAdapter(mAdapter); - - mKeyServer = (Spinner) findViewById(R.id.keyServer); - ArrayAdapter adapter = new ArrayAdapter(this, - android.R.layout.simple_spinner_item, Preferences.getPreferences(this) - .getKeyServers()); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mKeyServer.setAdapter(adapter); - if (adapter.getCount() > 0) { - mKeyServer.setSelection(0); - } else { - mSearch.setEnabled(false); - } - - mList.setOnItemClickListener(new OnItemClickListener() { - public void onItemClick(AdapterView adapter, View view, int position, long keyId) { - get(keyId); - } - }); - - mSearch.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - String query = mQuery.getText().toString(); - search(query); - } - }); - - Intent intent = getIntent(); - String action = intent.getAction(); - if (ACTION_LOOK_UP_KEY_ID.equals(action) || ACTION_LOOK_UP_KEY_ID_AND_RETURN.equals(action)) { - long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0); - if (keyId != 0) { - String query = "0x" + PGPHelper.keyToHex(keyId); - mQuery.setText(query); - search(query); - } - } - } - - private void search(String query) { - mQueryType = Id.keyserver.search; - mQueryString = query; - mAdapter.setKeys(new ArrayList()); - - start(); - } - - private void get(long keyId) { - mQueryType = Id.keyserver.get; - mQueryId = keyId; - - start(); - } - - private void start() { - Log.d(Constants.TAG, "start search with service"); - - // Send all information needed to service to query keys in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_QUERY_KEY); - - // fill values for this action - Bundle data = new Bundle(); - - String server = (String) mKeyServer.getSelectedItem(); - data.putString(ApgIntentService.QUERY_KEY_SERVER, server); - - data.putInt(ApgIntentService.QUERY_KEY_TYPE, mQueryType); - - if (mQueryType == Id.keyserver.search) { - data.putString(ApgIntentService.QUERY_KEY_STRING, mQueryString); - } else if (mQueryType == Id.keyserver.get) { - data.putLong(ApgIntentService.QUERY_KEY_ID, mQueryId); - } - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after querying is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_querying, - ProgressDialog.STYLE_SPINNER) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - if (mQueryType == Id.keyserver.search) { - mSearchResult = returnData - .getParcelableArrayList(ApgIntentService.RESULT_QUERY_KEY_SEARCH_RESULT); - } else if (mQueryType == Id.keyserver.get) { - mKeyData = returnData.getString(ApgIntentService.RESULT_QUERY_KEY_KEY_DATA); - } - - // TODO: IMPROVE CODE!!! some global variables can be avoided!!! - if (mQueryType == Id.keyserver.search) { - if (mSearchResult != null) { - Toast.makeText(KeyServerQueryActivity.this, - getString(R.string.keysFound, mSearchResult.size()), - Toast.LENGTH_SHORT).show(); - mAdapter.setKeys(mSearchResult); - } - } else if (mQueryType == Id.keyserver.get) { - Intent orgIntent = getIntent(); - if (ACTION_LOOK_UP_KEY_ID_AND_RETURN.equals(orgIntent.getAction())) { - if (mKeyData != null) { - Intent intent = new Intent(); - intent.putExtra(RESULT_EXTRA_TEXT, mKeyData); - setResult(RESULT_OK, intent); - } else { - setResult(RESULT_CANCELED); - } - finish(); - } else { - if (mKeyData != null) { - Intent intent = new Intent(KeyServerQueryActivity.this, - KeyListPublicActivity.class); - intent.setAction(KeyListActivity.ACTION_IMPORT); - intent.putExtra(KeyListActivity.EXTRA_TEXT, mKeyData); - startActivity(intent); - } - } - } - - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - public class KeyInfoListAdapter extends BaseAdapter { - protected LayoutInflater mInflater; - protected Activity mActivity; - protected List mKeys; - - public KeyInfoListAdapter(Activity activity) { - mActivity = activity; - mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mKeys = new ArrayList(); - } - - public void setKeys(List keys) { - mKeys = keys; - notifyDataSetChanged(); - } - - @Override - public boolean hasStableIds() { - return true; - } - - public int getCount() { - return mKeys.size(); - } - - public Object getItem(int position) { - return mKeys.get(position); - } - - public long getItemId(int position) { - return mKeys.get(position).keyId; - } - - public View getView(int position, View convertView, ViewGroup parent) { - KeyInfo keyInfo = mKeys.get(position); - - View view = mInflater.inflate(R.layout.key_server_query_result_item, null); - - 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 algorithm = (TextView) view.findViewById(R.id.algorithm); - algorithm.setText(""); - TextView status = (TextView) view.findViewById(R.id.status); - status.setText(""); - - String userId = keyInfo.userIds.get(0); - if (userId != null) { - String chunks[] = userId.split(" <", 2); - userId = chunks[0]; - if (chunks.length > 1) { - mainUserIdRest.setText("<" + chunks[1]); - } - mainUserId.setText(userId); - } - - keyId.setText(PGPHelper.getSmallFingerPrint(keyInfo.keyId)); - - if (mainUserIdRest.getText().length() == 0) { - mainUserIdRest.setVisibility(View.GONE); - } - - algorithm.setText("" + keyInfo.size + "/" + keyInfo.algorithm); - - if (keyInfo.revoked != null) { - status.setText("revoked"); - } else { - status.setVisibility(View.GONE); - } - - LinearLayout ll = (LinearLayout) view.findViewById(R.id.list); - if (keyInfo.userIds.size() == 1) { - ll.setVisibility(View.GONE); - } else { - boolean first = true; - boolean second = true; - for (String uid : keyInfo.userIds) { - if (first) { - first = false; - continue; - } - if (!second) { - View sep = new View(mActivity); - sep.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 1)); - sep.setBackgroundResource(android.R.drawable.divider_horizontal_dark); - ll.addView(sep); - } - TextView uidView = (TextView) mInflater.inflate( - R.layout.key_server_query_result_user_id, null); - uidView.setText(uid); - ll.addView(uidView); - second = false; - } - } - - return view; - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java deleted file mode 100644 index db907ed48..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2011 Senecaso - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.MenuItem; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.Spinner; -import android.widget.Toast; - -/** - * gpg --send-key activity - * - * Sends the selected public key to a key server - */ -public class KeyServerUploadActivity extends SherlockFragmentActivity { - - // Not used in sourcode, but listed in AndroidManifest! - public static final String ACTION_EXPORT_KEY_TO_SERVER = Constants.INTENT_PREFIX - + "EXPORT_KEY_TO_SERVER"; - - public static final String EXTRA_KEYRING_ROW_ID = "keyId"; - - private Button export; - private Spinner keyServer; - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, KeyListPublicActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - default: - break; - - } - return false; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.key_server_export_layout); - - export = (Button) findViewById(R.id.btn_export_to_server); - keyServer = (Spinner) findViewById(R.id.keyServer); - - ArrayAdapter adapter = new ArrayAdapter(this, - android.R.layout.simple_spinner_item, Preferences.getPreferences(this) - .getKeyServers()); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - keyServer.setAdapter(adapter); - if (adapter.getCount() > 0) { - keyServer.setSelection(0); - } else { - export.setEnabled(false); - } - - export.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - uploadKey(); - } - }); - } - - private void uploadKey() { - // Send all information needed to service to upload key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_UPLOAD_KEY); - - // fill values for this action - Bundle data = new Bundle(); - - int keyRingId = getIntent().getIntExtra(EXTRA_KEYRING_ROW_ID, -1); - data.putInt(ApgIntentService.UPLOAD_KEY_KEYRING_ROW_ID, keyRingId); - - String server = (String) keyServer.getSelectedItem(); - data.putString(ApgIntentService.UPLOAD_KEY_SERVER, server); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after uploading is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_exporting, - ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - - Toast.makeText(KeyServerUploadActivity.this, R.string.keySendSuccess, - Toast.LENGTH_SHORT).show(); - finish(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/MainActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/MainActivity.java deleted file mode 100644 index 7c25e4533..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/MainActivity.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; -import android.view.View; - -public class MainActivity extends SherlockActivity { - - public void manageKeysOnClick(View view) { - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(new Intent(this, KeyListPublicActivity.class), 0); - } - - public void myKeysOnClick(View view) { - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(new Intent(this, KeyListSecretActivity.class), 0); - } - - public void encryptOnClick(View view) { - Intent intent = new Intent(MainActivity.this, EncryptActivity.class); - intent.setAction(EncryptActivity.ACTION_ENCRYPT); - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(intent, 0); - } - - public void decryptOnClick(View view) { - Intent intent = new Intent(MainActivity.this, DecryptActivity.class); - intent.setAction(DecryptActivity.ACTION_DECRYPT); - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(intent, 0); - } - - public void scanQrcodeOnClick(View view) { - Intent intent = new Intent(this, ImportFromQRCodeActivity.class); - intent.setAction(ImportFromQRCodeActivity.IMPORT_FROM_QR_CODE); - startActivityForResult(intent, Id.request.import_from_qr_code); - } - - public void helpOnClick(View view) { - startActivity(new Intent(this, HelpActivity.class)); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences) - .setIcon(R.drawable.ic_menu_settings) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case Id.menu.option.preferences: - startActivity(new Intent(this, PreferencesActivity.class)); - return true; - - default: - break; - - } - return false; - } - -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/PreferencesActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/PreferencesActivity.java deleted file mode 100644 index e28b385f2..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/PreferencesActivity.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.openpgp.PGPEncryptedData; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.ui.widget.IntegerListPreference; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockPreferenceActivity; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.PreferenceScreen; - -public class PreferencesActivity extends SherlockPreferenceActivity { - private IntegerListPreference mPassPhraseCacheTtl = null; - private IntegerListPreference mEncryptionAlgorithm = null; - private IntegerListPreference mHashAlgorithm = null; - private IntegerListPreference mMessageCompression = null; - private IntegerListPreference mFileCompression = null; - private CheckBoxPreference mAsciiArmour = null; - private CheckBoxPreference mForceV3Signatures = null; - private PreferenceScreen mKeyServerPreference = null; - private Preferences mPreferences; - - @Override - protected void onCreate(Bundle savedInstanceState) { - mPreferences = Preferences.getPreferences(this); - super.onCreate(savedInstanceState); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - - addPreferencesFromResource(R.xml.apg_preferences); - - mPassPhraseCacheTtl = (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL); - mPassPhraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl()); - mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry()); - mPassPhraseCacheTtl - .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange(Preference preference, Object newValue) { - mPassPhraseCacheTtl.setValue(newValue.toString()); - mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry()); - mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString())); - return false; - } - }); - - mEncryptionAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM); - int valueIds[] = { PGPEncryptedData.AES_128, PGPEncryptedData.AES_192, - PGPEncryptedData.AES_256, PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH, - PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES, - PGPEncryptedData.IDEA, }; - String entries[] = { "AES-128", "AES-192", "AES-256", "Blowfish", "Twofish", "CAST5", - "DES", "Triple DES", "IDEA", }; - String values[] = new String[valueIds.length]; - for (int i = 0; i < values.length; ++i) { - values[i] = "" + valueIds[i]; - } - mEncryptionAlgorithm.setEntries(entries); - mEncryptionAlgorithm.setEntryValues(values); - mEncryptionAlgorithm.setValue("" + mPreferences.getDefaultEncryptionAlgorithm()); - mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry()); - mEncryptionAlgorithm - .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange(Preference preference, Object newValue) { - mEncryptionAlgorithm.setValue(newValue.toString()); - mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry()); - mPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue - .toString())); - return false; - } - }); - - mHashAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM); - valueIds = new int[] { HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160, - HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256, - HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512, }; - entries = new String[] { "MD5", "RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384", - "SHA-512", }; - values = new String[valueIds.length]; - for (int i = 0; i < values.length; ++i) { - values[i] = "" + valueIds[i]; - } - mHashAlgorithm.setEntries(entries); - mHashAlgorithm.setEntryValues(values); - mHashAlgorithm.setValue("" + mPreferences.getDefaultHashAlgorithm()); - mHashAlgorithm.setSummary(mHashAlgorithm.getEntry()); - mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange(Preference preference, Object newValue) { - mHashAlgorithm.setValue(newValue.toString()); - mHashAlgorithm.setSummary(mHashAlgorithm.getEntry()); - mPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString())); - return false; - } - }); - - mMessageCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION); - valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip, - Id.choice.compression.zlib, Id.choice.compression.bzip2, }; - entries = new String[] { - getString(R.string.choice_none) + " (" + getString(R.string.fast) + ")", - "ZIP (" + getString(R.string.fast) + ")", - "ZLIB (" + getString(R.string.fast) + ")", - "BZIP2 (" + getString(R.string.very_slow) + ")", }; - values = new String[valueIds.length]; - for (int i = 0; i < values.length; ++i) { - values[i] = "" + valueIds[i]; - } - mMessageCompression.setEntries(entries); - mMessageCompression.setEntryValues(values); - mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression()); - mMessageCompression.setSummary(mMessageCompression.getEntry()); - mMessageCompression - .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange(Preference preference, Object newValue) { - mMessageCompression.setValue(newValue.toString()); - mMessageCompression.setSummary(mMessageCompression.getEntry()); - mPreferences.setDefaultMessageCompression(Integer.parseInt(newValue - .toString())); - return false; - } - }); - - mFileCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION); - mFileCompression.setEntries(entries); - mFileCompression.setEntryValues(values); - mFileCompression.setValue("" + mPreferences.getDefaultFileCompression()); - mFileCompression.setSummary(mFileCompression.getEntry()); - mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange(Preference preference, Object newValue) { - mFileCompression.setValue(newValue.toString()); - mFileCompression.setSummary(mFileCompression.getEntry()); - mPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString())); - return false; - } - }); - - mAsciiArmour = (CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR); - mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour()); - mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange(Preference preference, Object newValue) { - mAsciiArmour.setChecked((Boolean) newValue); - mPreferences.setDefaultAsciiArmour((Boolean) newValue); - return false; - } - }); - - mForceV3Signatures = (CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES); - mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures()); - mForceV3Signatures - .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange(Preference preference, Object newValue) { - mForceV3Signatures.setChecked((Boolean) newValue); - mPreferences.setForceV3Signatures((Boolean) newValue); - return false; - } - }); - - mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS); - String servers[] = mPreferences.getKeyServers(); - mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, - servers.length)); - mKeyServerPreference - .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference preference) { - Intent intent = new Intent(PreferencesActivity.this, - PreferencesKeyServerActivity.class); - intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS, - mPreferences.getKeyServers()); - startActivityForResult(intent, Id.request.key_server_preference); - return false; - } - }); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.key_server_preference: { - if (resultCode == RESULT_CANCELED || data == null) { - return; - } - String servers[] = data - .getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS); - mPreferences.setKeyServers(servers); - mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, - servers.length)); - break; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - break; - } - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - default: - break; - - } - return false; - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java deleted file mode 100644 index b54cbde71..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.util.Vector; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.ui.widget.Editor; -import org.thialfihar.android.apg.ui.widget.KeyServerEditor; -import org.thialfihar.android.apg.ui.widget.Editor.EditorListener; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.TextView; - -public class PreferencesKeyServerActivity extends SherlockActivity implements OnClickListener, - EditorListener { - - public static final String EXTRA_KEY_SERVERS = "keyServers"; - - private LayoutInflater mInflater; - private ViewGroup mEditors; - private View mAdd; - private TextView mTitle; - private TextView mSummary; - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, PreferencesActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - - return true; - - case Id.menu.option.okay: - okClicked(); - - return true; - - case Id.menu.option.cancel: - cancelClicked(); - - return true; - - default: - break; - - } - return false; - } - - /** - * ActionBar menu is created based on class variables to change it at runtime - * - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - menu.add(1, Id.menu.option.cancel, 0, android.R.string.cancel).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - menu.add(1, Id.menu.option.okay, 1, android.R.string.ok).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - return true; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.key_server_preference); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - - mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - mTitle = (TextView) findViewById(R.id.title); - mSummary = (TextView) findViewById(R.id.summary); - - mTitle.setText(R.string.label_keyServers); - - mEditors = (ViewGroup) findViewById(R.id.editors); - mAdd = findViewById(R.id.add); - mAdd.setOnClickListener(this); - - Intent intent = getIntent(); - String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS); - if (servers != null) { - for (int i = 0; i < servers.length; ++i) { - KeyServerEditor view = (KeyServerEditor) mInflater.inflate( - R.layout.key_server_editor, mEditors, false); - view.setEditorListener(this); - view.setValue(servers[i]); - mEditors.addView(view); - } - } - } - - public void onDeleted(Editor editor) { - // nothing to do - } - - public void onClick(View v) { - KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, - mEditors, false); - view.setEditorListener(this); - mEditors.addView(view); - } - - private void cancelClicked() { - setResult(RESULT_CANCELED, null); - finish(); - } - - private void okClicked() { - Intent data = new Intent(); - Vector servers = new Vector(); - for (int i = 0; i < mEditors.getChildCount(); ++i) { - KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i); - String tmp = editor.getValue(); - if (tmp.length() > 0) { - servers.add(tmp); - } - } - String[] dummy = new String[0]; - data.putExtra(EXTRA_KEY_SERVERS, servers.toArray(dummy)); - setResult(RESULT_OK, data); - finish(); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java deleted file mode 100644 index 0da9e0ade..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; - -public class SelectPublicKeyActivity extends SherlockFragmentActivity { - - // Not used in sourcode, but listed in AndroidManifest! - public static final String ACTION_SELECT_PUBLIC_KEYS = Constants.INTENT_PREFIX - + "SELECT_PUBLIC_KEYS"; - - public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "masterKeyIds"; - - public static final String RESULT_EXTRA_MASTER_KEY_IDS = "masterKeyIds"; - public static final String RESULT_EXTRA_USER_IDS = "userIds"; - - SelectPublicKeyFragment mSelectFragment; - - long selectedMasterKeyIds[]; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.select_public_key_activity); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - - mSelectFragment = (SelectPublicKeyFragment) getSupportFragmentManager().findFragmentById( - R.id.select_public_key_fragment); - - // - // mFilterLayout = findViewById(R.id.layout_filter); - // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo); - // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear); - // - // mClearFilterButton.setOnClickListener(new OnClickListener() { - // public void onClick(View v) { - // handleIntent(new Intent()); - // } - // }); - - handleIntent(getIntent()); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - handleIntent(intent); - } - - private void handleIntent(Intent intent) { - // String searchString = null; - // if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - // searchString = intent.getStringExtra(SearchManager.QUERY); - // if (searchString != null && searchString.trim().length() == 0) { - // searchString = null; - // } - // } - - // if (searchString == null) { - // mFilterLayout.setVisibility(View.GONE); - // } else { - // mFilterLayout.setVisibility(View.VISIBLE); - // mFilterInfo.setText(getString(R.string.filterInfo, searchString)); - // } - - // preselected master keys - selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); - } - - /** - * returns preselected key ids, this is used in the fragment - * - * @return - */ - public long[] getSelectedMasterKeyIds() { - return selectedMasterKeyIds; - } - - private void cancelClicked() { - setResult(RESULT_CANCELED, null); - finish(); - } - - private void okClicked() { - Intent data = new Intent(); - data.putExtra(RESULT_EXTRA_MASTER_KEY_IDS, mSelectFragment.getSelectedMasterKeyIds()); - data.putExtra(RESULT_EXTRA_USER_IDS, mSelectFragment.getSelectedUserIds()); - setResult(RESULT_OK, data); - finish(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( - android.R.drawable.ic_menu_search); - menu.add(1, Id.menu.option.cancel, 0, R.string.btn_doNotSave).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(1, Id.menu.option.okay, 1, R.string.btn_okay).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - return true; - } - - /** - * Menu Options - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.okay: - okClicked(); - return true; - - case Id.menu.option.cancel: - cancelClicked(); - return true; - - default: - return super.onOptionsItemSelected(item); - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java deleted file mode 100644 index 744c3fa81..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.util.Date; -import java.util.Vector; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.provider.ApgDatabase; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.provider.ApgDatabase.Tables; -import org.thialfihar.android.apg.ui.widget.SelectKeyCursorAdapter; - -import com.actionbarsherlock.app.SherlockListFragment; - -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.view.View; -import android.widget.ListView; - -public class SelectPublicKeyFragment extends SherlockListFragment implements - LoaderManager.LoaderCallbacks { - - private SelectPublicKeyActivity mActivity; - private SelectKeyCursorAdapter mAdapter; - private ListView mListView; - - private long mSelectedMasterKeyIds[]; - - /** - * Define Adapter and Loader on create of Activity - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mActivity = (SelectPublicKeyActivity) getSherlockActivity(); - mListView = getListView(); - - // get selected master key ids, which are given to activity by intent - mSelectedMasterKeyIds = mActivity.getSelectedMasterKeyIds(); - - mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(getString(R.string.listEmpty)); - - mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.public_key); - - setListAdapter(mAdapter); - - // Start out with a progress indicator. - setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - /** - * Workaround for Android 4.1. Items are not checked in layout. See - * http://code.google.com/p/android/issues/detail?id=35885 - */ - @Override - public void onListItemClick(ListView l, View v, int position, long id) { - l.setItemChecked(position, l.isItemChecked(position)); - } - - /** - * Selects items based on master key ids in list view - * - * @param masterKeyIds - */ - private void preselectMasterKeyIds(long[] masterKeyIds) { - if (masterKeyIds != null) { - for (int i = 0; i < mListView.getCount(); ++i) { - long keyId = mAdapter.getMasterKeyId(i); - for (int j = 0; j < masterKeyIds.length; ++j) { - if (keyId == masterKeyIds[j]) { - mListView.setItemChecked(i, true); - break; - } - } - } - } - } - - /** - * Returns all selected master key ids - * - * @return - */ - public long[] getSelectedMasterKeyIds() { - // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key - // ids! - Vector vector = new Vector(); - for (int i = 0; i < mListView.getCount(); ++i) { - if (mListView.isItemChecked(i)) { - vector.add(mAdapter.getMasterKeyId(i)); - } - } - - // convert to long array - long[] selectedMasterKeyIds = new long[vector.size()]; - for (int i = 0; i < vector.size(); ++i) { - selectedMasterKeyIds[i] = vector.get(i); - } - - return selectedMasterKeyIds; - } - - /** - * Returns all selected user ids - * - * @return - */ - public String[] getSelectedUserIds() { - Vector userIds = new Vector(); - for (int i = 0; i < mListView.getCount(); ++i) { - if (mListView.isItemChecked(i)) { - userIds.add((String) mAdapter.getUserId(i)); - } - } - - // make empty array to not return null - String userIdArray[] = new String[0]; - return userIds.toArray(userIdArray); - } - - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); - - // These are the rows that we will retrieve. - long now = new Date().getTime() / 1000; - String[] projection = new String[] { - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - UserIds.USER_ID, - "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS - + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = " - + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID - + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys." - + Keys.CAN_ENCRYPT + " = '1') AS " - + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE, - "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS - + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = " - + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys." - + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_ENCRYPT - + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " - + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY - + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; - - String inMasterKeyList = null; - if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) { - inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN ("; - for (int i = 0; i < mSelectedMasterKeyIds.length; ++i) { - if (i != 0) { - inMasterKeyList += ", "; - } - inMasterKeyList += DatabaseUtils.sqlEscapeString("" + mSelectedMasterKeyIds[i]); - } - inMasterKeyList += ")"; - } - - // if (searchString != null && searchString.trim().length() > 0) { - // String[] chunks = searchString.trim().split(" +"); - // qb.appendWhere("(EXISTS (SELECT tmp." + UserIds._ID + " FROM " + UserIds.TABLE_NAME - // + " AS tmp WHERE " + "tmp." + UserIds.KEY_ID + " = " + Keys.TABLE_NAME + "." - // + Keys._ID); - // for (int i = 0; i < chunks.length; ++i) { - // qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE "); - // qb.appendWhereEscapeString("%" + chunks[i] + "%"); - // } - // qb.appendWhere("))"); - // - // if (inIdList != null) { - // qb.appendWhere(" OR (" + inIdList + ")"); - // } - // } - - String orderBy = UserIds.USER_ID + " ASC"; - if (inMasterKeyList != null) { - // sort by selected master keys - orderBy = inMasterKeyList + " DESC, " + orderBy; - } - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.swapCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - - // preselect given master keys - preselectMasterKeyIds(mSelectedMasterKeyIds); - } - - @Override - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.swapCursor(null); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java deleted file mode 100644 index 9efadd291..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; - -public class SelectSecretKeyActivity extends SherlockFragmentActivity { - - // Not used in sourcode, but listed in AndroidManifest! - public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX - + "SELECT_SECRET_KEY"; - - public static final String RESULT_EXTRA_MASTER_KEY_ID = "masterKeyId"; - public static final String RESULT_EXTRA_USER_ID = "userId"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.select_secret_key_activity); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - - // mFilterLayout = findViewById(R.id.layout_filter); - // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo); - // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear); - // - // mClearFilterButton.setOnClickListener(new OnClickListener() { - // public void onClick(View v) { - // handleIntent(new Intent()); - // } - // }); - - handleIntent(getIntent()); - } - - /** - * This is executed by SelectSecretKeyFragment after clicking on an item - * - * @param masterKeyId - * @param userId - */ - public void afterListSelection(long masterKeyId, String userId) { - Intent data = new Intent(); - data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId); - data.putExtra(RESULT_EXTRA_USER_ID, (String) userId); - setResult(RESULT_OK, data); - finish(); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - handleIntent(intent); - } - - private void handleIntent(Intent intent) { - // String searchString = null; - // if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - // searchString = intent.getStringExtra(SearchManager.QUERY); - // if (searchString != null && searchString.trim().length() == 0) { - // searchString = null; - // } - // } - - // if (searchString == null) { - // mFilterLayout.setVisibility(View.GONE); - // } else { - // mFilterLayout.setVisibility(View.VISIBLE); - // mFilterInfo.setText(getString(R.string.filterInfo, searchString)); - // } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( - android.R.drawable.ic_menu_search); - return true; - } - - /** - * Menu Options - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - default: - return super.onOptionsItemSelected(item); - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java deleted file mode 100644 index 1f3fcb816..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.util.Date; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.provider.ApgDatabase; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.provider.ApgDatabase.Tables; -import org.thialfihar.android.apg.ui.widget.SelectKeyCursorAdapter; - -import com.actionbarsherlock.app.SherlockListFragment; - -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ListView; - -public class SelectSecretKeyFragment extends SherlockListFragment implements - LoaderManager.LoaderCallbacks { - - private SelectSecretKeyActivity mActivity; - private SelectKeyCursorAdapter mAdapter; - private ListView mListView; - - /** - * Define Adapter and Loader on create of Activity - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mActivity = (SelectSecretKeyActivity) getSherlockActivity(); - mListView = getListView(); - - mListView.setOnItemClickListener(new OnItemClickListener() { - @Override - public void onItemClick(AdapterView adapterView, View view, int position, long id) { - long masterKeyId = mAdapter.getMasterKeyId(position); - String userId = mAdapter.getUserId(position); - - // return data to activity, which results in finishing it - mActivity.afterListSelection(masterKeyId, userId); - } - }); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(getString(R.string.listEmpty)); - - mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.secret_key); - - setListAdapter(mAdapter); - - // Start out with a progress indicator. - setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); - - // These are the rows that we will retrieve. - long now = new Date().getTime() / 1000; - String[] projection = new String[] { - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - UserIds.USER_ID, - "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS - + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = " - + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID - + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys." - + Keys.CAN_SIGN + " = '1') AS " - + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE, - "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS - + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = " - + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys." - + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_SIGN - + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " - + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY - + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; - - // if (searchString != null && searchString.trim().length() > 0) { - // String[] chunks = searchString.trim().split(" +"); - // qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " + UserIds.TABLE_NAME - // + " AS tmp WHERE " + "tmp." + UserIds.KEY_ID + " = " + Keys.TABLE_NAME + "." - // + Keys._ID); - // for (int i = 0; i < chunks.length; ++i) { - // qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE "); - // qb.appendWhereEscapeString("%" + chunks[i] + "%"); - // } - // qb.appendWhere(")"); - // } - - String orderBy = UserIds.USER_ID + " ASC"; - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.swapCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - } - - @Override - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.swapCursor(null); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/SignKeyActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/SignKeyActivity.java deleted file mode 100644 index b970cb3d3..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/SignKeyActivity.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2011 Senecaso - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.util.Iterator; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSignature; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; - -import org.thialfihar.android.apg.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.Spinner; -import android.widget.Toast; - -/** - * gpg --sign-key - * - * signs the specified public key with the specified secret master key - */ -public class SignKeyActivity extends SherlockFragmentActivity { - - public static final String EXTRA_KEY_ID = "keyId"; - - private long mPubKeyId = 0; - private long mMasterKeyId = 0; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // check we havent already signed it - setContentView(R.layout.sign_key_layout); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - - final Spinner keyServer = (Spinner) findViewById(R.id.keyServer); - ArrayAdapter adapter = new ArrayAdapter(this, - android.R.layout.simple_spinner_item, Preferences.getPreferences(this) - .getKeyServers()); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - keyServer.setAdapter(adapter); - - final CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey); - if (!sendKey.isChecked()) { - keyServer.setEnabled(false); - } else { - keyServer.setEnabled(true); - } - - sendKey.setOnCheckedChangeListener(new OnCheckedChangeListener() { - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (!isChecked) { - keyServer.setEnabled(false); - } else { - keyServer.setEnabled(true); - } - } - }); - - Button sign = (Button) findViewById(R.id.sign); - sign.setEnabled(false); // disabled until the user selects a key to sign with - sign.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - if (mPubKeyId != 0) { - initiateSigning(); - } - } - }); - - mPubKeyId = getIntent().getLongExtra(EXTRA_KEY_ID, 0); - if (mPubKeyId == 0) { - finish(); // nothing to do if we dont know what key to sign - } else { - // kick off the SecretKey selection activity so the user chooses which key to sign with - // first - Intent intent = new Intent(this, SelectSecretKeyActivity.class); - startActivityForResult(intent, Id.request.secret_keys); - } - } - - private void showPassphraseDialog(final long secretKeyId) { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - startSigning(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - try { - PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, - messenger, secretKeyId); - - passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); - } catch (PGPMain.ApgGeneralException e) { - Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); - // send message to handler to start encryption directly - returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); - } - } - - /** - * handles the UI bits of the signing process on the UI thread - */ - private void initiateSigning() { - PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, mPubKeyId); - if (pubring != null) { - // if we have already signed this key, dont bother doing it again - boolean alreadySigned = false; - - @SuppressWarnings("unchecked") - Iterator itr = pubring.getPublicKey(mPubKeyId).getSignatures(); - while (itr.hasNext()) { - PGPSignature sig = itr.next(); - if (sig.getKeyID() == mMasterKeyId) { - alreadySigned = true; - break; - } - } - - if (!alreadySigned) { - /* - * get the user's passphrase for this key (if required) - */ - String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId); - if (passphrase == null) { - showPassphraseDialog(mMasterKeyId); - return; // bail out; need to wait until the user has entered the passphrase - // before trying again - } else { - startSigning(); - } - } else { - Toast.makeText(this, "Key has already been signed", Toast.LENGTH_SHORT).show(); - - setResult(RESULT_CANCELED); - finish(); - } - } - } - - /** - * kicks off the actual signing process on a background thread - */ - private void startSigning() { - // Send all information needed to service to sign key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_SIGN_KEY); - - // fill values for this action - Bundle data = new Bundle(); - - data.putLong(ApgIntentService.SIGN_KEY_MASTER_KEY_ID, mMasterKeyId); - data.putLong(ApgIntentService.SIGN_KEY_PUB_KEY_ID, mPubKeyId); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after signing is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_signing, - ProgressDialog.STYLE_SPINNER) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - - Toast.makeText(SignKeyActivity.this, R.string.keySignSuccess, - Toast.LENGTH_SHORT).show(); - - // check if we need to send the key to the server or not - CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey); - if (sendKey.isChecked()) { - /* - * upload the newly signed key to the key server - */ - uploadKey(); - } else { - finish(); - } - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - private void uploadKey() { - // Send all information needed to service to upload key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_UPLOAD_KEY); - - // fill values for this action - Bundle data = new Bundle(); - - data.putLong(ApgIntentService.UPLOAD_KEY_KEYRING_ROW_ID, mPubKeyId); - - Spinner keyServer = (Spinner) findViewById(R.id.keyServer); - String server = (String) keyServer.getSelectedItem(); - data.putString(ApgIntentService.UPLOAD_KEY_SERVER, server); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after uploading is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_exporting, - ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - - Toast.makeText(SignKeyActivity.this, R.string.keySendSuccess, - Toast.LENGTH_SHORT).show(); - - finish(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.secret_keys: { - if (resultCode == RESULT_OK) { - mMasterKeyId = data.getLongExtra(EXTRA_KEY_ID, 0); - - // re-enable the sign button so the user can initiate the sign process - Button sign = (Button) findViewById(R.id.sign); - sign.setEnabled(true); - } - - break; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - } - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java deleted file mode 100644 index 311066389..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; -import android.widget.Toast; - -public class DeleteFileDialogFragment extends DialogFragment { - - private static final String ARG_DELETE_FILE = "delete_file"; - - /** - * Creates new instance of this delete file dialog fragment - */ - public static DeleteFileDialogFragment newInstance(String deleteFile) { - DeleteFileDialogFragment frag = new DeleteFileDialogFragment(); - Bundle args = new Bundle(); - - args.putString(ARG_DELETE_FILE, deleteFile); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final FragmentActivity activity = getActivity(); - - final String deleteFile = getArguments().getString(ARG_DELETE_FILE); - - AlertDialog.Builder alert = new AlertDialog.Builder(activity); - - alert.setIcon(android.R.drawable.ic_dialog_alert); - alert.setTitle(R.string.warning); - alert.setMessage(this.getString(R.string.fileDeleteConfirmation, deleteFile)); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(activity, ApgIntentService.class); - - // fill values for this action - Bundle data = new Bundle(); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_DELETE_FILE_SECURELY); - data.putString(ApgIntentService.DELETE_FILE, deleteFile); - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance( - R.string.progress_deletingSecurely, ProgressDialog.STYLE_HORIZONTAL); - - // Message is received after deleting is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(activity, deletingDialog) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - Toast.makeText(activity, R.string.fileDeleteSuccessful, - Toast.LENGTH_SHORT).show(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - deletingDialog.show(activity.getSupportFragmentManager(), "deletingDialog"); - - // start service with intent - activity.startService(intent); - } - }); - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - alert.setCancelable(true); - - return alert.create(); - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java deleted file mode 100644 index bb5271ce7..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.util.Log; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; - -public class DeleteKeyDialogFragment extends DialogFragment { - - private Messenger mMessenger; - - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_DELETE_KEY_RING_ROW_ID = "delete_file"; - private static final String ARG_KEY_TYPE = "key_type"; - - public static final int MESSAGE_OKAY = 1; - - /** - * Creates new instance of this delete file dialog fragment - */ - public static DeleteKeyDialogFragment newInstance(Messenger messenger, long deleteKeyRingRowId, - int keyType) { - DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment(); - Bundle args = new Bundle(); - - args.putParcelable(ARG_MESSENGER, messenger); - args.putLong(ARG_DELETE_KEY_RING_ROW_ID, deleteKeyRingRowId); - args.putInt(ARG_KEY_TYPE, keyType); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final FragmentActivity activity = getActivity(); - - final long deleteKeyRingRowId = getArguments().getLong(ARG_DELETE_KEY_RING_ROW_ID); - final int keyType = getArguments().getInt(ARG_KEY_TYPE); - - // TODO: better way to do this? - String userId = activity.getString(R.string.unknownUserId); - - if (keyType == Id.type.public_key) { - PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity, - deleteKeyRingRowId); - userId = PGPHelper.getMainUserIdSafe(activity, PGPHelper.getMasterKey(keyRing)); - } else { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity, - deleteKeyRingRowId); - userId = PGPHelper.getMainUserIdSafe(activity, PGPHelper.getMasterKey(keyRing)); - } - - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(R.string.warning); - builder.setMessage(getString( - keyType == 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) { - if (keyType == Id.type.public_key) { - ProviderHelper.deletePublicKeyRing(activity, deleteKeyRingRowId); - } else { - ProviderHelper.deleteSecretKeyRing(activity, deleteKeyRingRowId); - } - - dismiss(); - - sendMessageToHandler(MESSAGE_OKAY); - } - }); - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - return builder.create(); - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what) { - Message msg = Message.obtain(); - msg.what = what; - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java deleted file mode 100644 index 7786d9228..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.FileHelper; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; -import org.thialfihar.android.apg.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageButton; - -public class FileDialogFragment extends DialogFragment { - - private Messenger mMessenger; - - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_TITLE = "title"; - private static final String ARG_MESSAGE = "message"; - private static final String ARG_DEFAULT_FILE = "default_file"; - private static final String ARG_CHECKBOX_TEXT = "checkbox_text"; - private static final String ARG_REQUEST_CODE = "request_code"; - - public static final int MESSAGE_OKAY = 1; - - public static final String MESSAGE_DATA_FILENAME = "filename"; - public static final String MESSAGE_DATA_CHECKED = "checked"; - - /** - * Creates new instance of this file dialog fragment - */ - public static FileDialogFragment newInstance(Messenger messenger, String title, String message, - String defaultFile, String checkboxText, int requestCode) { - FileDialogFragment frag = new FileDialogFragment(); - Bundle args = new Bundle(); - args.putParcelable(ARG_MESSENGER, messenger); - - args.putString(ARG_TITLE, title); - args.putString(ARG_MESSAGE, message); - args.putString(ARG_DEFAULT_FILE, defaultFile); - args.putString(ARG_CHECKBOX_TEXT, checkboxText); - args.putInt(ARG_REQUEST_CODE, requestCode); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - String title = getArguments().getString(ARG_TITLE); - String message = getArguments().getString(ARG_MESSAGE); - String defaultFile = getArguments().getString(ARG_DEFAULT_FILE); - String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT); - final int requestCode = getArguments().getInt(ARG_REQUEST_CODE); - - final EditText mFilename; - final ImageButton mBrowse; - final CheckBox mCheckBox; - - LayoutInflater inflater = (LayoutInflater) activity - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - AlertDialog.Builder alert = new AlertDialog.Builder(activity); - - alert.setTitle(title); - alert.setMessage(message); - - View view = inflater.inflate(R.layout.file_dialog, null); - - mFilename = (EditText) view.findViewById(R.id.input); - mFilename.setText(defaultFile); - mBrowse = (ImageButton) view.findViewById(R.id.btn_browse); - mBrowse.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - // only .asc or .gpg files - FileHelper.openFile(activity, mFilename.getText().toString(), "text/plain", requestCode); - } - }); - - mCheckBox = (CheckBox) view.findViewById(R.id.checkbox); - if (checkboxText == null) { - mCheckBox.setEnabled(false); - mCheckBox.setVisibility(View.GONE); - } else { - mCheckBox.setEnabled(true); - mCheckBox.setVisibility(View.VISIBLE); - mCheckBox.setText(checkboxText); - } - - alert.setView(view); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - boolean checked = false; - if (mCheckBox.isEnabled()) { - checked = mCheckBox.isChecked(); - } - - // return resulting data back to activity - Bundle data = new Bundle(); - data.putString(MESSAGE_DATA_FILENAME, mFilename.getText().toString()); - data.putBoolean(MESSAGE_DATA_CHECKED, checked); - - sendMessageToHandler(MESSAGE_OKAY, data); - - dismiss(); - } - }); - - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - return alert.create(); - } - - /** - * Updates filename in dialog, normally called in onActivityResult in activity using the - * FileDialog - * - * @param messageId - * @param progress - * @param max - */ - public void setFilename(String filename) { - AlertDialog dialog = (AlertDialog) getDialog(); - EditText filenameEditText = (EditText) dialog.findViewById(R.id.input); - - if (filenameEditText != null) { - filenameEditText.setText(filename); - } - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what, Bundle data) { - Message msg = Message.obtain(); - msg.what = what; - if (data != null) { - msg.setData(data); - } - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java deleted file mode 100644 index 9797abc2a..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; - -import org.thialfihar.android.apg.ui.KeyServerQueryActivity; -import org.thialfihar.android.apg.util.Log; - -public class LookupUnknownKeyDialogFragment extends DialogFragment { - - private Messenger mMessenger; - - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_UNKNOWN_KEY_ID = "unknown_key_id"; - - public static final int MESSAGE_OKAY = 1; - public static final int MESSAGE_CANCEL = 2; - - /** - * Creates new instance of this dialog fragment - * - * @param messenger - * @param unknownKeyId - * @return - */ - public static LookupUnknownKeyDialogFragment newInstance(Messenger messenger, long unknownKeyId) { - LookupUnknownKeyDialogFragment frag = new LookupUnknownKeyDialogFragment(); - Bundle args = new Bundle(); - args.putLong(ARG_UNKNOWN_KEY_ID, unknownKeyId); - args.putParcelable(ARG_MESSENGER, messenger); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - - final long unknownKeyId = getArguments().getLong(ARG_UNKNOWN_KEY_ID); - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - AlertDialog.Builder alert = new AlertDialog.Builder(activity); - - alert.setIcon(android.R.drawable.ic_dialog_alert); - alert.setTitle(R.string.title_unknownSignatureKey); - alert.setMessage(getString(R.string.lookupUnknownKey, - PGPHelper.getSmallFingerPrint(unknownKeyId))); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - sendMessageToHandler(MESSAGE_OKAY); - - Intent intent = new Intent(activity, KeyServerQueryActivity.class); - intent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID); - intent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, unknownKeyId); - startActivityForResult(intent, Id.request.look_up_key_id); - } - }); - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - sendMessageToHandler(MESSAGE_CANCEL); - } - }); - alert.setCancelable(true); - alert.setOnCancelListener(new OnCancelListener() { - - @Override - public void onCancel(DialogInterface dialog) { - sendMessageToHandler(MESSAGE_CANCEL); - } - }); - - return alert.create(); - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what) { - Message msg = Message.obtain(); - msg.what = what; - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java deleted file mode 100644 index fb8f4b6d1..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPPrivateKey; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; - -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.util.Log; - -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager.LayoutParams; -import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; -import android.widget.Toast; - -public class PassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { - - private Messenger mMessenger; - - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_SECRET_KEY_ID = "secret_key_id"; - - public static final int MESSAGE_OKAY = 1; - - private EditText mPassphraseEditText; - - /** - * Creates new instance of this dialog fragment - * - * @param secretKeyId - * secret key id you want to use - * @param messenger - * to communicate back after caching the passphrase - * @return - * @throws ApgGeneralException - */ - public static PassphraseDialogFragment newInstance(Context context, Messenger messenger, - long secretKeyId) throws ApgGeneralException { - // check if secret key has a passphrase - if (!(secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none)) { - if (!hasPassphrase(context, secretKeyId)) { - throw new PGPMain.ApgGeneralException("No passphrase! No passphrase dialog needed!"); - } - } - - PassphraseDialogFragment frag = new PassphraseDialogFragment(); - Bundle args = new Bundle(); - args.putLong(ARG_SECRET_KEY_ID, secretKeyId); - args.putParcelable(ARG_MESSENGER, messenger); - - frag.setArguments(args); - - return frag; - } - - /** - * Checks if key has a passphrase - * - * @param secretKeyId - * @return true if it has a passphrase - */ - private static boolean hasPassphrase(Context context, long secretKeyId) { - // check if the key has no passphrase - try { - PGPSecretKey secretKey = PGPHelper.getMasterKey(ProviderHelper - .getPGPSecretKeyRingByKeyId(context, secretKeyId)); - // PGPSecretKey secretKey = - // PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); - - Log.d(Constants.TAG, "Check if key has no passphrase..."); - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - "SC").build("".toCharArray()); - PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); - if (testKey != null) { - Log.d(Constants.TAG, "Key has no passphrase! Caches empty passphrase!"); - - // cache empty passphrase - PassphraseCacheService.addCachedPassphrase(context, secretKey.getKeyID(), ""); - - return false; - } - } catch (PGPException e) { - // silently catch - } - - return true; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - - long secretKeyId = getArguments().getLong(ARG_SECRET_KEY_ID); - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - AlertDialog.Builder alert = new AlertDialog.Builder(activity); - - alert.setTitle(R.string.title_authentication); - - final PGPSecretKey secretKey; - - if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) { - secretKey = null; - alert.setMessage(R.string.passPhraseForSymmetricEncryption); - } else { - // TODO: by master key id??? - secretKey = PGPHelper.getMasterKey(ProviderHelper.getPGPSecretKeyRingByMasterKeyId( - activity, secretKeyId)); - // secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); - - if (secretKey == null) { - alert.setTitle(R.string.title_keyNotFound); - alert.setMessage(getString(R.string.keyNotFound, secretKeyId)); - alert.setPositiveButton(android.R.string.ok, new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dismiss(); - } - }); - alert.setCancelable(false); - return alert.create(); - } - String userId = PGPHelper.getMainUserIdSafe(activity, secretKey); - - Log.d(Constants.TAG, "User id: '" + userId + "'"); - alert.setMessage(getString(R.string.passPhraseFor, userId)); - } - - LayoutInflater inflater = activity.getLayoutInflater(); - View view = inflater.inflate(R.layout.passphrase, null); - alert.setView(view); - - mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - String passPhrase = mPassphraseEditText.getText().toString(); - long keyId; - if (secretKey != null) { - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(PGPMain.BOUNCY_CASTLE_PROVIDER_NAME).build( - passPhrase.toCharArray()); - PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); - if (testKey == null) { - Toast.makeText(activity, R.string.error_couldNotExtractPrivateKey, - Toast.LENGTH_SHORT).show(); - return; - } - } catch (PGPException e) { - Toast.makeText(activity, R.string.wrongPassPhrase, Toast.LENGTH_SHORT) - .show(); - return; - } - keyId = secretKey.getKeyID(); - } else { - keyId = Id.key.symmetric; - } - - // cache the new passphrase - Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); - PassphraseCacheService.addCachedPassphrase(activity, keyId, passPhrase); - - sendMessageToHandler(MESSAGE_OKAY); - } - }); - - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - - return alert.create(); - } - - @Override - public void onActivityCreated(Bundle arg0) { - super.onActivityCreated(arg0); - - // request focus and open soft keyboard - mPassphraseEditText.requestFocus(); - getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - - mPassphraseEditText.setOnEditorActionListener(this); - } - - /** - * Associate the "done" button on the soft keyboard with the okay button in the view - */ - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (EditorInfo.IME_ACTION_DONE == actionId) { - AlertDialog dialog = ((AlertDialog) getDialog()); - Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); - - bt.performClick(); - return true; - } - return false; - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what) { - Message msg = Message.obtain(); - msg.what = what; - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } - -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java deleted file mode 100644 index 2fb3b3256..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import android.app.Activity; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.DialogInterface; -import android.content.DialogInterface.OnKeyListener; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.view.KeyEvent; - -public class ProgressDialogFragment extends DialogFragment { - - private static final String ARG_MESSAGE_ID = "message_id"; - private static final String ARG_STYLE = "style"; - - /** - * Creates new instance of this fragment - * - * @param id - * @return - */ - public static ProgressDialogFragment newInstance(int messageId, int style) { - ProgressDialogFragment frag = new ProgressDialogFragment(); - Bundle args = new Bundle(); - args.putInt(ARG_MESSAGE_ID, messageId); - args.putInt(ARG_STYLE, style); - - frag.setArguments(args); - return frag; - } - - /** - * Updates progress of dialog - * - * @param messageId - * @param progress - * @param max - */ - public void setProgress(int messageId, int progress, int max) { - setProgress(getString(messageId), progress, max); - } - - /** - * Updates progress of dialog - * - * @param messageId - * @param progress - * @param max - */ - public void setProgress(int progress, int max) { - ProgressDialog dialog = (ProgressDialog) getDialog(); - - dialog.setProgress(progress); - dialog.setMax(max); - } - - /** - * Updates progress of dialog - * - * @param messageId - * @param progress - * @param max - */ - public void setProgress(String message, int progress, int max) { - ProgressDialog dialog = (ProgressDialog) getDialog(); - - dialog.setMessage(message); - dialog.setProgress(progress); - dialog.setMax(max); - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - Activity activity = getActivity(); - - ProgressDialog dialog = new ProgressDialog(activity); - dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); - dialog.setCancelable(false); - dialog.setCanceledOnTouchOutside(false); - - int messageId = getArguments().getInt(ARG_MESSAGE_ID); - int style = getArguments().getInt(ARG_STYLE); - - dialog.setMessage(getString(messageId)); - dialog.setProgressStyle(style); - - // Disable the back button - OnKeyListener keyListener = new OnKeyListener() { - - @Override - public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { - - if (keyCode == KeyEvent.KEYCODE_BACK) { - return true; - } - return false; - } - - }; - dialog.setOnKeyListener(keyListener); - - return dialog; - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java deleted file mode 100644 index b23101a0a..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; -import org.thialfihar.android.apg.util.Log; - -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager.LayoutParams; -import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.TextView.OnEditorActionListener; - -public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { - private Messenger mMessenger; - - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_TITLE = "title"; - - public static final int MESSAGE_OKAY = 1; - - public static final String MESSAGE_NEW_PASSPHRASE = "new_passphrase"; - - private EditText mPassphraseEditText; - private EditText mPassphraseAgainEditText; - - /** - * Creates new instance of this dialog fragment - * - * @param title - * title of dialog - * @param messenger - * to communicate back after setting the passphrase - * @return - */ - public static SetPassphraseDialogFragment newInstance(Messenger messenger, int title) { - SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment(); - Bundle args = new Bundle(); - args.putInt(ARG_TITLE, title); - args.putParcelable(ARG_MESSENGER, messenger); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - - int title = getArguments().getInt(ARG_TITLE); - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - AlertDialog.Builder alert = new AlertDialog.Builder(activity); - - alert.setTitle(title); - alert.setMessage(R.string.enterPassPhraseTwice); - - LayoutInflater inflater = activity.getLayoutInflater(); - View view = inflater.inflate(R.layout.passphrase_repeat, null); - alert.setView(view); - - mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); - mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - String passPhrase1 = mPassphraseEditText.getText().toString(); - String passPhrase2 = mPassphraseAgainEditText.getText().toString(); - if (!passPhrase1.equals(passPhrase2)) { - Toast.makeText( - activity, - getString(R.string.errorMessage, - getString(R.string.passPhrasesDoNotMatch)), Toast.LENGTH_SHORT) - .show(); - return; - } - - if (passPhrase1.equals("")) { - Toast.makeText( - activity, - getString(R.string.errorMessage, - getString(R.string.passPhraseMustNotBeEmpty)), - Toast.LENGTH_SHORT).show(); - return; - } - - // return resulting data back to activity - Bundle data = new Bundle(); - data.putString(MESSAGE_NEW_PASSPHRASE, passPhrase1); - - sendMessageToHandler(MESSAGE_OKAY, data); - } - }); - - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - - return alert.create(); - } - - @Override - public void onActivityCreated(Bundle arg0) { - super.onActivityCreated(arg0); - - // request focus and open soft keyboard - mPassphraseEditText.requestFocus(); - getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - - mPassphraseAgainEditText.setOnEditorActionListener(this); - } - - /** - * Associate the "done" button on the soft keyboard with the okay button in the view - */ - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (EditorInfo.IME_ACTION_DONE == actionId) { - AlertDialog dialog = ((AlertDialog) getDialog()); - Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); - - bt.performClick(); - return true; - } - return false; - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what, Bundle data) { - Message msg = Message.obtain(); - msg.what = what; - if (data != null) { - msg.setData(data); - } - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java deleted file mode 100644 index 8a516ad41..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; - -/** - * Custom layout that arranges children in a grid-like manner, optimizing for even horizontal and - * vertical whitespace. - */ -public class DashboardLayout extends ViewGroup { - private static final int UNEVEN_GRID_PENALTY_MULTIPLIER = 10; - - private int mMaxChildWidth = 0; - private int mMaxChildHeight = 0; - - public DashboardLayout(Context context) { - super(context, null); - } - - public DashboardLayout(Context context, AttributeSet attrs) { - super(context, attrs, 0); - } - - public DashboardLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - mMaxChildWidth = 0; - mMaxChildHeight = 0; - - // Measure once to find the maximum child size. - - int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST); - int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST); - - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - - mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth()); - mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight()); - } - - // Measure again for each child to be exactly the same size. - - childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildWidth, MeasureSpec.EXACTLY); - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildHeight, MeasureSpec.EXACTLY); - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - - setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec), - resolveSize(mMaxChildHeight, heightMeasureSpec)); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - int width = r - l; - int height = b - t; - - final int count = getChildCount(); - - // Calculate the number of visible children. - int visibleCount = 0; - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - ++visibleCount; - } - - if (visibleCount == 0) { - return; - } - - // Calculate what number of rows and columns will optimize for even horizontal and - // vertical whitespace between items. Start with a 1 x N grid, then try 2 x N, and so on. - int bestSpaceDifference = Integer.MAX_VALUE; - int spaceDifference; - - // Horizontal and vertical space between items - int hSpace = 0; - int vSpace = 0; - - int cols = 1; - int rows; - - while (true) { - rows = (visibleCount - 1) / cols + 1; - - hSpace = ((width - mMaxChildWidth * cols) / (cols + 1)); - vSpace = ((height - mMaxChildHeight * rows) / (rows + 1)); - - spaceDifference = Math.abs(vSpace - hSpace); - if (rows * cols != visibleCount) { - spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER; - } else if (rows * mMaxChildHeight > height || cols * mMaxChildWidth > width) { - spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER; - } - - if (spaceDifference < bestSpaceDifference) { - // Found a better whitespace squareness/ratio - bestSpaceDifference = spaceDifference; - - // If we found a better whitespace squareness and there's only 1 row, this is - // the best we can do. - if (rows == 1) { - break; - } - } else { - // This is a worse whitespace ratio, use the previous value of cols and exit. - --cols; - rows = (visibleCount - 1) / cols + 1; - hSpace = ((width - mMaxChildWidth * cols) / (cols + 1)); - vSpace = ((height - mMaxChildHeight * rows) / (rows + 1)); - break; - } - - ++cols; - } - - // Lay out children based on calculated best-fit number of rows and cols. - - // If we chose a layout that has negative horizontal or vertical space, force it to zero. - hSpace = Math.max(0, hSpace); - vSpace = Math.max(0, vSpace); - - // Re-use width/height variables to be child width/height. - width = (width - hSpace * (cols + 1)) / cols; - height = (height - vSpace * (rows + 1)) / rows; - - int left, top; - int col, row; - int visibleIndex = 0; - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - - row = visibleIndex / cols; - col = visibleIndex % cols; - - left = hSpace * (col + 1) + width * col; - top = vSpace * (row + 1) + height * row; - - child.layout(left, top, (hSpace == 0 && col == cols - 1) ? r : (left + width), - (vSpace == 0 && row == rows - 1) ? b : (top + height)); - ++visibleIndex; - } - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/Editor.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/Editor.java deleted file mode 100644 index a7200fad0..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/Editor.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -public interface Editor { - public interface EditorListener { - public void onDeleted(Editor editor); - } - - public void setEditorListener(EditorListener listener); -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java deleted file mode 100644 index 01488909f..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.app.Fragment; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnCreateContextMenuListener; -import android.view.ViewGroup; -import android.view.animation.AnimationUtils; -import android.widget.AdapterView; -import android.widget.ExpandableListAdapter; -import android.widget.ExpandableListView; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.ListAdapter; -import android.widget.ProgressBar; -import android.widget.TextView; - -/** - * @author Khoa Tran - * - * @see android.support.v4.app.ListFragment - * @see android.app.ExpandableListActivity - * - * ExpandableListFragment for Android < 3.0 - * - * from - * http://stackoverflow.com/questions/6051050/expandablelistfragment-with-loadermanager-for- - * compatibility-package - * - */ -public class ExpandableListFragment extends Fragment implements OnCreateContextMenuListener, - ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupCollapseListener, - ExpandableListView.OnGroupExpandListener { - - static final int INTERNAL_EMPTY_ID = 0x00ff0001; - static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002; - static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003; - - final private Handler mHandler = new Handler(); - - final private Runnable mRequestFocus = new Runnable() { - public void run() { - mExpandableList.focusableViewAvailable(mExpandableList); - } - }; - - final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { - public void onItemClick(AdapterView parent, View v, int position, long id) { - onListItemClick((ExpandableListView) parent, v, position, id); - } - }; - - ExpandableListAdapter mAdapter; - ExpandableListView mExpandableList; - boolean mFinishedStart = false; - View mEmptyView; - TextView mStandardEmptyView; - View mProgressContainer; - View mExpandableListContainer; - CharSequence mEmptyText; - boolean mExpandableListShown; - - public ExpandableListFragment() { - } - - /** - * Provide default implementation to return a simple list view. Subclasses can override to - * replace with their own layout. If doing so, the returned view hierarchy must have a - * ListView whose id is {@link android.R.id#list android.R.id.list} and can optionally have a - * sibling view id {@link android.R.id#empty android.R.id.empty} that is to be shown when the - * list is empty. - * - *

- * If you are overriding this method with your own custom content, consider including the - * standard layout {@link android.R.layout#list_content} in your layout file, so that you - * continue to retain all of the standard behavior of ListFragment. In particular, this is - * currently the only way to have the built-in indeterminant progress state be shown. - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final Context context = getActivity(); - - FrameLayout root = new FrameLayout(context); - - // ------------------------------------------------------------------ - - LinearLayout pframe = new LinearLayout(context); - pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); - pframe.setOrientation(LinearLayout.VERTICAL); - pframe.setVisibility(View.GONE); - pframe.setGravity(Gravity.CENTER); - - ProgressBar progress = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge); - pframe.addView(progress, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - - root.addView(pframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - FrameLayout lframe = new FrameLayout(context); - lframe.setId(INTERNAL_LIST_CONTAINER_ID); - - TextView tv = new TextView(getActivity()); - tv.setId(INTERNAL_EMPTY_ID); - tv.setGravity(Gravity.CENTER); - lframe.addView(tv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - ExpandableListView lv = new ExpandableListView(getActivity()); - lv.setId(android.R.id.list); - lv.setDrawSelectorOnTop(false); - lframe.addView(lv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - root.addView(lframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - return root; - } - - /** - * Attach to list view once the view hierarchy has been created. - */ - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - ensureList(); - } - - /** - * Detach from list view. - */ - @Override - public void onDestroyView() { - mHandler.removeCallbacks(mRequestFocus); - mExpandableList = null; - mExpandableListShown = false; - mEmptyView = mProgressContainer = mExpandableListContainer = null; - mStandardEmptyView = null; - super.onDestroyView(); - } - - /** - * This method will be called when an item in the list is selected. Subclasses should override. - * Subclasses can call getListView().getItemAtPosition(position) if they need to access the data - * associated with the selected item. - * - * @param l - * The ListView where the click happened - * @param v - * The view that was clicked within the ListView - * @param position - * The position of the view in the list - * @param id - * The row id of the item that was clicked - */ - public void onListItemClick(ExpandableListView l, View v, int position, long id) { - } - - /** - * Provide the cursor for the list view. - */ - public void setListAdapter(ExpandableListAdapter adapter) { - boolean hadAdapter = mAdapter != null; - mAdapter = adapter; - if (mExpandableList != null) { - mExpandableList.setAdapter(adapter); - if (!mExpandableListShown && !hadAdapter) { - // The list was hidden, and previously didn't have an - // adapter. It is now time to show it. - setListShown(true, getView().getWindowToken() != null); - } - } - } - - /** - * Set the currently selected list item to the specified position with the adapter's data - * - * @param position - */ - public void setSelection(int position) { - ensureList(); - mExpandableList.setSelection(position); - } - - /** - * Get the position of the currently selected list item. - */ - public int getSelectedItemPosition() { - ensureList(); - return mExpandableList.getSelectedItemPosition(); - } - - /** - * Get the cursor row ID of the currently selected list item. - */ - public long getSelectedItemId() { - ensureList(); - return mExpandableList.getSelectedItemId(); - } - - /** - * Get the activity's list view widget. - */ - public ExpandableListView getListView() { - ensureList(); - return mExpandableList; - } - - /** - * The default content for a ListFragment has a TextView that can be shown when the list is - * empty. If you would like to have it shown, call this method to supply the text it should use. - */ - public void setEmptyText(CharSequence text) { - ensureList(); - if (mStandardEmptyView == null) { - throw new IllegalStateException("Can't be used with a custom content view"); - } - mStandardEmptyView.setText(text); - if (mEmptyText == null) { - mExpandableList.setEmptyView(mStandardEmptyView); - } - mEmptyText = text; - } - - /** - * Control whether the list is being displayed. You can make it not displayed if you are waiting - * for the initial data to show in it. During this time an indeterminant progress indicator will - * be shown instead. - * - *

- * Applications do not normally need to use this themselves. The default behavior of - * ListFragment is to start with the list not being shown, only showing it once an adapter is - * given with {@link #setListAdapter(ListAdapter)}. If the list at that point had not been - * shown, when it does get shown it will be do without the user ever seeing the hidden state. - * - * @param shown - * If true, the list view is shown; if false, the progress indicator. The initial - * value is true. - */ - public void setListShown(boolean shown) { - setListShown(shown, true); - } - - /** - * Like {@link #setListShown(boolean)}, but no animation is used when transitioning from the - * previous state. - */ - public void setListShownNoAnimation(boolean shown) { - setListShown(shown, false); - } - - /** - * Control whether the list is being displayed. You can make it not displayed if you are waiting - * for the initial data to show in it. During this time an indeterminant progress indicator will - * be shown instead. - * - * @param shown - * If true, the list view is shown; if false, the progress indicator. The initial - * value is true. - * @param animate - * If true, an animation will be used to transition to the new state. - */ - private void setListShown(boolean shown, boolean animate) { - ensureList(); - if (mProgressContainer == null) { - throw new IllegalStateException("Can't be used with a custom content view"); - } - if (mExpandableListShown == shown) { - return; - } - mExpandableListShown = shown; - if (shown) { - if (animate) { - mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_out)); - mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_in)); - } else { - mProgressContainer.clearAnimation(); - mExpandableListContainer.clearAnimation(); - } - mProgressContainer.setVisibility(View.GONE); - mExpandableListContainer.setVisibility(View.VISIBLE); - } else { - if (animate) { - mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_in)); - mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_out)); - } else { - mProgressContainer.clearAnimation(); - mExpandableListContainer.clearAnimation(); - } - mProgressContainer.setVisibility(View.VISIBLE); - mExpandableListContainer.setVisibility(View.GONE); - } - } - - /** - * Get the ListAdapter associated with this activity's ListView. - */ - public ExpandableListAdapter getListAdapter() { - return mAdapter; - } - - private void ensureList() { - if (mExpandableList != null) { - return; - } - View root = getView(); - if (root == null) { - throw new IllegalStateException("Content view not yet created"); - } - if (root instanceof ExpandableListView) { - mExpandableList = (ExpandableListView) root; - } else { - mStandardEmptyView = (TextView) root.findViewById(INTERNAL_EMPTY_ID); - if (mStandardEmptyView == null) { - mEmptyView = root.findViewById(android.R.id.empty); - } else { - mStandardEmptyView.setVisibility(View.GONE); - } - mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID); - mExpandableListContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID); - View rawExpandableListView = root.findViewById(android.R.id.list); - if (!(rawExpandableListView instanceof ExpandableListView)) { - if (rawExpandableListView == null) { - throw new RuntimeException( - "Your content must have a ListView whose id attribute is " - + "'android.R.id.list'"); - } - throw new RuntimeException( - "Content has view with id attribute 'android.R.id.list' " - + "that is not a ListView class"); - } - mExpandableList = (ExpandableListView) rawExpandableListView; - if (mEmptyView != null) { - mExpandableList.setEmptyView(mEmptyView); - } else if (mEmptyText != null) { - mStandardEmptyView.setText(mEmptyText); - mExpandableList.setEmptyView(mStandardEmptyView); - } - } - mExpandableListShown = true; - mExpandableList.setOnItemClickListener(mOnClickListener); - if (mAdapter != null) { - ExpandableListAdapter adapter = mAdapter; - mAdapter = null; - setListAdapter(adapter); - } else { - // We are starting without an adapter, so assume we won't - // have our data right away and start with the progress indicator. - if (mProgressContainer != null) { - setListShown(false, false); - } - } - mHandler.post(mRequestFocus); - } - - /** - * Override this to populate the context menu when an item is long pressed. menuInfo will - * contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo} whose - * packedPosition is a packed position that should be used with - * {@link ExpandableListView#getPackedPositionType(long)} and the other similar methods. - *

- * {@inheritDoc} - */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - } - - /** - * Override this for receiving callbacks when a child has been clicked. - *

- * {@inheritDoc} - */ - public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, - int childPosition, long id) { - return false; - } - - /** - * Override this for receiving callbacks when a group has been collapsed. - */ - public void onGroupCollapse(int groupPosition) { - } - - /** - * Override this for receiving callbacks when a group has been expanded. - */ - public void onGroupExpand(int groupPosition) { - } - - // /** - // * Ensures the expandable list view has been created before Activity restores all - // * of the view states. - // * - // *@see Activity#onRestoreInstanceState(Bundle) - // */ - // @Override - // protected void onRestoreInstanceState(Bundle state) { - // ensureList(); - // super.onRestoreInstanceState(state); - // } - - /** - * Updates the screen state (current list and other views) when the content changes. - * - * @see Activity#onContentChanged() - */ - - public void onContentChanged() { - // super.onContentChanged(); - View emptyView = getView().findViewById(android.R.id.empty); - mExpandableList = (ExpandableListView) getView().findViewById(android.R.id.list); - if (mExpandableList == null) { - throw new RuntimeException( - "Your content must have a ExpandableListView whose id attribute is " - + "'android.R.id.list'"); - } - if (emptyView != null) { - mExpandableList.setEmptyView(emptyView); - } - mExpandableList.setOnChildClickListener(this); - mExpandableList.setOnGroupExpandListener(this); - mExpandableList.setOnGroupCollapseListener(this); - - if (mFinishedStart) { - setListAdapter(mAdapter); - } - mFinishedStart = true; - } - - /** - * Get the activity's expandable list view widget. This can be used to get the selection, set - * the selection, and many other useful functions. - * - * @see ExpandableListView - */ - public ExpandableListView getExpandableListView() { - ensureList(); - return mExpandableList; - } - - /** - * Get the ExpandableListAdapter associated with this activity's ExpandableListView. - */ - public ExpandableListAdapter getExpandableListAdapter() { - return mAdapter; - } - - /** - * Gets the ID of the currently selected group or child. - * - * @return The ID of the currently selected group or child. - */ - public long getSelectedId() { - return mExpandableList.getSelectedId(); - } - - /** - * Gets the position (in packed position representation) of the currently selected group or - * child. Use {@link ExpandableListView#getPackedPositionType}, - * {@link ExpandableListView#getPackedPositionGroup}, and - * {@link ExpandableListView#getPackedPositionChild} to unpack the returned packed position. - * - * @return A packed position representation containing the currently selected group or child's - * position and type. - */ - public long getSelectedPosition() { - return mExpandableList.getSelectedPosition(); - } - - /** - * Sets the selection to the specified child. If the child is in a collapsed group, the group - * will only be expanded and child subsequently selected if shouldExpandGroup is set to true, - * otherwise the method will return false. - * - * @param groupPosition - * The position of the group that contains the child. - * @param childPosition - * The position of the child within the group. - * @param shouldExpandGroup - * Whether the child's group should be expanded if it is collapsed. - * @return Whether the selection was successfully set on the child. - */ - public boolean setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup) { - return mExpandableList.setSelectedChild(groupPosition, childPosition, shouldExpandGroup); - } - - /** - * Sets the selection to the specified group. - * - * @param groupPosition - * The position of the group that should be selected. - */ - public void setSelectedGroup(int groupPosition) { - mExpandableList.setSelectedGroup(groupPosition); - } -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java deleted file mode 100644 index 1d5f9e3d7..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import android.content.Context; -import android.preference.ListPreference; -import android.util.AttributeSet; - -/** - * A list preference which persists its values as integers instead of strings. - * Code reading the values should use - * {@link android.content.SharedPreferences#getInt}. - * When using XML-declared arrays for entry values, the arrays should be regular - * string arrays containing valid integer values. - * - * @author Rodrigo Damazio - */ -public class IntegerListPreference extends ListPreference { - - public IntegerListPreference(Context context) { - super(context); - - verifyEntryValues(null); - } - - public IntegerListPreference(Context context, AttributeSet attrs) { - super(context, attrs); - - verifyEntryValues(null); - } - - @Override - public void setEntryValues(CharSequence[] entryValues) { - CharSequence[] oldValues = getEntryValues(); - super.setEntryValues(entryValues); - verifyEntryValues(oldValues); - } - - @Override - public void setEntryValues(int entryValuesResId) { - CharSequence[] oldValues = getEntryValues(); - super.setEntryValues(entryValuesResId); - verifyEntryValues(oldValues); - } - - @Override - protected String getPersistedString(String defaultReturnValue) { - // During initial load, there's no known default value - int defaultIntegerValue = Integer.MIN_VALUE; - if (defaultReturnValue != null) { - defaultIntegerValue = Integer.parseInt(defaultReturnValue); - } - - // When the list preference asks us to read a string, instead read an - // integer. - int value = getPersistedInt(defaultIntegerValue); - return Integer.toString(value); - } - - @Override - protected boolean persistString(String value) { - // When asked to save a string, instead save an integer - return persistInt(Integer.parseInt(value)); - } - - private void verifyEntryValues(CharSequence[] oldValues) { - CharSequence[] entryValues = getEntryValues(); - if (entryValues == null) { - return; - } - - for (CharSequence entryValue : entryValues) { - try { - Integer.parseInt(entryValue.toString()); - } catch (NumberFormatException nfe) { - super.setEntryValues(oldValues); - throw nfe; - } - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java deleted file mode 100644 index f8adc3b72..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPSecretKey; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.util.Choice; -import org.thialfihar.android.apg.R; - -import android.app.DatePickerDialog; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.util.AttributeSet; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.DatePicker; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.Spinner; -import android.widget.TextView; - -import java.text.DateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Vector; - -public class KeyEditor extends LinearLayout implements Editor, OnClickListener { - private PGPSecretKey mKey; - - private EditorListener mEditorListener = null; - - private boolean mIsMasterKey; - ImageButton mDeleteButton; - TextView mAlgorithm; - TextView mKeyId; - Spinner mUsage; - TextView mCreationDate; - Button mExpiryDateButton; - GregorianCalendar mExpiryDate; - - private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = new DatePickerDialog.OnDateSetListener() { - public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { - GregorianCalendar date = new GregorianCalendar(year, monthOfYear, dayOfMonth); - setExpiryDate(date); - } - }; - - public KeyEditor(Context context) { - super(context); - } - - public KeyEditor(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - setDrawingCacheEnabled(true); - setAlwaysDrawnWithCacheEnabled(true); - - mAlgorithm = (TextView) findViewById(R.id.algorithm); - mKeyId = (TextView) findViewById(R.id.keyId); - mCreationDate = (TextView) findViewById(R.id.creation); - mExpiryDateButton = (Button) findViewById(R.id.expiry); - mUsage = (Spinner) findViewById(R.id.usage); - Choice choices[] = { - new Choice(Id.choice.usage.sign_only, getResources().getString( - R.string.choice_signOnly)), - new Choice(Id.choice.usage.encrypt_only, getResources().getString( - R.string.choice_encryptOnly)), - new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( - R.string.choice_signAndEncrypt)), }; - ArrayAdapter adapter = new ArrayAdapter(getContext(), - android.R.layout.simple_spinner_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mUsage.setAdapter(adapter); - - mDeleteButton = (ImageButton) findViewById(R.id.delete); - mDeleteButton.setOnClickListener(this); - - setExpiryDate(null); - - mExpiryDateButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - GregorianCalendar date = mExpiryDate; - if (date == null) { - date = new GregorianCalendar(); - } - - DatePickerDialog dialog = new DatePickerDialog(getContext(), - mExpiryDateSetListener, date.get(Calendar.YEAR), date.get(Calendar.MONTH), - date.get(Calendar.DAY_OF_MONTH)); - dialog.setCancelable(true); - dialog.setButton(Dialog.BUTTON_NEGATIVE, getContext() - .getString(R.string.btn_noDate), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - setExpiryDate(null); - } - }); - dialog.show(); - } - }); - - super.onFinishInflate(); - } - - public void setValue(PGPSecretKey key, boolean isMasterKey, int usage) { - mKey = key; - - mIsMasterKey = isMasterKey; - if (mIsMasterKey) { - mDeleteButton.setVisibility(View.INVISIBLE); - } - - mAlgorithm.setText(PGPHelper.getAlgorithmInfo(key)); - String keyId1Str = PGPHelper.getSmallFingerPrint(key.getKeyID()); - String keyId2Str = PGPHelper.getSmallFingerPrint(key.getKeyID() >> 32); - mKeyId.setText(keyId1Str + " " + keyId2Str); - - Vector choices = new Vector(); - boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT); - boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA); - if (!isElGamalKey) { - choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString( - R.string.choice_signOnly))); - } - if (!mIsMasterKey && !isDSAKey) { - choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString( - R.string.choice_encryptOnly))); - } - if (!isElGamalKey && !isDSAKey) { - choices.add(new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( - R.string.choice_signAndEncrypt))); - } - - ArrayAdapter adapter = new ArrayAdapter(getContext(), - android.R.layout.simple_spinner_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mUsage.setAdapter(adapter); - - // Set value in choice dropdown to key - int selectId = 0; - if (PGPHelper.isEncryptionKey(key)) { - if (PGPHelper.isSigningKey(key)) { - selectId = Id.choice.usage.sign_and_encrypt; - } else { - selectId = Id.choice.usage.encrypt_only; - } - } else { - // set usage if it is predefined - if (usage != -1) { - selectId = usage; - } else { - selectId = Id.choice.usage.sign_only; - } - - } - - for (int i = 0; i < choices.size(); ++i) { - if (choices.get(i).getId() == selectId) { - mUsage.setSelection(i); - break; - } - } - - GregorianCalendar cal = new GregorianCalendar(); - cal.setTime(PGPHelper.getCreationDate(key)); - mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime())); - cal = new GregorianCalendar(); - Date date = PGPHelper.getExpiryDate(key); - if (date == null) { - setExpiryDate(null); - } else { - cal.setTime(PGPHelper.getExpiryDate(key)); - setExpiryDate(cal); - } - - } - - public PGPSecretKey getValue() { - return mKey; - } - - public void onClick(View v) { - final ViewGroup parent = (ViewGroup) getParent(); - if (v == mDeleteButton) { - parent.removeView(this); - if (mEditorListener != null) { - mEditorListener.onDeleted(this); - } - } - } - - public void setEditorListener(EditorListener listener) { - mEditorListener = listener; - } - - private void setExpiryDate(GregorianCalendar date) { - mExpiryDate = date; - if (date == null) { - mExpiryDateButton.setText(R.string.none); - } else { - mExpiryDateButton.setText(DateFormat.getDateInstance().format(date.getTime())); - } - } - - public GregorianCalendar getExpiryDate() { - return mExpiryDate; - } - - public int getUsage() { - return ((Choice) mUsage.getSelectedItem()).getId(); - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java deleted file mode 100644 index 1f5c3a97e..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.util.Log; - -import android.content.Context; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.MergeCursor; -import android.net.Uri; -import android.provider.BaseColumns; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CursorTreeAdapter; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -public class KeyListAdapter extends CursorTreeAdapter { - private Context mContext; - private LayoutInflater mInflater; - - protected int mKeyType; - - private static final int CHILD_KEY = 0; - private static final int CHILD_USER_ID = 1; - private static final int CHILD_FINGERPRINT = 2; - - public KeyListAdapter(Context context, Cursor groupCursor, int keyType) { - super(groupCursor, context); - mContext = context; - mInflater = LayoutInflater.from(context); - mKeyType = keyType; - } - - /** - * Inflate new view for group items - */ - @Override - public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) { - return mInflater.inflate(R.layout.key_list_group_item, null); - } - - /** - * Binds TextViews from group view to results from database group cursor. - */ - @Override - protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) { - int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID); - - TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); - mainUserId.setText(R.string.unknownUserId); - TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - mainUserIdRest.setText(""); - - String userId = cursor.getString(userIdIndex); - if (userId != null) { - String[] userIdSplit = OtherHelper.splitUserId(userId); - - if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - } - mainUserId.setText(userIdSplit[0]); - } - - if (mainUserId.getText().length() == 0) { - mainUserId.setText(R.string.unknownUserId); - } - - if (mainUserIdRest.getText().length() == 0) { - mainUserIdRest.setVisibility(View.GONE); - } - } - - /** - * Inflate new view for child items - */ - @Override - public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) { - return mInflater.inflate(R.layout.key_list_child_item, null); - } - - /** - * Bind TextViews from view of childs based on query results - */ - @Override - protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { - LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout); - LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout); - - // first entry is fingerprint - if (cursor.getPosition() == 0) { - // show only userId layout - keyLayout.setVisibility(View.GONE); - userIdLayout.setVisibility(View.VISIBLE); - - String fingerprint = PGPHelper.getFingerPrint(context, - cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID))); - fingerprint = fingerprint.replace(" ", "\n"); - - TextView userId = (TextView) view.findViewById(R.id.userId); - if (userId == null) { - Log.d(Constants.TAG, "userId is null!"); - } - userId.setText(context.getString(R.string.fingerprint) + ":\n" + fingerprint); - } else { - // differentiate between keys and userIds in MergeCursor - if (cursor.getColumnIndex(Keys.KEY_ID) != -1) { - keyLayout.setVisibility(View.VISIBLE); - userIdLayout.setVisibility(View.GONE); - - String keyIdStr = PGPHelper.getSmallFingerPrint(cursor.getLong(cursor - .getColumnIndex(Keys.KEY_ID))); - String algorithmStr = PGPHelper.getAlgorithmInfo( - cursor.getInt(cursor.getColumnIndex(Keys.ALGORITHM)), - cursor.getInt(cursor.getColumnIndex(Keys.KEY_SIZE))); - - TextView keyId = (TextView) view.findViewById(R.id.keyId); - keyId.setText(keyIdStr); - - TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails); - keyDetails.setText("(" + algorithmStr + ")"); - - ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey); - if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) != 1) { - masterKeyIcon.setVisibility(View.INVISIBLE); - } else { - masterKeyIcon.setVisibility(View.VISIBLE); - } - - ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); - if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) { - encryptIcon.setVisibility(View.GONE); - } else { - encryptIcon.setVisibility(View.VISIBLE); - } - - ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); - if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) { - signIcon.setVisibility(View.GONE); - } else { - signIcon.setVisibility(View.VISIBLE); - } - } else { - keyLayout.setVisibility(View.GONE); - userIdLayout.setVisibility(View.VISIBLE); - - String userIdStr = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID)); - - TextView userId = (TextView) view.findViewById(R.id.userId); - userId.setText(userIdStr); - } - } - } - - /** - * Given the group cursor, we start cursors for a fingerprint, keys, and userIds, which are - * merged together and build the child cursor - */ - @Override - protected Cursor getChildrenCursor(Cursor groupCursor) { - final long keyRingRowId = groupCursor.getLong(groupCursor.getColumnIndex(BaseColumns._ID)); - - Cursor fingerprintCursor = getChildCursor(keyRingRowId, CHILD_FINGERPRINT); - Cursor keyCursor = getChildCursor(keyRingRowId, CHILD_KEY); - Cursor userIdCursor = getChildCursor(keyRingRowId, CHILD_USER_ID); - - MergeCursor mergeCursor = new MergeCursor(new Cursor[] { fingerprintCursor, keyCursor, - userIdCursor }); - Log.d(Constants.TAG, "mergeCursor:" + DatabaseUtils.dumpCursorToString(mergeCursor)); - - return mergeCursor; - } - - /** - * This builds a cursor for a specific type of children - * - * @param keyRingRowId - * foreign row id of the keyRing - * @param type - * @return - */ - private Cursor getChildCursor(long keyRingRowId, int type) { - Uri uri = null; - String[] projection = null; - String sortOrder = null; - String selection = null; - - switch (type) { - case CHILD_FINGERPRINT: - projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, - Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; - sortOrder = Keys.RANK + " ASC"; - - // use only master key for fingerprint - selection = Keys.IS_MASTER_KEY + " = 1 "; - - if (mKeyType == Id.type.public_key) { - uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId)); - } else { - uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId)); - } - break; - - case CHILD_KEY: - projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, - Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; - sortOrder = Keys.RANK + " ASC"; - - if (mKeyType == Id.type.public_key) { - uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId)); - } else { - uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId)); - } - - break; - - case CHILD_USER_ID: - projection = new String[] { UserIds._ID, UserIds.USER_ID, UserIds.RANK, }; - sortOrder = UserIds.RANK + " ASC"; - - // not the main user id - selection = UserIds.RANK + " > 0 "; - - if (mKeyType == Id.type.public_key) { - uri = UserIds.buildPublicUserIdsUri(String.valueOf(keyRingRowId)); - } else { - uri = UserIds.buildSecretUserIdsUri(String.valueOf(keyRingRowId)); - } - - break; - - default: - return null; - - } - - return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder); - } - -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java deleted file mode 100644 index cbea7f031..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import org.thialfihar.android.apg.R; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.TextView; - -public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener { - private EditorListener mEditorListener = null; - - ImageButton mDeleteButton; - TextView mServer; - - public KeyServerEditor(Context context) { - super(context); - } - - public KeyServerEditor(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - setDrawingCacheEnabled(true); - setAlwaysDrawnWithCacheEnabled(true); - - mServer = (TextView) findViewById(R.id.server); - - mDeleteButton = (ImageButton) findViewById(R.id.delete); - mDeleteButton.setOnClickListener(this); - - super.onFinishInflate(); - } - - public void setValue(String value) { - mServer.setText(value); - } - - public String getValue() { - return mServer.getText().toString().trim(); - } - - public void onClick(View v) { - final ViewGroup parent = (ViewGroup)getParent(); - if (v == mDeleteButton) { - parent.removeView(this); - if (mEditorListener != null) { - mEditorListener.onDeleted(this); - } - } - } - - public void setEditorListener(EditorListener listener) { - mEditorListener = listener; - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/SectionView.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/SectionView.java deleted file mode 100644 index 45fbbaba1..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/SectionView.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.PGPConversionHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.ProgressDialogFragment; -import org.thialfihar.android.apg.ui.widget.Editor.EditorListener; -import org.thialfihar.android.apg.util.Choice; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.SherlockFragmentActivity; - -import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.Spinner; -import android.widget.TextView; - -import java.util.Iterator; -import java.util.Vector; - -public class SectionView extends LinearLayout implements OnClickListener, EditorListener { - private LayoutInflater mInflater; - private View mAdd; - private ViewGroup mEditors; - private TextView mTitle; - private int mType = 0; - - private Choice mNewKeyAlgorithmChoice; - private int mNewKeySize; - - private SherlockFragmentActivity mActivity; - - private ProgressDialogFragment mGeneratingDialog; - - public SectionView(Context context) { - super(context); - mActivity = (SherlockFragmentActivity) context; - } - - public SectionView(Context context, AttributeSet attrs) { - super(context, attrs); - mActivity = (SherlockFragmentActivity) context; - } - - public ViewGroup getEditors() { - return mEditors; - } - - public void setType(int type) { - mType = type; - switch (type) { - case Id.type.user_id: { - mTitle.setText(R.string.section_userIds); - break; - } - - case Id.type.key: { - mTitle.setText(R.string.section_keys); - break; - } - - default: { - break; - } - } - } - - /** {@inheritDoc} */ - @Override - protected void onFinishInflate() { - mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - setDrawingCacheEnabled(true); - setAlwaysDrawnWithCacheEnabled(true); - - mAdd = findViewById(R.id.header); - mAdd.setOnClickListener(this); - - mEditors = (ViewGroup) findViewById(R.id.editors); - mTitle = (TextView) findViewById(R.id.title); - - updateEditorsVisible(); - super.onFinishInflate(); - } - - /** {@inheritDoc} */ - public void onDeleted(Editor editor) { - this.updateEditorsVisible(); - } - - protected void updateEditorsVisible() { - final boolean hasChildren = mEditors.getChildCount() > 0; - mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE); - } - - /** {@inheritDoc} */ - public void onClick(View v) { - switch (mType) { - case Id.type.user_id: { - UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, - mEditors, false); - view.setEditorListener(this); - if (mEditors.getChildCount() == 0) { - view.setIsMainUserId(true); - } - mEditors.addView(view); - break; - } - - case Id.type.key: { - AlertDialog.Builder dialog = new AlertDialog.Builder(getContext()); - - View view = mInflater.inflate(R.layout.create_key, null); - dialog.setView(view); - dialog.setTitle(R.string.title_createKey); - - boolean wouldBeMasterKey = (mEditors.getChildCount() == 0); - - final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm); - Vector choices = new Vector(); - choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString(R.string.dsa))); - if (!wouldBeMasterKey) { - choices.add(new Choice(Id.choice.algorithm.elgamal, getResources().getString( - R.string.elgamal))); - } - - choices.add(new Choice(Id.choice.algorithm.rsa, getResources().getString(R.string.rsa))); - - ArrayAdapter adapter = new ArrayAdapter(getContext(), - android.R.layout.simple_spinner_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - algorithm.setAdapter(adapter); - // make RSA the default - for (int i = 0; i < choices.size(); ++i) { - if (choices.get(i).getId() == Id.choice.algorithm.rsa) { - algorithm.setSelection(i); - break; - } - } - - final EditText keySize = (EditText) view.findViewById(R.id.create_key_size); - - dialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface di, int id) { - di.dismiss(); - try { - mNewKeySize = Integer.parseInt("" + keySize.getText()); - } catch (NumberFormatException e) { - mNewKeySize = 0; - } - - mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem(); - createKey(); - } - }); - - dialog.setCancelable(true); - dialog.setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface di, int id) { - di.dismiss(); - } - }); - - dialog.create().show(); - break; - } - - default: { - break; - } - } - this.updateEditorsVisible(); - } - - public void setUserIds(Vector list) { - if (mType != Id.type.user_id) { - return; - } - - mEditors.removeAllViews(); - for (String userId : list) { - UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, - mEditors, false); - view.setEditorListener(this); - view.setValue(userId); - if (mEditors.getChildCount() == 0) { - view.setIsMainUserId(true); - } - mEditors.addView(view); - } - - this.updateEditorsVisible(); - } - - public void setKeys(Vector list, Vector usages) { - if (mType != Id.type.key) { - return; - } - - mEditors.removeAllViews(); - - // go through all keys and set view based on them - for (int i = 0; i < list.size(); i++) { - KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, - false); - view.setEditorListener(this); - boolean isMasterKey = (mEditors.getChildCount() == 0); - view.setValue(list.get(i), isMasterKey, usages.get(i)); - mEditors.addView(view); - } - - this.updateEditorsVisible(); - } - - private void createKey() { - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(mActivity, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_GENERATE_KEY); - - // fill values for this action - Bundle data = new Bundle(); - - String passPhrase; - if (mEditors.getChildCount() > 0) { - PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue(); - passPhrase = PassphraseCacheService - .getCachedPassphrase(mActivity, masterKey.getKeyID()); - - data.putByteArray(ApgIntentService.MASTER_KEY, - PGPConversionHelper.PGPSecretKeyToBytes(masterKey)); - } else { - passPhrase = ""; - } - data.putString(ApgIntentService.SYMMETRIC_PASSPHRASE, passPhrase); - data.putInt(ApgIntentService.ALGORITHM, mNewKeyAlgorithmChoice.getId()); - data.putInt(ApgIntentService.KEY_SIZE, mNewKeySize); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // show progress dialog - mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating, - ProgressDialog.STYLE_SPINNER); - - // Message is received after generating is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(mActivity, mGeneratingDialog) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get new key from data bundle returned from service - Bundle data = message.getData(); - PGPSecretKeyRing newKeyRing = (PGPSecretKeyRing) PGPConversionHelper - .BytesToPGPKeyRing(data.getByteArray(ApgIntentService.RESULT_NEW_KEY)); - - boolean isMasterKey = (mEditors.getChildCount() == 0); - - // take only the key from this ring - PGPSecretKey newKey = null; - @SuppressWarnings("unchecked") - Iterator it = newKeyRing.getSecretKeys(); - - if (isMasterKey) { - newKey = it.next(); - } else { - // first one is the master key - it.next(); - newKey = it.next(); - } - - // add view with new key - KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, - mEditors, false); - view.setEditorListener(SectionView.this); - view.setValue(newKey, isMasterKey, -1); - mEditors.addView(view); - SectionView.this.updateEditorsVisible(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - mGeneratingDialog.show(mActivity.getSupportFragmentManager(), "dialog"); - - // start service with intent - mActivity.startService(intent); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java deleted file mode 100644 index 0f6bd5f11..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; - -import android.content.Context; -import android.database.Cursor; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.ListView; -import android.widget.TextView; - -public class SelectKeyCursorAdapter extends CursorAdapter { - - protected int mKeyType; - - private LayoutInflater mInflater; - private ListView mListView; - - public final static String PROJECTION_ROW_AVAILABLE = "available"; - public final static String PROJECTION_ROW_VALID = "valid"; - - @SuppressWarnings("deprecation") - public SelectKeyCursorAdapter(Context context, ListView listView, Cursor c, int keyType) { - super(context, c); - - mInflater = LayoutInflater.from(context); - mListView = listView; - mKeyType = keyType; - } - - public String getUserId(int position) { - mCursor.moveToPosition(position); - return mCursor.getString(mCursor.getColumnIndex(UserIds.USER_ID)); - } - - public long getMasterKeyId(int position) { - mCursor.moveToPosition(position); - return mCursor.getLong(mCursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - boolean valid = cursor.getInt(cursor - .getColumnIndex(PROJECTION_ROW_VALID)) > 0; - - 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 = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID)); - if (userId != null) { - String[] userIdSplit = OtherHelper.splitUserId(userId); - - if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - } - mainUserId.setText(userIdSplit[0]); - } - - long masterKeyId = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); - keyId.setText(PGPHelper.getSmallFingerPrint(masterKeyId)); - - if (mainUserIdRest.getText().length() == 0) { - mainUserIdRest.setVisibility(View.GONE); - } - - if (valid) { - if (mKeyType == Id.type.public_key) { - status.setText(R.string.canEncrypt); - } else { - status.setText(R.string.canSign); - } - } else { - if (cursor.getInt(cursor - .getColumnIndex(PROJECTION_ROW_AVAILABLE)) > 0) { - // has some CAN_ENCRYPT keys, but col(ROW_VALID) = 0, so must be revoked or - // expired - status.setText(R.string.expired); - } else { - status.setText(R.string.noKey); - } - } - - CheckBox selected = (CheckBox) view.findViewById(R.id.selected); - if (mKeyType == Id.type.public_key) { - selected.setVisibility(View.VISIBLE); - - if (!valid) { - mListView.setItemChecked(cursor.getPosition(), false); - } - - selected.setChecked(mListView.isItemChecked(cursor.getPosition())); - selected.setEnabled(valid); - } else { - selected.setVisibility(View.GONE); - } - - status.setText(status.getText() + " "); - - view.setEnabled(valid); - mainUserId.setEnabled(valid); - mainUserIdRest.setEnabled(valid); - keyId.setEnabled(valid); - status.setEnabled(valid); - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.select_key_item, null); - } - -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java b/org_apg/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java deleted file mode 100644 index 2495a4f9d..000000000 --- a/org_apg/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.thialfihar.android.apg.R; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.RadioButton; - -public class UserIdEditor extends LinearLayout implements Editor, OnClickListener { - private EditorListener mEditorListener = null; - - private ImageButton mDeleteButton; - private RadioButton mIsMainUserId; - private EditText mName; - private EditText mEmail; - private EditText mComment; - - // see http://www.regular-expressions.info/email.html - // RFC 2822 if we omit the syntax using double quotes and square brackets - // android.util.Patterns.EMAIL_ADDRESS is only available as of Android 2.2+ - private static final Pattern EMAIL_PATTERN = Pattern - .compile( - "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", - Pattern.CASE_INSENSITIVE); - - public static class NoNameException extends Exception { - static final long serialVersionUID = 0xf812773343L; - - public NoNameException(String message) { - super(message); - } - } - - public static class NoEmailException extends Exception { - static final long serialVersionUID = 0xf812773344L; - - public NoEmailException(String message) { - super(message); - } - } - - public static class InvalidEmailException extends Exception { - static final long serialVersionUID = 0xf812773345L; - - public InvalidEmailException(String message) { - super(message); - } - } - - public UserIdEditor(Context context) { - super(context); - } - - public UserIdEditor(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - setDrawingCacheEnabled(true); - setAlwaysDrawnWithCacheEnabled(true); - - mDeleteButton = (ImageButton) findViewById(R.id.delete); - mDeleteButton.setOnClickListener(this); - mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId); - mIsMainUserId.setOnClickListener(this); - - mName = (EditText) findViewById(R.id.name); - mEmail = (EditText) findViewById(R.id.email); - mComment = (EditText) findViewById(R.id.comment); - - super.onFinishInflate(); - } - - public void setValue(String userId) { - mName.setText(""); - mComment.setText(""); - mEmail.setText(""); - - Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$"); - Matcher matcher = withComment.matcher(userId); - if (matcher.matches()) { - mName.setText(matcher.group(1)); - mComment.setText(matcher.group(2)); - mEmail.setText(matcher.group(3)); - return; - } - - Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$"); - matcher = withoutComment.matcher(userId); - if (matcher.matches()) { - mName.setText(matcher.group(1)); - mEmail.setText(matcher.group(2)); - return; - } - } - - public String getValue() throws NoNameException, NoEmailException, InvalidEmailException { - String name = ("" + mName.getText()).trim(); - String email = ("" + mEmail.getText()).trim(); - String comment = ("" + mComment.getText()).trim(); - - if (email.length() > 0) { - Matcher emailMatcher = EMAIL_PATTERN.matcher(email); - if (!emailMatcher.matches()) { - throw new InvalidEmailException(getContext().getString(R.string.error_invalidEmail, - email)); - } - } - - String userId = name; - if (comment.length() > 0) { - userId += " (" + comment + ")"; - } - if (email.length() > 0) { - userId += " <" + email + ">"; - } - - if (userId.equals("")) { - // ok, empty one... - return userId; - } - - // otherwise make sure that name and email exist - if (name.equals("")) { - throw new NoNameException("need a name"); - } - - if (email.equals("")) { - throw new NoEmailException("need an email"); - } - - return userId; - } - - public void onClick(View v) { - final ViewGroup parent = (ViewGroup) getParent(); - if (v == mDeleteButton) { - boolean wasMainUserId = mIsMainUserId.isChecked(); - parent.removeView(this); - if (mEditorListener != null) { - mEditorListener.onDeleted(this); - } - if (wasMainUserId && parent.getChildCount() > 0) { - UserIdEditor editor = (UserIdEditor) parent.getChildAt(0); - editor.setIsMainUserId(true); - } - } else if (v == mIsMainUserId) { - for (int i = 0; i < parent.getChildCount(); ++i) { - UserIdEditor editor = (UserIdEditor) parent.getChildAt(i); - if (editor == this) { - editor.setIsMainUserId(true); - } else { - editor.setIsMainUserId(false); - } - } - } - } - - public void setIsMainUserId(boolean value) { - mIsMainUserId.setChecked(value); - } - - public boolean isMainUserId() { - return mIsMainUserId.isChecked(); - } - - public void setEditorListener(EditorListener listener) { - mEditorListener = listener; - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/Choice.java b/org_apg/src/org/thialfihar/android/apg/util/Choice.java deleted file mode 100644 index 94cc58f55..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/Choice.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * 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.util; - -public class Choice { - private String mName; - private int mId; - - public Choice() { - mId = -1; - mName = ""; - } - - public Choice(int id, String name) { - mId = id; - mName = name; - } - - public int getId() { - return mId; - } - - public String getName() { - return mName; - } - - @Override - public String toString() { - return mName; - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/Compatibility.java b/org_apg/src/org/thialfihar/android/apg/util/Compatibility.java deleted file mode 100644 index 1597a01f4..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/Compatibility.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.util; - -import java.lang.reflect.Method; - -import android.content.Context; -import org.thialfihar.android.apg.util.Log; - -public class Compatibility { - - private static final String clipboardLabel = "APG"; - - /** - * Wrapper around ClipboardManager based on Android version using Reflection API - * - * @param context - * @param text - */ - public static void copyToClipboard(Context context, String text) { - Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE); - try { - if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) { - Method method = clipboard.getClass().getMethod("setText", CharSequence.class); - method.invoke(clipboard, text); - } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) { - Class clazz = Class.forName("android.content.ClipData"); - Method method = clazz.getMethod("newPlainText", CharSequence.class, - CharSequence.class); - Object clip = method.invoke(null, clipboardLabel, text); - method = clipboard.getClass().getMethod("setPrimaryClip", clazz); - method.invoke(clipboard, clip); - } - } catch (Exception e) { - Log.e("ProjectsException", "There was and error copying the text to the clipboard: " - + e.getMessage()); - } - } - - /** - * Wrapper around ClipboardManager based on Android version using Reflection API - * - * @param context - * @param text - */ - public static CharSequence getClipboardText(Context context) { - Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE); - try { - if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) { - // CharSequence text = clipboard.getText(); - Method method = clipboard.getClass().getMethod("getText"); - Object text = method.invoke(clipboard); - - return (CharSequence) text; - } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) { - // ClipData clipData = clipboard.getPrimaryClip(); - Method methodGetPrimaryClip = clipboard.getClass().getMethod("getPrimaryClip"); - Object clipData = methodGetPrimaryClip.invoke(clipboard); - - // ClipData.Item clipDataItem = clipData.getItemAt(0); - Method methodGetItemAt = clipData.getClass().getMethod("getItemAt", Integer.TYPE); - Object clipDataItem = methodGetItemAt.invoke(clipData, 0); - - // CharSequence text = clipDataItem.coerceToText(context); - Method methodGetString = clipDataItem.getClass().getMethod("coerceToText", - Context.class); - Object text = methodGetString.invoke(clipDataItem, context); - - return (CharSequence) text; - } else { - return null; - } - } catch (Exception e) { - Log.e("ProjectsException", "There was and error getting the text from the clipboard: " - + e.getMessage()); - - return null; - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/HkpKeyServer.java b/org_apg/src/org/thialfihar/android/apg/util/HkpKeyServer.java deleted file mode 100644 index 492e2e3d0..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/HkpKeyServer.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2011 Senecaso - * - * 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.util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLEncoder; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.util.EntityUtils; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; - -import android.text.Html; - -public class HkpKeyServer extends KeyServer { - private static class HttpError extends Exception { - private static final long serialVersionUID = 1718783705229428893L; - private int mCode; - private String mData; - - public HttpError(int code, String data) { - super("" + code + ": " + data); - mCode = code; - mData = data; - } - - public int getCode() { - return mCode; - } - - public String getData() { - return mData; - } - } - - private String mHost; - private short mPort = 11371; - - // example: - // pub 2048R/9F5C9090 2009-08-17 Jörg Runge - // <joerg@joergrunge.de> - public static Pattern PUB_KEY_LINE = Pattern - .compile( - "pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)", - Pattern.CASE_INSENSITIVE); - public static Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE - | Pattern.CASE_INSENSITIVE); - - public HkpKeyServer(String host) { - mHost = host; - } - - public HkpKeyServer(String host, short port) { - mHost = host; - mPort = port; - } - - static private String readAll(InputStream in, String encoding) throws IOException { - ByteArrayOutputStream raw = new ByteArrayOutputStream(); - - byte buffer[] = new byte[1 << 16]; - int n = 0; - while ((n = in.read(buffer)) != -1) { - raw.write(buffer, 0, n); - } - - if (encoding == null) { - encoding = "utf8"; - } - return raw.toString(encoding); - } - - private String query(String request) throws QueryException, HttpError { - InetAddress ips[]; - try { - ips = InetAddress.getAllByName(mHost); - } catch (UnknownHostException e) { - throw new QueryException(e.toString()); - } - for (int i = 0; i < ips.length; ++i) { - try { - String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request; - URL realUrl = new URL(url); - HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); - conn.setConnectTimeout(5000); - conn.setReadTimeout(25000); - conn.connect(); - int response = conn.getResponseCode(); - if (response >= 200 && response < 300) { - return readAll(conn.getInputStream(), conn.getContentEncoding()); - } else { - String data = readAll(conn.getErrorStream(), conn.getContentEncoding()); - throw new HttpError(response, data); - } - } catch (MalformedURLException e) { - // nothing to do, try next IP - } catch (IOException e) { - // nothing to do, try next IP - } - } - - throw new QueryException("querying server(s) for '" + mHost + "' failed"); - } - - @Override - public ArrayList search(String query) throws QueryException, TooManyResponses, - InsufficientQuery { - ArrayList results = new ArrayList(); - - if (query.length() < 3) { - throw new InsufficientQuery(); - } - - String encodedQuery; - try { - encodedQuery = URLEncoder.encode(query, "utf8"); - } catch (UnsupportedEncodingException e) { - return null; - } - String request = "/pks/lookup?op=index&search=" + encodedQuery; - - String data = null; - try { - data = query(request); - } catch (HttpError e) { - if (e.getCode() == 404) { - return results; - } else { - if (e.getData().toLowerCase().contains("no keys found")) { - return results; - } else if (e.getData().toLowerCase().contains("too many")) { - throw new TooManyResponses(); - } else if (e.getData().toLowerCase().contains("insufficient")) { - throw new InsufficientQuery(); - } - } - throw new QueryException("querying server(s) for '" + mHost + "' failed"); - } - - Matcher matcher = PUB_KEY_LINE.matcher(data); - while (matcher.find()) { - KeyInfo info = new KeyInfo(); - info.size = Integer.parseInt(matcher.group(1)); - info.algorithm = matcher.group(2); - info.keyId = PGPHelper.keyFromHex(matcher.group(3)); - info.fingerPrint = PGPHelper.getSmallFingerPrint(info.keyId); - String chunks[] = matcher.group(4).split("-"); - info.date = new GregorianCalendar(Integer.parseInt(chunks[0]), - Integer.parseInt(chunks[1]), Integer.parseInt(chunks[2])).getTime(); - info.userIds = new ArrayList(); - if (matcher.group(5).startsWith("*** KEY")) { - info.revoked = matcher.group(5); - } else { - String tmp = matcher.group(5).replaceAll("<.*?>", ""); - tmp = Html.fromHtml(tmp).toString(); - info.userIds.add(tmp); - } - if (matcher.group(6).length() > 0) { - Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6)); - while (matcher2.find()) { - String tmp = matcher2.group(1).replaceAll("<.*?>", ""); - tmp = Html.fromHtml(tmp).toString(); - info.userIds.add(tmp); - } - } - results.add(info); - } - - return results; - } - - @Override - public String get(long keyId) throws QueryException { - HttpClient client = new DefaultHttpClient(); - try { - HttpGet get = new HttpGet("http://" + mHost + ":" + mPort - + "/pks/lookup?op=get&search=0x" + PGPHelper.keyToHex(keyId)); - - HttpResponse response = client.execute(get); - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - throw new QueryException("not found"); - } - - HttpEntity entity = response.getEntity(); - InputStream is = entity.getContent(); - String data = readAll(is, EntityUtils.getContentCharSet(entity)); - Matcher matcher = PGPMain.PGP_PUBLIC_KEY.matcher(data); - if (matcher.find()) { - return matcher.group(1); - } - } catch (IOException e) { - // nothing to do, better luck on the next keyserver - } finally { - client.getConnectionManager().shutdown(); - } - - return null; - } - - @Override - public void add(String armouredText) throws AddKeyException { - HttpClient client = new DefaultHttpClient(); - try { - HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add"); - - List nameValuePairs = new ArrayList(2); - nameValuePairs.add(new BasicNameValuePair("keytext", armouredText)); - post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); - - HttpResponse response = client.execute(post); - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - throw new AddKeyException(); - } - } catch (IOException e) { - // nothing to do, better luck on the next keyserver - } finally { - client.getConnectionManager().shutdown(); - } - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/InputData.java b/org_apg/src/org/thialfihar/android/apg/util/InputData.java deleted file mode 100644 index 6b357e6de..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/InputData.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.util; - -import java.io.InputStream; - - -public class InputData { - private PositionAwareInputStream mInputStream; - private long mSize; - - public InputData(InputStream inputStream, long size) { - mInputStream = new PositionAwareInputStream(inputStream); - mSize = size; - } - - public InputStream getInputStream() { - return mInputStream; - } - - public long getSize() { - return mSize; - } - - public long getStreamPosition() { - return mInputStream.position(); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/IterableIterator.java b/org_apg/src/org/thialfihar/android/apg/util/IterableIterator.java deleted file mode 100644 index 1071cc81c..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/IterableIterator.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * 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.util; - -import java.util.Iterator; - -public class IterableIterator implements Iterable { - private Iterator mIter; - - public IterableIterator(Iterator iter) { - mIter = iter; - } - - public Iterator iterator() { - return mIter; - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/KeyServer.java b/org_apg/src/org/thialfihar/android/apg/util/KeyServer.java deleted file mode 100644 index 97c6c7636..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/KeyServer.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2011 Senecaso - * - * 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.util; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import android.os.Parcel; -import android.os.Parcelable; - -public abstract class KeyServer { - static public class QueryException extends Exception { - private static final long serialVersionUID = 2703768928624654512L; - - public QueryException(String message) { - super(message); - } - } - - static public class TooManyResponses extends Exception { - private static final long serialVersionUID = 2703768928624654513L; - } - - static public class InsufficientQuery extends Exception { - private static final long serialVersionUID = 2703768928624654514L; - } - - static public class AddKeyException extends Exception { - private static final long serialVersionUID = -507574859137295530L; - } - - static public class KeyInfo implements Serializable, Parcelable { - private static final long serialVersionUID = -7797972113284992662L; - public ArrayList userIds; - public String revoked; - public Date date; - public String fingerPrint; - public long keyId; - public int size; - public String algorithm; - - public KeyInfo() { - userIds = new ArrayList(); - } - - public KeyInfo(Parcel in) { - this(); - - in.readStringList(this.userIds); - this.revoked = in.readString(); - this.date = (Date) in.readSerializable(); - this.fingerPrint = in.readString(); - this.keyId = in.readLong(); - this.size = in.readInt(); - this.algorithm = in.readString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStringList(userIds); - dest.writeString(revoked); - dest.writeSerializable(date); - dest.writeString(fingerPrint); - dest.writeLong(keyId); - dest.writeInt(size); - dest.writeString(algorithm); - } - } - - abstract List search(String query) throws QueryException, TooManyResponses, - InsufficientQuery; - - abstract String get(long keyId) throws QueryException; - - abstract void add(String armouredText) throws AddKeyException; -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/Log.java b/org_apg/src/org/thialfihar/android/apg/util/Log.java deleted file mode 100644 index c2e3cc7a5..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/Log.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * 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.util; - -import org.thialfihar.android.apg.Constants; - -/** - * Wraps Android Logging to enable or disable debug output using Constants - * - */ -public final class Log { - - public static void v(String tag, String msg) { - if (Constants.DEBUG) { - android.util.Log.v(tag, msg); - } - } - - public static void v(String tag, String msg, Throwable tr) { - if (Constants.DEBUG) { - android.util.Log.v(tag, msg, tr); - } - } - - public static void d(String tag, String msg) { - if (Constants.DEBUG) { - android.util.Log.d(tag, msg); - } - } - - public static void d(String tag, String msg, Throwable tr) { - if (Constants.DEBUG) { - android.util.Log.d(tag, msg, tr); - } - } - - public static void i(String tag, String msg) { - if (Constants.DEBUG) { - android.util.Log.i(tag, msg); - } - } - - public static void i(String tag, String msg, Throwable tr) { - if (Constants.DEBUG) { - android.util.Log.i(tag, msg, tr); - } - } - - public static void w(String tag, String msg) { - android.util.Log.w(tag, msg); - } - - public static void w(String tag, String msg, Throwable tr) { - android.util.Log.w(tag, msg, tr); - } - - public static void w(String tag, Throwable tr) { - android.util.Log.w(tag, tr); - } - - public static void e(String tag, String msg) { - android.util.Log.e(tag, msg); - } - - public static void e(String tag, String msg, Throwable tr) { - android.util.Log.e(tag, msg, tr); - } - -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java b/org_apg/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java deleted file mode 100644 index 7850e2513..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.util; - -import java.io.IOException; -import java.io.InputStream; - -public class PositionAwareInputStream extends InputStream { - private InputStream mStream; - private long mPosition; - - public PositionAwareInputStream(InputStream in) { - mStream = in; - mPosition = 0; - } - - @Override - public int read() throws IOException { - int ch = mStream.read(); - ++mPosition; - return ch; - } - - @Override - public int available() throws IOException { - return mStream.available(); - } - - @Override - public void close() throws IOException { - mStream.close(); - } - - @Override - public boolean markSupported() { - return false; - } - - @Override - public int read(byte[] b) throws IOException { - int result = mStream.read(b); - mPosition += result; - return result; - } - - @Override - public int read(byte[] b, int offset, int length) throws IOException { - int result = mStream.read(b, offset, length); - mPosition += result; - return result; - } - - @Override - public synchronized void reset() throws IOException { - mStream.reset(); - mPosition = 0; - } - - @Override - public long skip(long n) throws IOException { - long result = mStream.skip(n); - mPosition += result; - return result; - } - - public long position() { - return mPosition; - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/Primes.java b/org_apg/src/org/thialfihar/android/apg/util/Primes.java deleted file mode 100644 index bc52927e8..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/Primes.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * 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.util; - -import java.math.BigInteger; - -/** - * Primes for ElGamal - */ -public final class Primes { - // taken from http://www.ietf.org/rfc/rfc3526.txt - public static final String P1536 = - "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + - "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + - "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + - "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + - "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + - "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + - "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + - "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF"; - - public static final String P2048 = - "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + - "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + - "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + - "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + - "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + - "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + - "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + - "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + - "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + - "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + - "15728E5A 8AACAA68 FFFFFFFF FFFFFFFF"; - - public static final String P3072 = - "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + - "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + - "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + - "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + - "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + - "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + - "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + - "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + - "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + - "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + - "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" + - "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" + - "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" + - "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" + - "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" + - "43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF"; - - public static final String P4096 = - "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + - "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + - "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + - "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + - "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + - "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + - "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + - "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + - "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + - "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + - "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" + - "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" + - "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" + - "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" + - "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" + - "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" + - "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" + - "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" + - "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" + - "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" + - "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199" + - "FFFFFFFF FFFFFFFF"; - - public static final String P6144 = - "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + - "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + - "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + - "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + - "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + - "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + - "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + - "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + - "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + - "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + - "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" + - "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" + - "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" + - "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" + - "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" + - "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" + - "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" + - "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" + - "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" + - "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" + - "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" + - "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" + - "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" + - "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" + - "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" + - "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" + - "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" + - "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" + - "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" + - "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" + - "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" + - "12BF2D5B 0B7474D6 E694F91E 6DCC4024 FFFFFFFF FFFFFFFF"; - - public static final String P8192 = - "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" + - "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" + - "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" + - "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" + - "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" + - "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" + - "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" + - "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" + - "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" + - "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" + - "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" + - "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" + - "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" + - "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" + - "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" + - "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" + - "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" + - "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" + - "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" + - "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" + - "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" + - "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" + - "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" + - "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" + - "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" + - "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" + - "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" + - "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" + - "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" + - "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" + - "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" + - "12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4" + - "38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300" + - "741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568" + - "3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9" + - "22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B" + - "4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A" + - "062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36" + - "4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1" + - "B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92" + - "4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47" + - "9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71" + - "60C980DD 98EDD3DF FFFFFFFF FFFFFFFF"; - - public static BigInteger getBestPrime(int keySize) { - String primeString; - if (keySize >= (8192 + 6144) / 2) { - primeString = P8192; - } else if (keySize >= (6144 + 4096) / 2) { - primeString = P6144; - } else if (keySize >= (4096 + 3072) / 2) { - primeString = P4096; - } else if (keySize >= (3072 + 2048) / 2) { - primeString = P3072; - } else if (keySize >= (2048 + 1536) / 2) { - primeString = P2048; - } else { - primeString = P1536; - } - - return new BigInteger(primeString.replaceAll(" ", ""), 16); - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java b/org_apg/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java deleted file mode 100644 index 1f76cb071..000000000 --- a/org_apg/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar - * - * 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.util; - -public interface ProgressDialogUpdater { - void setProgress(String message, int current, int total); - - void setProgress(int resourceId, int current, int total); - - void setProgress(int current, int total); -} diff --git a/org_apg_integration_demo/.gitignore b/org_apg_integration_demo/.gitignore deleted file mode 100644 index 2e423e1a3..000000000 --- a/org_apg_integration_demo/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -#Android generated -bin -gen -obj -libs/armeabi -lint.xml -local.properties - -#Eclipse -.project -.classpath -.settings - -#IntelliJ IDEA -.idea -*.iml - -#Maven -target -release.properties - -#Mac -.DS_Store \ No newline at end of file diff --git a/org_apg_integration_demo/AndroidManifest.xml b/org_apg_integration_demo/AndroidManifest.xml deleted file mode 100644 index 8d7a9e970..000000000 --- a/org_apg_integration_demo/AndroidManifest.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org_apg_integration_demo/build.xml b/org_apg_integration_demo/build.xml deleted file mode 100644 index e72865c84..000000000 --- a/org_apg_integration_demo/build.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org_apg_integration_demo/proguard-project.txt b/org_apg_integration_demo/proguard-project.txt deleted file mode 100644 index f2fe1559a..000000000 --- a/org_apg_integration_demo/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/org_apg_integration_demo/project.properties b/org_apg_integration_demo/project.properties deleted file mode 100644 index 8abe35df9..000000000 --- a/org_apg_integration_demo/project.properties +++ /dev/null @@ -1,12 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "ant.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-15 -android.library.reference.1=../org_apg_integration_lib diff --git a/org_apg_integration_demo/res/drawable-hdpi/icon.png b/org_apg_integration_demo/res/drawable-hdpi/icon.png deleted file mode 100644 index 6b8cc3d73..000000000 Binary files a/org_apg_integration_demo/res/drawable-hdpi/icon.png and /dev/null differ diff --git a/org_apg_integration_demo/res/drawable-ldpi/icon.png b/org_apg_integration_demo/res/drawable-ldpi/icon.png deleted file mode 100644 index a1adf6bcb..000000000 Binary files a/org_apg_integration_demo/res/drawable-ldpi/icon.png and /dev/null differ diff --git a/org_apg_integration_demo/res/drawable-mdpi/icon.png b/org_apg_integration_demo/res/drawable-mdpi/icon.png deleted file mode 100644 index 6b10a2ad3..000000000 Binary files a/org_apg_integration_demo/res/drawable-mdpi/icon.png and /dev/null differ diff --git a/org_apg_integration_demo/res/drawable-xhdpi/icon.png b/org_apg_integration_demo/res/drawable-xhdpi/icon.png deleted file mode 100644 index 03ee31bbd..000000000 Binary files a/org_apg_integration_demo/res/drawable-xhdpi/icon.png and /dev/null differ diff --git a/org_apg_integration_demo/res/layout/aidl_demo.xml b/org_apg_integration_demo/res/layout/aidl_demo.xml deleted file mode 100644 index 59977869d..000000000 --- a/org_apg_integration_demo/res/layout/aidl_demo.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - -