Merge from apg-integration
This commit is contained in:
parent
bae8a9736d
commit
7a4d12b53b
18 changed files with 1553 additions and 84 deletions
|
@ -22,6 +22,8 @@
|
|||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" />
|
||||
|
||||
<permission android:name="com.fsck.k9.permission.READ_ATTACHMENT"
|
||||
android:permissionGroup="android.permission-group.MESSAGES"
|
||||
android:protectionLevel="dangerous"
|
||||
|
|
BIN
res/drawable/overlay_error.png
Normal file
BIN
res/drawable/overlay_error.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
res/drawable/overlay_ok.png
Normal file
BIN
res/drawable/overlay_ok.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
BIN
res/drawable/signed_large.png
Normal file
BIN
res/drawable/signed_large.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
|
@ -70,6 +70,66 @@
|
|||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_encrypt"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:paddingLeft="6dip"
|
||||
android:paddingRight="6dip">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<CheckBox
|
||||
android:text="@string/btn_crypto_sign"
|
||||
android:id="@+id/cb_crypto_signature"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingRight="2dip">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userId"
|
||||
android:text=""
|
||||
android:ellipsize="end"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userIdRest"
|
||||
android:text=""
|
||||
android:textSize="10sp"
|
||||
android:ellipsize="end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox
|
||||
android:text="@string/btn_encrypt"
|
||||
android:id="@+id/cb_encrypt"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/subject"
|
||||
android:layout_width="fill_parent"
|
||||
|
|
|
@ -179,6 +179,73 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="@string/message_view_show_pictures_action" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_decrypt"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/crypto_signature"
|
||||
android:orientation="horizontal"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dip"
|
||||
android:layout_weight="2"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ic_crypto_signature"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/signed_large"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ic_crypto_signature_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/overlay_error"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingLeft="5dip">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userId"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:ellipsize="end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userIdRest"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:ellipsize="end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:text="@string/btn_decrypt"
|
||||
android:id="@+id/btn_decrypt"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
<!-- Content area -->
|
||||
<WebView
|
||||
android:id="@+id/message_content"
|
||||
|
|
|
@ -470,7 +470,6 @@ K-9 Mail セットアップにようこそ。\nK-9 はオープンソースで
|
|||
<string name="account_setup_options_mail_display_count_500">500件</string>
|
||||
<string name="account_setup_options_mail_display_count_1000">1000件</string>
|
||||
|
||||
|
||||
<string name="move_copy_cannot_copy_unsynced_message">サーバーと同期されていないメールをコピーまたは移動できません</string>
|
||||
|
||||
<string name="account_setup_failed_dlg_title">設定エラー</string>
|
||||
|
@ -886,7 +885,6 @@ K-9 Mail セットアップにようこそ。\nK-9 はオープンソースで
|
|||
|
||||
<string name="save_or_discard_draft_message_dlg_title">メッセージの下書き保存</string>
|
||||
<string name="save_or_discard_draft_message_instructions_fmt">メッセージを保存しますか?</string>
|
||||
|
||||
<string name="charset_not_found">このメッセージに使われている文字セット \"<xliff:g id="charset">%s</xliff:g>\" は存在していません</string>
|
||||
|
||||
<string name="select_text_now">選択したテキストをコピーします</string>
|
||||
|
|
|
@ -589,4 +589,14 @@
|
|||
<item>5</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="account_settings_crypto_app_entries">
|
||||
<item>@string/account_settings_crypto_app_none</item>
|
||||
<item>APG</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="account_settings_crypto_app_values">
|
||||
<item></item>
|
||||
<item>apg</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -512,6 +512,12 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
|||
<string name="account_settings_message_lists">Listing messages</string>
|
||||
<string name="account_settings_message_view">Viewing messages</string>
|
||||
<string name="account_settings_quote_prefix_label">Quote prefix</string>
|
||||
<string name="account_settings_crypto">Cryptography</string>
|
||||
<string name="account_settings_crypto_app">OpenPGP Provider</string>
|
||||
<string name="account_settings_crypto_app_none">None</string>
|
||||
<string name="account_settings_crypto_app_not_available">not available</string>
|
||||
<string name="account_settings_crypto_auto_signature">Auto-sign</string>
|
||||
<string name="account_settings_crypto_auto_signature_summary">Use the account\'s email address to guess the signature key.</string>
|
||||
|
||||
<string name="account_settings_mail_check_frequency_label">Folder poll check frequency</string>
|
||||
<string name="account_settings_second_class_check_frequency_label">2nd class check frequency</string>
|
||||
|
@ -891,6 +897,20 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
|||
<string name="misc_preferences_attachment_title">Use Gallery bug work-around</string>
|
||||
<string name="misc_preferences_attachment_description">Show buttons to add image/video attachments (to work around a Gallery 3D bug)</string>
|
||||
|
||||
<!-- APG related -->
|
||||
<string name="error_activity_not_found">No suitable application for this action found.</string>
|
||||
<string name="error_apg_version_not_supported">The installed APG version is not supported.</string>
|
||||
<string name="btn_crypto_sign">Sign</string>
|
||||
<string name="btn_encrypt">Encrypt</string>
|
||||
<string name="btn_decrypt">Decrypt</string>
|
||||
<string name="btn_verify">Verify</string>
|
||||
<string name="unknown_crypto_signature_user_id"><unknown></string>
|
||||
<string name="key_id">id: %s</string>
|
||||
<string name="insufficient_apg_permissions">K-9 doesn\'t have permission to access APG fully, please reinstall K-9 to fix that.</string>
|
||||
<string name="pgp_mime_unsupported">PGP/MIME messages are not supported yet.</string>
|
||||
<string name="attachment_encryption_unsupported">Warning: attachments are NOT signed or encrypted yet.</string>
|
||||
<string name="send_aborted">Send aborted.</string>
|
||||
|
||||
<string name="save_or_discard_draft_message_dlg_title">Save draft message?</string>
|
||||
<string name="save_or_discard_draft_message_instructions_fmt">Save or Discard this message?</string>
|
||||
|
||||
|
|
|
@ -207,6 +207,22 @@
|
|||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/account_settings_crypto">
|
||||
|
||||
<ListPreference
|
||||
android:key="crypto_app"
|
||||
android:title="@string/account_settings_crypto_app"
|
||||
android:entries="@array/account_settings_crypto_app_entries"
|
||||
android:entryValues="@array/account_settings_crypto_app_values"
|
||||
android:dialogTitle="@string/account_settings_crypto_app" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="crypto_auto_signature"
|
||||
android:title="@string/account_settings_crypto_auto_signature"
|
||||
android:summary="@string/account_settings_crypto_auto_signature_summary"
|
||||
android:dependency="crypto_app"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/account_settings_notifications">
|
||||
|
||||
<CheckBoxPreference
|
||||
|
|
|
@ -107,6 +107,8 @@ public class Account implements BaseAccount
|
|||
private boolean mRingNotified;
|
||||
private String mQuotePrefix;
|
||||
private boolean mSyncRemoteDeletions;
|
||||
private String mCryptoApp;
|
||||
private boolean mCryptoAutoSignature;
|
||||
|
||||
/**
|
||||
* Name of the folder that was last selected for a copy or move operation.
|
||||
|
@ -171,6 +173,8 @@ public class Account implements BaseAccount
|
|||
maximumAutoDownloadMessageSize = 32768;
|
||||
mQuotePrefix = DEFAULT_QUOTE_PREFIX;
|
||||
mSyncRemoteDeletions = true;
|
||||
mCryptoApp = "";
|
||||
mCryptoAutoSignature = false;
|
||||
|
||||
searchableFolders = Searchable.ALL;
|
||||
|
||||
|
@ -270,14 +274,12 @@ public class Account implements BaseAccount
|
|||
(random.nextInt(0x70) * 0xff) +
|
||||
(random.nextInt(0x70) * 0xffff) +
|
||||
0xff000000);
|
||||
|
||||
mLedColor = prefs.getInt(mUuid+".ledColor", mChipColor);
|
||||
|
||||
mVibrate = prefs.getBoolean(mUuid + ".vibrate", false);
|
||||
mVibratePattern = prefs.getInt(mUuid + ".vibratePattern", 0);
|
||||
mVibrateTimes = prefs.getInt(mUuid + ".vibrateTimes", 5);
|
||||
|
||||
|
||||
mRing = prefs.getBoolean(mUuid + ".ring", true);
|
||||
|
||||
try
|
||||
|
@ -356,6 +358,9 @@ public class Account implements BaseAccount
|
|||
|
||||
mIsSignatureBeforeQuotedText = prefs.getBoolean(mUuid + ".signatureBeforeQuotedText", false);
|
||||
identities = loadIdentities(prefs);
|
||||
|
||||
mCryptoApp = prefs.getString(mUuid + ".cryptoApp", "");
|
||||
mCryptoAutoSignature = prefs.getBoolean(mUuid + ".cryptoAutoSignature", false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -518,6 +523,8 @@ public class Account implements BaseAccount
|
|||
editor.putInt(mUuid + ".maximumPolledMessageAge", maximumPolledMessageAge);
|
||||
editor.putInt(mUuid + ".maximumAutoDownloadMessageSize", maximumAutoDownloadMessageSize);
|
||||
editor.putString(mUuid + ".quotePrefix", mQuotePrefix);
|
||||
editor.putString(mUuid + ".cryptoApp", mCryptoApp);
|
||||
editor.putBoolean(mUuid + ".cryptoAutoSignature", mCryptoAutoSignature);
|
||||
|
||||
for (String type : networkTypes)
|
||||
{
|
||||
|
@ -1411,6 +1418,25 @@ public class Account implements BaseAccount
|
|||
mEnableMoveButtons = enableMoveButtons;
|
||||
}
|
||||
|
||||
public String getCryptoApp()
|
||||
{
|
||||
return mCryptoApp;
|
||||
}
|
||||
|
||||
public void setCryptoApp(String cryptoApp)
|
||||
{
|
||||
mCryptoApp = cryptoApp;
|
||||
}
|
||||
|
||||
public boolean getCryptoAutoSignature()
|
||||
{
|
||||
return mCryptoAutoSignature;
|
||||
}
|
||||
|
||||
public void setCryptoAutoSignature(boolean cryptoAutoSignature)
|
||||
{
|
||||
mCryptoAutoSignature = cryptoAutoSignature;
|
||||
}
|
||||
public synchronized boolean syncRemoteDeletions()
|
||||
{
|
||||
return mSyncRemoteDeletions;
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
|
||||
package com.fsck.k9.activity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ContentResolver;
|
||||
|
@ -26,25 +36,42 @@ import android.view.View.OnClickListener;
|
|||
import android.view.View.OnFocusChangeListener;
|
||||
import android.view.Window;
|
||||
import android.widget.AutoCompleteTextView.Validator;
|
||||
import android.widget.*;
|
||||
import com.fsck.k9.*;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.MultiAutoCompleteTextView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.EmailAddressAdapter;
|
||||
import com.fsck.k9.EmailAddressValidator;
|
||||
import com.fsck.k9.Identity;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
import com.fsck.k9.crypto.CryptoProvider;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.*;
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.Body;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.Message.RecipientType;
|
||||
import com.fsck.k9.mail.internet.*;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Multipart;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||
import com.fsck.k9.mail.internet.MimeHeader;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody;
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
||||
|
||||
public class MessageCompose extends K9Activity implements OnClickListener, OnFocusChangeListener
|
||||
{
|
||||
|
@ -55,8 +82,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
private static final String ACTION_FORWARD = "com.fsck.k9.intent.action.FORWARD";
|
||||
private static final String ACTION_EDIT_DRAFT = "com.fsck.k9.intent.action.EDIT_DRAFT";
|
||||
|
||||
|
||||
private static final String EXTRA_ACCOUNT = "account";
|
||||
private static final String EXTRA_MESSAGE_BODY = "messageBody";
|
||||
private static final String EXTRA_MESSAGE_REFERENCE = "message_reference";
|
||||
|
||||
private static final String STATE_KEY_ATTACHMENTS =
|
||||
|
@ -75,6 +102,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
"com.fsck.k9.activity.MessageCompose.identityChanged";
|
||||
private static final String STATE_IDENTITY =
|
||||
"com.fsck.k9.activity.MessageCompose.identity";
|
||||
private static final String STATE_CRYPTO = "crypto";
|
||||
private static final String STATE_IN_REPLY_TO = "com.fsck.k9.activity.MessageCompose.inReplyTo";
|
||||
private static final String STATE_REFERENCES = "com.fsck.k9.activity.MessageCompose.references";
|
||||
|
||||
|
@ -110,6 +138,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
private MessageReference mMessageReference;
|
||||
|
||||
private Message mSourceMessage;
|
||||
private String mSourceMessageBody;
|
||||
|
||||
/**
|
||||
* Indicates that the source message has been processed at least once and should not
|
||||
|
@ -130,11 +159,19 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
private View mQuotedTextBar;
|
||||
private ImageButton mQuotedTextDelete;
|
||||
private EditText mQuotedText;
|
||||
private View mEncryptLayout;
|
||||
private CheckBox mCryptoSignatureCheckbox;
|
||||
private CheckBox mEncryptCheckbox;
|
||||
private TextView mCryptoSignatureUserId;
|
||||
private TextView mCryptoSignatureUserIdRest;
|
||||
|
||||
private CryptoProvider mCrypto = null;
|
||||
|
||||
private String mReferences;
|
||||
private String mInReplyTo;
|
||||
|
||||
private boolean mDraftNeedsSaving = false;
|
||||
private boolean mPreventDraftSaving = false;
|
||||
|
||||
/**
|
||||
* The draft uid of this message. This is used when saving drafts so that the same draft is
|
||||
|
@ -220,14 +257,17 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
* @param account
|
||||
* @param message
|
||||
* @param replyAll
|
||||
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
|
||||
*/
|
||||
public static void actionReply(
|
||||
Context context,
|
||||
Account account,
|
||||
Message message,
|
||||
boolean replyAll)
|
||||
boolean replyAll,
|
||||
String messageBody)
|
||||
{
|
||||
Intent i = new Intent(context, MessageCompose.class);
|
||||
i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
|
||||
i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference());
|
||||
if (replyAll)
|
||||
{
|
||||
|
@ -245,10 +285,16 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
* @param context
|
||||
* @param account
|
||||
* @param message
|
||||
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
|
||||
*/
|
||||
public static void actionForward(Context context, Account account, Message message)
|
||||
public static void actionForward(
|
||||
Context context,
|
||||
Account account,
|
||||
Message message,
|
||||
String messageBody)
|
||||
{
|
||||
Intent i = new Intent(context, MessageCompose.class);
|
||||
i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
|
||||
i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference());
|
||||
i.setAction(ACTION_FORWARD);
|
||||
context.startActivity(i);
|
||||
|
@ -282,6 +328,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
final Intent intent = getIntent();
|
||||
|
||||
mMessageReference = (MessageReference) intent.getSerializableExtra(EXTRA_MESSAGE_REFERENCE);
|
||||
mSourceMessageBody = (String) intent.getStringExtra(EXTRA_MESSAGE_BODY);
|
||||
|
||||
final String accountUuid = (mMessageReference != null) ?
|
||||
mMessageReference.accountUuid :
|
||||
|
@ -613,9 +660,113 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
mMessageContentView.requestFocus();
|
||||
}
|
||||
|
||||
mEncryptLayout = (View)findViewById(R.id.layout_encrypt);
|
||||
mCryptoSignatureCheckbox = (CheckBox)findViewById(R.id.cb_crypto_signature);
|
||||
mCryptoSignatureUserId = (TextView)findViewById(R.id.userId);
|
||||
mCryptoSignatureUserIdRest = (TextView)findViewById(R.id.userIdRest);
|
||||
mEncryptCheckbox = (CheckBox)findViewById(R.id.cb_encrypt);
|
||||
|
||||
initializeCrypto();
|
||||
if (mCrypto.isAvailable(this))
|
||||
{
|
||||
mEncryptLayout.setVisibility(View.VISIBLE);
|
||||
mCryptoSignatureCheckbox.setOnClickListener(new OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
CheckBox checkBox = (CheckBox) v;
|
||||
if (checkBox.isChecked())
|
||||
{
|
||||
mPreventDraftSaving = true;
|
||||
if (!mCrypto.selectSecretKey(MessageCompose.this))
|
||||
{
|
||||
mPreventDraftSaving = false;
|
||||
}
|
||||
checkBox.setChecked(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCrypto.setSignatureKeyId(0);
|
||||
updateEncryptLayout();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (mAccount.getCryptoAutoSignature())
|
||||
{
|
||||
long ids[] = mCrypto.getSecretKeyIdsFromEmail(this, mIdentity.getEmail());
|
||||
if (ids != null && ids.length > 0)
|
||||
{
|
||||
mCrypto.setSignatureKeyId(ids[0]);
|
||||
mCrypto.setSignatureUserId(mCrypto.getUserId(this, ids[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
mCrypto.setSignatureKeyId(0);
|
||||
mCrypto.setSignatureUserId(null);
|
||||
}
|
||||
}
|
||||
updateEncryptLayout();
|
||||
}
|
||||
else
|
||||
{
|
||||
mEncryptLayout.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mDraftNeedsSaving = false;
|
||||
}
|
||||
|
||||
private void initializeCrypto()
|
||||
{
|
||||
if (mCrypto != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mCrypto = CryptoProvider.createInstance(mAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the encrypt layout with the latest data about signature key and encryption keys.
|
||||
*/
|
||||
public void updateEncryptLayout()
|
||||
{
|
||||
if (!mCrypto.hasSignatureKey())
|
||||
{
|
||||
mCryptoSignatureCheckbox.setText(R.string.btn_crypto_sign);
|
||||
mCryptoSignatureCheckbox.setChecked(false);
|
||||
mCryptoSignatureUserId.setVisibility(View.INVISIBLE);
|
||||
mCryptoSignatureUserIdRest.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if a signature key is selected, then the checkbox itself has no text
|
||||
mCryptoSignatureCheckbox.setText("");
|
||||
mCryptoSignatureCheckbox.setChecked(true);
|
||||
mCryptoSignatureUserId.setVisibility(View.VISIBLE);
|
||||
mCryptoSignatureUserIdRest.setVisibility(View.VISIBLE);
|
||||
mCryptoSignatureUserId.setText(R.string.unknown_crypto_signature_user_id);
|
||||
mCryptoSignatureUserIdRest.setText("");
|
||||
|
||||
String userId = mCrypto.getSignatureUserId();
|
||||
if (userId == null)
|
||||
{
|
||||
userId = mCrypto.getUserId(this, mCrypto.getSignatureKeyId());
|
||||
mCrypto.setSignatureUserId(userId);
|
||||
}
|
||||
|
||||
if (userId != null)
|
||||
{
|
||||
String chunks[] = mCrypto.getSignatureUserId().split(" <", 2);
|
||||
mCryptoSignatureUserId.setText(chunks[0]);
|
||||
if (chunks.length > 1)
|
||||
{
|
||||
mCryptoSignatureUserIdRest.setText("<" + chunks[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
|
@ -659,6 +810,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
outState.putString(STATE_KEY_DRAFT_UID, mDraftUid);
|
||||
outState.putSerializable(STATE_IDENTITY, mIdentity);
|
||||
outState.putBoolean(STATE_IDENTITY_CHANGED, mIdentityChanged);
|
||||
outState.putSerializable(STATE_CRYPTO, mCrypto);
|
||||
outState.putString(STATE_IN_REPLY_TO, mInReplyTo);
|
||||
outState.putString(STATE_REFERENCES, mReferences);
|
||||
}
|
||||
|
@ -682,10 +834,14 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
mDraftUid = savedInstanceState.getString(STATE_KEY_DRAFT_UID);
|
||||
mIdentity = (Identity)savedInstanceState.getSerializable(STATE_IDENTITY);
|
||||
mIdentityChanged = savedInstanceState.getBoolean(STATE_IDENTITY_CHANGED);
|
||||
mCrypto = (CryptoProvider) savedInstanceState.getSerializable(STATE_CRYPTO);
|
||||
mInReplyTo = savedInstanceState.getString(STATE_IN_REPLY_TO);
|
||||
mReferences = savedInstanceState.getString(STATE_REFERENCES);
|
||||
|
||||
initializeCrypto();
|
||||
updateFrom();
|
||||
updateSignature();
|
||||
updateEncryptLayout();
|
||||
|
||||
mDraftNeedsSaving = false;
|
||||
}
|
||||
|
@ -733,6 +889,32 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
return addresses;
|
||||
}
|
||||
|
||||
private String buildText(boolean appendSig)
|
||||
{
|
||||
/*
|
||||
* Build the Body that will contain the text of the message. We'll decide where to
|
||||
* include it later.
|
||||
*/
|
||||
|
||||
String text = mMessageContentView.getText().toString();
|
||||
if (appendSig && mAccount.isSignatureBeforeQuotedText())
|
||||
{
|
||||
text = appendSignature(text);
|
||||
}
|
||||
|
||||
if (mQuotedTextBar.getVisibility() == View.VISIBLE)
|
||||
{
|
||||
text += "\n" + mQuotedText.getText().toString();
|
||||
}
|
||||
|
||||
if (appendSig && mAccount.isSignatureBeforeQuotedText() == false)
|
||||
{
|
||||
text = appendSignature(text);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private MimeMessage createMessage(boolean appendSig) throws MessagingException
|
||||
{
|
||||
MimeMessage message = new MimeMessage();
|
||||
|
@ -761,27 +943,14 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
message.setReferences(mReferences);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Build the Body that will contain the text of the message. We'll decide where to
|
||||
* include it later.
|
||||
*/
|
||||
|
||||
String text = mMessageContentView.getText().toString();
|
||||
if (appendSig && mAccount.isSignatureBeforeQuotedText())
|
||||
String text = null;
|
||||
if (mCrypto.getEncryptedData() != null)
|
||||
{
|
||||
text = appendSignature(text);
|
||||
text = mCrypto.getEncryptedData();
|
||||
}
|
||||
|
||||
if (mQuotedTextBar.getVisibility() == View.VISIBLE)
|
||||
else
|
||||
{
|
||||
text += "\n" + mQuotedText.getText().toString();
|
||||
}
|
||||
|
||||
|
||||
if (appendSig && mAccount.isSignatureBeforeQuotedText() == false)
|
||||
{
|
||||
text = appendSignature(text);
|
||||
text = buildText(appendSig);
|
||||
}
|
||||
|
||||
TextBody body = new TextBody(text);
|
||||
|
@ -869,7 +1038,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
}
|
||||
|
||||
|
||||
|
||||
private void sendMessage()
|
||||
{
|
||||
new SendMessageTask().execute();
|
||||
|
@ -881,10 +1049,37 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
|
||||
private void saveIfNeeded()
|
||||
{
|
||||
if (!mDraftNeedsSaving)
|
||||
if (!mDraftNeedsSaving || mPreventDraftSaving || mCrypto.hasEncryptionKeys())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mDraftNeedsSaving = false;
|
||||
saveMessage();
|
||||
}
|
||||
|
||||
public void onEncryptionKeySelectionDone()
|
||||
{
|
||||
if (mCrypto.hasEncryptionKeys())
|
||||
{
|
||||
onSend();
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.makeText(this, R.string.send_aborted, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
public void onEncryptDone()
|
||||
{
|
||||
if (mCrypto.getEncryptedData() != null)
|
||||
{
|
||||
onSend();
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.makeText(this, R.string.send_aborted, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
mDraftNeedsSaving = false;
|
||||
saveMessage();
|
||||
}
|
||||
|
@ -897,6 +1092,51 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
Toast.makeText(this, getString(R.string.message_compose_error_no_recipients), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
if (mEncryptCheckbox.isChecked() && !mCrypto.hasEncryptionKeys())
|
||||
{
|
||||
// key selection before encryption
|
||||
String emails = "";
|
||||
Address[][] addresses = new Address[][] { getAddresses(mToView),
|
||||
getAddresses(mCcView),
|
||||
getAddresses(mBccView)
|
||||
};
|
||||
for (Address[] addressArray : addresses)
|
||||
{
|
||||
for (Address address : addressArray)
|
||||
{
|
||||
if (emails.length() != 0)
|
||||
{
|
||||
emails += ",";
|
||||
}
|
||||
emails += address.getAddress();
|
||||
}
|
||||
}
|
||||
if (emails.length() != 0)
|
||||
{
|
||||
emails += ",";
|
||||
}
|
||||
emails += mIdentity.getEmail();
|
||||
|
||||
mPreventDraftSaving = true;
|
||||
if (!mCrypto.selectEncryptionKeys(MessageCompose.this, emails))
|
||||
{
|
||||
mPreventDraftSaving = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mCrypto.hasEncryptionKeys() || mCrypto.hasSignatureKey())
|
||||
{
|
||||
if (mCrypto.getEncryptedData() == null)
|
||||
{
|
||||
String text = buildText(true);
|
||||
mPreventDraftSaving = true;
|
||||
if (!mCrypto.encrypt(this, text))
|
||||
{
|
||||
mPreventDraftSaving = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
sendMessage();
|
||||
mDraftNeedsSaving = false;
|
||||
finish();
|
||||
|
@ -956,6 +1196,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
*/
|
||||
private void onAddAttachment2(final String mime_type)
|
||||
{
|
||||
if (mCrypto.isAvailable(this))
|
||||
{
|
||||
Toast.makeText(this, R.string.attachment_encryption_unsupported, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
i.setType(mime_type);
|
||||
|
@ -1050,6 +1294,14 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
// if a CryptoSystem activity is returning, then mPreventDraftSaving was set to true
|
||||
mPreventDraftSaving = false;
|
||||
|
||||
if (mCrypto.onActivityResult(this, requestCode, resultCode, data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (resultCode != RESULT_OK)
|
||||
return;
|
||||
if (data == null)
|
||||
|
@ -1059,7 +1311,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
switch (requestCode)
|
||||
{
|
||||
case ACTIVITY_REQUEST_PICK_ATTACHMENT:
|
||||
|
||||
addAttachment(data.getData());
|
||||
mDraftNeedsSaving = true;
|
||||
break;
|
||||
|
@ -1196,6 +1447,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.send:
|
||||
mCrypto.setEncryptionKeys(null);
|
||||
onSend();
|
||||
break;
|
||||
case R.id.save:
|
||||
|
@ -1442,7 +1694,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
|
||||
Part part = MimeUtility.findFirstPartByMimeType(mSourceMessage,
|
||||
"text/plain");
|
||||
if (part != null)
|
||||
if (part != null || mSourceMessageBody != null)
|
||||
{
|
||||
String quotedText = String.format(
|
||||
getString(R.string.message_compose_reply_header_fmt),
|
||||
|
@ -1452,8 +1704,16 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
// "$" and "\" in the quote prefix have to be escaped for
|
||||
// the replaceAll() invocation.
|
||||
final String escapedPrefix = prefix.replaceAll("(\\\\|\\$)", "\\\\$1");
|
||||
|
||||
if (mSourceMessageBody != null)
|
||||
{
|
||||
quotedText += mSourceMessageBody.replaceAll("(?m)^", escapedPrefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
quotedText += MimeUtility.getTextFromPart(part).replaceAll(
|
||||
"(?m)^", escapedPrefix);
|
||||
}
|
||||
|
||||
quotedText = quotedText.replaceAll("\\\r", "");
|
||||
mQuotedText.setText(quotedText);
|
||||
|
@ -1548,9 +1808,13 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
|||
{
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
}
|
||||
if (part != null)
|
||||
if (part != null || mSourceMessageBody != null)
|
||||
{
|
||||
String quotedText = MimeUtility.getTextFromPart(part);
|
||||
String quotedText = mSourceMessageBody;
|
||||
if (quotedText == null)
|
||||
{
|
||||
quotedText = MimeUtility.getTextFromPart(part);
|
||||
}
|
||||
if (quotedText != null)
|
||||
{
|
||||
String text = String.format(
|
||||
|
|
|
@ -1103,17 +1103,17 @@ public class MessageList
|
|||
|
||||
private void onReply(MessageInfoHolder holder)
|
||||
{
|
||||
MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, false);
|
||||
MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, false, null);
|
||||
}
|
||||
|
||||
private void onReplyAll(MessageInfoHolder holder)
|
||||
{
|
||||
MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, true);
|
||||
MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, true, null);
|
||||
}
|
||||
|
||||
private void onForward(MessageInfoHolder holder)
|
||||
{
|
||||
MessageCompose.actionForward(this, holder.message.getFolder().getAccount(), holder.message);
|
||||
MessageCompose.actionForward(this, holder.message.getFolder().getAccount(), holder.message, null);
|
||||
}
|
||||
|
||||
private void onMarkAllAsRead(final Account account, final String folder)
|
||||
|
|
|
@ -64,6 +64,7 @@ import com.fsck.k9.Preferences;
|
|||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
import com.fsck.k9.crypto.CryptoProvider;
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Message;
|
||||
|
@ -83,6 +84,8 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
private static final String EXTRA_MESSAGE_REFERENCES = "com.fsck.k9.MessageView_messageReferences";
|
||||
private static final String EXTRA_NEXT = "com.fsck.k9.MessageView_next";
|
||||
|
||||
private static final String STATE_CRYPTO = "crypto";
|
||||
|
||||
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
|
||||
private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
|
||||
|
||||
|
@ -95,6 +98,12 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
public View chip;
|
||||
private CheckBox mFlagged;
|
||||
private int defaultSubjectColor;
|
||||
private View mDecryptLayout;
|
||||
private Button mDecryptButton;
|
||||
private LinearLayout mCryptoSignatureLayout = null;
|
||||
private ImageView mCryptoSignatureStatusImage = null;
|
||||
private TextView mCryptoSignatureUserId = null;
|
||||
private TextView mCryptoSignatureUserIdRest = null;
|
||||
private WebView mMessageContentView;
|
||||
private LinearLayout mHeaderContainer;
|
||||
private LinearLayout mAttachments;
|
||||
|
@ -125,17 +134,16 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
private ArrayList<MessageReference> mMessageReferences;
|
||||
|
||||
private Message mMessage;
|
||||
private CryptoProvider mCrypto = null;
|
||||
|
||||
private static final int PREVIOUS = 1;
|
||||
private static final int NEXT = 2;
|
||||
|
||||
private int mLastDirection = PREVIOUS;
|
||||
|
||||
|
||||
private MessageReference mNextMessage = null;
|
||||
private MessageReference mPreviousMessage = null;
|
||||
|
||||
|
||||
private Menu optionsMenu = null;
|
||||
|
||||
private Listener mListener = new Listener();
|
||||
|
@ -734,6 +742,39 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
mTopView = mToggleScrollView = (ToggleScrollView)findViewById(R.id.top_view);
|
||||
mMessageContentView = (WebView)findViewById(R.id.message_content);
|
||||
|
||||
mDecryptLayout = (View)findViewById(R.id.layout_decrypt);
|
||||
mDecryptButton = (Button)findViewById(R.id.btn_decrypt);
|
||||
mDecryptButton.setOnClickListener(new OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
try
|
||||
{
|
||||
String data = null;
|
||||
Part part = MimeUtility.findFirstPartByMimeType(mMessage, "text/plain");
|
||||
if (part == null)
|
||||
{
|
||||
part = MimeUtility.findFirstPartByMimeType(mMessage, "text/html");
|
||||
}
|
||||
if (part != null)
|
||||
{
|
||||
data = MimeUtility.getTextFromPart(part);
|
||||
}
|
||||
mCrypto.decrypt(MessageView.this, data);
|
||||
}
|
||||
catch (MessagingException me)
|
||||
{
|
||||
Log.e(K9.LOG_TAG, "Unable to decrypt email.", me);
|
||||
}
|
||||
}
|
||||
});
|
||||
mCryptoSignatureLayout = (LinearLayout) findViewById(R.id.crypto_signature);
|
||||
mCryptoSignatureStatusImage = (ImageView) findViewById(R.id.ic_crypto_signature_status);
|
||||
mCryptoSignatureUserId = (TextView) findViewById(R.id.userId);
|
||||
mCryptoSignatureUserIdRest = (TextView) findViewById(R.id.userIdRest);
|
||||
mCryptoSignatureLayout.setVisibility(View.INVISIBLE);
|
||||
|
||||
mAttachments = (LinearLayout)findViewById(R.id.attachments);
|
||||
mAttachmentIcon = findViewById(R.id.attachment);
|
||||
mShowPicturesSection = findViewById(R.id.show_pictures_section);
|
||||
|
@ -812,6 +853,9 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
{
|
||||
mMessageReference = (MessageReference)icicle.getSerializable(EXTRA_MESSAGE_REFERENCE);
|
||||
mMessageReferences = (ArrayList<MessageReference>)icicle.getSerializable(EXTRA_MESSAGE_REFERENCES);
|
||||
|
||||
mCrypto = (CryptoProvider) icicle.getSerializable(STATE_CRYPTO);
|
||||
updateDecryptLayout();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -859,6 +903,7 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "MessageView got message " + mMessageReference);
|
||||
|
||||
|
@ -953,6 +998,18 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
{
|
||||
outState.putSerializable(EXTRA_MESSAGE_REFERENCE, mMessageReference);
|
||||
outState.putSerializable(EXTRA_MESSAGE_REFERENCES, mMessageReferences);
|
||||
outState.putSerializable(STATE_CRYPTO, mCrypto);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState)
|
||||
{
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
|
||||
mCrypto = (CryptoProvider) savedInstanceState.getSerializable(STATE_CRYPTO);
|
||||
initializeCrypto();
|
||||
|
||||
updateDecryptLayout();
|
||||
}
|
||||
|
||||
private void displayMessage(MessageReference ref)
|
||||
|
@ -970,6 +1027,9 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
mAttachments.removeAllViews();
|
||||
findSurroundingMessagesUid();
|
||||
|
||||
mCrypto = null;
|
||||
initializeCrypto();
|
||||
|
||||
setupDisplayMessageButtons();
|
||||
|
||||
MessagingController.getInstance(getApplication()).loadMessageForView(
|
||||
|
@ -1251,7 +1311,7 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
{
|
||||
if (mMessage != null)
|
||||
{
|
||||
MessageCompose.actionReply(this, mAccount, mMessage, false);
|
||||
MessageCompose.actionReply(this, mAccount, mMessage, false, mCrypto.getDecryptedData());
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@ -1260,7 +1320,7 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
{
|
||||
if (mMessage != null)
|
||||
{
|
||||
MessageCompose.actionReply(this, mAccount, mMessage, true);
|
||||
MessageCompose.actionReply(this, mAccount, mMessage, true, mCrypto.getDecryptedData());
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@ -1269,7 +1329,7 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
{
|
||||
if (mMessage != null)
|
||||
{
|
||||
MessageCompose.actionForward(this, mAccount, mMessage);
|
||||
MessageCompose.actionForward(this, mAccount, mMessage, mCrypto.getDecryptedData());
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@ -1376,6 +1436,11 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (mCrypto.onActivityResult(this, requestCode, resultCode, data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (resultCode != RESULT_OK)
|
||||
return;
|
||||
|
||||
|
@ -1409,6 +1474,9 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1966,6 +2034,7 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
public void run()
|
||||
{
|
||||
mMessageContentView.loadUrl("file:///android_asset/downloading.html");
|
||||
updateDecryptLayout();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2007,6 +2076,14 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
mHandler.removeAllAttachments();
|
||||
|
||||
String text;
|
||||
String type = "text/html";
|
||||
if (mCrypto.getDecryptedData() != null)
|
||||
{
|
||||
text = mCrypto.getDecryptedData();
|
||||
type = "text/plain";
|
||||
}
|
||||
else
|
||||
{
|
||||
Part part = MimeUtility.findFirstPartByMimeType(mMessage, "text/html");
|
||||
if (part == null)
|
||||
{
|
||||
|
@ -2032,6 +2109,7 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
{
|
||||
text = MimeUtility.getTextFromPart(part);
|
||||
}
|
||||
}
|
||||
|
||||
if (text != null)
|
||||
{
|
||||
|
@ -2040,11 +2118,13 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
* get background images and a million other things that HTML allows.
|
||||
*/
|
||||
final String emailText = text;
|
||||
final String mimeType = type;
|
||||
mHandler.post(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
mMessageContentView.loadDataWithBaseURL("http://", emailText, "text/html", "utf-8", null);
|
||||
mMessageContentView.loadDataWithBaseURL("http://", emailText, mimeType, "utf-8", null);
|
||||
updateDecryptLayout();
|
||||
}
|
||||
});
|
||||
mHandler.showShowPictures(text.contains("<img"));
|
||||
|
@ -2056,6 +2136,7 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
public void run()
|
||||
{
|
||||
mMessageContentView.loadUrl("file:///android_asset/empty.html");
|
||||
updateDecryptLayout();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2099,6 +2180,7 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
!MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL))
|
||||
{
|
||||
mMessageContentView.loadUrl("file:///android_asset/empty.html");
|
||||
updateDecryptLayout();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -2137,6 +2219,7 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
public void run()
|
||||
{
|
||||
mMessageContentView.loadUrl("file:///android_asset/loading.html");
|
||||
updateDecryptLayout();
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
}
|
||||
});
|
||||
|
@ -2293,7 +2376,118 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||
return slide;
|
||||
}
|
||||
|
||||
private void initializeCrypto()
|
||||
{
|
||||
if (mCrypto != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (mAccount == null)
|
||||
{
|
||||
mAccount = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid);
|
||||
}
|
||||
mCrypto = CryptoProvider.createInstance(mAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the decrypt layout with signature data, if known, make controls visible, if
|
||||
* they should be visible.
|
||||
*/
|
||||
public void updateDecryptLayout()
|
||||
{
|
||||
if (mCrypto.getSignatureKeyId() != 0)
|
||||
{
|
||||
mCryptoSignatureUserIdRest.setText(
|
||||
getString(R.string.key_id, Long.toHexString(mCrypto.getSignatureKeyId() & 0xffffffffL)));
|
||||
String userId = mCrypto.getSignatureUserId();
|
||||
if (userId == null)
|
||||
{
|
||||
userId = getString(R.string.unknown_crypto_signature_user_id);
|
||||
}
|
||||
String chunks[] = userId.split(" <", 2);
|
||||
String name = chunks[0];
|
||||
if (chunks.length > 1)
|
||||
{
|
||||
mCryptoSignatureUserIdRest.setText("<" + chunks[1]);
|
||||
}
|
||||
mCryptoSignatureUserId.setText(name);
|
||||
|
||||
if (mCrypto.getSignatureSuccess())
|
||||
{
|
||||
mCryptoSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
|
||||
}
|
||||
else if (mCrypto.getSignatureUnknown())
|
||||
{
|
||||
mCryptoSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCryptoSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||
}
|
||||
mCryptoSignatureLayout.setVisibility(View.VISIBLE);
|
||||
mDecryptLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (!true || ((mMessage == null) && (mCrypto.getDecryptedData() == null)))
|
||||
{
|
||||
mDecryptLayout.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCrypto.getDecryptedData() != null)
|
||||
{
|
||||
if (mCrypto.getSignatureKeyId() == 0)
|
||||
{
|
||||
mDecryptLayout.setVisibility(View.GONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no need to show this after decryption/verification
|
||||
mDecryptButton.setVisibility(View.GONE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mDecryptButton.setVisibility(View.VISIBLE);
|
||||
|
||||
if (mCrypto.isEncrypted(mMessage))
|
||||
{
|
||||
mDecryptButton.setText(R.string.btn_decrypt);
|
||||
mDecryptLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else if (mCrypto.isSigned(mMessage))
|
||||
{
|
||||
mDecryptButton.setText(R.string.btn_verify);
|
||||
mDecryptLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
mDecryptLayout.setVisibility(View.GONE);
|
||||
try
|
||||
{
|
||||
// check for PGP/MIME encryption
|
||||
Part pgp = MimeUtility.findFirstPartByMimeType(mMessage, "application/pgp-encrypted");
|
||||
if (pgp != null)
|
||||
{
|
||||
Toast.makeText(this, R.string.pgp_mime_unsupported, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
catch (MessagingException e)
|
||||
{
|
||||
// nothing to do...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onDecryptDone()
|
||||
{
|
||||
// TODO: this might not be enough if the orientation was changed while in APG,
|
||||
// sometimes shows the original encrypted content
|
||||
mMessageContentView.loadDataWithBaseURL("email://", mCrypto.getDecryptedData(), "text/plain", "utf-8", null);
|
||||
updateDecryptLayout();
|
||||
}
|
||||
|
||||
/*
|
||||
* Emulate the shift key being pressed to trigger the text selection mode
|
||||
* of a WebView.
|
||||
*/
|
||||
|
|
|
@ -5,16 +5,25 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.*;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.RingtonePreference;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import com.fsck.k9.*;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.Account.FolderMode;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.ChooseFolder;
|
||||
import com.fsck.k9.activity.ChooseIdentity;
|
||||
import com.fsck.k9.activity.ColorPickerDialog;
|
||||
import com.fsck.k9.activity.K9PreferenceActivity;
|
||||
import com.fsck.k9.activity.ManageIdentities;
|
||||
import com.fsck.k9.crypto.Apg;
|
||||
import com.fsck.k9.mail.Store;
|
||||
import com.fsck.k9.service.MailService;
|
||||
|
||||
|
@ -60,7 +69,8 @@ public class AccountSettings extends K9PreferenceActivity
|
|||
private static final String PREFERENCE_MESSAGE_SIZE = "account_autodownload_size";
|
||||
private static final String PREFERENCE_QUOTE_PREFIX = "account_quote_prefix";
|
||||
private static final String PREFERENCE_SYNC_REMOTE_DELETIONS = "account_sync_remote_deletetions";
|
||||
|
||||
private static final String PREFERENCE_CRYPTO_APP = "crypto_app";
|
||||
private static final String PREFERENCE_CRYPTO_AUTO_SIGNATURE = "crypto_auto_signature";
|
||||
|
||||
private Account mAccount;
|
||||
|
||||
|
@ -94,7 +104,8 @@ public class AccountSettings extends K9PreferenceActivity
|
|||
private CheckBoxPreference mNotificationOpensUnread;
|
||||
private EditTextPreference mAccountQuotePrefix;
|
||||
private CheckBoxPreference mSyncRemoteDeletions;
|
||||
|
||||
private ListPreference mCryptoApp;
|
||||
private CheckBoxPreference mCryptoAutoSignature;
|
||||
|
||||
public static void actionSettings(Context context, Account account)
|
||||
{
|
||||
|
@ -510,6 +521,53 @@ public class AccountSettings extends K9PreferenceActivity
|
|||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mCryptoApp = (ListPreference) findPreference(PREFERENCE_CRYPTO_APP);
|
||||
CharSequence cryptoAppEntries[] = mCryptoApp.getEntries();
|
||||
if (!new Apg().isAvailable(this))
|
||||
{
|
||||
int apgIndex = mCryptoApp.findIndexOfValue(Apg.NAME);
|
||||
if (apgIndex >= 0)
|
||||
{
|
||||
cryptoAppEntries[apgIndex] = "APG (" + getResources().getString(R.string.account_settings_crypto_app_not_available) + ")";
|
||||
mCryptoApp.setEntries(cryptoAppEntries);
|
||||
}
|
||||
}
|
||||
mCryptoApp.setValue(String.valueOf(mAccount.getCryptoApp()));
|
||||
mCryptoApp.setSummary(mCryptoApp.getEntry());
|
||||
mCryptoApp.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
|
||||
{
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue)
|
||||
{
|
||||
String value = newValue.toString();
|
||||
int index = mCryptoApp.findIndexOfValue(value);
|
||||
mCryptoApp.setSummary(mCryptoApp.getEntries()[index]);
|
||||
mCryptoApp.setValue(value);
|
||||
handleCryptoAppDependencies();
|
||||
if (Apg.NAME.equals(value))
|
||||
{
|
||||
Apg.createInstance(null).test(AccountSettings.this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mCryptoAutoSignature = (CheckBoxPreference) findPreference(PREFERENCE_CRYPTO_AUTO_SIGNATURE);
|
||||
mCryptoAutoSignature.setChecked(mAccount.getCryptoAutoSignature());
|
||||
|
||||
handleCryptoAppDependencies();
|
||||
}
|
||||
|
||||
private void handleCryptoAppDependencies()
|
||||
{
|
||||
if ("".equals(mCryptoApp.getValue()))
|
||||
{
|
||||
mCryptoAutoSignature.setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCryptoAutoSignature.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -543,6 +601,8 @@ public class AccountSettings extends K9PreferenceActivity
|
|||
mAccount.setSyncRemoteDeletions(mSyncRemoteDeletions.isChecked());
|
||||
mAccount.setSearchableFolders(Account.Searchable.valueOf(mSearchableFolders.getValue()));
|
||||
mAccount.setQuotePrefix(mAccountQuotePrefix.getText());
|
||||
mAccount.setCryptoApp(mCryptoApp.getValue());
|
||||
mAccount.setCryptoAutoSignature(mCryptoAutoSignature.isChecked());
|
||||
|
||||
boolean needsRefresh = mAccount.setAutomaticCheckIntervalMinutes(Integer.parseInt(mCheckFrequency.getValue()));
|
||||
needsRefresh |= mAccount.setFolderSyncMode(Account.FolderMode.valueOf(mSyncMode.getValue()));
|
||||
|
|
556
src/com/fsck/k9/crypto/Apg.java
Normal file
556
src/com/fsck/k9/crypto/Apg.java
Normal file
|
@ -0,0 +1,556 @@
|
|||
package com.fsck.k9.crypto;
|
||||
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.MessageCompose;
|
||||
import com.fsck.k9.activity.MessageView;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
|
||||
public class Apg extends CryptoProvider
|
||||
{
|
||||
static final long serialVersionUID = 0x21071235;
|
||||
public static final String NAME = "apg";
|
||||
|
||||
private static final String mApgPackageName = "org.thialfihar.android.apg";
|
||||
private static final int mMinRequiredVersion = 16;
|
||||
|
||||
public static final String AUTHORITY = "org.thialfihar.android.apg.provider";
|
||||
public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID =
|
||||
Uri.parse("content://" + AUTHORITY + "/key_rings/secret/key_id/");
|
||||
public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_EMAILS =
|
||||
Uri.parse("content://" + AUTHORITY + "/key_rings/secret/emails/");
|
||||
|
||||
public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_KEY_ID =
|
||||
Uri.parse("content://" + AUTHORITY + "/key_rings/public/key_id/");
|
||||
public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS =
|
||||
Uri.parse("content://" + AUTHORITY + "/key_rings/public/emails/");
|
||||
|
||||
public static class Intent
|
||||
{
|
||||
public static final String DECRYPT = "org.thialfihar.android.apg.intent.DECRYPT";
|
||||
public static final String ENCRYPT = "org.thialfihar.android.apg.intent.ENCRYPT";
|
||||
public static final String DECRYPT_FILE = "org.thialfihar.android.apg.intent.DECRYPT_FILE";
|
||||
public static final String ENCRYPT_FILE = "org.thialfihar.android.apg.intent.ENCRYPT_FILE";
|
||||
public static final String DECRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN";
|
||||
public static final String ENCRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN";
|
||||
public static final String SELECT_PUBLIC_KEYS = "org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS";
|
||||
public static final String SELECT_SECRET_KEY = "org.thialfihar.android.apg.intent.SELECT_SECRET_KEY";
|
||||
}
|
||||
|
||||
public static final String EXTRA_TEXT = "text";
|
||||
public static final String EXTRA_DATA = "data";
|
||||
public static final String EXTRA_STATUS = "status";
|
||||
public static final String EXTRA_ERROR = "error";
|
||||
public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage";
|
||||
public static final String EXTRA_ENCRYPTED_MESSAGE = "encryptedMessage";
|
||||
public static final String EXTRA_SIGNATURE = "signature";
|
||||
public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId";
|
||||
public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId";
|
||||
public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess";
|
||||
public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown";
|
||||
public static final String EXTRA_USER_ID = "userId";
|
||||
public static final String EXTRA_KEY_ID = "keyId";
|
||||
public static final String EXTRA_REPLY_TO = "replyTo";
|
||||
public static final String EXTRA_SEND_TO = "sendTo";
|
||||
public static final String EXTRA_SUBJECT = "subject";
|
||||
public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds";
|
||||
public static final String EXTRA_SELECTION = "selection";
|
||||
public static final String EXTRA_MESSAGE = "message";
|
||||
public static final String EXTRA_PROGRESS = "progress";
|
||||
public static final String EXTRA_MAX = "max";
|
||||
public static final String EXTRA_ACCOUNT = "account";
|
||||
|
||||
public static final int DECRYPT_MESSAGE = 0x21070001;
|
||||
public static final int ENCRYPT_MESSAGE = 0x21070002;
|
||||
public static final int SELECT_PUBLIC_KEYS = 0x21070003;
|
||||
public static final int SELECT_SECRET_KEY = 0x21070004;
|
||||
|
||||
public static Pattern PGP_MESSAGE =
|
||||
Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
|
||||
Pattern.DOTALL);
|
||||
|
||||
public static Pattern PGP_SIGNED_MESSAGE =
|
||||
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
||||
Pattern.DOTALL);
|
||||
|
||||
public static Apg createInstance(Account account)
|
||||
{
|
||||
return new Apg();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether APG is installed and at a high enough version.
|
||||
*
|
||||
* @param context
|
||||
* @return whether a suitable version of APG was found
|
||||
*/
|
||||
@Override
|
||||
public boolean isAvailable(Context context)
|
||||
{
|
||||
try
|
||||
{
|
||||
PackageInfo pi = context.getPackageManager().getPackageInfo(mApgPackageName, 0);
|
||||
if (pi.versionCode >= mMinRequiredVersion)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.makeText(context,
|
||||
R.string.error_apg_version_not_supported, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
catch (NameNotFoundException e)
|
||||
{
|
||||
// not found
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the signature key.
|
||||
*
|
||||
* @param activity
|
||||
* @return success or failure
|
||||
*/
|
||||
@Override
|
||||
public boolean selectSecretKey(Activity activity)
|
||||
{
|
||||
android.content.Intent intent = new android.content.Intent(Intent.SELECT_SECRET_KEY);
|
||||
try
|
||||
{
|
||||
activity.startActivityForResult(intent, Apg.SELECT_SECRET_KEY);
|
||||
return true;
|
||||
}
|
||||
catch (ActivityNotFoundException e)
|
||||
{
|
||||
Toast.makeText(activity,
|
||||
R.string.error_activity_not_found,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select encryption keys.
|
||||
*
|
||||
* @param activity
|
||||
* @param emails The emails that should be used for preselection.
|
||||
* @return success or failure
|
||||
*/
|
||||
@Override
|
||||
public boolean selectEncryptionKeys(Activity activity, String emails)
|
||||
{
|
||||
android.content.Intent intent = new android.content.Intent(Apg.Intent.SELECT_PUBLIC_KEYS);
|
||||
long[] initialKeyIds = null;
|
||||
if (!hasEncryptionKeys())
|
||||
{
|
||||
Vector<Long> keyIds = new Vector<Long>();
|
||||
if (hasSignatureKey())
|
||||
{
|
||||
keyIds.add(getSignatureKeyId());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Uri contentUri = Uri.withAppendedPath(
|
||||
Apg.CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS,
|
||||
emails);
|
||||
Cursor c = activity.getContentResolver().query(contentUri,
|
||||
new String[] { "master_key_id" },
|
||||
null, null, null);
|
||||
if (c != null)
|
||||
{
|
||||
while (c.moveToNext())
|
||||
{
|
||||
keyIds.add(c.getLong(0));
|
||||
}
|
||||
}
|
||||
|
||||
if (c != null)
|
||||
{
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
catch (SecurityException e)
|
||||
{
|
||||
Toast.makeText(activity,
|
||||
activity.getResources().getString(R.string.insufficient_apg_permissions),
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
if (keyIds.size() > 0)
|
||||
{
|
||||
initialKeyIds = new long[keyIds.size()];
|
||||
for (int i = 0, size = keyIds.size(); i < size; ++i)
|
||||
{
|
||||
initialKeyIds[i] = keyIds.get(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
initialKeyIds = mEncryptionKeyIds;
|
||||
}
|
||||
intent.putExtra(Apg.EXTRA_SELECTION, initialKeyIds);
|
||||
try
|
||||
{
|
||||
activity.startActivityForResult(intent, Apg.SELECT_PUBLIC_KEYS);
|
||||
return true;
|
||||
}
|
||||
catch (ActivityNotFoundException e)
|
||||
{
|
||||
Toast.makeText(activity,
|
||||
R.string.error_activity_not_found,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secret key ids based on a given email.
|
||||
*
|
||||
* @param context
|
||||
* @param email The email in question.
|
||||
* @return key ids
|
||||
*/
|
||||
@Override
|
||||
public long[] getSecretKeyIdsFromEmail(Context context, String email)
|
||||
{
|
||||
long ids[] = null;
|
||||
try
|
||||
{
|
||||
Uri contentUri = Uri.withAppendedPath(Apg.CONTENT_URI_SECRET_KEY_RING_BY_EMAILS,
|
||||
email);
|
||||
Cursor c = context.getContentResolver().query(contentUri,
|
||||
new String[] { "master_key_id" },
|
||||
null, null, null);
|
||||
if (c != null && c.getCount() > 0)
|
||||
{
|
||||
ids = new long[c.getCount()];
|
||||
while (c.moveToNext())
|
||||
{
|
||||
ids[c.getPosition()] = c.getLong(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (c != null)
|
||||
{
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
catch (SecurityException e)
|
||||
{
|
||||
Toast.makeText(context,
|
||||
context.getResources().getString(R.string.insufficient_apg_permissions),
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user id based on the key id.
|
||||
*
|
||||
* @param context
|
||||
* @param keyId
|
||||
* @return user id
|
||||
*/
|
||||
@Override
|
||||
public String getUserId(Context context, long keyId)
|
||||
{
|
||||
String userId = null;
|
||||
try
|
||||
{
|
||||
Uri contentUri = ContentUris.withAppendedId(
|
||||
Apg.CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID,
|
||||
keyId);
|
||||
Cursor c = context.getContentResolver().query(contentUri,
|
||||
new String[] { "user_id" },
|
||||
null, null, null);
|
||||
if (c != null && c.moveToFirst())
|
||||
{
|
||||
userId = c.getString(0);
|
||||
}
|
||||
|
||||
if (c != null)
|
||||
{
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
catch (SecurityException e)
|
||||
{
|
||||
Toast.makeText(context,
|
||||
context.getResources().getString(R.string.insufficient_apg_permissions),
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
if (userId == null)
|
||||
{
|
||||
userId = context.getString(R.string.unknown_crypto_signature_user_id);
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the activity results that concern us.
|
||||
*
|
||||
* @param activity
|
||||
* @param requestCode
|
||||
* @param resultCode
|
||||
* @param data
|
||||
* @return handled or not
|
||||
*/
|
||||
@Override
|
||||
public boolean onActivityResult(Activity activity, int requestCode, int resultCode,
|
||||
android.content.Intent data)
|
||||
{
|
||||
switch (requestCode)
|
||||
{
|
||||
case Apg.SELECT_SECRET_KEY:
|
||||
if (resultCode != Activity.RESULT_OK || data == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
setSignatureKeyId(data.getLongExtra(Apg.EXTRA_KEY_ID, 0));
|
||||
setSignatureUserId(data.getStringExtra(Apg.EXTRA_USER_ID));
|
||||
((MessageCompose) activity).updateEncryptLayout();
|
||||
break;
|
||||
|
||||
case Apg.SELECT_PUBLIC_KEYS:
|
||||
if (resultCode != Activity.RESULT_OK || data == null)
|
||||
{
|
||||
mEncryptionKeyIds = null;
|
||||
((MessageCompose) activity).onEncryptionKeySelectionDone();
|
||||
break;
|
||||
}
|
||||
mEncryptionKeyIds = data.getLongArrayExtra(Apg.EXTRA_SELECTION);
|
||||
((MessageCompose) activity).onEncryptionKeySelectionDone();
|
||||
break;
|
||||
|
||||
case Apg.ENCRYPT_MESSAGE:
|
||||
if (resultCode != Activity.RESULT_OK || data == null)
|
||||
{
|
||||
mEncryptedData = null;
|
||||
((MessageCompose) activity).onEncryptDone();
|
||||
break;
|
||||
}
|
||||
mEncryptedData = data.getStringExtra(Apg.EXTRA_ENCRYPTED_MESSAGE);
|
||||
// this was a stupid bug in an earlier version, just gonna leave this in for an APG
|
||||
// version or two
|
||||
if (mEncryptedData == null) {
|
||||
mEncryptedData = data.getStringExtra(Apg.EXTRA_DECRYPTED_MESSAGE);
|
||||
}
|
||||
if (mEncryptedData != null)
|
||||
{
|
||||
((MessageCompose) activity).onEncryptDone();
|
||||
}
|
||||
break;
|
||||
|
||||
case Apg.DECRYPT_MESSAGE:
|
||||
if (resultCode != Activity.RESULT_OK || data == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
mSignatureUserId = data.getStringExtra(Apg.EXTRA_SIGNATURE_USER_ID);
|
||||
mSignatureKeyId = data.getLongExtra(Apg.EXTRA_SIGNATURE_KEY_ID, 0);
|
||||
mSignatureSuccess = data.getBooleanExtra(Apg.EXTRA_SIGNATURE_SUCCESS, false);
|
||||
mSignatureUnknown = data.getBooleanExtra(Apg.EXTRA_SIGNATURE_UNKNOWN, false);
|
||||
|
||||
mDecryptedData = data.getStringExtra(Apg.EXTRA_DECRYPTED_MESSAGE);
|
||||
((MessageView) activity).onDecryptDone();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the encrypt activity.
|
||||
*
|
||||
* @param activity
|
||||
* @param data
|
||||
* @return success or failure
|
||||
*/
|
||||
@Override
|
||||
public boolean encrypt(Activity activity, String data)
|
||||
{
|
||||
android.content.Intent intent = new android.content.Intent(Intent.ENCRYPT_AND_RETURN);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Apg.EXTRA_TEXT, data);
|
||||
intent.putExtra(Apg.EXTRA_ENCRYPTION_KEY_IDS, mEncryptionKeyIds);
|
||||
intent.putExtra(Apg.EXTRA_SIGNATURE_KEY_ID, mSignatureKeyId);
|
||||
try
|
||||
{
|
||||
activity.startActivityForResult(intent, Apg.ENCRYPT_MESSAGE);
|
||||
return true;
|
||||
}
|
||||
catch (ActivityNotFoundException e)
|
||||
{
|
||||
Toast.makeText(activity,
|
||||
R.string.error_activity_not_found,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the decrypt activity.
|
||||
*
|
||||
* @param activity
|
||||
* @param data
|
||||
* @return success or failure
|
||||
*/
|
||||
@Override
|
||||
public boolean decrypt(Activity activity, String data)
|
||||
{
|
||||
android.content.Intent intent = new android.content.Intent(Apg.Intent.DECRYPT_AND_RETURN);
|
||||
intent.setType("text/plain");
|
||||
if (data == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
intent.putExtra(EXTRA_TEXT, data);
|
||||
activity.startActivityForResult(intent, Apg.DECRYPT_MESSAGE);
|
||||
return true;
|
||||
}
|
||||
catch (ActivityNotFoundException e)
|
||||
{
|
||||
Toast.makeText(activity,
|
||||
R.string.error_activity_not_found,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEncrypted(Message message)
|
||||
{
|
||||
String data = null;
|
||||
try {
|
||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||
if (part == null)
|
||||
{
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
}
|
||||
if (part != null)
|
||||
{
|
||||
data = MimeUtility.getTextFromPart(part);
|
||||
}
|
||||
}
|
||||
catch (MessagingException e)
|
||||
{
|
||||
// guess not...
|
||||
// TODO: maybe log this?
|
||||
}
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Matcher matcher = PGP_MESSAGE.matcher(data);
|
||||
return matcher.matches();
|
||||
}
|
||||
|
||||
public boolean isSigned(Message message)
|
||||
{
|
||||
String data = null;
|
||||
try {
|
||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||
if (part == null)
|
||||
{
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
}
|
||||
if (part != null)
|
||||
{
|
||||
data = MimeUtility.getTextFromPart(part);
|
||||
}
|
||||
}
|
||||
catch (MessagingException e)
|
||||
{
|
||||
// guess not...
|
||||
// TODO: maybe log this?
|
||||
}
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Matcher matcher = PGP_SIGNED_MESSAGE.matcher(data);
|
||||
return matcher.matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the provider.
|
||||
*
|
||||
* @return provider name
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the APG installation.
|
||||
*
|
||||
* @return success or failure
|
||||
*/
|
||||
public boolean test(Context context)
|
||||
{
|
||||
if (!isAvailable(context))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// try out one content provider to check permissions
|
||||
Uri contentUri = ContentUris.withAppendedId(
|
||||
Apg.CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID,
|
||||
12345);
|
||||
Cursor c = context.getContentResolver().query(contentUri,
|
||||
new String[] { "user_id" },
|
||||
null, null, null);
|
||||
if (c != null)
|
||||
{
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
catch (SecurityException e)
|
||||
{
|
||||
// if there was a problem, then let the user know, this will not stop K9/APG from
|
||||
// working, but some features won't be available, so we can still return "true"
|
||||
Toast.makeText(context,
|
||||
context.getResources().getString(R.string.insufficient_apg_permissions),
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
107
src/com/fsck/k9/crypto/CryptoProvider.java
Normal file
107
src/com/fsck/k9/crypto/CryptoProvider.java
Normal file
|
@ -0,0 +1,107 @@
|
|||
package com.fsck.k9.crypto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.mail.Message;
|
||||
|
||||
abstract public class CryptoProvider implements Serializable
|
||||
{
|
||||
static final long serialVersionUID = 0x21071234;
|
||||
|
||||
protected long mEncryptionKeyIds[] = null;
|
||||
protected long mSignatureKeyId = 0;
|
||||
protected String mSignatureUserId = null;
|
||||
protected boolean mSignatureSuccess = false;
|
||||
protected boolean mSignatureUnknown = false;
|
||||
protected String mDecryptedData = null;
|
||||
protected String mEncryptedData = null;
|
||||
|
||||
abstract public boolean isAvailable(Context context);
|
||||
abstract public boolean isEncrypted(Message message);
|
||||
abstract public boolean isSigned(Message message);
|
||||
abstract public boolean onActivityResult(Activity activity, int requestCode, int resultCode,
|
||||
Intent data);
|
||||
abstract public boolean selectSecretKey(Activity activity);
|
||||
abstract public boolean selectEncryptionKeys(Activity activity, String emails);
|
||||
abstract public boolean encrypt(Activity activity, String data);
|
||||
abstract public boolean decrypt(Activity activity, String data);
|
||||
abstract public long[] getSecretKeyIdsFromEmail(Context context, String email);
|
||||
abstract public String getUserId(Context context, long keyId);
|
||||
abstract public String getName();
|
||||
abstract public boolean test(Context context);
|
||||
|
||||
public static CryptoProvider createInstance(Account account)
|
||||
{
|
||||
if (Apg.NAME.equals(account.getCryptoApp()))
|
||||
{
|
||||
return Apg.createInstance(account);
|
||||
}
|
||||
|
||||
return None.createInstance(account);
|
||||
}
|
||||
|
||||
public void setSignatureKeyId(long keyId)
|
||||
{
|
||||
mSignatureKeyId = keyId;
|
||||
}
|
||||
|
||||
public long getSignatureKeyId()
|
||||
{
|
||||
return mSignatureKeyId;
|
||||
}
|
||||
|
||||
public void setEncryptionKeys(long keyIds[])
|
||||
{
|
||||
mEncryptionKeyIds = keyIds;
|
||||
}
|
||||
|
||||
public long[] getEncryptionKeys()
|
||||
{
|
||||
return mEncryptionKeyIds;
|
||||
}
|
||||
|
||||
public boolean hasSignatureKey()
|
||||
{
|
||||
return mSignatureKeyId != 0;
|
||||
}
|
||||
|
||||
public boolean hasEncryptionKeys()
|
||||
{
|
||||
return (mEncryptionKeyIds != null) && (mEncryptionKeyIds.length > 0);
|
||||
}
|
||||
|
||||
public String getEncryptedData()
|
||||
{
|
||||
return mEncryptedData;
|
||||
}
|
||||
|
||||
public String getDecryptedData()
|
||||
{
|
||||
return mDecryptedData;
|
||||
}
|
||||
|
||||
public void setSignatureUserId(String userId)
|
||||
{
|
||||
mSignatureUserId = userId;
|
||||
}
|
||||
|
||||
public String getSignatureUserId()
|
||||
{
|
||||
return mSignatureUserId;
|
||||
}
|
||||
|
||||
public boolean getSignatureSuccess()
|
||||
{
|
||||
return mSignatureSuccess;
|
||||
}
|
||||
|
||||
public boolean getSignatureUnknown()
|
||||
{
|
||||
return mSignatureUnknown;
|
||||
}
|
||||
}
|
89
src/com/fsck/k9/crypto/None.java
Normal file
89
src/com/fsck/k9/crypto/None.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
package com.fsck.k9.crypto;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.mail.Message;
|
||||
|
||||
public class None extends CryptoProvider
|
||||
{
|
||||
static final long serialVersionUID = 0x21071230;
|
||||
public static final String NAME = "";
|
||||
|
||||
public static None createInstance(Account account)
|
||||
{
|
||||
return new None();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(Context context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean selectSecretKey(Activity activity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean selectEncryptionKeys(Activity activity, String emails)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] getSecretKeyIdsFromEmail(Context context, String email)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserId(Context context, long keyId)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActivityResult(Activity activity, int requestCode, int resultCode,
|
||||
android.content.Intent data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean encrypt(Activity activity, String data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean decrypt(Activity activity, String data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEncrypted(Message message)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isSigned(Message message)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Context context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue