renaming whole package to org.apg to simplifiy name

This commit is contained in:
Dominik 2012-03-09 16:27:29 +01:00
parent df6933bfb8
commit 8452fb62b7
69 changed files with 1914 additions and 1628 deletions

View File

@ -3,7 +3,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="auto" android:installLocation="auto"
package="org.thialfihar.android.apg" package="org.apg"
android:versionCode="11000" android:versionCode="11000"
android:versionName="1.1" > android:versionName="1.1" >
@ -33,7 +33,7 @@
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:label="@string/app_name" > android:label="@string/app_name" >
<activity <activity
android:name=".MainActivity" android:name=".ui.MainActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/app_name" > android:label="@string/app_name" >
<intent-filter> <intent-filter>
@ -43,7 +43,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".PublicKeyListActivity" android:name=".ui.PublicKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_managePublicKeys" android:label="@string/title_managePublicKeys"
android:launchMode="singleTop" > android:launchMode="singleTop" >
@ -56,7 +56,7 @@
android:resource="@xml/searchable_public_keys" /> android:resource="@xml/searchable_public_keys" />
</activity> </activity>
<activity <activity
android:name=".SecretKeyListActivity" android:name=".ui.SecretKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_manageSecretKeys" android:label="@string/title_manageSecretKeys"
android:launchMode="singleTop" > android:launchMode="singleTop" >
@ -69,11 +69,11 @@
android:resource="@xml/searchable_secret_keys" /> android:resource="@xml/searchable_secret_keys" />
</activity> </activity>
<activity <activity
android:name=".EditKeyActivity" android:name=".ui.EditKeyActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_editKey" /> android:label="@string/title_editKey" />
<activity <activity
android:name=".SelectPublicKeyListActivity" android:name=".ui.SelectPublicKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_selectRecipients" android:label="@string/title_selectRecipients"
android:launchMode="singleTop" > android:launchMode="singleTop" >
@ -91,7 +91,7 @@
android:resource="@xml/searchable_public_keys" /> android:resource="@xml/searchable_public_keys" />
</activity> </activity>
<activity <activity
android:name=".SelectSecretKeyListActivity" android:name=".ui.SelectSecretKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_selectSignature" android:label="@string/title_selectSignature"
android:launchMode="singleTop" > android:launchMode="singleTop" >
@ -109,7 +109,7 @@
android:resource="@xml/searchable_secret_keys" /> android:resource="@xml/searchable_secret_keys" />
</activity> </activity>
<activity <activity
android:name=".EncryptActivity" android:name=".ui.EncryptActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_encrypt" > android:label="@string/title_encrypt" >
<intent-filter> <intent-filter>
@ -124,7 +124,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".DecryptActivity" android:name=".ui.DecryptActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_decrypt" > android:label="@string/title_decrypt" >
<intent-filter> <intent-filter>
@ -138,7 +138,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".GeneralActivity" android:name=".ui.GeneralActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@android:style/Theme.Dialog" > android:theme="@android:style/Theme.Dialog" >
@ -170,31 +170,31 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".MailListActivity" android:name=".ui.MailListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_mailInbox" /> android:label="@string/title_mailInbox" />
<activity <activity
android:name=".KeyServerQueryActivity" android:name=".ui.KeyServerQueryActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_keyServerQuery" /> android:label="@string/title_keyServerQuery" />
<activity <activity
android:name=".SendKeyActivity" android:name=".ui.SendKeyActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_sendKey" /> android:label="@string/title_sendKey" />
<activity <activity
android:name=".PreferencesActivity" android:name=".ui.PreferencesActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_preferences" /> android:label="@string/title_preferences" />
<activity <activity
android:name=".KeyServerPreferenceActivity" android:name=".ui.KeyServerPreferenceActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_keyServerPreference" /> android:label="@string/title_keyServerPreference" />
<activity <activity
android:name=".SignKeyActivity" android:name=".ui.SignKeyActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_signKey" /> android:label="@string/title_signKey" />
<activity <activity
android:name=".ImportFromQRCodeActivity" android:name=".ui.ImportFromQRCodeActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_importFromQRCode" /> android:label="@string/title_importFromQRCode" />
@ -215,11 +215,11 @@
</service> </service>
<provider <provider
android:name="org.thialfihar.android.apg.provider.DataProvider" android:name=".provider.DataProvider"
android:authorities="org.thialfihar.android.apg.provider" android:authorities="org.thialfihar.android.apg.provider"
android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" /> android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" />
<provider <provider
android:name="org.thialfihar.android.apg.provider.ApgServiceBlobProvider" android:name=".provider.ApgServiceBlobProvider"
android:authorities="org.thialfihar.android.apg.provider.apgserviceblobprovider" android:authorities="org.thialfihar.android.apg.provider.apgserviceblobprovider"
android:permission="org.thialfihar.android.apg.permission.STORE_BLOBS" /> android:permission="org.thialfihar.android.apg.permission.STORE_BLOBS" />
</application> </application>

202
COPYING Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -14,7 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<org.thialfihar.android.apg.ui.widget.SectionView <org.apg.ui.widget.SectionView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -63,4 +63,4 @@
android:paddingBottom="6dip" android:paddingBottom="6dip"
android:orientation="vertical"/> android:orientation="vertical"/>
</org.thialfihar.android.apg.ui.widget.SectionView> </org.apg.ui.widget.SectionView>

View File

@ -14,7 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<org.thialfihar.android.apg.ui.widget.UserIdEditor <org.apg.ui.widget.UserIdEditor
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -112,4 +112,4 @@
</LinearLayout> </LinearLayout>
</org.thialfihar.android.apg.ui.widget.UserIdEditor> </org.apg.ui.widget.UserIdEditor>

View File

@ -14,7 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<org.thialfihar.android.apg.ui.widget.KeyServerEditor <org.apg.ui.widget.KeyServerEditor
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -49,4 +49,4 @@
android:layout_height="1dip" android:layout_height="1dip"
android:background="?android:attr/listDivider"/> android:background="?android:attr/listDivider"/>
</org.thialfihar.android.apg.ui.widget.KeyServerEditor> </org.apg.ui.widget.KeyServerEditor>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> <!--
Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,6 +16,7 @@
--> -->
<resources> <resources>
<style name="MinusButton"> <style name="MinusButton">
<item name="android:background">@drawable/btn_circle</item> <item name="android:background">@drawable/btn_circle</item>
<item name="android:src">@drawable/ic_btn_round_minus</item> <item name="android:src">@drawable/ic_btn_round_minus</item>
@ -24,4 +26,5 @@
<item name="android:background">@drawable/btn_circle</item> <item name="android:background">@drawable/btn_circle</item>
<item name="android:src">@drawable/ic_btn_round_plus</item> <item name="android:src">@drawable/ic_btn_round_plus</item>
</style> </style>
</resources> </resources>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> <!--
Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,71 +15,56 @@
limitations under the License. limitations under the License.
--> -->
<PreferenceScreen <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/section_general">
<PreferenceCategory android:title="@string/section_general" >
<ListPreference <ListPreference
android:key="language" android:dialogTitle="@string/label_language"
android:title="@string/label_language"
android:entries="@array/language_entries" android:entries="@array/language_entries"
android:entryValues="@array/language_values" android:entryValues="@array/language_values"
android:dialogTitle="@string/label_language" /> android:key="language"
android:title="@string/label_language" />
<org.thialfihar.android.apg.ui.widget.IntegerListPreference <org.apg.ui.widget.IntegerListPreference
android:persistent="false"
android:key="passPhraseCacheTtl"
android:entries="@array/pass_phrase_cache_ttl_entries" android:entries="@array/pass_phrase_cache_ttl_entries"
android:entryValues="@array/pass_phrase_cache_ttl_values" android:entryValues="@array/pass_phrase_cache_ttl_values"
android:key="passPhraseCacheTtl"
android:persistent="false"
android:title="@string/label_passPhraseCacheTtl" /> android:title="@string/label_passPhraseCacheTtl" />
<PreferenceScreen <PreferenceScreen
android:persistent="false"
android:key="keyServers" android:key="keyServers"
android:persistent="false"
android:title="@string/label_keyServers" /> android:title="@string/label_keyServers" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/section_defaults" >
<PreferenceCategory <org.apg.ui.widget.IntegerListPreference
android:title="@string/section_defaults">
<org.thialfihar.android.apg.ui.widget.IntegerListPreference
android:persistent="false"
android:key="defaultEncryptionAlgorithm" android:key="defaultEncryptionAlgorithm"
android:persistent="false"
android:title="@string/label_encryptionAlgorithm" /> android:title="@string/label_encryptionAlgorithm" />
<org.apg.ui.widget.IntegerListPreference
<org.thialfihar.android.apg.ui.widget.IntegerListPreference
android:persistent="false"
android:key="defaultHashAlgorithm" android:key="defaultHashAlgorithm"
android:persistent="false"
android:title="@string/label_hashAlgorithm" /> android:title="@string/label_hashAlgorithm" />
<org.apg.ui.widget.IntegerListPreference
<org.thialfihar.android.apg.ui.widget.IntegerListPreference
android:persistent="false"
android:key="defaultMessageCompression" android:key="defaultMessageCompression"
android:title="@string/label_messageCompression" />
<org.thialfihar.android.apg.ui.widget.IntegerListPreference
android:persistent="false" android:persistent="false"
android:title="@string/label_messageCompression" />
<org.apg.ui.widget.IntegerListPreference
android:key="defaultFileCompression" android:key="defaultFileCompression"
android:persistent="false"
android:title="@string/label_fileCompression" /> android:title="@string/label_fileCompression" />
<CheckBoxPreference <CheckBoxPreference
android:persistent="false"
android:key="defaultAsciiArmour" android:key="defaultAsciiArmour"
android:title="@string/label_asciiArmour" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/section_advanced">
<CheckBoxPreference
android:persistent="false" android:persistent="false"
android:title="@string/label_asciiArmour" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/section_advanced" >
<CheckBoxPreference
android:key="forceV3Signatures" android:key="forceV3Signatures"
android:persistent="false"
android:title="@string/label_forceV3Signature" /> android:title="@string/label_forceV3Signature" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> <!--
Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<searchable <searchable xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" android:hint="@string/hint_publicKeys"
android:label="@string/app_name" android:label="@string/app_name" >
android:hint="@string/hint_publicKeys">
</searchable> </searchable>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> <!--
Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<searchable <searchable xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" android:hint="@string/hint_secretKeys"
android:label="@string/app_name" android:label="@string/app_name" >
android:hint="@string/hint_secretKeys">
</searchable> </searchable>

View File

@ -14,8 +14,19 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import org.apg.KeyServer.AddKeyException;
import org.apg.provider.DataProvider;
import org.apg.provider.Database;
import org.apg.provider.KeyRings;
import org.apg.provider.Keys;
import org.apg.provider.UserIds;
import org.apg.ui.BaseActivity;
import org.apg.ui.widget.KeyEditor;
import org.apg.ui.widget.SectionView;
import org.apg.ui.widget.UserIdEditor;
import org.apg.util.IterableIterator;
import org.spongycastle.bcpg.ArmoredInputStream; import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream; import org.spongycastle.bcpg.BCPGOutputStream;
@ -53,16 +64,7 @@ import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector; import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.PGPV3SignatureGenerator; import org.spongycastle.openpgp.PGPV3SignatureGenerator;
import org.thialfihar.android.apg.KeyServer.AddKeyException; import org.apg.R;
import org.thialfihar.android.apg.provider.DataProvider;
import org.thialfihar.android.apg.provider.Database;
import org.thialfihar.android.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds;
import org.thialfihar.android.apg.ui.widget.KeyEditor;
import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.ui.widget.UserIdEditor;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
@ -155,54 +157,45 @@ public class Apg {
public static final String AUTHORITY = DataProvider.AUTHORITY; public static final String AUTHORITY = DataProvider.AUTHORITY;
public static final Uri CONTENT_URI_SECRET_KEY_RINGS = public static final Uri CONTENT_URI_SECRET_KEY_RINGS = Uri.parse("content://" + AUTHORITY
Uri.parse("content://" + AUTHORITY + "/key_rings/secret/"); + "/key_rings/secret/");
public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID = public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID = Uri.parse("content://"
Uri.parse("content://" + AUTHORITY + "/key_rings/secret/key_id/"); + AUTHORITY + "/key_rings/secret/key_id/");
public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_EMAILS = public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_EMAILS = Uri.parse("content://"
Uri.parse("content://" + AUTHORITY + "/key_rings/secret/emails/"); + AUTHORITY + "/key_rings/secret/emails/");
public static final Uri CONTENT_URI_PUBLIC_KEY_RINGS = public static final Uri CONTENT_URI_PUBLIC_KEY_RINGS = Uri.parse("content://" + AUTHORITY
Uri.parse("content://" + AUTHORITY + "/key_rings/public/"); + "/key_rings/public/");
public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_KEY_ID = public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_KEY_ID = Uri.parse("content://"
Uri.parse("content://" + AUTHORITY + "/key_rings/public/key_id/"); + AUTHORITY + "/key_rings/public/key_id/");
public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS = public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS = Uri.parse("content://"
Uri.parse("content://" + AUTHORITY + "/key_rings/public/emails/"); + AUTHORITY + "/key_rings/public/emails/");
private static String VERSION = null; private static String VERSION = null;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] {
new int[] { SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5,
SymmetricKeyAlgorithmTags.AES_192,
SymmetricKeyAlgorithmTags.AES_128,
SymmetricKeyAlgorithmTags.CAST5,
SymmetricKeyAlgorithmTags.TRIPLE_DES }; SymmetricKeyAlgorithmTags.TRIPLE_DES };
private static final int[] PREFERRED_HASH_ALGORITHMS = private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1,
new int[] { HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160 };
HashAlgorithmTags.SHA1, private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] {
HashAlgorithmTags.SHA256, CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
HashAlgorithmTags.RIPEMD160 };
private static final int[] PREFERRED_COMPRESSION_ALGORITHMS =
new int[] {
CompressionAlgorithmTags.ZLIB,
CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP }; CompressionAlgorithmTags.ZIP };
public static Pattern PGP_MESSAGE = public static Pattern PGP_MESSAGE = Pattern.compile(
Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", ".*?(-----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); Pattern.DOTALL);
public static Pattern PGP_SIGNED_MESSAGE = public static Pattern PGP_PUBLIC_KEY = Pattern.compile(
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*", ".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
Pattern.DOTALL); Pattern.DOTALL);
public static Pattern PGP_PUBLIC_KEY = private static HashMap<Long, CachedPassPhrase> mPassPhraseCache = new HashMap<Long, CachedPassPhrase>();
Pattern.compile(".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
Pattern.DOTALL);
private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
new HashMap<Long, CachedPassPhrase>();
private static String mEditPassPhrase = null; private static String mEditPassPhrase = null;
private static Database mDatabase = null; private static Database mDatabase = null;
@ -293,11 +286,10 @@ public class Apg {
return delay; return delay;
} }
public static PGPSecretKey createKey(Context context, public static PGPSecretKey createKey(Context context, int algorithmChoice, int keySize,
int algorithmChoice, int keySize, String passPhrase, String passPhrase, PGPSecretKey masterKey) throws NoSuchAlgorithmException,
PGPSecretKey masterKey) PGPException, NoSuchProviderException, GeneralException,
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException, InvalidAlgorithmParameterException {
GeneralException, InvalidAlgorithmParameterException {
if (keySize < 512) { if (keySize < 512) {
throw new GeneralException(context.getString(R.string.error_keySizeMinimum512bit)); throw new GeneralException(context.getString(R.string.error_keySizeMinimum512bit));
@ -322,7 +314,8 @@ public class Apg {
case Id.choice.algorithm.elgamal: { case Id.choice.algorithm.elgamal: {
if (masterKey == null) { if (masterKey == null) {
throw new GeneralException(context.getString(R.string.error_masterKeyMustNotBeElGamal)); throw new GeneralException(
context.getString(R.string.error_masterKeyMustNotBeElGamal));
} }
keyGen = KeyPairGenerator.getInstance("ELGAMAL", new BouncyCastleProvider()); keyGen = KeyPairGenerator.getInstance("ELGAMAL", new BouncyCastleProvider());
BigInteger p = Primes.getBestPrime(keySize); BigInteger p = Primes.getBestPrime(keySize);
@ -354,27 +347,21 @@ public class Apg {
if (masterKey == null) { if (masterKey == null) {
// enough for now, as we assemble the key again later anyway // enough for now, as we assemble the key again later anyway
secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, keyPair, "", secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, keyPair, "",
PGPEncryptedData.CAST5, passPhrase.toCharArray(), PGPEncryptedData.CAST5, passPhrase.toCharArray(), null, null,
null, null,
new SecureRandom(), new BouncyCastleProvider().getName()); new SecureRandom(), new BouncyCastleProvider().getName());
} else { } else {
PGPPublicKey tmpKey = masterKey.getPublicKey(); PGPPublicKey tmpKey = masterKey.getPublicKey();
PGPPublicKey masterPublicKey = PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(),
new PGPPublicKey(tmpKey.getAlgorithm(), tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime());
tmpKey.getKey(new BouncyCastleProvider()), PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(passPhrase.toCharArray(),
tmpKey.getCreationTime());
PGPPrivateKey masterPrivateKey =
masterKey.extractPrivateKey(passPhrase.toCharArray(),
new BouncyCastleProvider()); new BouncyCastleProvider());
PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
PGPKeyRingGenerator ringGen = PGPKeyRingGenerator ringGen = new PGPKeyRingGenerator(
new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, "", PGPEncryptedData.CAST5,
masterKeyPair, "", passPhrase.toCharArray(), null, null, new SecureRandom(),
PGPEncryptedData.CAST5, passPhrase.toCharArray(), new BouncyCastleProvider().getName());
null, null,
new SecureRandom(), new BouncyCastleProvider().getName());
ringGen.addSubKey(keyPair); ringGen.addSubKey(keyPair);
PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing(); PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing();
Iterator<PGPSecretKey> it = secKeyRing.getSecretKeys(); Iterator<PGPSecretKey> it = secKeyRing.getSecretKeys();
@ -398,12 +385,11 @@ public class Apg {
return numDays; return numDays;
} }
public static void buildSecretKey(Activity context, public static void buildSecretKey(Activity context, SectionView userIdsView,
SectionView userIdsView, SectionView keysView, SectionView keysView, String oldPassPhrase, String newPassPhrase,
String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) throws Apg.GeneralException, NoSuchProviderException,
ProgressDialogUpdater progress) PGPException, NoSuchAlgorithmException, SignatureException, IOException,
throws Apg.GeneralException, NoSuchProviderException, PGPException, Database.GeneralException {
NoSuchAlgorithmException, SignatureException, IOException, Database.GeneralException {
if (progress != null) if (progress != null)
progress.setProgress(R.string.progress_buildingKey, 0, 100); progress.setProgress(R.string.progress_buildingKey, 0, 100);
@ -433,7 +419,8 @@ public class Apg {
} catch (UserIdEditor.NoNameException e) { } catch (UserIdEditor.NoNameException e) {
throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAName)); throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAName));
} catch (UserIdEditor.NoEmailException e) { } catch (UserIdEditor.NoEmailException e) {
throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress)); throw new Apg.GeneralException(
context.getString(R.string.error_userIdNeedsAnEmailAddress));
} catch (UserIdEditor.InvalidEmailException e) { } catch (UserIdEditor.InvalidEmailException e) {
throw new Apg.GeneralException("" + e); throw new Apg.GeneralException("" + e);
} }
@ -455,7 +442,8 @@ public class Apg {
} }
if (!gotMainUserId) { if (!gotMainUserId) {
throw new Apg.GeneralException(context.getString(R.string.error_mainUserIdMustNotBeEmpty)); throw new Apg.GeneralException(
context.getString(R.string.error_mainUserIdMustNotBeEmpty));
} }
if (keyEditors.getChildCount() == 0) { if (keyEditors.getChildCount() == 0) {
@ -471,21 +459,16 @@ public class Apg {
progress.setProgress(R.string.progress_preparingMasterKey, 10, 100); progress.setProgress(R.string.progress_preparingMasterKey, 10, 100);
KeyEditor keyEditor = (KeyEditor) keyEditors.getChildAt(0); KeyEditor keyEditor = (KeyEditor) keyEditors.getChildAt(0);
int usageId = keyEditor.getUsage(); int usageId = keyEditor.getUsage();
boolean canSign = (usageId == Id.choice.usage.sign_only || boolean canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt);
usageId == Id.choice.usage.sign_and_encrypt); boolean canEncrypt = (usageId == Id.choice.usage.encrypt_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); String mainUserId = userIds.get(0);
PGPSecretKey masterKey = keys.get(0); PGPSecretKey masterKey = keys.get(0);
PGPPublicKey tmpKey = masterKey.getPublicKey(); PGPPublicKey tmpKey = masterKey.getPublicKey();
PGPPublicKey masterPublicKey = PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(),
new PGPPublicKey(tmpKey.getAlgorithm(), tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime());
tmpKey.getKey(new BouncyCastleProvider()), PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(oldPassPhrase.toCharArray(),
tmpKey.getCreationTime());
PGPPrivateKey masterPrivateKey =
masterKey.extractPrivateKey(oldPassPhrase.toCharArray(),
new BouncyCastleProvider()); new BouncyCastleProvider());
if (progress != null) if (progress != null)
@ -493,8 +476,7 @@ public class Apg {
for (int i = 0; i < userIds.size(); ++i) { for (int i = 0; i < userIds.size(); ++i) {
String userId = userIds.get(i); String userId = userIds.get(i);
PGPSignatureGenerator sGen = PGPSignatureGenerator sGen = new PGPSignatureGenerator(masterPublicKey.getAlgorithm(),
new PGPSignatureGenerator(masterPublicKey.getAlgorithm(),
HashAlgorithmTags.SHA1, new BouncyCastleProvider()); HashAlgorithmTags.SHA1, new BouncyCastleProvider());
sGen.initSign(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); sGen.initSign(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
@ -528,19 +510,18 @@ public class Apg {
GregorianCalendar expiryDate = keyEditor.getExpiryDate(); GregorianCalendar expiryDate = keyEditor.getExpiryDate();
long numDays = getNumDaysBetween(creationDate, expiryDate); long numDays = getNumDaysBetween(creationDate, expiryDate);
if (numDays <= 0) { if (numDays <= 0) {
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation)); throw new GeneralException(
context.getString(R.string.error_expiryMustComeAfterCreation));
} }
hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
} }
if (progress != null) if (progress != null)
progress.setProgress(R.string.progress_buildingMasterKeyRing, 30, 100); progress.setProgress(R.string.progress_buildingMasterKeyRing, 30, 100);
PGPKeyRingGenerator keyGen = PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, mainUserId, PGPEncryptedData.CAST5, newPassPhrase.toCharArray(),
masterKeyPair, mainUserId, hashedPacketsGen.generate(), unhashedPacketsGen.generate(), new SecureRandom(),
PGPEncryptedData.CAST5, newPassPhrase.toCharArray(), new BouncyCastleProvider().getName());
hashedPacketsGen.generate(), unhashedPacketsGen.generate(),
new SecureRandom(), new BouncyCastleProvider().getName());
if (progress != null) if (progress != null)
progress.setProgress(R.string.progress_addingSubKeys, 40, 100); progress.setProgress(R.string.progress_addingSubKeys, 40, 100);
@ -550,13 +531,10 @@ public class Apg {
PGPSecretKey subKey = keys.get(i); PGPSecretKey subKey = keys.get(i);
keyEditor = (KeyEditor) keyEditors.getChildAt(i); keyEditor = (KeyEditor) keyEditors.getChildAt(i);
PGPPublicKey subPublicKey = subKey.getPublicKey(); PGPPublicKey subPublicKey = subKey.getPublicKey();
PGPPrivateKey subPrivateKey = PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(oldPassPhrase.toCharArray(),
subKey.extractPrivateKey(oldPassPhrase.toCharArray(),
new BouncyCastleProvider()); new BouncyCastleProvider());
PGPKeyPair subKeyPair = PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey.getAlgorithm(),
new PGPKeyPair(subPublicKey.getAlgorithm(), subPublicKey.getKey(new BouncyCastleProvider()), subPrivateKey.getKey(),
subPublicKey.getKey(new BouncyCastleProvider()),
subPrivateKey.getKey(),
subPublicKey.getCreationTime()); subPublicKey.getCreationTime());
hashedPacketsGen = new PGPSignatureSubpacketGenerator(); hashedPacketsGen = new PGPSignatureSubpacketGenerator();
@ -564,10 +542,8 @@ public class Apg {
keyFlags = 0; keyFlags = 0;
usageId = keyEditor.getUsage(); usageId = keyEditor.getUsage();
canSign = (usageId == Id.choice.usage.sign_only || canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt);
usageId == Id.choice.usage.sign_and_encrypt); canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt);
canEncrypt = (usageId == Id.choice.usage.encrypt_only ||
usageId == Id.choice.usage.sign_and_encrypt);
if (canSign) { if (canSign) {
keyFlags |= KeyFlags.SIGN_DATA; keyFlags |= KeyFlags.SIGN_DATA;
} }
@ -583,13 +559,13 @@ public class Apg {
GregorianCalendar expiryDate = keyEditor.getExpiryDate(); GregorianCalendar expiryDate = keyEditor.getExpiryDate();
long numDays = getNumDaysBetween(creationDate, expiryDate); long numDays = getNumDaysBetween(creationDate, expiryDate);
if (numDays <= 0) { if (numDays <= 0) {
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation)); throw new GeneralException(
context.getString(R.string.error_expiryMustComeAfterCreation));
} }
hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
} }
keyGen.addSubKey(subKeyPair, keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate());
hashedPacketsGen.generate(), unhashedPacketsGen.generate());
} }
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
@ -623,9 +599,11 @@ public class Apg {
PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring; PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring;
boolean save = true; boolean save = true;
try { try {
PGPPrivateKey testKey = secretKeyRing.getSecretKey().extractPrivateKey(new char[] {}, new BouncyCastleProvider()); PGPPrivateKey testKey = secretKeyRing.getSecretKey().extractPrivateKey(
new char[] {}, new BouncyCastleProvider());
if (testKey == null) { if (testKey == null) {
// this is bad, something is very wrong... likely a --export-secret-subkeys export // this is bad, something is very wrong... likely a --export-secret-subkeys
// export
save = false; save = false;
status = Id.return_value.bad; status = Id.return_value.bad;
} }
@ -673,10 +651,9 @@ public class Apg {
} }
} }
public static Bundle importKeyRings(Activity context, int type, public static Bundle importKeyRings(Activity context, int type, InputData data,
InputData data, ProgressDialogUpdater progress) throws GeneralException, FileNotFoundException,
ProgressDialogUpdater progress) PGPException, IOException {
throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
if (type == Id.type.secret_key) { if (type == Id.type.secret_key) {
@ -705,8 +682,8 @@ public class Apg {
int status = Integer.MIN_VALUE; // out of bounds value int status = Integer.MIN_VALUE; // out of bounds value
// if this key is what we expect it to be, save it // if this key is what we expect it to be, save it
if ((type == Id.type.secret_key && keyring instanceof PGPSecretKeyRing) || if ((type == Id.type.secret_key && keyring instanceof PGPSecretKeyRing)
(type == Id.type.public_key && keyring instanceof PGPPublicKeyRing)) { || (type == Id.type.public_key && keyring instanceof PGPPublicKeyRing)) {
status = storeKeyRingInCache(keyring); status = storeKeyRingInCache(keyring);
} }
@ -746,9 +723,8 @@ public class Apg {
} }
public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds, public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds,
OutputStream outStream, OutputStream outStream, ProgressDialogUpdater progress) throws GeneralException,
ProgressDialogUpdater progress) FileNotFoundException, PGPException, IOException {
throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
if (keyRingIds.size() == 1) { if (keyRingIds.size() == 1) {
@ -878,8 +854,8 @@ public class Apg {
Date creationDate = getCreationDate(key); Date creationDate = getCreationDate(key);
Date expiryDate = getExpiryDate(key); Date expiryDate = getExpiryDate(key);
Date now = new Date(); Date now = new Date();
if (now.compareTo(creationDate) >= 0 && if (now.compareTo(creationDate) >= 0
(expiryDate == null || now.compareTo(expiryDate) <= 0)) { && (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
return false; return false;
} }
return true; return true;
@ -1007,15 +983,15 @@ public class Apg {
} }
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null &&(hashed.getKeyFlags() & if (hashed != null
(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { && (hashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
return true; return true;
} }
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null &&(unhashed.getKeyFlags() & if (unhashed != null
(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { && (unhashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
return true; return true;
} }
} }
@ -1209,10 +1185,8 @@ public class Apg {
public static Vector<Integer> getKeyRingIds(int type) { public static Vector<Integer> getKeyRingIds(int type) {
SQLiteDatabase db = mDatabase.db(); SQLiteDatabase db = mDatabase.db();
Vector<Integer> keyIds = new Vector<Integer>(); Vector<Integer> keyIds = new Vector<Integer>();
Cursor c = db.query(KeyRings.TABLE_NAME, Cursor c = db.query(KeyRings.TABLE_NAME, new String[] { KeyRings._ID }, KeyRings.TYPE
new String[] { KeyRings._ID }, + " = ?", new String[] { "" + type }, null, null, null);
KeyRings.TYPE + " = ?", new String[] { "" + type },
null, null, null);
if (c != null && c.moveToFirst()) { if (c != null && c.moveToFirst()) {
do { do {
keyIds.add(c.getInt(0)); keyIds.add(c.getInt(0));
@ -1228,25 +1202,16 @@ public class Apg {
public static String getMainUserId(long keyId, int type) { public static String getMainUserId(long keyId, int type) {
SQLiteDatabase db = mDatabase.db(); SQLiteDatabase db = mDatabase.db();
Cursor c = db.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" + Cursor c = db.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON ("
KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "."
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ") " + + Keys.KEY_RING_ID + ") " + " INNER JOIN " + Keys.TABLE_NAME + " AS masterKey ON ("
" INNER JOIN " + Keys.TABLE_NAME + " AS masterKey ON (" + + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + "masterKey."
KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + + Keys.KEY_RING_ID + " AND " + "masterKey." + Keys.IS_MASTER_KEY + " = '1') "
"masterKey." + Keys.KEY_RING_ID + " AND " + + " INNER JOIN " + UserIds.TABLE_NAME + " ON (" + UserIds.TABLE_NAME + "."
"masterKey." + Keys.IS_MASTER_KEY + " = '1') " + + UserIds.KEY_ID + " = " + "masterKey." + Keys._ID + " AND " + UserIds.TABLE_NAME
" INNER JOIN " + UserIds.TABLE_NAME + " ON (" + + "." + UserIds.RANK + " = '0')", new String[] { UserIds.USER_ID }, Keys.TABLE_NAME
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " = " + + "." + Keys.KEY_ID + " = ? AND " + KeyRings.TABLE_NAME + "." + KeyRings.TYPE
"masterKey." + Keys._ID + " AND " + + " = ?", new String[] { "" + keyId, "" + type, }, null, null, null);
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0')",
new String[] { UserIds.USER_ID },
Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
new String[] {
"" + keyId,
"" + type,
},
null, null, null);
String userId = ""; String userId = "";
if (c != null && c.moveToFirst()) { if (c != null && c.moveToFirst()) {
do { do {
@ -1261,15 +1226,10 @@ public class Apg {
return userId; return userId;
} }
public static void encrypt(Context context, public static void encrypt(Context context, InputData data, OutputStream outStream,
InputData data, OutputStream outStream, boolean armored, long encryptionKeyIds[], long signatureKeyId,
boolean armored, String signaturePassPhrase, ProgressDialogUpdater progress, int symmetricAlgorithm,
long encryptionKeyIds[], long signatureKeyId, int hashAlgorithm, int compression, boolean forceV3Signature, String passPhrase)
String signaturePassPhrase,
ProgressDialogUpdater progress,
int symmetricAlgorithm, int hashAlgorithm, int compression,
boolean forceV3Signature,
String passPhrase)
throws IOException, GeneralException, PGPException, NoSuchProviderException, throws IOException, GeneralException, PGPException, NoSuchProviderException,
NoSuchAlgorithmException, SignatureException { NoSuchAlgorithmException, SignatureException {
Security.addProvider(new BouncyCastleProvider()); Security.addProvider(new BouncyCastleProvider());
@ -1293,7 +1253,8 @@ public class Apg {
PGPPrivateKey signaturePrivateKey = null; PGPPrivateKey signaturePrivateKey = null;
if (encryptionKeyIds.length == 0 && passPhrase == null) { if (encryptionKeyIds.length == 0 && passPhrase == null) {
throw new GeneralException(context.getString(R.string.error_noEncryptionKeysOrPassPhrase)); throw new GeneralException(
context.getString(R.string.error_noEncryptionKeysOrPassPhrase));
} }
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
@ -1311,16 +1272,16 @@ public class Apg {
signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(), signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
new BouncyCastleProvider()); new BouncyCastleProvider());
if (signaturePrivateKey == null) { if (signaturePrivateKey == null) {
throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey)); throw new GeneralException(
context.getString(R.string.error_couldNotExtractPrivateKey));
} }
} }
if (progress != null) if (progress != null)
progress.setProgress(R.string.progress_preparingStreams, 5, 100); progress.setProgress(R.string.progress_preparingStreams, 5, 100);
// encrypt and compress input file content // encrypt and compress input file content
PGPEncryptedDataGenerator cPk = PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(symmetricAlgorithm, true,
new PGPEncryptedDataGenerator(symmetricAlgorithm, true, new SecureRandom(), new SecureRandom(), new BouncyCastleProvider());
new BouncyCastleProvider());
if (encryptionKeyIds.length == 0) { if (encryptionKeyIds.length == 0) {
// symmetric encryption // symmetric encryption
@ -1341,16 +1302,12 @@ public class Apg {
if (progress != null) if (progress != null)
progress.setProgress(R.string.progress_preparingSignature, 10, 100); progress.setProgress(R.string.progress_preparingSignature, 10, 100);
if (forceV3Signature) { if (forceV3Signature) {
signatureV3Generator = signatureV3Generator = new PGPV3SignatureGenerator(signingKey.getPublicKey()
new PGPV3SignatureGenerator(signingKey.getPublicKey().getAlgorithm(), .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
hashAlgorithm,
new BouncyCastleProvider());
signatureV3Generator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); signatureV3Generator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
} else { } else {
signatureGenerator = signatureGenerator = new PGPSignatureGenerator(signingKey.getPublicKey()
new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(), .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
hashAlgorithm,
new BouncyCastleProvider());
signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
String userId = getMainUserId(getMasterKey(signingKeyRing)); String userId = getMainUserId(getMasterKey(signingKeyRing));
@ -1378,8 +1335,8 @@ public class Apg {
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator(); PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
// file name not needed, so empty string // file name not needed, so empty string
OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(),
new Date(), new byte[1 << 16]); new byte[1 << 16]);
if (progress != null) if (progress != null)
progress.setProgress(R.string.progress_encrypting, 20, 100); progress.setProgress(R.string.progress_encrypting, 20, 100);
@ -1426,14 +1383,10 @@ public class Apg {
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
} }
public static void signText(Context context, public static void signText(Context context, InputData data, OutputStream outStream,
InputData data, OutputStream outStream, long signatureKeyId, String signaturePassPhrase, int hashAlgorithm,
long signatureKeyId, String signaturePassPhrase, boolean forceV3Signature, ProgressDialogUpdater progress) throws GeneralException,
int hashAlgorithm, PGPException, IOException, NoSuchAlgorithmException, SignatureException {
boolean forceV3Signature,
ProgressDialogUpdater progress)
throws GeneralException, PGPException, IOException, NoSuchAlgorithmException,
SignatureException {
Security.addProvider(new BouncyCastleProvider()); Security.addProvider(new BouncyCastleProvider());
ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream); ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
@ -1456,8 +1409,7 @@ public class Apg {
if (signaturePassPhrase == null) { if (signaturePassPhrase == null) {
throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase)); throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
} }
signaturePrivateKey = signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
new BouncyCastleProvider()); new BouncyCastleProvider());
if (signaturePrivateKey == null) { if (signaturePrivateKey == null) {
throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey)); throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey));
@ -1472,15 +1424,13 @@ public class Apg {
PGPV3SignatureGenerator signatureV3Generator = null; PGPV3SignatureGenerator signatureV3Generator = null;
if (forceV3Signature) { if (forceV3Signature) {
signatureV3Generator = signatureV3Generator = new PGPV3SignatureGenerator(signingKey.getPublicKey()
new PGPV3SignatureGenerator(signingKey.getPublicKey().getAlgorithm(), .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
hashAlgorithm, signatureV3Generator
new BouncyCastleProvider()); .initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
signatureV3Generator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
} else { } else {
signatureGenerator = signatureGenerator = new PGPSignatureGenerator(
new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(), signingKey.getPublicKey().getAlgorithm(), hashAlgorithm,
hashAlgorithm,
new BouncyCastleProvider()); new BouncyCastleProvider());
signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
@ -1538,13 +1488,9 @@ public class Apg {
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
} }
public static void generateSignature(Context context, public static void generateSignature(Context context, InputData data, OutputStream outStream,
InputData data, OutputStream outStream, boolean armored, boolean binary, long signatureKeyId, String signaturePassPhrase,
boolean armored, boolean binary, int hashAlgorithm, boolean forceV3Signature, ProgressDialogUpdater progress)
long signatureKeyId, String signaturePassPhrase,
int hashAlgorithm,
boolean forceV3Signature,
ProgressDialogUpdater progress)
throws GeneralException, PGPException, IOException, NoSuchAlgorithmException, throws GeneralException, PGPException, IOException, NoSuchAlgorithmException,
SignatureException { SignatureException {
Security.addProvider(new BouncyCastleProvider()); Security.addProvider(new BouncyCastleProvider());
@ -1576,8 +1522,7 @@ public class Apg {
if (signaturePassPhrase == null) { if (signaturePassPhrase == null) {
throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase)); throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
} }
signaturePrivateKey = signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
new BouncyCastleProvider()); new BouncyCastleProvider());
if (signaturePrivateKey == null) { if (signaturePrivateKey == null) {
throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey)); throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey));
@ -1597,15 +1542,12 @@ public class Apg {
} }
if (forceV3Signature) { if (forceV3Signature) {
signatureV3Generator = signatureV3Generator = new PGPV3SignatureGenerator(signingKey.getPublicKey()
new PGPV3SignatureGenerator(signingKey.getPublicKey().getAlgorithm(), .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
hashAlgorithm,
new BouncyCastleProvider());
signatureV3Generator.initSign(type, signaturePrivateKey); signatureV3Generator.initSign(type, signaturePrivateKey);
} else { } else {
signatureGenerator = signatureGenerator = new PGPSignatureGenerator(
new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(), signingKey.getPublicKey().getAlgorithm(), hashAlgorithm,
hashAlgorithm,
new BouncyCastleProvider()); new BouncyCastleProvider());
signatureGenerator.initSign(type, signaturePrivateKey); signatureGenerator.initSign(type, signaturePrivateKey);
@ -1663,8 +1605,8 @@ public class Apg {
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
} }
public static long getDecryptionKeyId(Context context, InputData data) public static long getDecryptionKeyId(Context context, InputData data) throws GeneralException,
throws GeneralException, NoAsymmetricEncryptionException, IOException { NoAsymmetricEncryptionException, IOException {
InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc; PGPEncryptedDataList enc;
@ -1738,10 +1680,8 @@ public class Apg {
return false; return false;
} }
public static Bundle decrypt(Context context, public static Bundle decrypt(Context context, InputData data, OutputStream outStream,
InputData data, OutputStream outStream, String passPhrase, ProgressDialogUpdater progress, boolean assumeSymmetric)
String passPhrase, ProgressDialogUpdater progress,
boolean assumeSymmetric)
throws IOException, GeneralException, PGPException, SignatureException { throws IOException, GeneralException, PGPException, SignatureException {
if (passPhrase == null) { if (passPhrase == null) {
passPhrase = ""; passPhrase = "";
@ -1787,7 +1727,8 @@ public class Apg {
} }
if (pbe == null) { if (pbe == null) {
throw new GeneralException(context.getString(R.string.error_noSymmetricEncryptionPacket)); throw new GeneralException(
context.getString(R.string.error_noSymmetricEncryptionPacket));
} }
if (progress != null) if (progress != null)
@ -1829,7 +1770,8 @@ public class Apg {
throw new PGPException(context.getString(R.string.error_wrongPassPhrase)); throw new PGPException(context.getString(R.string.error_wrongPassPhrase));
} }
if (privateKey == null) { if (privateKey == null) {
throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey)); throw new GeneralException(
context.getString(R.string.error_couldNotExtractPrivateKey));
} }
currentProgress += 5; currentProgress += 5;
if (progress != null) if (progress != null)
@ -1848,8 +1790,8 @@ public class Apg {
if (dataChunk instanceof PGPCompressedData) { if (dataChunk instanceof PGPCompressedData) {
if (progress != null) if (progress != null)
progress.setProgress(R.string.progress_decompressingData, currentProgress, 100); progress.setProgress(R.string.progress_decompressingData, currentProgress, 100);
PGPObjectFactory fact = PGPObjectFactory fact = new PGPObjectFactory(
new PGPObjectFactory(((PGPCompressedData) dataChunk).getDataStream()); ((PGPCompressedData) dataChunk).getDataStream());
dataChunk = fact.nextObject(); dataChunk = fact.nextObject();
plainFact = fact; plainFact = fact;
currentProgress += 10; currentProgress += 10;
@ -1928,12 +1870,13 @@ public class Apg {
} }
} }
// unknown size, but try to at least have a moving, slowing down progress bar // unknown size, but try to at least have a moving, slowing down progress bar
currentProgress = startProgress + (endProgress - startProgress) * done / (done + 100000); currentProgress = startProgress + (endProgress - startProgress) * done
/ (done + 100000);
if (data.getSize() - startPos == 0) { if (data.getSize() - startPos == 0) {
currentProgress = endProgress; currentProgress = endProgress;
} else { } else {
currentProgress = (int)(startProgress + (endProgress - startProgress) * currentProgress = (int) (startProgress + (endProgress - startProgress)
(data.getStreamPosition() - startPos) / (data.getSize() - startPos)); * (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
} }
if (progress != null) if (progress != null)
progress.setProgress(currentProgress, 100); progress.setProgress(currentProgress, 100);
@ -1970,10 +1913,9 @@ public class Apg {
return returnData; return returnData;
} }
public static Bundle verifyText(BaseActivity context, public static Bundle verifyText(BaseActivity context, InputData data, OutputStream outStream,
InputData data, OutputStream outStream, ProgressDialogUpdater progress) throws IOException, GeneralException, PGPException,
ProgressDialogUpdater progress) SignatureException {
throws IOException, GeneralException, PGPException, SignatureException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
@ -2074,8 +2016,7 @@ public class Apg {
signature.update((byte) '\n'); signature.update((byte) '\n');
processLine(signature, lineOut.toByteArray()); processLine(signature, lineOut.toByteArray());
} } while (lookAhead != -1);
while (lookAhead != -1);
} }
returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, signature.verify()); returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, signature.verify());
@ -2085,14 +2026,12 @@ public class Apg {
return returnData; return returnData;
} }
public static int getStreamContent(Context context, InputStream inStream) public static int getStreamContent(Context context, InputStream inStream) throws IOException {
throws IOException {
InputStream in = PGPUtil.getDecoderStream(inStream); InputStream in = PGPUtil.getDecoderStream(inStream);
PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPObjectFactory pgpF = new PGPObjectFactory(in);
Object object = pgpF.nextObject(); Object object = pgpF.nextObject();
while (object != null) { while (object != null) {
if (object instanceof PGPPublicKeyRing || if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) {
object instanceof PGPSecretKeyRing) {
return Id.content.keys; return Id.content.keys;
} else if (object instanceof PGPEncryptedDataList) { } else if (object instanceof PGPEncryptedDataList) {
return Id.content.encrypted_data; return Id.content.encrypted_data;
@ -2103,10 +2042,8 @@ public class Apg {
return Id.content.unknown; return Id.content.unknown;
} }
private static void processLine(final String pLine, private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
final ArmoredOutputStream pArmoredOutput, final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException {
final PGPSignatureGenerator pSignatureGenerator)
throws IOException, SignatureException {
if (pLine == null) { if (pLine == null) {
return; return;
@ -2130,10 +2067,9 @@ public class Apg {
pSignatureGenerator.update(data); pSignatureGenerator.update(data);
} }
private static void processLine(final String pLine, private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
final ArmoredOutputStream pArmoredOutput, final PGPV3SignatureGenerator pSignatureGenerator) throws IOException,
final PGPV3SignatureGenerator pSignatureGenerator) SignatureException {
throws IOException, SignatureException {
if (pLine == null) { if (pLine == null) {
return; return;
@ -2158,8 +2094,8 @@ public class Apg {
} }
// taken from ClearSignedFileProcessor in BC // taken from ClearSignedFileProcessor in BC
private static void processLine(PGPSignature sig, byte[] line) private static void processLine(PGPSignature sig, byte[] line) throws SignatureException,
throws SignatureException, IOException { IOException {
int length = getLengthWithoutWhiteSpace(line); int length = getLengthWithoutWhiteSpace(line);
if (length > 0) { if (length > 0) {
sig.update(line, 0, length); sig.update(line, 0, length);
@ -2196,8 +2132,7 @@ public class Apg {
lookAhead = readPassedEOL(bOut, ch, fIn); lookAhead = readPassedEOL(bOut, ch, fIn);
break; break;
} }
} } while ((ch = fIn.read()) >= 0);
while ((ch = fIn.read()) >= 0);
if (ch < 0) { if (ch < 0) {
lookAhead = -1; lookAhead = -1;
@ -2292,12 +2227,9 @@ public class Apg {
public static String generateRandomString(int length) { public static String generateRandomString(int length) {
SecureRandom random = new SecureRandom(); SecureRandom random = new SecureRandom();
/* /*
try { * try { random = SecureRandom.getInstance("SHA1PRNG", new BouncyCastleProvider()); } catch
random = SecureRandom.getInstance("SHA1PRNG", new BouncyCastleProvider()); * (NoSuchAlgorithmException e) { // TODO: need to handle this case somehow return null; }
} catch (NoSuchAlgorithmException e) { */
// TODO: need to handle this case somehow
return null;
}*/
byte bytes[] = new byte[length]; byte bytes[] = new byte[length];
random.nextBytes(bytes); random.nextBytes(bytes);
String result = ""; String result = "";
@ -2328,7 +2260,7 @@ public class Apg {
return size; return size;
} }
static void deleteFileSecurely(Context context, File file, ProgressDialogUpdater progress) public static void deleteFileSecurely(Context context, File file, ProgressDialogUpdater progress)
throws FileNotFoundException, IOException { throws FileNotFoundException, IOException {
long length = file.length(); long length = file.length();
SecureRandom random = new SecureRandom(); SecureRandom random = new SecureRandom();

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -11,9 +11,10 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import org.thialfihar.android.apg.provider.KeyRings; import org.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys; import org.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds; import org.apg.provider.UserIds;
import org.apg.IApgService;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Intent; import android.content.Intent;
@ -31,17 +32,14 @@ public class ApgService extends Service {
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
if( LOCAL_LOGD ) Log.d(TAG, "bound"); if (LOCAL_LOGD)
Log.d(TAG, "bound");
return mBinder; return mBinder;
} }
/** error status */ /** error status */
private static enum error { private static enum error {
ARGUMENTS_MISSING, ARGUMENTS_MISSING, APG_FAILURE, NO_MATCHING_SECRET_KEY, PRIVATE_KEY_PASSPHRASE_WRONG, PRIVATE_KEY_PASSPHRASE_MISSING;
APG_FAILURE,
NO_MATCHING_SECRET_KEY,
PRIVATE_KEY_PASSPHRASE_WRONG,
PRIVATE_KEY_PASSPHRASE_MISSING;
public int shiftedOrdinal() { public int shiftedOrdinal() {
return ordinal() + 100; return ordinal() + 100;
@ -49,10 +47,7 @@ public class ApgService extends Service {
} }
private static enum call { private static enum call {
encrypt_with_passphrase, encrypt_with_passphrase, encrypt_with_public_key, decrypt, get_keys
encrypt_with_public_key,
decrypt,
get_keys
} }
/** all arguments that can be passed by calling application */ /** all arguments that can be passed by calling application */
@ -139,11 +134,16 @@ public class ApgService extends Service {
private static final HashMap<String, Method> FUNCTIONS_DEFAULTS_METHODS = new HashMap<String, Method>(); private static final HashMap<String, Method> FUNCTIONS_DEFAULTS_METHODS = new HashMap<String, Method>();
static { static {
try { try {
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultEncryptionAlgorithm", Preferences.class.getMethod("getDefaultEncryptionAlgorithm")); FUNCTIONS_DEFAULTS_METHODS.put("getDefaultEncryptionAlgorithm",
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultHashAlgorithm", Preferences.class.getMethod("getDefaultHashAlgorithm")); Preferences.class.getMethod("getDefaultEncryptionAlgorithm"));
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultAsciiArmour", Preferences.class.getMethod("getDefaultAsciiArmour")); FUNCTIONS_DEFAULTS_METHODS.put("getDefaultHashAlgorithm",
FUNCTIONS_DEFAULTS_METHODS.put("getForceV3Signatures", Preferences.class.getMethod("getForceV3Signatures")); Preferences.class.getMethod("getDefaultHashAlgorithm"));
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultMessageCompression", Preferences.class.getMethod("getDefaultMessageCompression")); FUNCTIONS_DEFAULTS_METHODS.put("getDefaultAsciiArmour",
Preferences.class.getMethod("getDefaultAsciiArmour"));
FUNCTIONS_DEFAULTS_METHODS.put("getForceV3Signatures",
Preferences.class.getMethod("getForceV3Signatures"));
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultMessageCompression",
Preferences.class.getMethod("getDefaultMessageCompression"));
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Function method exception: " + e.getMessage()); Log.e(TAG, "Function method exception: " + e.getMessage());
} }
@ -159,22 +159,24 @@ public class ApgService extends Service {
private static Cursor getKeyEntries(HashMap<String, Object> pParams) { private static Cursor getKeyEntries(HashMap<String, Object> pParams) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "("
+ "." + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + ") " + " INNER JOIN " + UserIds.TABLE_NAME + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "."
+ " ON " + "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY
+ UserIds.RANK + " = '0') "); + " = '1'" + ") " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + "("
+ Keys.TABLE_NAME + "." + Keys._ID + " = " + UserIds.TABLE_NAME + "."
+ UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
String orderBy = pParams.containsKey("order_by") ? (String) pParams.get("order_by") : UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC"; String orderBy = pParams.containsKey("order_by") ? (String) pParams.get("order_by")
: UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
String typeVal[] = null; String typeVal[] = null;
String typeWhere = null; String typeWhere = null;
if (pParams.containsKey("key_type")) { if (pParams.containsKey("key_type")) {
typeWhere = KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?"; typeWhere = KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?";
typeVal = new String[] { typeVal = new String[] { "" + pParams.get("key_type") };
"" + pParams.get("key_type")
};
} }
return qb.query(Apg.getDatabase().db(), (String[]) pParams.get("columns"), typeWhere, typeVal, null, null, orderBy); return qb.query(Apg.getDatabase().db(), (String[]) pParams.get("columns"), typeWhere,
typeVal, null, null, orderBy);
} }
/** /**
@ -202,35 +204,38 @@ public class ApgService extends Service {
* maps fingerprints or user ids of keys to master keys in database * maps fingerprints or user ids of keys to master keys in database
* *
* @param search_keys * @param search_keys
* a list of keys (fingerprints or user ids) to look for in * a list of keys (fingerprints or user ids) to look for in database
* database
* @return an array of master keys * @return an array of master keys
*/ */
private static long[] getMasterKey(ArrayList<String> pSearchKeys, Bundle pReturn) { private static long[] getMasterKey(ArrayList<String> pSearchKeys, Bundle pReturn) {
HashMap<String, Object> qParams = new HashMap<String, Object>(); HashMap<String, Object> qParams = new HashMap<String, Object>();
qParams.put("columns", new String[] { qParams.put("columns", new String[] { KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0
UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1 UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1
}); });
qParams.put("key_type", Id.database.type_public); qParams.put("key_type", Id.database.type_public);
Cursor mCursor = getKeyEntries(qParams); Cursor mCursor = getKeyEntries(qParams);
if( LOCAL_LOGV ) Log.v(TAG, "going through installed user keys"); if (LOCAL_LOGV)
Log.v(TAG, "going through installed user keys");
ArrayList<Long> masterKeys = new ArrayList<Long>(); ArrayList<Long> masterKeys = new ArrayList<Long>();
while (mCursor.moveToNext()) { while (mCursor.moveToNext()) {
long curMkey = mCursor.getLong(0); long curMkey = mCursor.getLong(0);
String curUser = mCursor.getString(1); String curUser = mCursor.getString(1);
String curFprint = Apg.getSmallFingerPrint(curMkey); String curFprint = Apg.getSmallFingerPrint(curMkey);
if( LOCAL_LOGV ) Log.v(TAG, "current user: " + curUser + " (" + curFprint + ")"); if (LOCAL_LOGV)
Log.v(TAG, "current user: " + curUser + " (" + curFprint + ")");
if (pSearchKeys.contains(curFprint) || pSearchKeys.contains(curUser)) { if (pSearchKeys.contains(curFprint) || pSearchKeys.contains(curUser)) {
if( LOCAL_LOGV ) Log.v(TAG, "master key found for: " + curFprint); if (LOCAL_LOGV)
Log.v(TAG, "master key found for: " + curFprint);
masterKeys.add(curMkey); masterKeys.add(curMkey);
pSearchKeys.remove(curFprint); pSearchKeys.remove(curFprint);
} else { } else {
if( LOCAL_LOGV ) Log.v(TAG, "Installed key " + curFprint + " is not in the list of public keys to encrypt with"); if (LOCAL_LOGV)
Log.v(TAG, "Installed key " + curFprint
+ " is not in the list of public keys to encrypt with");
} }
} }
mCursor.close(); mCursor.close();
@ -243,12 +248,14 @@ public class ApgService extends Service {
if (i == 0) { if (i == 0) {
Log.w(TAG, "Found not one public key"); Log.w(TAG, "Found not one public key");
pReturn.getStringArrayList(ret.WARNINGS.name()).add("Searched for public key(s) but found not one"); pReturn.getStringArrayList(ret.WARNINGS.name()).add(
"Searched for public key(s) but found not one");
} }
for (String key : pSearchKeys) { for (String key : pSearchKeys) {
Log.w(TAG, "Searched for key " + key + " but cannot find it in APG"); Log.w(TAG, "Searched for key " + key + " but cannot find it in APG");
pReturn.getStringArrayList(ret.WARNINGS.name()).add("Searched for key " + key + " but cannot find it in APG"); pReturn.getStringArrayList(ret.WARNINGS.name()).add(
"Searched for key " + key + " but cannot find it in APG");
} }
return masterKeyLongs; return masterKeyLongs;
@ -269,18 +276,27 @@ public class ApgService extends Service {
while (iter.hasNext()) { while (iter.hasNext()) {
arg currentArg = iter.next(); arg currentArg = iter.next();
String currentKey = currentArg.name(); String currentKey = currentArg.name();
if (!pArgs.containsKey(currentKey) && FUNCTIONS_OPTIONAL_ARGS.get(pCall).contains(currentArg)) { if (!pArgs.containsKey(currentKey)
&& FUNCTIONS_OPTIONAL_ARGS.get(pCall).contains(currentArg)) {
String currentFunctionName = FUNCTIONS_DEFAULTS.get(currentArg); String currentFunctionName = FUNCTIONS_DEFAULTS.get(currentArg);
try { try {
Class<?> returnType = FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName).getReturnType(); Class<?> returnType = FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
.getReturnType();
if (returnType == String.class) { if (returnType == String.class) {
pArgs.putString(currentKey, (String) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName).invoke(preferences)); pArgs.putString(currentKey,
(String) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
.invoke(preferences));
} else if (returnType == boolean.class) { } else if (returnType == boolean.class) {
pArgs.putBoolean(currentKey, (Boolean) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName).invoke(preferences)); pArgs.putBoolean(currentKey,
(Boolean) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
.invoke(preferences));
} else if (returnType == int.class) { } else if (returnType == int.class) {
pArgs.putInt(currentKey, (Integer) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName).invoke(preferences)); pArgs.putInt(currentKey,
(Integer) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
.invoke(preferences));
} else { } else {
Log.e(TAG, "Unknown return type " + returnType.toString() + " for default option"); Log.e(TAG, "Unknown return type " + returnType.toString()
+ " for default option");
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Exception in add_default_arguments " + e.getMessage()); Log.e(TAG, "Exception in add_default_arguments " + e.getMessage());
@ -320,17 +336,19 @@ public class ApgService extends Service {
while (iter.hasNext()) { while (iter.hasNext()) {
String curArg = iter.next().name(); String curArg = iter.next().name();
if (!pArgs.containsKey(curArg)) { if (!pArgs.containsKey(curArg)) {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Argument missing: " + curArg); pReturn.getStringArrayList(ret.ERRORS.name())
.add("Argument missing: " + curArg);
} }
} }
} }
if(pFunction.equals(call.encrypt_with_passphrase.name()) || if (pFunction.equals(call.encrypt_with_passphrase.name())
pFunction.equals(call.encrypt_with_public_key.name()) || || pFunction.equals(call.encrypt_with_public_key.name())
pFunction.equals(call.decrypt.name())) { || pFunction.equals(call.decrypt.name())) {
// check that either MESSAGE or BLOB are there // check that either MESSAGE or BLOB are there
if (!pArgs.containsKey(arg.MESSAGE.name()) && !pArgs.containsKey(arg.BLOB.name())) { if (!pArgs.containsKey(arg.MESSAGE.name()) && !pArgs.containsKey(arg.BLOB.name())) {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Arguments missing: Neither MESSAGE nor BLOG found"); pReturn.getStringArrayList(ret.ERRORS.name()).add(
"Arguments missing: Neither MESSAGE nor BLOG found");
} }
} }
@ -363,7 +381,8 @@ public class ApgService extends Service {
try { try {
arg curArg = arg.valueOf(curKey); arg curArg = arg.valueOf(curKey);
if (!allArgs.contains(curArg)) { if (!allArgs.contains(curArg)) {
pReturn.getStringArrayList(ret.WARNINGS.name()).add("Unknown argument: " + curKey); pReturn.getStringArrayList(ret.WARNINGS.name()).add(
"Unknown argument: " + curKey);
unknownArgs.add(curKey); unknownArgs.add(curKey);
} }
} catch (Exception e) { } catch (Exception e) {
@ -386,23 +405,28 @@ public class ApgService extends Service {
/* add default arguments if missing */ /* add default arguments if missing */
addDefaultArguments(pCall, pArgs); addDefaultArguments(pCall, pArgs);
if( LOCAL_LOGV ) Log.v(TAG, "add_default_arguments"); if (LOCAL_LOGV)
Log.v(TAG, "add_default_arguments");
/* check for required arguments */ /* check for required arguments */
checkForRequiredArgs(pCall, pArgs, pReturn); checkForRequiredArgs(pCall, pArgs, pReturn);
if( LOCAL_LOGV ) Log.v(TAG, "check_required_args"); if (LOCAL_LOGV)
Log.v(TAG, "check_required_args");
/* check for unknown arguments and add to warning if found */ /* check for unknown arguments and add to warning if found */
checkForUnknownArgs(pCall, pArgs, pReturn); checkForUnknownArgs(pCall, pArgs, pReturn);
if( LOCAL_LOGV ) Log.v(TAG, "check_unknown_args"); if (LOCAL_LOGV)
Log.v(TAG, "check_unknown_args");
/* return if errors happened */ /* return if errors happened */
if (pReturn.getStringArrayList(ret.ERRORS.name()).size() != 0) { if (pReturn.getStringArrayList(ret.ERRORS.name()).size() != 0) {
if( LOCAL_LOGV ) Log.v(TAG, "Errors after preparing, not executing "+pCall); if (LOCAL_LOGV)
Log.v(TAG, "Errors after preparing, not executing " + pCall);
pReturn.putInt(ret.ERROR.name(), error.ARGUMENTS_MISSING.shiftedOrdinal()); pReturn.putInt(ret.ERROR.name(), error.ARGUMENTS_MISSING.shiftedOrdinal());
return false; return false;
} }
if( LOCAL_LOGV ) Log.v(TAG, "error return"); if (LOCAL_LOGV)
Log.v(TAG, "error return");
return true; return true;
} }
@ -414,7 +438,8 @@ public class ApgService extends Service {
if (pArgs.containsKey(arg.PUBLIC_KEYS.name())) { if (pArgs.containsKey(arg.PUBLIC_KEYS.name())) {
ArrayList<String> list = pArgs.getStringArrayList(arg.PUBLIC_KEYS.name()); ArrayList<String> list = pArgs.getStringArrayList(arg.PUBLIC_KEYS.name());
ArrayList<String> pubKeys = new ArrayList<String>(); ArrayList<String> pubKeys = new ArrayList<String>();
if( LOCAL_LOGV ) Log.v(TAG, "Long size: " + list.size()); if (LOCAL_LOGV)
Log.v(TAG, "Long size: " + list.size());
Iterator<String> iter = list.iterator(); Iterator<String> iter = list.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
pubKeys.add(iter.next()); pubKeys.add(iter.next());
@ -436,14 +461,16 @@ public class ApgService extends Service {
InputData in = new InputData(inStream, 0); // XXX Size second param? InputData in = new InputData(inStream, 0); // XXX Size second param?
OutputStream out = new ByteArrayOutputStream(); OutputStream out = new ByteArrayOutputStream();
if( LOCAL_LOGV ) Log.v(TAG, "About to encrypt"); if (LOCAL_LOGV)
Log.v(TAG, "About to encrypt");
try { try {
Apg.encrypt(getBaseContext(), // context Apg.encrypt(getBaseContext(), // context
in, // input stream in, // input stream
out, // output stream out, // output stream
pArgs.getBoolean(arg.ARMORED_OUTPUT.name()), // ARMORED_OUTPUT pArgs.getBoolean(arg.ARMORED_OUTPUT.name()), // ARMORED_OUTPUT
pubMasterKeys, // encryption keys pubMasterKeys, // encryption keys
getMasterKey(pArgs.getString(arg.SIGNATURE_KEY.name()), pReturn), // signature key getMasterKey(pArgs.getString(arg.SIGNATURE_KEY.name()), pReturn), // signature
// key
pArgs.getString(arg.PRIVATE_KEY_PASSPHRASE.name()), // signature passphrase pArgs.getString(arg.PRIVATE_KEY_PASSPHRASE.name()), // signature passphrase
null, // progress null, // progress
pArgs.getInt(arg.ENCRYPTION_ALGORYTHM.name()), // encryption pArgs.getInt(arg.ENCRYPTION_ALGORYTHM.name()), // encryption
@ -456,22 +483,33 @@ public class ApgService extends Service {
Log.e(TAG, "Exception in encrypt"); Log.e(TAG, "Exception in encrypt");
String msg = e.getMessage(); String msg = e.getMessage();
if (msg.equals(getBaseContext().getString(R.string.error_noSignaturePassPhrase))) { if (msg.equals(getBaseContext().getString(R.string.error_noSignaturePassPhrase))) {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " missing): " + msg); pReturn.getStringArrayList(ret.ERRORS.name()).add(
pReturn.putInt(ret.ERROR.name(), error.PRIVATE_KEY_PASSPHRASE_MISSING.shiftedOrdinal()); "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " missing): "
} else if (msg.equals(getBaseContext().getString(R.string.error_couldNotExtractPrivateKey))) { + msg);
pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " probably wrong): " + msg); pReturn.putInt(ret.ERROR.name(),
pReturn.putInt(ret.ERROR.name(), error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal()); error.PRIVATE_KEY_PASSPHRASE_MISSING.shiftedOrdinal());
} else if (msg.equals(getBaseContext().getString(
R.string.error_couldNotExtractPrivateKey))) {
pReturn.getStringArrayList(ret.ERRORS.name()).add(
"Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name()
+ " probably wrong): " + msg);
pReturn.putInt(ret.ERROR.name(),
error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal());
} else { } else {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Internal failure (" + e.getClass() + ") in APG when encrypting: " + e.getMessage()); pReturn.getStringArrayList(ret.ERRORS.name()).add(
"Internal failure (" + e.getClass() + ") in APG when encrypting: "
+ e.getMessage());
pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal()); pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal());
} }
return false; return false;
} }
if( LOCAL_LOGV ) Log.v(TAG, "Encrypted"); if (LOCAL_LOGV)
Log.v(TAG, "Encrypted");
if (isBlob) { if (isBlob) {
ContentResolver cr = getContentResolver(); ContentResolver cr = getContentResolver();
try { try {
OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB
.name())));
writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream); writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream);
outStream.close(); outStream.close();
} catch (Exception e) { } catch (Exception e) {
@ -501,7 +539,8 @@ public class ApgService extends Service {
ArrayList<String> fPrints = new ArrayList<String>(); ArrayList<String> fPrints = new ArrayList<String>();
ArrayList<String> ids = new ArrayList<String>(); ArrayList<String> ids = new ArrayList<String>();
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
if( LOCAL_LOGV ) Log.v(TAG, "adding key "+Apg.getSmallFingerPrint(cursor.getLong(0))); if (LOCAL_LOGV)
Log.v(TAG, "adding key " + Apg.getSmallFingerPrint(cursor.getLong(0)));
fPrints.add(Apg.getSmallFingerPrint(cursor.getLong(0))); fPrints.add(Apg.getSmallFingerPrint(cursor.getLong(0)));
ids.add(cursor.getString(1)); ids.add(cursor.getString(1));
} }
@ -536,7 +575,8 @@ public class ApgService extends Service {
boolean isBlob = pArgs.containsKey(arg.BLOB.name()); boolean isBlob = pArgs.containsKey(arg.BLOB.name());
String passphrase = pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null ? pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) : pArgs String passphrase = pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null ? pArgs
.getString(arg.SYMMETRIC_PASSPHRASE.name()) : pArgs
.getString(arg.PRIVATE_KEY_PASSPHRASE.name()); .getString(arg.PRIVATE_KEY_PASSPHRASE.name());
InputStream inStream = null; InputStream inStream = null;
@ -553,7 +593,8 @@ public class ApgService extends Service {
InputData in = new InputData(inStream, 0); // XXX what size in second parameter? InputData in = new InputData(inStream, 0); // XXX what size in second parameter?
OutputStream out = new ByteArrayOutputStream(); OutputStream out = new ByteArrayOutputStream();
if( LOCAL_LOGV ) Log.v(TAG, "About to decrypt"); if (LOCAL_LOGV)
Log.v(TAG, "About to decrypt");
try { try {
Apg.decrypt(getBaseContext(), in, out, passphrase, null, // progress Apg.decrypt(getBaseContext(), in, out, passphrase, null, // progress
pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null // symmetric pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null // symmetric
@ -565,21 +606,29 @@ public class ApgService extends Service {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt: " + msg); pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt: " + msg);
pReturn.putInt(ret.ERROR.name(), error.NO_MATCHING_SECRET_KEY.shiftedOrdinal()); pReturn.putInt(ret.ERROR.name(), error.NO_MATCHING_SECRET_KEY.shiftedOrdinal());
} else if (msg.equals(getBaseContext().getString(R.string.error_wrongPassPhrase))) { } else if (msg.equals(getBaseContext().getString(R.string.error_wrongPassPhrase))) {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " wrong/missing): " + msg); pReturn.getStringArrayList(ret.ERRORS.name()).add(
pReturn.putInt(ret.ERROR.name(), error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal()); "Cannot decrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name()
+ " wrong/missing): " + msg);
pReturn.putInt(ret.ERROR.name(),
error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal());
} else { } else {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Internal failure (" + e.getClass() + ") in APG when decrypting: " + msg); pReturn.getStringArrayList(ret.ERRORS.name()).add(
"Internal failure (" + e.getClass() + ") in APG when decrypting: "
+ msg);
pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal()); pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal());
} }
return false; return false;
} }
if( LOCAL_LOGV ) Log.v(TAG, "... decrypted"); if (LOCAL_LOGV)
Log.v(TAG, "... decrypted");
if (isBlob) { if (isBlob) {
ContentResolver cr = getContentResolver(); ContentResolver cr = getContentResolver();
try { try {
OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB
writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream); .name())));
writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()),
outStream);
outStream.close(); outStream.close();
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "... exception on writing blob", e); Log.e(TAG, "... exception on writing blob", e);

View File

@ -14,12 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
@ -66,8 +67,8 @@ public class AskForSecretKeyPassPhrase {
alert.setMessage(context.getString(R.string.passPhraseFor, userId)); alert.setMessage(context.getString(R.string.passPhraseFor, userId));
} }
LayoutInflater inflater = LayoutInflater inflater = (LayoutInflater) context
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.pass_phrase, null); View view = inflater.inflate(R.layout.pass_phrase, null);
final EditText input = (EditText) view.findViewById(R.id.passPhrase); final EditText input = (EditText) view.findViewById(R.id.passPhrase);
final EditText inputNotUsed = (EditText) view.findViewById(R.id.passPhraseAgain); final EditText inputNotUsed = (EditText) view.findViewById(R.id.passPhraseAgain);
@ -76,26 +77,23 @@ public class AskForSecretKeyPassPhrase {
alert.setView(view); alert.setView(view);
final PassPhraseCallbackInterface cb = callback; final PassPhraseCallbackInterface cb = callback;
alert.setPositiveButton(android.R.string.ok, alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
activity.removeDialog(Id.dialog.pass_phrase); activity.removeDialog(Id.dialog.pass_phrase);
String passPhrase = "" + input.getText(); String passPhrase = "" + input.getText();
long keyId; long keyId;
if (secretKey != null) { if (secretKey != null) {
try { try {
PGPPrivateKey testKey = secretKey.extractPrivateKey(passPhrase.toCharArray(), PGPPrivateKey testKey = secretKey.extractPrivateKey(
new BouncyCastleProvider()); passPhrase.toCharArray(), new BouncyCastleProvider());
if (testKey == null) { if (testKey == null) {
Toast.makeText(activity, Toast.makeText(activity, R.string.error_couldNotExtractPrivateKey,
R.string.error_couldNotExtractPrivateKey,
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
return; return;
} }
} catch (PGPException e) { } catch (PGPException e) {
Toast.makeText(activity, Toast.makeText(activity, R.string.wrongPassPhrase, Toast.LENGTH_SHORT)
R.string.wrongPassPhrase, .show();
Toast.LENGTH_SHORT).show();
return; return;
} }
keyId = secretKey.getKeyID(); keyId = secretKey.getKeyID();
@ -106,8 +104,7 @@ public class AskForSecretKeyPassPhrase {
} }
}); });
alert.setNegativeButton(android.R.string.cancel, alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
activity.removeDialog(Id.dialog.pass_phrase); activity.removeDialog(Id.dialog.pass_phrase);
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
public class CachedPassPhrase { public class CachedPassPhrase {
public final long timestamp; public final long timestamp;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import android.os.Environment; import android.os.Environment;

View File

@ -0,0 +1,81 @@
package org.apg;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apg.Apg.GeneralException;
import org.apg.R;
import android.content.Context;
import android.os.Environment;
public class DataDestination {
private String mStreamFilename;
private String mFilename;
private int mMode = Id.mode.undefined;
public DataDestination() {
}
public void setMode(int mode) {
mMode = mode;
}
public void setFilename(String filename) {
mFilename = filename;
}
public String getStreamFilename() {
return mStreamFilename;
}
public OutputStream getOutputStream(Context context) throws Apg.GeneralException,
FileNotFoundException, IOException {
OutputStream out = null;
mStreamFilename = null;
switch (mMode) {
case Id.mode.stream: {
try {
while (true) {
mStreamFilename = Apg.generateRandomString(32);
if (mStreamFilename == null) {
throw new Apg.GeneralException("couldn't generate random file name");
}
context.openFileInput(mStreamFilename).close();
}
} catch (FileNotFoundException e) {
// found a name that isn't used yet
}
out = context.openFileOutput(mStreamFilename, Context.MODE_PRIVATE);
break;
}
case Id.mode.byte_array: {
out = new ByteArrayOutputStream();
break;
}
case Id.mode.file: {
if (mFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new GeneralException(
context.getString(R.string.error_externalStorageNotReady));
}
}
out = new FileOutputStream(mFilename);
break;
}
default: {
break;
}
}
return out;
}
}

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
@ -7,7 +7,8 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.thialfihar.android.apg.Apg.GeneralException; import org.apg.Apg.GeneralException;
import org.apg.R;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
@ -56,8 +57,8 @@ public class DataSource {
return mData != null || mContentUri != null; return mData != null || mContentUri != null;
} }
public InputData getInputData(Context context, boolean withSize) public InputData getInputData(Context context, boolean withSize) throws GeneralException,
throws GeneralException, FileNotFoundException, IOException { FileNotFoundException, IOException {
InputStream in = null; InputStream in = null;
long size = 0; long size = 0;
@ -67,7 +68,8 @@ public class DataSource {
String path = Uri.decode(mContentUri.toString().substring(7)); String path = Uri.decode(mContentUri.toString().substring(7));
if (path.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { if (path.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new GeneralException(context.getString(R.string.error_externalStorageNotReady)); throw new GeneralException(
context.getString(R.string.error_externalStorageNotReady));
} }
} }
in = new FileInputStream(path); in = new FileInputStream(path);

View File

@ -14,7 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
@ -41,18 +43,17 @@ public class FileDialog {
public static interface OnClickListener { public static interface OnClickListener {
public void onCancelClick(); public void onCancelClick();
public void onOkClick(String filename, boolean checkbox); public void onOkClick(String filename, boolean checkbox);
} }
public static AlertDialog build(Activity activity, String title, String message, public static AlertDialog build(Activity activity, String title, String message,
String defaultFile, OnClickListener onClickListener, String defaultFile, OnClickListener onClickListener, String fileManagerTitle,
String fileManagerTitle, String fileManagerButton, String fileManagerButton, String checkboxText, int requestCode) {
String checkboxText,
int requestCode) {
// TODO: fileManagerTitle and fileManagerButton are deprecated, no use for them right now, // TODO: fileManagerTitle and fileManagerButton are deprecated, no use for them right now,
// but maybe the Intent now used will someday support them again, so leaving them in // but maybe the Intent now used will someday support them again, so leaving them in
LayoutInflater inflater = LayoutInflater inflater = (LayoutInflater) activity
(LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
AlertDialog.Builder alert = new AlertDialog.Builder(activity); AlertDialog.Builder alert = new AlertDialog.Builder(activity);
alert.setTitle(title); alert.setTitle(title);
@ -92,13 +93,11 @@ public class FileDialog {
if (mCheckBox.isEnabled()) { if (mCheckBox.isEnabled()) {
checked = mCheckBox.isChecked(); checked = mCheckBox.isChecked();
} }
clickListener.onOkClick(mFilename.getText().toString(), clickListener.onOkClick(mFilename.getText().toString(), checked);
checked);
} }
}); });
alert.setNegativeButton(android.R.string.cancel, alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
clickListener.onCancelClick(); clickListener.onCancelClick();
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@ -56,9 +56,15 @@ public class HkpKeyServer extends KeyServer {
private short mPort = 11371; private short mPort = 11371;
// example: // example:
// pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a href="/pks/lookup?op=vindex&search=0x887DF4BE9F5C9090">Jörg Runge &lt;joerg@joergrunge.de&gt;</a> // pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a
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); // href="/pks/lookup?op=vindex&search=0x887DF4BE9F5C9090">Jörg Runge
public static Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); // &lt;joerg@joergrunge.de&gt;</a>
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) { public HkpKeyServer(String host) {
mHost = host; mHost = host;
@ -119,7 +125,8 @@ public class HkpKeyServer extends KeyServer {
// TODO: replace this with httpclient // TODO: replace this with httpclient
@Override @Override
List<KeyInfo> search(String query) throws QueryException, TooManyResponses, InsufficientQuery { public List<KeyInfo> search(String query) throws QueryException, TooManyResponses,
InsufficientQuery {
Vector<KeyInfo> results = new Vector<KeyInfo>(); Vector<KeyInfo> results = new Vector<KeyInfo>();
if (query.length() < 3) { if (query.length() < 3) {
@ -160,7 +167,8 @@ public class HkpKeyServer extends KeyServer {
info.keyId = Apg.keyFromHex(matcher.group(3)); info.keyId = Apg.keyFromHex(matcher.group(3));
info.fingerPrint = Apg.getSmallFingerPrint(info.keyId); info.fingerPrint = Apg.getSmallFingerPrint(info.keyId);
String chunks[] = matcher.group(4).split("-"); String chunks[] = matcher.group(4).split("-");
info.date = new GregorianCalendar(Integer.parseInt(chunks[0]), Integer.parseInt(chunks[1]), Integer.parseInt(chunks[2])).getTime(); info.date = new GregorianCalendar(Integer.parseInt(chunks[0]),
Integer.parseInt(chunks[1]), Integer.parseInt(chunks[2])).getTime();
info.userIds = new Vector<String>(); info.userIds = new Vector<String>();
if (matcher.group(5).startsWith("*** KEY")) { if (matcher.group(5).startsWith("*** KEY")) {
info.revoked = matcher.group(5); info.revoked = matcher.group(5);
@ -184,10 +192,11 @@ public class HkpKeyServer extends KeyServer {
} }
@Override @Override
String get(long keyId) throws QueryException { public String get(long keyId) throws QueryException {
HttpClient client = new DefaultHttpClient(); HttpClient client = new DefaultHttpClient();
try { try {
HttpGet get = new HttpGet("http://" + mHost + ":" + mPort + "/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId)); HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
+ "/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId));
HttpResponse response = client.execute(get); HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
interface IApgService { interface IApgService {

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.CompressionAlgorithmTags;

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.InputStream; import java.io.InputStream;
@ -6,7 +6,7 @@ public class InputData {
private PositionAwareInputStream mInputStream; private PositionAwareInputStream mInputStream;
private long mSize; private long mSize;
InputData(InputStream inputStream, long size) { public InputData(InputStream inputStream, long size) {
mInputStream = new PositionAwareInputStream(inputStream); mInputStream = new PositionAwareInputStream(inputStream);
mSize = size; mSize = size;
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
@ -8,6 +8,7 @@ import java.util.Vector;
public abstract class KeyServer { public abstract class KeyServer {
static public class QueryException extends Exception { static public class QueryException extends Exception {
private static final long serialVersionUID = 2703768928624654512L; private static final long serialVersionUID = 2703768928624654512L;
public QueryException(String message) { public QueryException(String message) {
super(message); super(message);
} }
@ -27,16 +28,19 @@ public abstract class KeyServer {
static public class KeyInfo implements Serializable { static public class KeyInfo implements Serializable {
private static final long serialVersionUID = -7797972113284992662L; private static final long serialVersionUID = -7797972113284992662L;
Vector<String> userIds; public Vector<String> userIds;
String revoked; public String revoked;
Date date; public Date date;
String fingerPrint; public String fingerPrint;
long keyId; public long keyId;
int size; public int size;
String algorithm; public String algorithm;
} }
abstract List<KeyInfo> search(String query) throws QueryException, TooManyResponses, InsufficientQuery; abstract List<KeyInfo> search(String query) throws QueryException, TooManyResponses,
InsufficientQuery;
abstract String get(long keyId) throws QueryException; abstract String get(long keyId) throws QueryException;
abstract void add(String armouredText) throws AddKeyException; abstract void add(String armouredText) throws AddKeyException;
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
public class PausableThread extends Thread { public class PausableThread extends Thread {
private boolean mPaused = false; private boolean mPaused = false;

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPEncryptedData;
@ -16,16 +16,14 @@ public class Preferences {
return getPreferences(context, false); return getPreferences(context, false);
} }
public static synchronized Preferences getPreferences(Context context, boolean force_new) public static synchronized Preferences getPreferences(Context context, boolean force_new) {
{
if (mPreferences == null || force_new) { if (mPreferences == null || force_new) {
mPreferences = new Preferences(context); mPreferences = new Preferences(context);
} }
return mPreferences; return mPreferences;
} }
private Preferences(Context context) private Preferences(Context context) {
{
mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE); mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE);
} }
@ -120,8 +118,7 @@ public class Preferences {
} }
public boolean hasSeenChangeLog(String version) { public boolean hasSeenChangeLog(String version) {
return mSharedPreferences.getBoolean(Constants.pref.has_seen_change_log + version, return mSharedPreferences.getBoolean(Constants.pref.has_seen_change_log + version, false);
false);
} }
public void setHasSeenChangeLog(String version, boolean value) { public void setHasSeenChangeLog(String version, boolean value) {

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import java.math.BigInteger; import java.math.BigInteger;

View File

@ -14,10 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
public interface ProgressDialogUpdater { public interface ProgressDialogUpdater {
void setProgress(String message, int current, int total); void setProgress(String message, int current, int total);
void setProgress(int resourceId, int current, int total); void setProgress(int resourceId, int current, int total);
void setProgress(int current, int total); void setProgress(int current, int total);
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import android.content.Intent; import android.content.Intent;
import android.os.Binder; import android.os.Binder;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import android.provider.BaseColumns; import android.provider.BaseColumns;

View File

@ -1,6 +1,6 @@
package org.thialfihar.android.apg.provider; package org.apg.provider;
import org.thialfihar.android.apg.ApgService; import org.apg.ApgService;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.ContentValues; import android.content.ContentValues;

View File

@ -1,7 +1,7 @@
package org.thialfihar.android.apg.provider; package org.apg.provider;
import org.thialfihar.android.apg.ApgService; import org.apg.ApgService;
import org.thialfihar.android.apg.Constants; import org.apg.Constants;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;

View File

@ -14,13 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.HashMap; import java.util.HashMap;
import org.thialfihar.android.apg.Id; import org.apg.Id;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;

View File

@ -1,13 +1,13 @@
package org.thialfihar.android.apg.provider; package org.apg.provider;
import org.apg.Apg;
import org.apg.Id;
import org.apg.util.IterableIterator;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.Apg;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import android.provider.BaseColumns; import android.provider.BaseColumns;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import android.provider.BaseColumns; import android.provider.BaseColumns;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import android.provider.BaseColumns; import android.provider.BaseColumns;

View File

@ -0,0 +1,461 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apg.ui;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Locale;
import org.apg.Apg;
import org.apg.AskForSecretKeyPassPhrase;
import org.apg.Constants;
import org.apg.Id;
import org.apg.PausableThread;
import org.apg.Preferences;
import org.apg.ProgressDialogUpdater;
import org.apg.Service;
import org.apg.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class BaseActivity extends Activity implements Runnable, ProgressDialogUpdater,
AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
private ProgressDialog mProgressDialog = null;
private PausableThread mRunningThread = null;
private Thread mDeletingThread = null;
private long mSecretKeyId = 0;
private String mDeleteFile = null;
protected Preferences mPreferences;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
handlerCallback(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPreferences = Preferences.getPreferences(this);
setLanguage(this, mPreferences.getLanguage());
Apg.initialize(this);
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(Constants.path.app_dir);
if (!dir.exists() && !dir.mkdirs()) {
// ignore this for now, it's not crucial
// that the directory doesn't exist at this point
}
}
startCacheService(this, mPreferences);
}
public static void startCacheService(Activity activity, Preferences preferences) {
Intent intent = new Intent(activity, Service.class);
intent.putExtra(Service.EXTRA_TTL, preferences.getPassPhraseCacheTtl());
activity.startService(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences).setIcon(
android.R.drawable.ic_menu_preferences);
menu.add(0, Id.menu.option.about, 1, R.string.menu_about).setIcon(
android.R.drawable.ic_menu_info_details);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Id.menu.option.about: {
showDialog(Id.dialog.about);
return true;
}
case Id.menu.option.preferences: {
startActivity(new Intent(this, PreferencesActivity.class));
return true;
}
case Id.menu.option.search: {
startSearch("", false, null, false);
return true;
}
default: {
break;
}
}
return false;
}
@Override
protected Dialog onCreateDialog(int id) {
// in case it is a progress dialog
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
switch (id) {
case Id.dialog.encrypting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.decrypting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.saving: {
mProgressDialog.setMessage(this.getString(R.string.progress_saving));
return mProgressDialog;
}
case Id.dialog.importing: {
mProgressDialog.setMessage(this.getString(R.string.progress_importing));
return mProgressDialog;
}
case Id.dialog.exporting: {
mProgressDialog.setMessage(this.getString(R.string.progress_exporting));
return mProgressDialog;
}
case Id.dialog.deleting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.querying: {
mProgressDialog.setMessage(this.getString(R.string.progress_querying));
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case Id.dialog.signing: {
mProgressDialog.setMessage(this.getString(R.string.progress_signing));
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
default: {
break;
}
}
mProgressDialog = null;
switch (id) {
case Id.dialog.about: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("About " + Apg.getFullVersion(this));
LayoutInflater inflater = (LayoutInflater) this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.info, null);
TextView message = (TextView) layout.findViewById(R.id.message);
message.setText("This is an attempt to bring OpenPGP to Android. "
+ "It is far from complete, but more features are planned (see website).\n\n"
+ "Feel free to send bug reports, suggestions, feature requests, feedback, "
+ "photographs.\n\n" + "mail: thi@thialfihar.org\n"
+ "site: http://apg.thialfihar.org\n\n"
+ "This software is provided \"as is\", without warranty of any kind.");
alert.setView(layout);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
BaseActivity.this.removeDialog(Id.dialog.about);
}
});
return alert.create();
}
case Id.dialog.pass_phrase: {
return AskForSecretKeyPassPhrase.createDialog(this, getSecretKeyId(), this);
}
case Id.dialog.pass_phrases_do_not_match: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.error);
alert.setMessage(R.string.passPhrasesDoNotMatch);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.pass_phrases_do_not_match);
}
});
alert.setCancelable(false);
return alert.create();
}
case Id.dialog.no_pass_phrase: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.error);
alert.setMessage(R.string.passPhraseMustNotBeEmpty);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.no_pass_phrase);
}
});
alert.setCancelable(false);
return alert.create();
}
case Id.dialog.delete_file: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.warning);
alert.setMessage(this.getString(R.string.fileDeleteConfirmation, getDeleteFile()));
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_file);
final File file = new File(getDeleteFile());
showDialog(Id.dialog.deleting);
mDeletingThread = new Thread(new Runnable() {
public void run() {
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.delete_done);
try {
Apg.deleteFileSecurely(BaseActivity.this, file, BaseActivity.this);
} catch (FileNotFoundException e) {
data.putString(Apg.EXTRA_ERROR, BaseActivity.this.getString(
R.string.error_fileNotFound, file));
} catch (IOException e) {
data.putString(Apg.EXTRA_ERROR, BaseActivity.this.getString(
R.string.error_fileDeleteFailed, file));
}
Message msg = new Message();
msg.setData(data);
sendMessage(msg);
}
});
mDeletingThread.start();
}
});
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_file);
}
});
alert.setCancelable(true);
return alert.create();
}
default: {
break;
}
}
return super.onCreateDialog(id);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
} else {
setSecretKeyId(Id.key.none);
}
break;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void setProgress(int resourceId, int progress, int max) {
setProgress(getString(resourceId), progress, max);
}
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.progress_update);
data.putInt(Constants.extras.progress, progress);
data.putInt(Constants.extras.progress_max, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.progress_update);
data.putString(Constants.extras.message, message);
data.putInt(Constants.extras.progress, progress);
data.putInt(Constants.extras.progress_max, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
public void handlerCallback(Message msg) {
Bundle data = msg.getData();
if (data == null) {
return;
}
int type = data.getInt(Constants.extras.status);
switch (type) {
case Id.message.progress_update: {
String message = data.getString(Constants.extras.message);
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
mProgressDialog.setMax(data.getInt(Constants.extras.progress_max));
mProgressDialog.setProgress(data.getInt(Constants.extras.progress));
}
break;
}
case Id.message.delete_done: {
mProgressDialog = null;
deleteDoneCallback(msg);
break;
}
case Id.message.import_done: // intentionally no break
case Id.message.export_done: // intentionally no break
case Id.message.query_done: // intentionally no break
case Id.message.done: {
mProgressDialog = null;
doneCallback(msg);
break;
}
default: {
break;
}
}
}
public void doneCallback(Message msg) {
}
public void deleteDoneCallback(Message msg) {
removeDialog(Id.dialog.deleting);
mDeletingThread = null;
Bundle data = msg.getData();
String error = data.getString(Apg.EXTRA_ERROR);
String message;
if (error != null) {
message = getString(R.string.errorMessage, error);
} else {
message = getString(R.string.fileDeleteSuccessful);
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
public void passPhraseCallback(long keyId, String passPhrase) {
Apg.setCachedPassPhrase(keyId, passPhrase);
}
public void sendMessage(Message msg) {
mHandler.sendMessage(msg);
}
public PausableThread getRunningThread() {
return mRunningThread;
}
public void startThread() {
mRunningThread = new PausableThread(this);
mRunningThread.start();
}
public void run() {
}
public void setSecretKeyId(long id) {
mSecretKeyId = id;
}
public long getSecretKeyId() {
return mSecretKeyId;
}
protected void setDeleteFile(String deleteFile) {
mDeleteFile = deleteFile;
}
protected String getDeleteFile() {
return mDeleteFile;
}
public static void setLanguage(Context context, String language) {
Locale locale;
if (language == null || language.equals("")) {
locale = Locale.getDefault();
} else {
locale = new Locale(language);
}
Configuration config = new Configuration();
config.locale = locale;
context.getResources().updateConfiguration(config,
context.getResources().getDisplayMetrics());
}
}

View File

@ -14,13 +14,22 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.DataDestination;
import org.apg.DataSource;
import org.apg.FileDialog;
import org.apg.Id;
import org.apg.InputData;
import org.apg.PausableThread;
import org.apg.provider.DataProvider;
import org.apg.util.Compatibility;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.thialfihar.android.apg.provider.DataProvider; import org.apg.R;
import org.thialfihar.android.apg.utils.Compatibility;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,15 +14,19 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.Id;
import org.apg.provider.Database;
import org.apg.ui.widget.KeyEditor;
import org.apg.ui.widget.SectionView;
import org.apg.util.IterableIterator;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.provider.Database; import org.apg.R;
import org.thialfihar.android.apg.ui.widget.KeyEditor;
import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
@ -104,8 +108,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
mSaveButton.setOnClickListener(this); mSaveButton.setOnClickListener(this);
mDiscardButton.setOnClickListener(this); mDiscardButton.setOnClickListener(this);
LayoutInflater inflater = LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout container = (LinearLayout) findViewById(R.id.container); LinearLayout container = (LinearLayout) findViewById(R.id.container);
mUserIds = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mUserIds = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
@ -124,7 +127,8 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
updatePassPhraseButtonText(); updatePassPhraseButtonText();
Toast.makeText(this, getString(R.string.warningMessage, getString(R.string.keyEditingIsBeta)), Toast.makeText(this,
getString(R.string.warningMessage, getString(R.string.keyEditingIsBeta)),
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
} }
@ -136,16 +140,16 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
} }
public boolean havePassPhrase() { public boolean havePassPhrase() {
return (!mCurrentPassPhrase.equals("")) || return (!mCurrentPassPhrase.equals(""))
(mNewPassPhrase != null && !mNewPassPhrase.equals("")); || (mNewPassPhrase != null && !mNewPassPhrase.equals(""));
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences) menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences).setIcon(
.setIcon(android.R.drawable.ic_menu_preferences); android.R.drawable.ic_menu_preferences);
menu.add(0, Id.menu.option.about, 1, R.string.menu_about) menu.add(0, Id.menu.option.about, 1, R.string.menu_about).setIcon(
.setIcon(android.R.drawable.ic_menu_info_details); android.R.drawable.ic_menu_info_details);
return true; return true;
} }
@ -162,16 +166,14 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
} }
alert.setMessage(R.string.enterPassPhraseTwice); alert.setMessage(R.string.enterPassPhraseTwice);
LayoutInflater inflater = LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.pass_phrase, null); View view = inflater.inflate(R.layout.pass_phrase, null);
final EditText input1 = (EditText) view.findViewById(R.id.passPhrase); final EditText input1 = (EditText) view.findViewById(R.id.passPhrase);
final EditText input2 = (EditText) view.findViewById(R.id.passPhraseAgain); final EditText input2 = (EditText) view.findViewById(R.id.passPhraseAgain);
alert.setView(view); alert.setView(view);
alert.setPositiveButton(android.R.string.ok, alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.new_pass_phrase); removeDialog(Id.dialog.new_pass_phrase);
@ -192,8 +194,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
} }
}); });
alert.setNegativeButton(android.R.string.cancel, alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.new_pass_phrase); removeDialog(Id.dialog.new_pass_phrase);
} }
@ -275,8 +276,8 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
String error = data.getString(Apg.EXTRA_ERROR); String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) { if (error != null) {
Toast.makeText(EditKeyActivity.this, Toast.makeText(EditKeyActivity.this, getString(R.string.errorMessage, error),
getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
} else { } else {
Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show(); Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show();
setResult(RESULT_OK); setResult(RESULT_OK);
@ -285,7 +286,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
} }
private void updatePassPhraseButtonText() { private void updatePassPhraseButtonText() {
mChangePassPhrase.setText( mChangePassPhrase.setText(havePassPhrase() ? R.string.btn_changePassPhrase
havePassPhrase() ? R.string.btn_changePassPhrase : R.string.btn_setPassPhrase); : R.string.btn_setPassPhrase);
} }
} }

View File

@ -14,16 +14,24 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.DataDestination;
import org.apg.DataSource;
import org.apg.FileDialog;
import org.apg.Id;
import org.apg.InputData;
import org.apg.provider.DataProvider;
import org.apg.util.Choice;
import org.apg.util.Compatibility;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.provider.DataProvider; import org.apg.R;
import org.thialfihar.android.apg.utils.Choice;
import org.thialfihar.android.apg.utils.Compatibility;
import android.app.Dialog; import android.app.Dialog;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
@ -205,14 +213,14 @@ public class EncryptActivity extends BaseActivity {
mFileCompression = (Spinner) findViewById(R.id.fileCompression); mFileCompression = (Spinner) findViewById(R.id.fileCompression);
Choice[] choices = new Choice[] { Choice[] choices = new Choice[] {
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
" (" + getString(R.string.fast) + ")"), + getString(R.string.fast) + ")"),
new Choice(Id.choice.compression.zip, "ZIP (" + 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.zlib, "ZLIB (" + getString(R.string.fast) + ")"),
new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow) + ")"), new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow)
}; + ")"), };
ArrayAdapter<Choice> adapter = ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(this,
new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices); android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mFileCompression.setAdapter(adapter); mFileCompression.setAdapter(adapter);
@ -265,18 +273,18 @@ public class EncryptActivity extends BaseActivity {
}); });
mIntent = getIntent(); mIntent = getIntent();
if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || if (Apg.Intent.ENCRYPT.equals(mIntent.getAction())
Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) || || Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())
Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction()) || || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
mContentUri = mIntent.getData(); mContentUri = mIntent.getData();
Bundle extras = mIntent.getExtras(); Bundle extras = mIntent.getExtras();
if (extras == null) { if (extras == null) {
extras = new Bundle(); extras = new Bundle();
} }
if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction()) || if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
mReturnResult = true; mReturnResult = true;
} }
@ -341,9 +349,9 @@ public class EncryptActivity extends BaseActivity {
} }
} }
if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || if (Apg.Intent.ENCRYPT.equals(mIntent.getAction())
Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction()) || || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
if (textData != null) { if (textData != null) {
mMessage.setText(textData); mMessage.setText(textData);
} }
@ -385,11 +393,9 @@ public class EncryptActivity extends BaseActivity {
updateButtons(); updateButtons();
if (mReturnResult && if (mReturnResult
(mMessage.getText().length() > 0 || mData != null || mContentUri != null) && && (mMessage.getText().length() > 0 || mData != null || mContentUri != null)
((mEncryptionKeyIds != null && && ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) || getSecretKeyId() != 0)) {
mEncryptionKeyIds.length > 0) ||
getSecretKeyId() != 0)) {
encryptClicked(); encryptClicked();
} }
} }
@ -550,8 +556,9 @@ public class EncryptActivity extends BaseActivity {
if (!mInputFilename.startsWith("content")) { if (!mInputFilename.startsWith("content")) {
File file = new File(mInputFilename); File file = new File(mInputFilename);
if (!file.exists() || !file.isFile()) { if (!file.exists() || !file.isFile()) {
Toast.makeText(this, getString(R.string.errorMessage, Toast.makeText(
getString(R.string.error_fileNotFound)), this,
getString(R.string.errorMessage, getString(R.string.error_fileNotFound)),
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
return; return;
} }
@ -582,8 +589,8 @@ public class EncryptActivity extends BaseActivity {
} }
if (!encryptIt && getSecretKeyId() == 0) { if (!encryptIt && getSecretKeyId() == 0) {
Toast.makeText(this, R.string.selectEncryptionOrSignatureKey, Toast.makeText(this, R.string.selectEncryptionOrSignatureKey, Toast.LENGTH_SHORT)
Toast.LENGTH_SHORT).show(); .show();
return; return;
} }
@ -667,26 +674,20 @@ public class EncryptActivity extends BaseActivity {
if (mGenerateSignature) { if (mGenerateSignature) {
Apg.generateSignature(this, in, out, useAsciiArmour, mDataSource.isBinary(), Apg.generateSignature(this, in, out, useAsciiArmour, mDataSource.isBinary(),
getSecretKeyId(), getSecretKeyId(), Apg.getCachedPassPhrase(getSecretKeyId()),
Apg.getCachedPassPhrase(getSecretKeyId()),
mPreferences.getDefaultHashAlgorithm(), mPreferences.getDefaultHashAlgorithm(),
mPreferences.getForceV3Signatures(), mPreferences.getForceV3Signatures(), this);
this);
} else if (signOnly) { } else if (signOnly) {
Apg.signText(this, in, out, getSecretKeyId(), Apg.signText(this, in, out, getSecretKeyId(),
Apg.getCachedPassPhrase(getSecretKeyId()), Apg.getCachedPassPhrase(getSecretKeyId()),
mPreferences.getDefaultHashAlgorithm(), mPreferences.getDefaultHashAlgorithm(),
mPreferences.getForceV3Signatures(), mPreferences.getForceV3Signatures(), this);
this);
} else { } else {
Apg.encrypt(this, in, out, useAsciiArmour, Apg.encrypt(this, in, out, useAsciiArmour, encryptionKeyIds, signatureKeyId,
encryptionKeyIds, signatureKeyId,
Apg.getCachedPassPhrase(signatureKeyId), this, Apg.getCachedPassPhrase(signatureKeyId), this,
mPreferences.getDefaultEncryptionAlgorithm(), mPreferences.getDefaultEncryptionAlgorithm(),
mPreferences.getDefaultHashAlgorithm(), mPreferences.getDefaultHashAlgorithm(), compressionId,
compressionId, mPreferences.getForceV3Signatures(), passPhrase);
mPreferences.getForceV3Signatures(),
passPhrase);
} }
out.close(); out.close();
@ -746,8 +747,8 @@ public class EncryptActivity extends BaseActivity {
} else if (mEncryptionKeyIds.length == 1) { } else if (mEncryptionKeyIds.length == 1) {
mSelectKeysButton.setText(R.string.oneKeySelected); mSelectKeysButton.setText(R.string.oneKeySelected);
} else { } else {
mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " " + mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " "
getResources().getString(R.string.nKeysSelected)); + getResources().getString(R.string.nKeysSelected));
} }
if (getSecretKeyId() == 0) { if (getSecretKeyId() == 0) {
@ -875,15 +876,16 @@ public class EncryptActivity extends BaseActivity {
Bundle data = msg.getData(); Bundle data = msg.getData();
String error = data.getString(Apg.EXTRA_ERROR); String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) { if (error != null) {
Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show(); Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT)
.show();
return; return;
} }
switch (mEncryptTarget) { switch (mEncryptTarget) {
case Id.target.clipboard: { case Id.target.clipboard: {
String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE); String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE);
Compatibility.copyToClipboard(this, message); Compatibility.copyToClipboard(this, message);
Toast.makeText(this, R.string.encryptionToClipboardSuccessful, Toast.makeText(this, R.string.encryptionToClipboardSuccessful, Toast.LENGTH_SHORT)
Toast.LENGTH_SHORT).show(); .show();
break; break;
} }
@ -901,15 +903,12 @@ public class EncryptActivity extends BaseActivity {
emailIntent.setType("text/plain; charset=utf-8"); emailIntent.setType("text/plain; charset=utf-8");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message); emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
if (mSubject != null) { if (mSubject != null) {
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, mSubject);
mSubject);
} }
if (mSendTo != null) { if (mSendTo != null) {
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { mSendTo });
new String[] { mSendTo });
} }
EncryptActivity.this. EncryptActivity.this.startActivity(Intent.createChooser(emailIntent,
startActivity(Intent.createChooser(emailIntent,
getString(R.string.title_sendEmail))); getString(R.string.title_sendEmail)));
break; break;
} }
@ -935,8 +934,7 @@ public class EncryptActivity extends BaseActivity {
switch (id) { switch (id) {
case Id.dialog.output_filename: { case Id.dialog.output_filename: {
return FileDialog.build(this, getString(R.string.title_encryptToFile), return FileDialog.build(this, getString(R.string.title_encryptToFile),
getString(R.string.specifyFileToEncryptTo), getString(R.string.specifyFileToEncryptTo), mOutputFilename,
mOutputFilename,
new FileDialog.OnClickListener() { new FileDialog.OnClickListener() {
public void onOkClick(String filename, boolean checked) { public void onOkClick(String filename, boolean checked) {
removeDialog(Id.dialog.output_filename); removeDialog(Id.dialog.output_filename);
@ -947,11 +945,8 @@ public class EncryptActivity extends BaseActivity {
public void onCancelClick() { public void onCancelClick() {
removeDialog(Id.dialog.output_filename); removeDialog(Id.dialog.output_filename);
} }
}, }, getString(R.string.filemanager_titleSave),
getString(R.string.filemanager_titleSave), getString(R.string.filemanager_btnSave), null, Id.request.output_filename);
getString(R.string.filemanager_btnSave),
null,
Id.request.output_filename);
} }
default: { default: {

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -6,7 +6,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Vector; import java.util.Vector;
import org.thialfihar.android.apg.utils.Choice; import org.apg.Apg;
import org.apg.Id;
import org.apg.util.Choice;
import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;

View File

@ -1,11 +1,16 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.HkpKeyServer;
import org.apg.Id;
import org.apg.KeyServer.QueryException;
import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.thialfihar.android.apg.KeyServer.QueryException; import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,14 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.FileDialog;
import org.apg.Id;
import org.apg.InputData;
import org.apg.provider.KeyRings;
import org.apg.provider.Keys;
import org.apg.provider.UserIds;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.provider.KeyRings; import org.apg.R;
import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,13 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Vector; import java.util.Vector;
import org.thialfihar.android.apg.ui.widget.Editor; import org.apg.Apg;
import org.thialfihar.android.apg.ui.widget.Editor.EditorListener; import org.apg.ui.widget.Editor;
import org.thialfihar.android.apg.ui.widget.KeyServerEditor; import org.apg.ui.widget.KeyServerEditor;
import org.apg.ui.widget.Editor.EditorListener;
import org.apg.R;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;

View File

@ -1,12 +1,17 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
import org.thialfihar.android.apg.KeyServer.InsufficientQuery; import org.apg.Apg;
import org.thialfihar.android.apg.KeyServer.KeyInfo; import org.apg.Constants;
import org.thialfihar.android.apg.KeyServer.QueryException; import org.apg.HkpKeyServer;
import org.thialfihar.android.apg.KeyServer.TooManyResponses; import org.apg.Id;
import org.apg.KeyServer.InsufficientQuery;
import org.apg.KeyServer.KeyInfo;
import org.apg.KeyServer.QueryException;
import org.apg.KeyServer.TooManyResponses;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,11 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Vector; import java.util.Vector;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import org.apg.Apg;
import org.apg.Preferences;
import org.apg.R;
import android.app.ListActivity; import android.app.ListActivity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;

View File

@ -14,14 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.security.Security; import java.security.Security;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apg.Apg;
import org.apg.Id;
import org.apg.Id.dialog;
import org.apg.Id.menu;
import org.apg.Id.menu.option;
import org.apg.provider.Accounts;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.thialfihar.android.apg.provider.Accounts; import org.apg.R;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,11 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.Id;
import org.apg.Preferences;
import org.apg.Constants.pref;
import org.apg.Id.choice;
import org.apg.Id.request;
import org.apg.Id.choice.compression;
import org.apg.ui.widget.IntegerListPreference;
import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPEncryptedData;
import org.thialfihar.android.apg.ui.widget.IntegerListPreference; import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,9 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.Id;
import org.apg.Constants.path;
import org.apg.Id.menu;
import org.apg.Id.request;
import org.apg.Id.type;
import org.apg.Id.menu.option;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,7 +14,19 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.AskForSecretKeyPassPhrase;
import org.apg.Constants;
import org.apg.Id;
import org.apg.Constants.path;
import org.apg.Id.dialog;
import org.apg.Id.menu;
import org.apg.Id.message;
import org.apg.Id.type;
import org.apg.Id.menu.option;
import org.apg.R;
import android.app.Dialog; import android.app.Dialog;
import android.content.Intent; import android.content.Intent;

View File

@ -14,10 +14,16 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Vector; import java.util.Vector;
import org.apg.Apg;
import org.apg.Id;
import org.apg.Id.menu;
import org.apg.Id.menu.option;
import org.apg.R;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,13 +14,17 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Date; import java.util.Date;
import org.thialfihar.android.apg.provider.KeyRings; import org.apg.Apg;
import org.thialfihar.android.apg.provider.Keys; import org.apg.Id;
import org.thialfihar.android.apg.provider.UserIds; import org.apg.Id.database;
import org.apg.provider.KeyRings;
import org.apg.provider.Keys;
import org.apg.provider.UserIds;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;

View File

@ -14,7 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Id;
import org.apg.Id.menu;
import org.apg.Id.menu.option;
import org.apg.R;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.Intent; import android.content.Intent;

View File

@ -1,10 +1,14 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Date; import java.util.Date;
import org.thialfihar.android.apg.provider.KeyRings; import org.apg.Apg;
import org.thialfihar.android.apg.provider.Keys; import org.apg.Id;
import org.thialfihar.android.apg.provider.UserIds; import org.apg.Id.database;
import org.apg.provider.KeyRings;
import org.apg.provider.Keys;
import org.apg.provider.UserIds;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;

View File

@ -1,7 +1,14 @@
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.HkpKeyServer;
import org.apg.Id;
import org.apg.Constants.extras;
import org.apg.Id.message;
import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.apg.R;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message; import android.os.Message;

View File

@ -1,10 +1,19 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Iterator; import java.util.Iterator;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.HkpKeyServer;
import org.apg.Id;
import org.apg.Constants.extras;
import org.apg.Id.dialog;
import org.apg.Id.message;
import org.apg.Id.request;
import org.apg.Id.return_value;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPrivateKey;
@ -16,6 +25,7 @@ import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector; import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPUtil;
import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
public interface Editor { public interface Editor {
public interface EditorListener { public interface EditorListener {

View File

@ -14,7 +14,7 @@
* the License. * the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import android.content.Context; import android.content.Context;
import android.preference.ListPreference; import android.preference.ListPreference;

View File

@ -14,14 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import org.apg.Apg;
import org.apg.Id;
import org.apg.util.Choice;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.thialfihar.android.apg.Apg; import org.apg.R;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.utils.Choice;
import android.app.DatePickerDialog; import android.app.DatePickerDialog;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,9 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import org.thialfihar.android.apg.R; import org.apg.R;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;

View File

@ -14,15 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import org.apg.Apg;
import org.apg.Id;
import org.apg.ui.widget.Editor.EditorListener;
import org.apg.util.Choice;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.thialfihar.android.apg.Apg; import org.apg.R;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.ui.widget.Editor.EditorListener;
import org.thialfihar.android.apg.utils.Choice;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;

View File

@ -14,12 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.thialfihar.android.apg.R; import org.apg.R;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;

View File

@ -14,10 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.utils; package org.apg.util;
import org.thialfihar.android.apg.IApgService; import org.apg.util.ApgConInterface.OnCallFinishListener;
import org.thialfihar.android.apg.utils.ApgConInterface.OnCallFinishListener; import org.apg.IApgService;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentResolver; import android.content.ContentResolver;

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg.utils; package org.apg.util;
public interface ApgConInterface { public interface ApgConInterface {
public static interface OnCallFinishListener { public static interface OnCallFinishListener {

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.utils; package org.apg.util;
public class Choice { public class Choice {
private String mName; private String mName;

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg.utils; package org.apg.util;
import java.lang.reflect.Method; import java.lang.reflect.Method;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.utils; package org.apg.util;
import java.util.Iterator; import java.util.Iterator;

View File

@ -1,464 +0,0 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Locale;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class BaseActivity extends Activity
implements Runnable, ProgressDialogUpdater,
AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
private ProgressDialog mProgressDialog = null;
private PausableThread mRunningThread = null;
private Thread mDeletingThread = null;
private long mSecretKeyId = 0;
private String mDeleteFile = null;
protected Preferences mPreferences;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
handlerCallback(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPreferences = Preferences.getPreferences(this);
setLanguage(this, mPreferences.getLanguage());
Apg.initialize(this);
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(Constants.path.app_dir);
if (!dir.exists() && !dir.mkdirs()) {
// ignore this for now, it's not crucial
// that the directory doesn't exist at this point
}
}
startCacheService(this, mPreferences);
}
public static void startCacheService(Activity activity, Preferences preferences) {
Intent intent = new Intent(activity, Service.class);
intent.putExtra(Service.EXTRA_TTL, preferences.getPassPhraseCacheTtl());
activity.startService(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences)
.setIcon(android.R.drawable.ic_menu_preferences);
menu.add(0, Id.menu.option.about, 1, R.string.menu_about)
.setIcon(android.R.drawable.ic_menu_info_details);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Id.menu.option.about: {
showDialog(Id.dialog.about);
return true;
}
case Id.menu.option.preferences: {
startActivity(new Intent(this, PreferencesActivity.class));
return true;
}
case Id.menu.option.search: {
startSearch("", false, null, false);
return true;
}
default: {
break;
}
}
return false;
}
@Override
protected Dialog onCreateDialog(int id) {
// in case it is a progress dialog
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
switch (id) {
case Id.dialog.encrypting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.decrypting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.saving: {
mProgressDialog.setMessage(this.getString(R.string.progress_saving));
return mProgressDialog;
}
case Id.dialog.importing: {
mProgressDialog.setMessage(this.getString(R.string.progress_importing));
return mProgressDialog;
}
case Id.dialog.exporting: {
mProgressDialog.setMessage(this.getString(R.string.progress_exporting));
return mProgressDialog;
}
case Id.dialog.deleting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.querying: {
mProgressDialog.setMessage(this.getString(R.string.progress_querying));
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case Id.dialog.signing: {
mProgressDialog.setMessage(this.getString(R.string.progress_signing));
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
default: {
break;
}
}
mProgressDialog = null;
switch (id) {
case Id.dialog.about: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("About " + Apg.getFullVersion(this));
LayoutInflater inflater =
(LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.info, null);
TextView message = (TextView) layout.findViewById(R.id.message);
message.setText("This is an attempt to bring OpenPGP to Android. " +
"It is far from complete, but more features are planned (see website).\n\n" +
"Feel free to send bug reports, suggestions, feature requests, feedback, " +
"photographs.\n\n" +
"mail: thi@thialfihar.org\n" +
"site: http://apg.thialfihar.org\n\n" +
"This software is provided \"as is\", without warranty of any kind.");
alert.setView(layout);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
BaseActivity.this.removeDialog(Id.dialog.about);
}
});
return alert.create();
}
case Id.dialog.pass_phrase: {
return AskForSecretKeyPassPhrase.createDialog(this, getSecretKeyId(), this);
}
case Id.dialog.pass_phrases_do_not_match: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.error);
alert.setMessage(R.string.passPhrasesDoNotMatch);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.pass_phrases_do_not_match);
}
});
alert.setCancelable(false);
return alert.create();
}
case Id.dialog.no_pass_phrase: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.error);
alert.setMessage(R.string.passPhraseMustNotBeEmpty);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.no_pass_phrase);
}
});
alert.setCancelable(false);
return alert.create();
}
case Id.dialog.delete_file: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.warning);
alert.setMessage(this.getString(R.string.fileDeleteConfirmation, getDeleteFile()));
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_file);
final File file = new File(getDeleteFile());
showDialog(Id.dialog.deleting);
mDeletingThread = new Thread(new Runnable() {
public void run() {
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.delete_done);
try {
Apg.deleteFileSecurely(BaseActivity.this, file, BaseActivity.this);
} catch (FileNotFoundException e) {
data.putString(Apg.EXTRA_ERROR,
BaseActivity.this.getString(
R.string.error_fileNotFound, file));
} catch (IOException e) {
data.putString(Apg.EXTRA_ERROR,
BaseActivity.this.getString(
R.string.error_fileDeleteFailed, file));
}
Message msg = new Message();
msg.setData(data);
sendMessage(msg);
}
});
mDeletingThread.start();
}
});
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_file);
}
});
alert.setCancelable(true);
return alert.create();
}
default: {
break;
}
}
return super.onCreateDialog(id);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
} else {
setSecretKeyId(Id.key.none);
}
break;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void setProgress(int resourceId, int progress, int max) {
setProgress(getString(resourceId), progress, max);
}
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.progress_update);
data.putInt(Constants.extras.progress, progress);
data.putInt(Constants.extras.progress_max, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.progress_update);
data.putString(Constants.extras.message, message);
data.putInt(Constants.extras.progress, progress);
data.putInt(Constants.extras.progress_max, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
public void handlerCallback(Message msg) {
Bundle data = msg.getData();
if (data == null) {
return;
}
int type = data.getInt(Constants.extras.status);
switch (type) {
case Id.message.progress_update: {
String message = data.getString(Constants.extras.message);
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
mProgressDialog.setMax(data.getInt(Constants.extras.progress_max));
mProgressDialog.setProgress(data.getInt(Constants.extras.progress));
}
break;
}
case Id.message.delete_done: {
mProgressDialog = null;
deleteDoneCallback(msg);
break;
}
case Id.message.import_done: // intentionally no break
case Id.message.export_done: // intentionally no break
case Id.message.query_done: // intentionally no break
case Id.message.done: {
mProgressDialog = null;
doneCallback(msg);
break;
}
default: {
break;
}
}
}
public void doneCallback(Message msg) {
}
public void deleteDoneCallback(Message msg) {
removeDialog(Id.dialog.deleting);
mDeletingThread = null;
Bundle data = msg.getData();
String error = data.getString(Apg.EXTRA_ERROR);
String message;
if (error != null) {
message = getString(R.string.errorMessage, error);
} else {
message = getString(R.string.fileDeleteSuccessful);
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
public void passPhraseCallback(long keyId, String passPhrase) {
Apg.setCachedPassPhrase(keyId, passPhrase);
}
public void sendMessage(Message msg) {
mHandler.sendMessage(msg);
}
public PausableThread getRunningThread() {
return mRunningThread;
}
public void startThread() {
mRunningThread = new PausableThread(this);
mRunningThread.start();
}
public void run() {
}
public void setSecretKeyId(long id) {
mSecretKeyId = id;
}
public long getSecretKeyId() {
return mSecretKeyId;
}
protected void setDeleteFile(String deleteFile) {
mDeleteFile = deleteFile;
}
protected String getDeleteFile() {
return mDeleteFile;
}
public static void setLanguage(Context context, String language)
{
Locale locale;
if (language == null || language.equals(""))
{
locale = Locale.getDefault();
}
else
{
locale = new Locale(language);
}
Configuration config = new Configuration();
config.locale = locale;
context.getResources().updateConfiguration(config,
context.getResources().getDisplayMetrics());
}
}

View File

@ -1,79 +0,0 @@
package org.thialfihar.android.apg;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.thialfihar.android.apg.Apg.GeneralException;
import android.content.Context;
import android.os.Environment;
public class DataDestination {
private String mStreamFilename;
private String mFilename;
private int mMode = Id.mode.undefined;
public DataDestination() {
}
public void setMode(int mode) {
mMode = mode;
}
public void setFilename(String filename) {
mFilename = filename;
}
public String getStreamFilename() {
return mStreamFilename;
}
protected OutputStream getOutputStream(Context context)
throws Apg.GeneralException, FileNotFoundException, IOException {
OutputStream out = null;
mStreamFilename = null;
switch (mMode) {
case Id.mode.stream: {
try {
while (true) {
mStreamFilename = Apg.generateRandomString(32);
if (mStreamFilename == null) {
throw new Apg.GeneralException("couldn't generate random file name");
}
context.openFileInput(mStreamFilename).close();
}
} catch (FileNotFoundException e) {
// found a name that isn't used yet
}
out = context.openFileOutput(mStreamFilename, Context.MODE_PRIVATE);
break;
}
case Id.mode.byte_array: {
out = new ByteArrayOutputStream();
break;
}
case Id.mode.file: {
if (mFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
}
}
out = new FileOutputStream(mFilename);
break;
}
default: {
break;
}
}
return out;
}
}