From 2c47734f0fdb1dccfd41dec8cfad8d93f311ea52 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Fri, 9 Jul 2010 20:55:17 +0000 Subject: [PATCH] trying to add support for various source and destination modes for encryption and decryption (String, byte[], filename, content stream), also more Intent options added --- src/org/thialfihar/android/apg/Apg.java | 46 ++--- .../android/apg/DataDestination.java | 79 ++++++++ .../thialfihar/android/apg/DataSource.java | 88 +++++++++ .../android/apg/DecryptActivity.java | 169 ++++++++---------- .../android/apg/EncryptActivity.java | 129 ++++++------- .../android/apg/GeneralActivity.java | 2 +- src/org/thialfihar/android/apg/Id.java | 7 + src/org/thialfihar/android/apg/InputData.java | 25 +++ .../android/apg/KeyListActivity.java | 6 +- .../android/apg/MailListActivity.java | 2 +- .../thialfihar/android/apg/MainActivity.java | 6 +- .../android/apg/PreferencesActivity.java | 1 - .../android/apg/PublicKeyListActivity.java | 2 +- .../android/apg/SecretKeyListActivity.java | 2 +- .../apg/SelectSecretKeyListActivity.java | 2 +- 15 files changed, 380 insertions(+), 186 deletions(-) create mode 100644 src/org/thialfihar/android/apg/DataDestination.java create mode 100644 src/org/thialfihar/android/apg/DataSource.java create mode 100644 src/org/thialfihar/android/apg/InputData.java diff --git a/src/org/thialfihar/android/apg/Apg.java b/src/org/thialfihar/android/apg/Apg.java index d606a553c..e2c6df5b9 100644 --- a/src/org/thialfihar/android/apg/Apg.java +++ b/src/org/thialfihar/android/apg/Apg.java @@ -117,7 +117,9 @@ public class Apg { public static final String EXTRA_STATUS = "status"; public static final String EXTRA_ERROR = "error"; public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage"; + public static final String EXTRA_DECRYPTED_DATA = "decryptedData"; public static final String EXTRA_ENCRYPTED_MESSAGE = "encryptedMessage"; + public static final String EXTRA_ENCRYPTED_DATA = "encryptedData"; public static final String EXTRA_RESULT_URI = "resultUri"; public static final String EXTRA_SIGNATURE = "signature"; public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId"; @@ -135,6 +137,8 @@ public class Apg { public static final String EXTRA_PROGRESS = "progress"; public static final String EXTRA_MAX = "max"; public static final String EXTRA_ACCOUNT = "account"; + public static final String EXTRA_ASCII_ARMOUR = "asciiArmour"; + public static final String EXTRA_BINARY = "binary"; public static final String AUTHORITY = DataProvider.AUTHORITY; @@ -576,7 +580,7 @@ public class Apg { } public static Bundle importKeyRings(Activity context, int type, - InputStream inStream, long dataLength, + InputData data, ProgressDialogUpdater progress) throws GeneralException, FileNotFoundException, PGPException, IOException { Bundle returnData = new Bundle(); @@ -591,7 +595,7 @@ public class Apg { throw new GeneralException(context.getString(R.string.error_externalStorageNotReady)); } - PositionAwareInputStream progressIn = new PositionAwareInputStream(inStream); + PositionAwareInputStream progressIn = new PositionAwareInputStream(data.getInputStream()); // need to have access to the bufferedInput, so we can reuse it for the possible // PGPObject chunks after the first one, e.g. files with several consecutive ASCII // armour blocks @@ -637,7 +641,7 @@ public class Apg { } else if (retValue == Id.return_value.ok) { ++newKeys; } - progress.setProgress((int)(100 * progressIn.position() / dataLength), 100); + progress.setProgress((int)(100 * progressIn.position() / data.getSize()), 100); obj = objectFactory.nextObject(); } } @@ -1110,8 +1114,7 @@ public class Apg { } public static void encrypt(Context context, - InputStream inStream, OutputStream outStream, - long dataLength, + InputData data, OutputStream outStream, boolean armored, long encryptionKeyIds[], long signatureKeyId, String signaturePassPhrase, @@ -1213,14 +1216,15 @@ public class Apg { long done = 0; int n = 0; byte[] buffer = new byte[1 << 16]; - while ((n = inStream.read(buffer)) > 0) { + InputStream in = data.getInputStream(); + while ((n = in.read(buffer)) > 0) { pOut.write(buffer, 0, n); if (signatureKeyId != 0) { signatureGenerator.update(buffer, 0, n); } done += n; - if (dataLength != 0) { - progress.setProgress((int) (20 + (95 - 20) * done / dataLength), 100); + if (data.getSize() != 0) { + progress.setProgress((int) (20 + (95 - 20) * done / data.getSize()), 100); } } @@ -1242,7 +1246,7 @@ public class Apg { } public static void signText(Context context, - InputStream inStream, OutputStream outStream, + InputData data, OutputStream outStream, long signatureKeyId, String signaturePassPhrase, int hashAlgorithm, ProgressDialogUpdater progress) @@ -1294,6 +1298,7 @@ public class Apg { armorOut.beginClearText(hashAlgorithm); ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + InputStream inStream = data.getInputStream(); int lookAhead = readInputLine(lineOut, inStream); processLine(armorOut, signatureGenerator, lineOut.toByteArray()); @@ -1319,9 +1324,9 @@ public class Apg { progress.setProgress(R.string.progress_done, 100, 100); } - public static long getDecryptionKeyId(Context context, InputStream inStream) + public static long getDecryptionKeyId(Context context, InputData data) throws GeneralException, NoAsymmetricEncryptionException, IOException { - InputStream in = PGPUtil.getDecoderStream(inStream); + InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPEncryptedDataList enc; Object o = pgpF.nextObject(); @@ -1365,9 +1370,9 @@ public class Apg { return secretKey.getKeyID(); } - public static boolean hasSymmetricEncryption(Context context, InputStream inStream) + public static boolean hasSymmetricEncryption(Context context, InputData data) throws GeneralException, IOException { - InputStream in = PGPUtil.getDecoderStream(inStream); + InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPEncryptedDataList enc; Object o = pgpF.nextObject(); @@ -1395,8 +1400,7 @@ public class Apg { } public static Bundle decrypt(Context context, - PositionAwareInputStream inStream, OutputStream outStream, - long dataLength, + InputData data, OutputStream outStream, String passPhrase, ProgressDialogUpdater progress, boolean assumeSymmetric) throws IOException, GeneralException, PGPException, SignatureException { @@ -1404,7 +1408,7 @@ public class Apg { passPhrase = ""; } Bundle returnData = new Bundle(); - InputStream in = PGPUtil.getDecoderStream(inStream); + InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPEncryptedDataList enc; Object o = pgpF.nextObject(); @@ -1557,7 +1561,7 @@ public class Apg { } int n = 0; int done = 0; - long startPos = inStream.position(); + long startPos = data.getStreamPosition(); while ((n = dataIn.read(buffer)) > 0) { out.write(buffer, 0, n); done += n; @@ -1571,11 +1575,11 @@ public class Apg { } // unknown size, but try to at least have a moving, slowing down progress bar currentProgress = startProgress + (endProgress - startProgress) * done / (done + 100000); - if (dataLength - startPos == 0) { + if (data.getSize() - startPos == 0) { currentProgress = endProgress; } else { currentProgress = (int)(startProgress + (endProgress - startProgress) * - (inStream.position() - startPos) / (dataLength - startPos)); + (data.getStreamPosition() - startPos) / (data.getSize() - startPos)); } progress.setProgress(currentProgress, 100); } @@ -1609,13 +1613,13 @@ public class Apg { } public static Bundle verifyText(Context context, - InputStream inStream, OutputStream outStream, + InputData data, OutputStream outStream, ProgressDialogUpdater progress) throws IOException, GeneralException, PGPException, SignatureException { Bundle returnData = new Bundle(); ByteArrayOutputStream out = new ByteArrayOutputStream(); - ArmoredInputStream aIn = new ArmoredInputStream(inStream); + ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream()); progress.setProgress(R.string.progress_done, 0, 100); diff --git a/src/org/thialfihar/android/apg/DataDestination.java b/src/org/thialfihar/android/apg/DataDestination.java new file mode 100644 index 000000000..28cacd7ae --- /dev/null +++ b/src/org/thialfihar/android/apg/DataDestination.java @@ -0,0 +1,79 @@ +package org.thialfihar.android.apg; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.thialfihar.android.apg.Apg.GeneralException; + +import android.content.Context; +import android.os.Environment; + +public class DataDestination { + private String mStreamFilename; + private String mFilename; + private int mMode = Id.mode.undefined; + + public DataDestination() { + + } + + public void setMode(int mode) { + mMode = mode; + } + + public void setFilename(String filename) { + mFilename = filename; + } + + public String getStreamFilename() { + return mStreamFilename; + } + + protected OutputStream getOutputStream(Context context) + throws Apg.GeneralException, FileNotFoundException, IOException { + OutputStream out = null; + mStreamFilename = null; + + switch (mMode) { + case Id.mode.stream: { + try { + while (true) { + mStreamFilename = Apg.generateRandomString(32); + if (mStreamFilename == null) { + throw new Apg.GeneralException("couldn't generate random file name"); + } + context.openFileInput(mStreamFilename).close(); + } + } catch (FileNotFoundException e) { + // found a name that isn't used yet + } + out = context.openFileOutput(mStreamFilename, Context.MODE_PRIVATE); + break; + } + + case Id.mode.byte_array: { + out = new ByteArrayOutputStream(); + break; + } + + case Id.mode.file: { + if (mFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new GeneralException(context.getString(R.string.error_externalStorageNotReady)); + } + } + out = new FileOutputStream(mFilename); + break; + } + + default: { + break; + } + } + + return out; + } +} diff --git a/src/org/thialfihar/android/apg/DataSource.java b/src/org/thialfihar/android/apg/DataSource.java new file mode 100644 index 000000000..159cdb349 --- /dev/null +++ b/src/org/thialfihar/android/apg/DataSource.java @@ -0,0 +1,88 @@ +package org.thialfihar.android.apg; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import org.thialfihar.android.apg.Apg.GeneralException; + +import android.content.Context; +import android.net.Uri; +import android.os.Environment; + +public class DataSource { + private Uri mContentUri = null; + private String mText = null; + private byte[] mData = null; + + public DataSource() { + + } + + public void setUri(Uri uri) { + mContentUri = uri; + } + + public void setUri(String uri) { + if (uri.startsWith("/")) { + setUri(Uri.parse("file://" + uri)); + } else { + setUri(Uri.parse(uri)); + } + } + + public void setText(String text) { + mText = text; + } + + public void setData(byte[] data) { + mData = data; + } + + public InputData getInputData(Context context, boolean withSize) + throws GeneralException, FileNotFoundException, IOException { + InputStream in = null; + long size = 0; + + if (mContentUri != null) { + if (mContentUri.getScheme().equals("file")) { + // get the rest after "file://" + String path = mContentUri.toString().substring(6); + if (path.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new GeneralException(context.getString(R.string.error_externalStorageNotReady)); + } + } + in = new FileInputStream(path); + File file = new File(path); + if (withSize) { + size = file.length(); + } + } else { + in = context.getContentResolver().openInputStream(mContentUri); + if (withSize) { + InputStream tmp = context.getContentResolver().openInputStream(mContentUri); + size = Apg.getLengthOfStream(tmp); + tmp.close(); + } + } + } else if (mText != null || mData != null) { + byte[] bytes = null; + if (mData != null) { + bytes = mData; + } else { + bytes = mText.getBytes(); + } + in = new ByteArrayInputStream(bytes); + if (withSize) { + size = bytes.length; + } + } + + return new InputData(in, size); + } + +} diff --git a/src/org/thialfihar/android/apg/DecryptActivity.java b/src/org/thialfihar/android/apg/DecryptActivity.java index 571edbdf5..d8b7ccd9c 100644 --- a/src/org/thialfihar/android/apg/DecryptActivity.java +++ b/src/org/thialfihar/android/apg/DecryptActivity.java @@ -16,12 +16,9 @@ package org.thialfihar.android.apg; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -88,6 +85,11 @@ public class DecryptActivity extends BaseActivity { private String mOutputFilename = null; private Uri mContentUri = null; + private byte[] mData = null; + private boolean mReturnBinary = false; + + private DataSource mDataSource = null; + private DataDestination mDataDestination = null; @Override public void onCreate(Bundle savedInstanceState) { @@ -186,21 +188,26 @@ public class DecryptActivity extends BaseActivity { if (extras == null) { extras = new Bundle(); } - String data = extras.getString(Apg.EXTRA_TEXT); - if (data != null) { - Matcher matcher = Apg.PGP_MESSAGE.matcher(data); + + mData = extras.getByteArray(Apg.EXTRA_DATA); + String textData = null; + if (mData == null) { + textData = extras.getString(Apg.EXTRA_TEXT); + } + if (textData != null) { + Matcher matcher = Apg.PGP_MESSAGE.matcher(textData); if (matcher.matches()) { - data = matcher.group(1); + textData = matcher.group(1); // replace non breakable spaces - data = data.replaceAll("\\xa0", " "); - mMessage.setText(data); + textData = textData.replaceAll("\\xa0", " "); + mMessage.setText(textData); } else { - matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data); + matcher = Apg.PGP_SIGNED_MESSAGE.matcher(textData); if (matcher.matches()) { - data = matcher.group(1); + textData = matcher.group(1); // replace non breakable spaces - data = data.replaceAll("\\xa0", " "); - mMessage.setText(data); + textData = textData.replaceAll("\\xa0", " "); + mMessage.setText(textData); mDecryptButton.setText(R.string.btn_verify); } } @@ -208,15 +215,12 @@ public class DecryptActivity extends BaseActivity { mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO); mSubject = extras.getString(Apg.EXTRA_SUBJECT); } else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) { + mInputFilename = mIntent.getDataString(); if ("file".equals(mIntent.getScheme())) { - mInputFilename = mIntent.getDataString().replace("file://", ""); - mFilename.setText(mInputFilename); - guessOutputFilename(); - } else if ("content".equals(mIntent.getScheme())) { - mInputFilename = mIntent.getDataString(); - mFilename.setText(mInputFilename); - guessOutputFilename(); + mInputFilename = mInputFilename.substring(6); } + mFilename.setText(mInputFilename); + guessOutputFilename(); mSource.setInAnimation(null); mSource.setOutAnimation(null); while (mSource.getCurrentView().getId() != R.id.sourceFile) { @@ -224,11 +228,15 @@ public class DecryptActivity extends BaseActivity { } } else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) { mContentUri = mIntent.getData(); + Bundle extras = mIntent.getExtras(); + if (extras == null) { + extras = new Bundle(); + } + + mReturnBinary = extras.getBoolean(Apg.EXTRA_BINARY, false); + if (mContentUri == null) { - Bundle extras = mIntent.getExtras(); - if (extras == null) { - extras = new Bundle(); - } + mData = extras.getByteArray(Apg.EXTRA_DATA); String data = extras.getString(Apg.EXTRA_TEXT); if (data != null) { Matcher matcher = Apg.PGP_MESSAGE.matcher(data); @@ -397,19 +405,9 @@ public class DecryptActivity extends BaseActivity { // else treat it as an decrypted message/file mSignedOnly = false; String error = null; + fillDataSource(); try { - InputStream in; - if (mContentUri != null) { - in = getContentResolver().openInputStream(mContentUri); - } else if (mDecryptTarget == Id.target.file) { - if (mInputFilename.startsWith("file")) { - in = new FileInputStream(mInputFilename); - } else { - in = getContentResolver().openInputStream(Uri.parse(mInputFilename)); - } - } else { - in = new ByteArrayInputStream(mMessage.getText().toString().getBytes()); - } + InputData in = mDataSource.getInputData(this, false); try { setSecretKeyId(Apg.getDecryptionKeyId(this, in)); if (getSecretKeyId() == Id.key.none) { @@ -418,19 +416,7 @@ public class DecryptActivity extends BaseActivity { mAssumeSymmetricEncryption = false; } catch (Apg.NoAsymmetricEncryptionException e) { setSecretKeyId(Id.key.symmetric); - // look at the file/message again to check whether there's - // symmetric encryption data in there - if (mContentUri != null) { - in = getContentResolver().openInputStream(mContentUri); - } else if (mDecryptTarget == Id.target.file) { - if (mInputFilename.startsWith("file")) { - in = new FileInputStream(mInputFilename); - } else { - in = getContentResolver().openInputStream(Uri.parse(mInputFilename)); - } - } else { - in = new ByteArrayInputStream(mMessage.getText().toString().getBytes()); - } + in = mDataSource.getInputData(this, false); if (!Apg.hasSymmetricEncryption(this, in)) { throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound)); } @@ -500,60 +486,32 @@ public class DecryptActivity extends BaseActivity { Bundle data = new Bundle(); Message msg = new Message(); - + fillDataSource(); + fillDataDestination(); try { - PositionAwareInputStream in = null; - OutputStream out = null; - String randomString = null; - long size = 0; - - if (mContentUri != null) { - in = new PositionAwareInputStream(getContentResolver().openInputStream(mContentUri)); - size = Apg.getLengthOfStream(getContentResolver().openInputStream(mContentUri)); - try { - while (true) { - randomString = Apg.generateRandomString(32); - if (randomString == null) { - throw new Apg.GeneralException("couldn't generate random file name"); - } - this.openFileInput(randomString).close(); - } - } catch (FileNotFoundException e) { - // found a name that isn't used yet - } - out = openFileOutput(randomString, MODE_PRIVATE); - } else if (mDecryptTarget == Id.target.message) { - String messageData = mMessage.getText().toString(); - in = new PositionAwareInputStream(new ByteArrayInputStream(messageData.getBytes())); - out = new ByteArrayOutputStream(); - size = messageData.getBytes().length; - } else { - if (mInputFilename.startsWith("content")) { - size = Apg.getLengthOfStream(getContentResolver().openInputStream(Uri.parse(mInputFilename))); - in = new PositionAwareInputStream( - getContentResolver().openInputStream(Uri.parse(mInputFilename))); - } else { - in = new PositionAwareInputStream(new FileInputStream(mInputFilename)); - File file = new File(mInputFilename); - size = file.length(); - } - out = new FileOutputStream(mOutputFilename); - } + InputData in = mDataSource.getInputData(this, true); + OutputStream out = mDataDestination.getOutputStream(this); if (mSignedOnly) { data = Apg.verifyText(this, in, out, this); } else { - data = Apg.decrypt(this, in, out, size, Apg.getCachedPassPhrase(getSecretKeyId()), + data = Apg.decrypt(this, in, out, Apg.getCachedPassPhrase(getSecretKeyId()), this, mAssumeSymmetricEncryption); } out.close(); - if (randomString != null) { - data.putString(Apg.EXTRA_RESULT_URI, "content://" + DataProvider.AUTHORITY + "/data/" + randomString); + if (mDataDestination.getStreamFilename() != null) { + data.putString(Apg.EXTRA_RESULT_URI, "content://" + DataProvider.AUTHORITY + + "/data/" + mDataDestination.getStreamFilename()); } else if (mDecryptTarget == Id.target.message) { - data.putString(Apg.EXTRA_DECRYPTED_MESSAGE, - new String(((ByteArrayOutputStream) out).toByteArray())); + if (mReturnBinary) { + data.putByteArray(Apg.EXTRA_DECRYPTED_DATA, + ((ByteArrayOutputStream) out).toByteArray()); + } else { + data.putString(Apg.EXTRA_DECRYPTED_MESSAGE, + new String(((ByteArrayOutputStream) out).toByteArray())); + } } } catch (PGPException e) { error = "" + e; @@ -727,4 +685,31 @@ public class DecryptActivity extends BaseActivity { return super.onCreateDialog(id); } + + protected void fillDataSource() { + mDataSource = new DataSource(); + if (mContentUri != null) { + mDataSource.setUri(mContentUri); + } else if (mDecryptTarget == Id.target.file) { + mDataSource.setUri(mInputFilename); + } else { + if (mData != null) { + mDataSource.setData(mData); + } else { + mDataSource.setText(mMessage.getText().toString()); + } + } + } + + protected void fillDataDestination() { + mDataDestination = new DataDestination(); + if (mContentUri != null) { + mDataDestination.setMode(Id.mode.stream); + } else if (mDecryptTarget == Id.target.file) { + mDataDestination.setFilename(mOutputFilename); + mDataDestination.setMode(Id.mode.file); + } else { + mDataDestination.setMode(Id.mode.byte_array); + } + } } diff --git a/src/org/thialfihar/android/apg/EncryptActivity.java b/src/org/thialfihar/android/apg/EncryptActivity.java index f4fa6ae74..4854f355c 100644 --- a/src/org/thialfihar/android/apg/EncryptActivity.java +++ b/src/org/thialfihar/android/apg/EncryptActivity.java @@ -16,13 +16,9 @@ package org.thialfihar.android.apg; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -34,8 +30,6 @@ import org.bouncycastle2.openpgp.PGPPublicKey; import org.bouncycastle2.openpgp.PGPPublicKeyRing; import org.bouncycastle2.openpgp.PGPSecretKey; import org.bouncycastle2.openpgp.PGPSecretKeyRing; -import org.bouncycastle2.util.Strings; -import org.thialfihar.android.apg.Apg.GeneralException; import org.thialfihar.android.apg.utils.Choice; import android.app.Dialog; @@ -43,7 +37,6 @@ import android.content.ActivityNotFoundException; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.os.Environment; import android.os.Message; import android.text.ClipboardManager; import android.view.View; @@ -100,6 +93,14 @@ public class EncryptActivity extends BaseActivity { private String mInputFilename = null; private String mOutputFilename = null; + private boolean mAsciiArmourDemand = false; + private boolean mOverrideAsciiArmour = false; + private Uri mContentUri = null; + private byte[] mData = null; + + private DataSource mDataSource = null; + private DataDestination mDataDestination = null; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -271,6 +272,7 @@ public class EncryptActivity extends BaseActivity { if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { + mContentUri = mIntent.getData(); Bundle extras = mIntent.getExtras(); if (extras == null) { extras = new Bundle(); @@ -280,7 +282,17 @@ public class EncryptActivity extends BaseActivity { mReturnResult = true; } - String data = extras.getString(Apg.EXTRA_TEXT); + if (extras.containsKey(Apg.EXTRA_ASCII_ARMOUR)) { + mAsciiArmourDemand = extras.getBoolean(Apg.EXTRA_ASCII_ARMOUR, true); + mOverrideAsciiArmour = true; + mAsciiArmour.setChecked(mAsciiArmourDemand); + } + + mData = extras.getByteArray(Apg.EXTRA_DATA); + String textData = null; + if (mData == null) { + textData = extras.getString(Apg.EXTRA_TEXT); + } mSendTo = extras.getString(Apg.EXTRA_SEND_TO); mSubject = extras.getString(Apg.EXTRA_SUBJECT); long signatureKeyId = extras.getLong(Apg.EXTRA_SIGNATURE_KEY_ID); @@ -327,8 +339,8 @@ public class EncryptActivity extends BaseActivity { if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { - if (data != null) { - mMessage.setText(data); + if (textData != null) { + mMessage.setText(textData); } mSource.setInAnimation(null); mSource.setOutAnimation(null); @@ -548,11 +560,11 @@ public class EncryptActivity extends BaseActivity { String error = null; Bundle data = new Bundle(); Message msg = new Message(); - + fillDataSource(); + fillDataDestination(); try { - InputStream in; + InputData in; OutputStream out; - long size; boolean useAsciiArmour = true; long encryptionKeyIds[] = null; long signatureKeyId = 0; @@ -571,65 +583,28 @@ public class EncryptActivity extends BaseActivity { signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0); } + // streams + in = mDataSource.getInputData(this, true); + out = mDataDestination.getOutputStream(this); + if (mEncryptTarget == Id.target.file) { - if (mInputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath()) || - mOutputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new GeneralException(getString(R.string.error_externalStorageNotReady)); - } - } - - if (mInputFilename.startsWith("content")) { - in = getContentResolver().openInputStream(Uri.parse(mInputFilename)); - size = 0; - long n = 0; - byte dummy[] = new byte[0x10000]; - while ((n = in.read(dummy)) > 0) { - size += n; - } - in = getContentResolver().openInputStream(Uri.parse(mInputFilename)); - } else { - in = new FileInputStream(mInputFilename); - File file = new File(mInputFilename); - size = file.length(); - } - out = new FileOutputStream(mOutputFilename); - useAsciiArmour = mAsciiArmour.isChecked(); compressionId = ((Choice) mFileCompression.getSelectedItem()).getId(); } else { - String message = mMessage.getText().toString(); - - if (signOnly && !mReturnResult) { - // fix the message a bit, trailing spaces and newlines break stuff, - // because GMail sends as HTML and such things fuck up the signature, - // TODO: things like "<" and ">" also fuck up the signature - message = message.replaceAll(" +\n", "\n"); - message = message.replaceAll("\n\n+", "\n\n"); - message = message.replaceFirst("^\n+", ""); - // make sure there'll be exactly one newline at the end - message = message.replaceFirst("\n*$", "\n"); - } - - if (signOnly && !message.endsWith("\n")) { - message += '\n'; - } - - byte[] byteData = Strings.toUTF8ByteArray(message); - in = new ByteArrayInputStream(byteData); - out = new ByteArrayOutputStream(); - - size = byteData.length; useAsciiArmour = true; compressionId = mPreferences.getDefaultMessageCompression(); } + if (mOverrideAsciiArmour) { + useAsciiArmour = mAsciiArmourDemand; + } + if (signOnly) { Apg.signText(this, in, out, getSecretKeyId(), Apg.getCachedPassPhrase(getSecretKeyId()), mPreferences.getDefaultHashAlgorithm(), this); } else { - Apg.encrypt(this, in, out, size, useAsciiArmour, + Apg.encrypt(this, in, out, useAsciiArmour, encryptionKeyIds, signatureKeyId, Apg.getCachedPassPhrase(signatureKeyId), this, mPreferences.getDefaultEncryptionAlgorithm(), @@ -639,8 +614,13 @@ public class EncryptActivity extends BaseActivity { out.close(); if (mEncryptTarget != Id.target.file) { - data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE, - new String(((ByteArrayOutputStream)out).toByteArray())); + if (useAsciiArmour) { + data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE, + new String(((ByteArrayOutputStream)out).toByteArray())); + } else { + data.putByteArray(Apg.EXTRA_ENCRYPTED_DATA, + ((ByteArrayOutputStream)out).toByteArray()); + } } } catch (IOException e) { error = "" + e; @@ -889,4 +869,31 @@ public class EncryptActivity extends BaseActivity { return super.onCreateDialog(id); } + + protected void fillDataSource() { + mDataSource = new DataSource(); + if (mContentUri != null) { + mDataSource.setUri(mContentUri); + } else if (mEncryptTarget == Id.target.file) { + mDataSource.setUri(mInputFilename); + } else { + if (mData != null) { + mDataSource.setData(mData); + } else { + mDataSource.setText(mMessage.getText().toString()); + } + } + } + + protected void fillDataDestination() { + mDataDestination = new DataDestination(); + if (mContentUri != null) { + mDataDestination.setMode(Id.mode.stream); + } else if (mEncryptTarget == Id.target.file) { + mDataDestination.setFilename(mOutputFilename); + mDataDestination.setMode(Id.mode.file); + } else { + mDataDestination.setMode(Id.mode.byte_array); + } + } } \ No newline at end of file diff --git a/src/org/thialfihar/android/apg/GeneralActivity.java b/src/org/thialfihar/android/apg/GeneralActivity.java index 686d6fd39..49cbdcf2a 100644 --- a/src/org/thialfihar/android/apg/GeneralActivity.java +++ b/src/org/thialfihar/android/apg/GeneralActivity.java @@ -14,11 +14,11 @@ import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.Toast; -import android.widget.AdapterView.OnItemClickListener; public class GeneralActivity extends BaseActivity { private Intent mIntent; diff --git a/src/org/thialfihar/android/apg/Id.java b/src/org/thialfihar/android/apg/Id.java index 69966e2ff..bc8b7e443 100644 --- a/src/org/thialfihar/android/apg/Id.java +++ b/src/org/thialfihar/android/apg/Id.java @@ -135,6 +135,13 @@ public final class Id { public static final int message = 0x21070004; } + public static final class mode { + public static final int undefined = 0x21070001; + public static final int byte_array = 0x21070002; + public static final int file = 0x21070003; + public static final int stream = 0x21070004; + } + public static final class key { public static final int none = 0; public static final int symmetric = -1; diff --git a/src/org/thialfihar/android/apg/InputData.java b/src/org/thialfihar/android/apg/InputData.java new file mode 100644 index 000000000..cf49f1c33 --- /dev/null +++ b/src/org/thialfihar/android/apg/InputData.java @@ -0,0 +1,25 @@ +package org.thialfihar.android.apg; + +import java.io.InputStream; + +public class InputData { + private PositionAwareInputStream mInputStream; + private long mSize; + + InputData(InputStream inputStream, long size) { + mInputStream = new PositionAwareInputStream(inputStream); + mSize = size; + } + + public InputStream getInputStream() { + return mInputStream; + } + + public long getSize() { + return mSize; + } + + public long getStreamPosition() { + return mInputStream.position(); + } +} diff --git a/src/org/thialfihar/android/apg/KeyListActivity.java b/src/org/thialfihar/android/apg/KeyListActivity.java index ac861f0ac..26c744f33 100644 --- a/src/org/thialfihar/android/apg/KeyListActivity.java +++ b/src/org/thialfihar/android/apg/KeyListActivity.java @@ -48,15 +48,15 @@ import android.os.Message; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.view.View.OnClickListener; +import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.Button; import android.widget.ExpandableListView; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; public class KeyListActivity extends BaseActivity { protected ExpandableListView mList; @@ -328,7 +328,7 @@ public class KeyListActivity extends BaseActivity { } if (mTask == Id.task.import_keys) { - data = Apg.importKeyRings(this, mKeyType, importInputStream, size, this); + data = Apg.importKeyRings(this, mKeyType, new InputData(importInputStream, size), this); } else { Vector keyRingIds = new Vector(); if (mSelectedItem == -1) { diff --git a/src/org/thialfihar/android/apg/MailListActivity.java b/src/org/thialfihar/android/apg/MailListActivity.java index fb4d39f6a..d78dd47e1 100644 --- a/src/org/thialfihar/android/apg/MailListActivity.java +++ b/src/org/thialfihar/android/apg/MailListActivity.java @@ -30,11 +30,11 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.TextView; -import android.widget.AdapterView.OnItemClickListener; public class MailListActivity extends ListActivity { LayoutInflater mInflater = null; diff --git a/src/org/thialfihar/android/apg/MainActivity.java b/src/org/thialfihar/android/apg/MainActivity.java index f103e9548..31ff027d3 100644 --- a/src/org/thialfihar/android/apg/MainActivity.java +++ b/src/org/thialfihar/android/apg/MainActivity.java @@ -29,21 +29,21 @@ import android.database.SQLException; import android.net.Uri; import android.os.Bundle; import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; -import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnClickListener; +import android.view.ViewGroup; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; import android.widget.CursorAdapter; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; -import android.widget.AdapterView.OnItemClickListener; public class MainActivity extends BaseActivity { private ListView mAccounts = null; diff --git a/src/org/thialfihar/android/apg/PreferencesActivity.java b/src/org/thialfihar/android/apg/PreferencesActivity.java index 7e89e40a7..7e584a7a6 100644 --- a/src/org/thialfihar/android/apg/PreferencesActivity.java +++ b/src/org/thialfihar/android/apg/PreferencesActivity.java @@ -18,7 +18,6 @@ package org.thialfihar.android.apg; import org.bouncycastle2.bcpg.HashAlgorithmTags; import org.bouncycastle2.openpgp.PGPEncryptedData; -import org.thialfihar.android.apg.utils.Choice; import android.os.Bundle; import android.preference.CheckBoxPreference; diff --git a/src/org/thialfihar/android/apg/PublicKeyListActivity.java b/src/org/thialfihar/android/apg/PublicKeyListActivity.java index 74e964ec8..916d4e6c2 100644 --- a/src/org/thialfihar/android/apg/PublicKeyListActivity.java +++ b/src/org/thialfihar/android/apg/PublicKeyListActivity.java @@ -18,9 +18,9 @@ package org.thialfihar.android.apg; import android.os.Bundle; import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; import android.widget.ExpandableListView; public class PublicKeyListActivity extends KeyListActivity { diff --git a/src/org/thialfihar/android/apg/SecretKeyListActivity.java b/src/org/thialfihar/android/apg/SecretKeyListActivity.java index fb8ad92c6..9ff7f0fa3 100644 --- a/src/org/thialfihar/android/apg/SecretKeyListActivity.java +++ b/src/org/thialfihar/android/apg/SecretKeyListActivity.java @@ -20,10 +20,10 @@ import android.app.Dialog; import android.content.Intent; import android.os.Bundle; import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; import android.widget.ExpandableListView; import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import android.widget.ExpandableListView.OnChildClickListener; diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java index c0ab57710..0b18ecc15 100644 --- a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java +++ b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java @@ -23,10 +23,10 @@ import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; -import android.widget.AdapterView.OnItemClickListener; public class SelectSecretKeyListActivity extends BaseActivity { protected ListView mList;