diff --git a/src/org/thialfihar/android/apg/Apg.java b/src/org/thialfihar/android/apg/Apg.java index b2ae19368..d606a553c 100644 --- a/src/org/thialfihar/android/apg/Apg.java +++ b/src/org/thialfihar/android/apg/Apg.java @@ -37,7 +37,6 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Vector; import java.util.regex.Pattern; @@ -118,7 +117,8 @@ 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_ENCRYPTED_MESSAGE = "decryptedMessage"; + public static final String EXTRA_ENCRYPTED_MESSAGE = "encryptedMessage"; + public static final String EXTRA_RESULT_URI = "resultUri"; 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"; @@ -1851,4 +1851,43 @@ public class Apg { public static String getFullVersion(Context context) { return "APG v" + getVersion(context); } + + public static String generateRandomString(int length) { + SecureRandom random = new SecureRandom(); + /* + try { + random = SecureRandom.getInstance("SHA1PRNG", new BouncyCastleProvider()); + } catch (NoSuchAlgorithmException e) { + // TODO: need to handle this case somehow + return null; + }*/ + byte bytes[] = new byte[length]; + random.nextBytes(bytes); + String result = ""; + for (int i = 0; i < length; ++i) { + int v = ((int)bytes[i] + 256) % 64; + if (v < 10) { + result += (char)((int)'0' + v); + } else if (v < 36) { + result += (char)((int)'A' + v - 10); + } else if (v < 62) { + result += (char)((int)'a' + v - 36); + } else if (v == 62) { + result += '_'; + } else if (v == 63) { + result += '.'; + } + } + return result; + } + + static long getLengthOfStream(InputStream in) throws IOException { + long size = 0; + long n = 0; + byte dummy[] = new byte[0x10000]; + while ((n = in.read(dummy)) > 0) { + size += n; + } + return size; + } } diff --git a/src/org/thialfihar/android/apg/DecryptActivity.java b/src/org/thialfihar/android/apg/DecryptActivity.java index 98480bbf7..571edbdf5 100644 --- a/src/org/thialfihar/android/apg/DecryptActivity.java +++ b/src/org/thialfihar/android/apg/DecryptActivity.java @@ -31,6 +31,7 @@ import java.util.regex.Matcher; import org.bouncycastle2.jce.provider.BouncyCastleProvider; import org.bouncycastle2.openpgp.PGPException; +import org.thialfihar.android.apg.provider.DataProvider; import android.app.Dialog; import android.content.ActivityNotFoundException; @@ -86,6 +87,8 @@ public class DecryptActivity extends BaseActivity { private String mInputFilename = null; private String mOutputFilename = null; + private Uri mContentUri = null; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -220,26 +223,29 @@ public class DecryptActivity extends BaseActivity { mSource.showNext(); } } else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) { - Bundle extras = mIntent.getExtras(); - if (extras == null) { - extras = new Bundle(); - } - String data = extras.getString(Apg.EXTRA_TEXT); - if (data != null) { - Matcher matcher = Apg.PGP_MESSAGE.matcher(data); - if (matcher.matches()) { - data = matcher.group(1); - // replace non breakable spaces - data = data.replaceAll("\\xa0", " "); - mMessage.setText(data); - } else { - matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data); + mContentUri = mIntent.getData(); + if (mContentUri == null) { + Bundle extras = mIntent.getExtras(); + if (extras == null) { + extras = new Bundle(); + } + String data = extras.getString(Apg.EXTRA_TEXT); + if (data != null) { + Matcher matcher = Apg.PGP_MESSAGE.matcher(data); if (matcher.matches()) { data = matcher.group(1); // replace non breakable spaces data = data.replaceAll("\\xa0", " "); mMessage.setText(data); - mDecryptButton.setText(R.string.btn_verify); + } else { + matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data); + if (matcher.matches()) { + data = matcher.group(1); + // replace non breakable spaces + data = data.replaceAll("\\xa0", " "); + mMessage.setText(data); + mDecryptButton.setText(R.string.btn_verify); + } } } } @@ -393,7 +399,9 @@ public class DecryptActivity extends BaseActivity { String error = null; try { InputStream in; - if (mDecryptTarget == Id.target.file) { + if (mContentUri != null) { + in = getContentResolver().openInputStream(mContentUri); + } else if (mDecryptTarget == Id.target.file) { if (mInputFilename.startsWith("file")) { in = new FileInputStream(mInputFilename); } else { @@ -412,7 +420,9 @@ public class DecryptActivity extends BaseActivity { setSecretKeyId(Id.key.symmetric); // look at the file/message again to check whether there's // symmetric encryption data in there - if (mDecryptTarget == Id.target.file) { + if (mContentUri != null) { + in = getContentResolver().openInputStream(mContentUri); + } else if (mDecryptTarget == Id.target.file) { if (mInputFilename.startsWith("file")) { in = new FileInputStream(mInputFilename); } else { @@ -494,22 +504,32 @@ public class DecryptActivity extends BaseActivity { try { PositionAwareInputStream in = null; OutputStream out = null; + String randomString = null; long size = 0; - if (mDecryptTarget == Id.target.message) { + 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")) { - InputStream tmp = getContentResolver().openInputStream(Uri.parse(mInputFilename)); - size = 0; - long n = 0; - byte dummy[] = new byte[0x10000]; - while ((n = tmp.read(dummy)) > 0) { - size += n; - } + size = Apg.getLengthOfStream(getContentResolver().openInputStream(Uri.parse(mInputFilename))); in = new PositionAwareInputStream( getContentResolver().openInputStream(Uri.parse(mInputFilename))); } else { @@ -528,7 +548,10 @@ public class DecryptActivity extends BaseActivity { } out.close(); - if (mDecryptTarget == Id.target.message) { + + if (randomString != null) { + data.putString(Apg.EXTRA_RESULT_URI, "content://" + DataProvider.AUTHORITY + "/data/" + randomString); + } else if (mDecryptTarget == Id.target.message) { data.putString(Apg.EXTRA_DECRYPTED_MESSAGE, new String(((ByteArrayOutputStream) out).toByteArray())); } @@ -570,6 +593,14 @@ public class DecryptActivity extends BaseActivity { } Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show(); + if (mReturnResult) { + Intent intent = new Intent(); + intent.putExtras(data); + setResult(RESULT_OK, intent); + finish(); + return; + } + switch (mDecryptTarget) { case Id.target.message: { String decryptedMessage = data.getString(Apg.EXTRA_DECRYPTED_MESSAGE); @@ -616,13 +647,6 @@ public class DecryptActivity extends BaseActivity { } mSignatureLayout.setVisibility(View.VISIBLE); } - - if (mReturnResult) { - Intent intent = new Intent(); - intent.putExtras(data); - setResult(RESULT_OK, intent); - finish(); - } } @Override diff --git a/src/org/thialfihar/android/apg/provider/DataProvider.java b/src/org/thialfihar/android/apg/provider/DataProvider.java index 0a4bfbad4..9cf083528 100644 --- a/src/org/thialfihar/android/apg/provider/DataProvider.java +++ b/src/org/thialfihar/android/apg/provider/DataProvider.java @@ -16,6 +16,8 @@ package org.thialfihar.android.apg.provider; +import java.io.File; +import java.io.FileNotFoundException; import java.util.HashMap; import org.thialfihar.android.apg.Id; @@ -27,6 +29,7 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; +import android.os.ParcelFileDescriptor; import android.text.TextUtils; public class DataProvider extends ContentProvider { @@ -50,6 +53,8 @@ public class DataProvider extends ContentProvider { private static final int SECRET_KEY_RING_USER_ID = 221; private static final int SECRET_KEY_RING_USER_ID_RANK = 222; + private static final int DATA_STREAM = 301; + private static final String PUBLIC_KEY_RING_CONTENT_DIR_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key_ring"; private static final String PUBLIC_KEY_RING_CONTENT_ITEM_TYPE = @@ -109,6 +114,8 @@ public class DataProvider extends ContentProvider { mUriMatcher.addURI(AUTHORITY, "key_rings/secret", SECRET_KEY_RING); mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*", SECRET_KEY_RING_ID); + + mUriMatcher.addURI(AUTHORITY, "data/*", DATA_STREAM); } @Override @@ -360,4 +367,15 @@ public class DataProvider extends ContentProvider { // not supported return 0; } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + int match = mUriMatcher.match(uri); + if (match != DATA_STREAM) { + throw new FileNotFoundException(); + } + String fileName = uri.getPathSegments().get(1); + File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName); + return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); + } }