diff options
15 files changed, 380 insertions, 186 deletions
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);              } -            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); +            // streams +            in = mDataSource.getInputData(this, true); +            out = mDataDestination.getOutputStream(this); +            if (mEncryptTarget == Id.target.file) {                  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<Integer> keyRingIds = new Vector<Integer>();
                  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;  | 
