trying to add support for various source and destination modes for encryption and decryption (String, byte[], filename, content stream), also more Intent options added

This commit is contained in:
Thialfihar 2010-07-09 20:55:17 +00:00
parent 3ac472125a
commit 2c47734f0f
15 changed files with 380 additions and 186 deletions

View File

@ -117,7 +117,9 @@ public class Apg {
public static final String EXTRA_STATUS = "status"; public static final String EXTRA_STATUS = "status";
public static final String EXTRA_ERROR = "error"; public static final String EXTRA_ERROR = "error";
public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage"; 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_MESSAGE = "encryptedMessage";
public static final String EXTRA_ENCRYPTED_DATA = "encryptedData";
public static final String EXTRA_RESULT_URI = "resultUri"; public static final String EXTRA_RESULT_URI = "resultUri";
public static final String EXTRA_SIGNATURE = "signature"; public static final String EXTRA_SIGNATURE = "signature";
public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId"; 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_PROGRESS = "progress";
public static final String EXTRA_MAX = "max"; public static final String EXTRA_MAX = "max";
public static final String EXTRA_ACCOUNT = "account"; 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; public static final String AUTHORITY = DataProvider.AUTHORITY;
@ -576,7 +580,7 @@ public class Apg {
} }
public static Bundle importKeyRings(Activity context, int type, public static Bundle importKeyRings(Activity context, int type,
InputStream inStream, long dataLength, InputData data,
ProgressDialogUpdater progress) ProgressDialogUpdater progress)
throws GeneralException, FileNotFoundException, PGPException, IOException { throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
@ -591,7 +595,7 @@ public class Apg {
throw new GeneralException(context.getString(R.string.error_externalStorageNotReady)); 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 // 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 // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
// armour blocks // armour blocks
@ -637,7 +641,7 @@ public class Apg {
} else if (retValue == Id.return_value.ok) { } else if (retValue == Id.return_value.ok) {
++newKeys; ++newKeys;
} }
progress.setProgress((int)(100 * progressIn.position() / dataLength), 100); progress.setProgress((int)(100 * progressIn.position() / data.getSize()), 100);
obj = objectFactory.nextObject(); obj = objectFactory.nextObject();
} }
} }
@ -1110,8 +1114,7 @@ public class Apg {
} }
public static void encrypt(Context context, public static void encrypt(Context context,
InputStream inStream, OutputStream outStream, InputData data, OutputStream outStream,
long dataLength,
boolean armored, boolean armored,
long encryptionKeyIds[], long signatureKeyId, long encryptionKeyIds[], long signatureKeyId,
String signaturePassPhrase, String signaturePassPhrase,
@ -1213,14 +1216,15 @@ public class Apg {
long done = 0; long done = 0;
int n = 0; int n = 0;
byte[] buffer = new byte[1 << 16]; 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); pOut.write(buffer, 0, n);
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
signatureGenerator.update(buffer, 0, n); signatureGenerator.update(buffer, 0, n);
} }
done += n; done += n;
if (dataLength != 0) { if (data.getSize() != 0) {
progress.setProgress((int) (20 + (95 - 20) * done / dataLength), 100); progress.setProgress((int) (20 + (95 - 20) * done / data.getSize()), 100);
} }
} }
@ -1242,7 +1246,7 @@ public class Apg {
} }
public static void signText(Context context, public static void signText(Context context,
InputStream inStream, OutputStream outStream, InputData data, OutputStream outStream,
long signatureKeyId, String signaturePassPhrase, long signatureKeyId, String signaturePassPhrase,
int hashAlgorithm, int hashAlgorithm,
ProgressDialogUpdater progress) ProgressDialogUpdater progress)
@ -1294,6 +1298,7 @@ public class Apg {
armorOut.beginClearText(hashAlgorithm); armorOut.beginClearText(hashAlgorithm);
ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
InputStream inStream = data.getInputStream();
int lookAhead = readInputLine(lineOut, inStream); int lookAhead = readInputLine(lineOut, inStream);
processLine(armorOut, signatureGenerator, lineOut.toByteArray()); processLine(armorOut, signatureGenerator, lineOut.toByteArray());
@ -1319,9 +1324,9 @@ public class Apg {
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
} }
public static long getDecryptionKeyId(Context context, InputStream inStream) public static long getDecryptionKeyId(Context context, InputData data)
throws GeneralException, NoAsymmetricEncryptionException, IOException { throws GeneralException, NoAsymmetricEncryptionException, IOException {
InputStream in = PGPUtil.getDecoderStream(inStream); InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc; PGPEncryptedDataList enc;
Object o = pgpF.nextObject(); Object o = pgpF.nextObject();
@ -1365,9 +1370,9 @@ public class Apg {
return secretKey.getKeyID(); return secretKey.getKeyID();
} }
public static boolean hasSymmetricEncryption(Context context, InputStream inStream) public static boolean hasSymmetricEncryption(Context context, InputData data)
throws GeneralException, IOException { throws GeneralException, IOException {
InputStream in = PGPUtil.getDecoderStream(inStream); InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc; PGPEncryptedDataList enc;
Object o = pgpF.nextObject(); Object o = pgpF.nextObject();
@ -1395,8 +1400,7 @@ public class Apg {
} }
public static Bundle decrypt(Context context, public static Bundle decrypt(Context context,
PositionAwareInputStream inStream, OutputStream outStream, InputData data, OutputStream outStream,
long dataLength,
String passPhrase, ProgressDialogUpdater progress, String passPhrase, ProgressDialogUpdater progress,
boolean assumeSymmetric) boolean assumeSymmetric)
throws IOException, GeneralException, PGPException, SignatureException { throws IOException, GeneralException, PGPException, SignatureException {
@ -1404,7 +1408,7 @@ public class Apg {
passPhrase = ""; passPhrase = "";
} }
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
InputStream in = PGPUtil.getDecoderStream(inStream); InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc; PGPEncryptedDataList enc;
Object o = pgpF.nextObject(); Object o = pgpF.nextObject();
@ -1557,7 +1561,7 @@ public class Apg {
} }
int n = 0; int n = 0;
int done = 0; int done = 0;
long startPos = inStream.position(); long startPos = data.getStreamPosition();
while ((n = dataIn.read(buffer)) > 0) { while ((n = dataIn.read(buffer)) > 0) {
out.write(buffer, 0, n); out.write(buffer, 0, n);
done += n; done += n;
@ -1571,11 +1575,11 @@ public class Apg {
} }
// unknown size, but try to at least have a moving, slowing down progress bar // unknown size, but try to at least have a moving, slowing down progress bar
currentProgress = startProgress + (endProgress - startProgress) * done / (done + 100000); currentProgress = startProgress + (endProgress - startProgress) * done / (done + 100000);
if (dataLength - startPos == 0) { if (data.getSize() - startPos == 0) {
currentProgress = endProgress; currentProgress = endProgress;
} else { } else {
currentProgress = (int)(startProgress + (endProgress - startProgress) * currentProgress = (int)(startProgress + (endProgress - startProgress) *
(inStream.position() - startPos) / (dataLength - startPos)); (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
} }
progress.setProgress(currentProgress, 100); progress.setProgress(currentProgress, 100);
} }
@ -1609,13 +1613,13 @@ public class Apg {
} }
public static Bundle verifyText(Context context, public static Bundle verifyText(Context context,
InputStream inStream, OutputStream outStream, InputData data, OutputStream outStream,
ProgressDialogUpdater progress) ProgressDialogUpdater progress)
throws IOException, GeneralException, PGPException, SignatureException { throws IOException, GeneralException, PGPException, SignatureException {
Bundle returnData = new Bundle(); Bundle returnData = new Bundle();
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
ArmoredInputStream aIn = new ArmoredInputStream(inStream); ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream());
progress.setProgress(R.string.progress_done, 0, 100); progress.setProgress(R.string.progress_done, 0, 100);

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -16,12 +16,9 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -88,6 +85,11 @@ public class DecryptActivity extends BaseActivity {
private String mOutputFilename = null; private String mOutputFilename = null;
private Uri mContentUri = null; private Uri mContentUri = null;
private byte[] mData = null;
private boolean mReturnBinary = false;
private DataSource mDataSource = null;
private DataDestination mDataDestination = null;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -186,21 +188,26 @@ public class DecryptActivity extends BaseActivity {
if (extras == null) { if (extras == null) {
extras = new Bundle(); extras = new Bundle();
} }
String data = extras.getString(Apg.EXTRA_TEXT);
if (data != null) { mData = extras.getByteArray(Apg.EXTRA_DATA);
Matcher matcher = Apg.PGP_MESSAGE.matcher(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()) { if (matcher.matches()) {
data = matcher.group(1); textData = matcher.group(1);
// replace non breakable spaces // replace non breakable spaces
data = data.replaceAll("\\xa0", " "); textData = textData.replaceAll("\\xa0", " ");
mMessage.setText(data); mMessage.setText(textData);
} else { } else {
matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data); matcher = Apg.PGP_SIGNED_MESSAGE.matcher(textData);
if (matcher.matches()) { if (matcher.matches()) {
data = matcher.group(1); textData = matcher.group(1);
// replace non breakable spaces // replace non breakable spaces
data = data.replaceAll("\\xa0", " "); textData = textData.replaceAll("\\xa0", " ");
mMessage.setText(data); mMessage.setText(textData);
mDecryptButton.setText(R.string.btn_verify); mDecryptButton.setText(R.string.btn_verify);
} }
} }
@ -208,15 +215,12 @@ public class DecryptActivity extends BaseActivity {
mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO); mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO);
mSubject = extras.getString(Apg.EXTRA_SUBJECT); mSubject = extras.getString(Apg.EXTRA_SUBJECT);
} else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) { } else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) {
mInputFilename = mIntent.getDataString();
if ("file".equals(mIntent.getScheme())) { if ("file".equals(mIntent.getScheme())) {
mInputFilename = mIntent.getDataString().replace("file://", ""); mInputFilename = mInputFilename.substring(6);
mFilename.setText(mInputFilename);
guessOutputFilename();
} else if ("content".equals(mIntent.getScheme())) {
mInputFilename = mIntent.getDataString();
mFilename.setText(mInputFilename);
guessOutputFilename();
} }
mFilename.setText(mInputFilename);
guessOutputFilename();
mSource.setInAnimation(null); mSource.setInAnimation(null);
mSource.setOutAnimation(null); mSource.setOutAnimation(null);
while (mSource.getCurrentView().getId() != R.id.sourceFile) { while (mSource.getCurrentView().getId() != R.id.sourceFile) {
@ -224,11 +228,15 @@ public class DecryptActivity extends BaseActivity {
} }
} else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) { } else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) {
mContentUri = mIntent.getData(); mContentUri = mIntent.getData();
Bundle extras = mIntent.getExtras();
if (extras == null) {
extras = new Bundle();
}
mReturnBinary = extras.getBoolean(Apg.EXTRA_BINARY, false);
if (mContentUri == null) { if (mContentUri == null) {
Bundle extras = mIntent.getExtras(); mData = extras.getByteArray(Apg.EXTRA_DATA);
if (extras == null) {
extras = new Bundle();
}
String data = extras.getString(Apg.EXTRA_TEXT); String data = extras.getString(Apg.EXTRA_TEXT);
if (data != null) { if (data != null) {
Matcher matcher = Apg.PGP_MESSAGE.matcher(data); Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
@ -397,19 +405,9 @@ public class DecryptActivity extends BaseActivity {
// else treat it as an decrypted message/file // else treat it as an decrypted message/file
mSignedOnly = false; mSignedOnly = false;
String error = null; String error = null;
fillDataSource();
try { try {
InputStream in; InputData in = mDataSource.getInputData(this, false);
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());
}
try { try {
setSecretKeyId(Apg.getDecryptionKeyId(this, in)); setSecretKeyId(Apg.getDecryptionKeyId(this, in));
if (getSecretKeyId() == Id.key.none) { if (getSecretKeyId() == Id.key.none) {
@ -418,19 +416,7 @@ public class DecryptActivity extends BaseActivity {
mAssumeSymmetricEncryption = false; mAssumeSymmetricEncryption = false;
} catch (Apg.NoAsymmetricEncryptionException e) { } catch (Apg.NoAsymmetricEncryptionException e) {
setSecretKeyId(Id.key.symmetric); setSecretKeyId(Id.key.symmetric);
// look at the file/message again to check whether there's in = mDataSource.getInputData(this, false);
// 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());
}
if (!Apg.hasSymmetricEncryption(this, in)) { if (!Apg.hasSymmetricEncryption(this, in)) {
throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound)); throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound));
} }
@ -500,60 +486,32 @@ public class DecryptActivity extends BaseActivity {
Bundle data = new Bundle(); Bundle data = new Bundle();
Message msg = new Message(); Message msg = new Message();
fillDataSource();
fillDataDestination();
try { try {
PositionAwareInputStream in = null; InputData in = mDataSource.getInputData(this, true);
OutputStream out = null; OutputStream out = mDataDestination.getOutputStream(this);
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);
}
if (mSignedOnly) { if (mSignedOnly) {
data = Apg.verifyText(this, in, out, this); data = Apg.verifyText(this, in, out, this);
} else { } else {
data = Apg.decrypt(this, in, out, size, Apg.getCachedPassPhrase(getSecretKeyId()), data = Apg.decrypt(this, in, out, Apg.getCachedPassPhrase(getSecretKeyId()),
this, mAssumeSymmetricEncryption); this, mAssumeSymmetricEncryption);
} }
out.close(); out.close();
if (randomString != null) { if (mDataDestination.getStreamFilename() != null) {
data.putString(Apg.EXTRA_RESULT_URI, "content://" + DataProvider.AUTHORITY + "/data/" + randomString); data.putString(Apg.EXTRA_RESULT_URI, "content://" + DataProvider.AUTHORITY +
"/data/" + mDataDestination.getStreamFilename());
} else if (mDecryptTarget == Id.target.message) { } else if (mDecryptTarget == Id.target.message) {
data.putString(Apg.EXTRA_DECRYPTED_MESSAGE, if (mReturnBinary) {
new String(((ByteArrayOutputStream) out).toByteArray())); data.putByteArray(Apg.EXTRA_DECRYPTED_DATA,
((ByteArrayOutputStream) out).toByteArray());
} else {
data.putString(Apg.EXTRA_DECRYPTED_MESSAGE,
new String(((ByteArrayOutputStream) out).toByteArray()));
}
} }
} catch (PGPException e) { } catch (PGPException e) {
error = "" + e; error = "" + e;
@ -727,4 +685,31 @@ public class DecryptActivity extends BaseActivity {
return super.onCreateDialog(id); 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);
}
}
} }

View File

@ -16,13 +16,9 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
@ -34,8 +30,6 @@ import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing; import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.bouncycastle2.openpgp.PGPSecretKey; import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing; import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.bouncycastle2.util.Strings;
import org.thialfihar.android.apg.Apg.GeneralException;
import org.thialfihar.android.apg.utils.Choice; import org.thialfihar.android.apg.utils.Choice;
import android.app.Dialog; import android.app.Dialog;
@ -43,7 +37,6 @@ import android.content.ActivityNotFoundException;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Message; import android.os.Message;
import android.text.ClipboardManager; import android.text.ClipboardManager;
import android.view.View; import android.view.View;
@ -100,6 +93,14 @@ public class EncryptActivity extends BaseActivity {
private String mInputFilename = null; private String mInputFilename = null;
private String mOutputFilename = 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 @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -271,6 +272,7 @@ public class EncryptActivity extends BaseActivity {
if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) ||
Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) || Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) ||
Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
mContentUri = mIntent.getData();
Bundle extras = mIntent.getExtras(); Bundle extras = mIntent.getExtras();
if (extras == null) { if (extras == null) {
extras = new Bundle(); extras = new Bundle();
@ -280,7 +282,17 @@ public class EncryptActivity extends BaseActivity {
mReturnResult = true; 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); mSendTo = extras.getString(Apg.EXTRA_SEND_TO);
mSubject = extras.getString(Apg.EXTRA_SUBJECT); mSubject = extras.getString(Apg.EXTRA_SUBJECT);
long signatureKeyId = extras.getLong(Apg.EXTRA_SIGNATURE_KEY_ID); 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()) || if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) ||
Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
if (data != null) { if (textData != null) {
mMessage.setText(data); mMessage.setText(textData);
} }
mSource.setInAnimation(null); mSource.setInAnimation(null);
mSource.setOutAnimation(null); mSource.setOutAnimation(null);
@ -548,11 +560,11 @@ public class EncryptActivity extends BaseActivity {
String error = null; String error = null;
Bundle data = new Bundle(); Bundle data = new Bundle();
Message msg = new Message(); Message msg = new Message();
fillDataSource();
fillDataDestination();
try { try {
InputStream in; InputData in;
OutputStream out; OutputStream out;
long size;
boolean useAsciiArmour = true; boolean useAsciiArmour = true;
long encryptionKeyIds[] = null; long encryptionKeyIds[] = null;
long signatureKeyId = 0; long signatureKeyId = 0;
@ -571,65 +583,28 @@ public class EncryptActivity extends BaseActivity {
signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0); signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0);
} }
// streams
in = mDataSource.getInputData(this, true);
out = mDataDestination.getOutputStream(this);
if (mEncryptTarget == Id.target.file) { 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(); useAsciiArmour = mAsciiArmour.isChecked();
compressionId = ((Choice) mFileCompression.getSelectedItem()).getId(); compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
} else { } 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; useAsciiArmour = true;
compressionId = mPreferences.getDefaultMessageCompression(); compressionId = mPreferences.getDefaultMessageCompression();
} }
if (mOverrideAsciiArmour) {
useAsciiArmour = mAsciiArmourDemand;
}
if (signOnly) { if (signOnly) {
Apg.signText(this, in, out, getSecretKeyId(), Apg.signText(this, in, out, getSecretKeyId(),
Apg.getCachedPassPhrase(getSecretKeyId()), Apg.getCachedPassPhrase(getSecretKeyId()),
mPreferences.getDefaultHashAlgorithm(), this); mPreferences.getDefaultHashAlgorithm(), this);
} else { } else {
Apg.encrypt(this, in, out, size, useAsciiArmour, Apg.encrypt(this, in, out, useAsciiArmour,
encryptionKeyIds, signatureKeyId, encryptionKeyIds, signatureKeyId,
Apg.getCachedPassPhrase(signatureKeyId), this, Apg.getCachedPassPhrase(signatureKeyId), this,
mPreferences.getDefaultEncryptionAlgorithm(), mPreferences.getDefaultEncryptionAlgorithm(),
@ -639,8 +614,13 @@ public class EncryptActivity extends BaseActivity {
out.close(); out.close();
if (mEncryptTarget != Id.target.file) { if (mEncryptTarget != Id.target.file) {
data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE, if (useAsciiArmour) {
new String(((ByteArrayOutputStream)out).toByteArray())); data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE,
new String(((ByteArrayOutputStream)out).toByteArray()));
} else {
data.putByteArray(Apg.EXTRA_ENCRYPTED_DATA,
((ByteArrayOutputStream)out).toByteArray());
}
} }
} catch (IOException e) { } catch (IOException e) {
error = "" + e; error = "" + e;
@ -889,4 +869,31 @@ public class EncryptActivity extends BaseActivity {
return super.onCreateDialog(id); 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);
}
}
} }

View File

@ -14,11 +14,11 @@ import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button; import android.widget.Button;
import android.widget.ListView; import android.widget.ListView;
import android.widget.Toast; import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
public class GeneralActivity extends BaseActivity { public class GeneralActivity extends BaseActivity {
private Intent mIntent; private Intent mIntent;

View File

@ -135,6 +135,13 @@ public final class Id {
public static final int message = 0x21070004; 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 class key {
public static final int none = 0; public static final int none = 0;
public static final int symmetric = -1; public static final int symmetric = -1;

View File

@ -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();
}
}

View File

@ -48,15 +48,15 @@ import android.os.Message;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter; import android.widget.BaseExpandableListAdapter;
import android.widget.Button; import android.widget.Button;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
public class KeyListActivity extends BaseActivity { public class KeyListActivity extends BaseActivity {
protected ExpandableListView mList; protected ExpandableListView mList;
@ -328,7 +328,7 @@ public class KeyListActivity extends BaseActivity {
} }
if (mTask == Id.task.import_keys) { 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 { } else {
Vector<Integer> keyRingIds = new Vector<Integer>(); Vector<Integer> keyRingIds = new Vector<Integer>();
if (mSelectedItem == -1) { if (mSelectedItem == -1) {

View File

@ -30,11 +30,11 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ListAdapter; import android.widget.ListAdapter;
import android.widget.TextView; import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class MailListActivity extends ListActivity { public class MailListActivity extends ListActivity {
LayoutInflater mInflater = null; LayoutInflater mInflater = null;

View File

@ -29,21 +29,21 @@ import android.database.SQLException;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button; import android.widget.Button;
import android.widget.CursorAdapter; import android.widget.CursorAdapter;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
public class MainActivity extends BaseActivity { public class MainActivity extends BaseActivity {
private ListView mAccounts = null; private ListView mAccounts = null;

View File

@ -18,7 +18,6 @@ package org.thialfihar.android.apg;
import org.bouncycastle2.bcpg.HashAlgorithmTags; import org.bouncycastle2.bcpg.HashAlgorithmTags;
import org.bouncycastle2.openpgp.PGPEncryptedData; import org.bouncycastle2.openpgp.PGPEncryptedData;
import org.thialfihar.android.apg.utils.Choice;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;

View File

@ -18,9 +18,9 @@ package org.thialfihar.android.apg;
import android.os.Bundle; import android.os.Bundle;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu; import android.view.Menu;
import android.view.View; import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
public class PublicKeyListActivity extends KeyListActivity { public class PublicKeyListActivity extends KeyListActivity {

View File

@ -20,10 +20,10 @@ import android.app.Dialog;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView.OnChildClickListener; import android.widget.ExpandableListView.OnChildClickListener;

View File

@ -23,10 +23,10 @@ import android.view.Menu;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button; import android.widget.Button;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class SelectSecretKeyListActivity extends BaseActivity { public class SelectSecretKeyListActivity extends BaseActivity {
protected ListView mList; protected ListView mList;