From a0263aabadf04869da704a0075f01bfde98f279a Mon Sep 17 00:00:00 2001 From: Zeapo Date: Fri, 1 Aug 2014 00:17:05 +0100 Subject: Better distinction between categories and passwords, multilevel categories --- app/src/main/AndroidManifest.xml | 46 +-- app/src/main/java/com/zeapo/pwdstore/GitClone.java | 2 + .../zeapo/pwdstore/OpenPgpProviderActivity.java | 382 -------------------- .../java/com/zeapo/pwdstore/PasswordAdapter.java | 50 --- .../java/com/zeapo/pwdstore/PasswordFragment.java | 39 ++- .../com/zeapo/pwdstore/PasswordRepository.java | 95 ----- .../java/com/zeapo/pwdstore/PasswordStore.java | 121 ++++--- .../main/java/com/zeapo/pwdstore/PgpHandler.java | 210 ----------- .../java/com/zeapo/pwdstore/UserPreference.java | 2 + .../pwdstore/crypto/OpenPgpProviderActivity.java | 383 +++++++++++++++++++++ .../java/com/zeapo/pwdstore/crypto/PgpHandler.java | 276 +++++++++++++++ .../com/zeapo/pwdstore/utils/PasswordAdapter.java | 63 ++++ .../com/zeapo/pwdstore/utils/PasswordItem.java | 103 ++++++ .../zeapo/pwdstore/utils/PasswordRepository.java | 88 +++++ app/src/main/res/layout/activity_pgp_handler.xml | 62 +++- app/src/main/res/layout/password_row_layout.xml | 22 +- app/src/main/res/menu/pgp_handler.xml | 2 +- app/src/main/res/menu/pwdstore.xml | 5 + app/src/main/res/values/styles.xml | 2 +- 19 files changed, 1125 insertions(+), 828 deletions(-) delete mode 100644 app/src/main/java/com/zeapo/pwdstore/OpenPgpProviderActivity.java delete mode 100644 app/src/main/java/com/zeapo/pwdstore/PasswordAdapter.java delete mode 100644 app/src/main/java/com/zeapo/pwdstore/PasswordRepository.java delete mode 100644 app/src/main/java/com/zeapo/pwdstore/PgpHandler.java create mode 100644 app/src/main/java/com/zeapo/pwdstore/crypto/OpenPgpProviderActivity.java create mode 100644 app/src/main/java/com/zeapo/pwdstore/crypto/PgpHandler.java create mode 100644 app/src/main/java/com/zeapo/pwdstore/utils/PasswordAdapter.java create mode 100644 app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.java create mode 100644 app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.java (limited to 'app/src/main') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index edec0fa1..822ad8cd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,10 +2,6 @@ - - - - + android:label="@string/title_activity_git_clone" + android:parentActivityName=".PasswordStore"> + - - - + + + + + - - - + - - + + + - - - + + + diff --git a/app/src/main/java/com/zeapo/pwdstore/GitClone.java b/app/src/main/java/com/zeapo/pwdstore/GitClone.java index 6166ba2c..de0cbde0 100644 --- a/app/src/main/java/com/zeapo/pwdstore/GitClone.java +++ b/app/src/main/java/com/zeapo/pwdstore/GitClone.java @@ -74,6 +74,8 @@ public class GitClone extends Activity { context = getApplicationContext(); activity = this; + getActionBar().setDisplayHomeAsUpEnabled(true); + // init the spinner for protocols Spinner protcol_spinner = (Spinner) findViewById(R.id.clone_protocol); ArrayAdapter protocol_adapter = ArrayAdapter.createFromResource(this, diff --git a/app/src/main/java/com/zeapo/pwdstore/OpenPgpProviderActivity.java b/app/src/main/java/com/zeapo/pwdstore/OpenPgpProviderActivity.java deleted file mode 100644 index 3d0e09c3..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/OpenPgpProviderActivity.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (C) 2013-2014 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.zeapo.pwdstore; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Intent; -import android.content.IntentSender; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Toast; - -import org.openintents.openpgp.OpenPgpError; -import org.openintents.openpgp.OpenPgpSignatureResult; -import org.openintents.openpgp.util.OpenPgpApi; -import org.openintents.openpgp.util.OpenPgpServiceConnection; -import org.openintents.openpgp.util.OpenPgpUtils; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.util.Arrays; - -public class OpenPgpProviderActivity extends Activity { - private EditText mMessage; - private EditText mCiphertext; - private EditText mEncryptUserIds; - private Button mSign; - private Button mEncrypt; - private Button mSignAndEncrypt; - private Button mDecryptAndVerify; - private EditText mAccount; - private EditText mGetKeyEdit; - private EditText mGetKeyIdsEdit; - private Button mGetKey; - private Button mGetKeyIds; - - private OpenPgpServiceConnection mServiceConnection; - - public static final int REQUEST_CODE_SIGN = 9910; - public static final int REQUEST_CODE_ENCRYPT = 9911; - public static final int REQUEST_CODE_SIGN_AND_ENCRYPT = 9912; - public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913; - public static final int REQUEST_CODE_GET_KEY = 9914; - public static final int REQUEST_CODE_GET_KEY_IDS = 9915; - - public final class Constants { - public static final String TAG = "Keychain"; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.openpgp_provider); - - mMessage = (EditText) findViewById(R.id.crypto_provider_demo_message); - mCiphertext = (EditText) findViewById(R.id.crypto_provider_demo_ciphertext); - mEncryptUserIds = (EditText) findViewById(R.id.crypto_provider_demo_encrypt_user_id); - mSign = (Button) findViewById(R.id.crypto_provider_demo_sign); - mEncrypt = (Button) findViewById(R.id.crypto_provider_demo_encrypt); - mSignAndEncrypt = (Button) findViewById(R.id.crypto_provider_demo_sign_and_encrypt); - mDecryptAndVerify = (Button) findViewById(R.id.crypto_provider_demo_decrypt_and_verify); - mAccount = (EditText) findViewById(R.id.crypto_provider_demo_account); - mGetKeyEdit = (EditText) findViewById(R.id.crypto_provider_demo_get_key_edit); - mGetKeyIdsEdit = (EditText) findViewById(R.id.crypto_provider_demo_get_key_ids_edit); - mGetKey = (Button) findViewById(R.id.crypto_provider_demo_get_key); - mGetKeyIds = (Button) findViewById(R.id.crypto_provider_demo_get_key_ids); - - mSign.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - sign(new Intent()); - } - }); - mEncrypt.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - encrypt(new Intent()); - } - }); - mSignAndEncrypt.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - signAndEncrypt(new Intent()); - } - }); - mDecryptAndVerify.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - decryptAndVerify(new Intent()); - } - }); - mGetKey.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - getKey(new Intent()); - } - }); - mGetKeyIds.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - getKeyIds(new Intent()); - } - }); - - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this); - String providerPackageName = settings.getString("openpgp_provider_list", ""); - if (TextUtils.isEmpty(providerPackageName)) { - Toast.makeText(this, "No OpenPGP Provider selected!", Toast.LENGTH_LONG).show(); - finish(); - } else { - // bind to service - mServiceConnection = new OpenPgpServiceConnection( - OpenPgpProviderActivity.this, providerPackageName); - mServiceConnection.bindToService(); - } - } - - private void handleError(final OpenPgpError error) { - runOnUiThread(new Runnable() { - - @Override - public void run() { - Toast.makeText(OpenPgpProviderActivity.this, - "onError id:" + error.getErrorId() + "\n\n" + error.getMessage(), - Toast.LENGTH_LONG).show(); - Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId()); - Log.e(Constants.TAG, "onError getMessage:" + error.getMessage()); - } - }); - } - - private void showToast(final String message) { - runOnUiThread(new Runnable() { - - @Override - public void run() { - Toast.makeText(OpenPgpProviderActivity.this, - message, - Toast.LENGTH_SHORT).show(); - } - }); - } - - /** - * Takes input from message or ciphertext EditText and turns it into a ByteArrayInputStream - * - * @param ciphertext - * @return - */ - private InputStream getInputstream(boolean ciphertext) { - InputStream is = null; - try { - String inputStr; - if (ciphertext) { - inputStr = mCiphertext.getText().toString(); - } else { - inputStr = mMessage.getText().toString(); - } - is = new ByteArrayInputStream(inputStr.getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - Log.e(Constants.TAG, "UnsupportedEncodingException", e); - } - - return is; - } - - private class MyCallback implements OpenPgpApi.IOpenPgpCallback { - boolean returnToCiphertextField; - ByteArrayOutputStream os; - int requestCode; - - private MyCallback(boolean returnToCiphertextField, ByteArrayOutputStream os, int requestCode) { - this.returnToCiphertextField = returnToCiphertextField; - this.os = os; - this.requestCode = requestCode; - } - - @Override - public void onReturn(Intent result) { - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: { - showToast("RESULT_CODE_SUCCESS"); - - // encrypt/decrypt/sign/verify - if (os != null) { - try { - Log.d(OpenPgpApi.TAG, "result: " + os.toByteArray().length - + " str=" + os.toString("UTF-8")); - - if (returnToCiphertextField) { - mCiphertext.setText(os.toString("UTF-8")); - } else { - mMessage.setText(os.toString("UTF-8")); - } - } catch (UnsupportedEncodingException e) { - Log.e(Constants.TAG, "UnsupportedEncodingException", e); - } - } - - // verify - if (result.hasExtra(OpenPgpApi.RESULT_SIGNATURE)) { - OpenPgpSignatureResult sigResult - = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); - showToast(sigResult.toString()); - } - - // get key ids - if (result.hasExtra(OpenPgpApi.RESULT_KEY_IDS)) { - long[] keyIds = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS); - String out = "keyIds: "; - for (int i = 0; i < keyIds.length; i++) { - out += OpenPgpUtils.convertKeyIdToHex(keyIds[i]) + ", "; - } - - showToast(out); - } - break; - } - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: { - showToast("RESULT_CODE_USER_INTERACTION_REQUIRED"); - - PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); - try { - OpenPgpProviderActivity.this.startIntentSenderForResult(pi.getIntentSender(), - requestCode, null, 0, 0, 0); - } catch (IntentSender.SendIntentException e) { - Log.e(Constants.TAG, "SendIntentException", e); - } - break; - } - case OpenPgpApi.RESULT_CODE_ERROR: { - showToast("RESULT_CODE_ERROR"); - - OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); - handleError(error); - break; - } - } - } - } - - public void sign(Intent data) { - data.setAction(OpenPgpApi.ACTION_SIGN); - data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); - - InputStream is = getInputstream(false); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); - api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN)); - } - - public void encrypt(Intent data) { - data.setAction(OpenPgpApi.ACTION_ENCRYPT); - data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(",")); - data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); - - InputStream is = getInputstream(false); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); - api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_ENCRYPT)); - } - - public void signAndEncrypt(Intent data) { - data.setAction(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT); - data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(",")); - data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); - - InputStream is = getInputstream(false); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); - api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN_AND_ENCRYPT)); - } - - public void decryptAndVerify(Intent data) { - data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); - data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); - - InputStream is = getInputstream(true); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); - api.executeApiAsync(data, is, os, new MyCallback(false, os, REQUEST_CODE_DECRYPT_AND_VERIFY)); - } - - public void getKey(Intent data) { - data.setAction(OpenPgpApi.ACTION_GET_KEY); - data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); - data.putExtra(OpenPgpApi.EXTRA_KEY_ID, Long.decode(mGetKeyEdit.getText().toString())); - - OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); - api.executeApiAsync(data, null, null, new MyCallback(false, null, REQUEST_CODE_GET_KEY)); - } - - public void getKeyIds(Intent data) { - data.setAction(OpenPgpApi.ACTION_GET_KEY_IDS); - data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); - data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mGetKeyIdsEdit.getText().toString().split(",")); - - OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); - api.executeApiAsync(data, null, null, new MyCallback(false, null, REQUEST_CODE_GET_KEY_IDS)); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - Log.d(Constants.TAG, "onActivityResult resultCode: " + resultCode); - - // try again after user interaction - if (resultCode == RESULT_OK) { - /* - * The data originally given to one of the methods above, is again - * returned here to be used when calling the method again after user - * interaction. The Intent now also contains results from the user - * interaction, for example selected key ids. - */ - switch (requestCode) { - case REQUEST_CODE_SIGN: { - sign(data); - break; - } - case REQUEST_CODE_ENCRYPT: { - encrypt(data); - break; - } - case REQUEST_CODE_SIGN_AND_ENCRYPT: { - signAndEncrypt(data); - break; - } - case REQUEST_CODE_DECRYPT_AND_VERIFY: { - decryptAndVerify(data); - break; - } - case REQUEST_CODE_GET_KEY: { - getKey(data); - break; - } - case REQUEST_CODE_GET_KEY_IDS: { - getKeyIds(data); - break; - } - } - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - - if (mServiceConnection != null) { - mServiceConnection.unbindFromService(); - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordAdapter.java b/app/src/main/java/com/zeapo/pwdstore/PasswordAdapter.java deleted file mode 100644 index 172f8756..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.zeapo.pwdstore; - -import android.content.Context; -import android.util.Pair; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; - -import com.zeapo.pwdstore.R; - -import java.util.ArrayList; -import java.util.HashMap; - -public class PasswordAdapter extends ArrayAdapter { - private final Context context; - private final ArrayList values; - - static class ViewHolder { - public TextView text; - } - - public PasswordAdapter(Context context, ArrayList values) { - super(context, R.layout.password_row_layout, values); - this.context = context; - this.values = values; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View rowView = convertView; - - // reuse for performance, holder pattern! - if (rowView == null) { - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - rowView = inflater.inflate(R.layout.password_row_layout, null); - - ViewHolder viewHolder = new ViewHolder(); - viewHolder.text = (TextView) rowView.findViewById(R.id.label); - rowView.setTag(viewHolder); - } - - ViewHolder holder = (ViewHolder) rowView.getTag(); - holder.text.setText( values.get(position) ); - - return rowView; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java index 9023320a..261729eb 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java @@ -1,7 +1,6 @@ package com.zeapo.pwdstore; import android.app.Activity; -import android.content.Context; import android.os.Bundle; import android.app.Fragment; import android.view.LayoutInflater; @@ -11,9 +10,14 @@ import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListAdapter; -import android.widget.TextView; +import android.widget.ListView; -import java.util.ArrayList; +import com.zeapo.pwdstore.utils.PasswordAdapter; +import com.zeapo.pwdstore.utils.PasswordItem; +import com.zeapo.pwdstore.utils.PasswordRepository; + +import java.io.File; +import java.util.List; /** * A fragment representing a list of Items. @@ -29,13 +33,13 @@ public class PasswordFragment extends Fragment implements AbsListView.OnItemClic /** * The fragment's ListView/GridView. */ - private AbsListView mListView; + private ListView mListView; /** * The Adapter which will be used to populate the ListView/GridView with * Views. */ - private ListAdapter mAdapter; + private PasswordAdapter mAdapter; /** * Mandatory empty constructor for the fragment manager to instantiate the @@ -47,8 +51,8 @@ public class PasswordFragment extends Fragment implements AbsListView.OnItemClic public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - System.out.println(PasswordRepository.getFilesList().size()); - mAdapter = new PasswordAdapter(getActivity(), PasswordRepository.getFilesList()); + String path = getArguments().getString("Path"); + mAdapter = new PasswordAdapter(getActivity(), PasswordRepository.getPasswords(new File(path))); } @Override @@ -57,11 +61,12 @@ public class PasswordFragment extends Fragment implements AbsListView.OnItemClic View view = inflater.inflate(R.layout.fragment_password, container, false); // Set the adapter - mListView = (AbsListView) view.findViewById(R.id.pass_list); + mListView = (ListView) view.findViewById(R.id.pass_list); ((AdapterView) mListView).setAdapter(mAdapter); // Set OnItemClickListener so we can be notified on item clicks mListView.setOnItemClickListener(this); + mListView.setSelectionFromTop(getArguments().getInt("Position"), 0); return view; } @@ -83,18 +88,30 @@ public class PasswordFragment extends Fragment implements AbsListView.OnItemClic mListener = null; } + @Override + public void onPause() { + super.onPause(); + mListener.savePosition(mListView.getFirstVisiblePosition()); + + } + @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (null != mListener) { // Notify the active callbacks interface (the activity, if the // fragment is attached to one) that an item has been selected. - mListener.onFragmentInteraction((String) mAdapter.getItem(position)); + mListener.onFragmentInteraction(mAdapter.getItem(position)); } } public interface OnFragmentInteractionListener { - // TODO: Update argument type and name - public void onFragmentInteraction(String id); + public void onFragmentInteraction(PasswordItem item); + public void savePosition(Integer position); + } + + public void updateAdapter() { + mAdapter.clear(); + mAdapter.addAll(PasswordRepository.getPasswords()); } } diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordRepository.java b/app/src/main/java/com/zeapo/pwdstore/PasswordRepository.java deleted file mode 100644 index 30181e69..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordRepository.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.zeapo.pwdstore; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.filefilter.TrueFileFilter; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.internal.storage.file.FileRepository; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.lib.RepositoryBuilder; -import org.eclipse.jgit.storage.file.FileRepositoryBuilder; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; - -public class PasswordRepository { - - private static Repository repository; - private static LinkedHashMap> mainPasswordMap; - private static ArrayList mainListOfFiles; - - protected PasswordRepository(){ } - - public static Repository getRepository(File localDir) { - if (repository == null) { - FileRepositoryBuilder builder = new FileRepositoryBuilder(); - try { - repository = builder.setGitDir(localDir) - .readEnvironment() - .findGitDir() - .build(); - - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - return repository; - } - - public static void closeRepository() { - repository.close(); - } - - public static ArrayList getFilesList(){ - return getFilesList(repository.getWorkTree()); - } - - public static LinkedHashMap> getPasswords() { - return getPasswords(repository.getWorkTree()); - } - - public static File getFile(String name) { - return new File(repository.getWorkTree() + "/" + name); - } - - public static ArrayList getFilesList(File path){ - if (!path.exists()) return new ArrayList(); - - List files = (List) FileUtils.listFiles(path, new String[] {"gpg"}, true); - ArrayList filePaths = new ArrayList(); - for (File file : files) { - filePaths.add(file.getAbsolutePath().replace(repository.getWorkTree().getAbsolutePath() + "/", "")); - } - return filePaths; - } - - public static LinkedHashMap> getPasswords(File path) { - //We need to recover the passwords then parse the files - ArrayList passList = getFilesList(path); - - if (passList.size() == 0) return new LinkedHashMap>(); - - LinkedHashMap> passMap = new LinkedHashMap>(); - passMap.put("Without Category", new ArrayList()); - - for (String file : passList) { - String[] parts = file.split("/"); - if (parts.length == 1) { - passMap.get("Without Category").add(parts[0]); - } else { - if (passMap.containsKey(parts[0])) { - passMap.get(parts[0]).add(parts[1]); - } else { - ArrayList tempList = new ArrayList(); - tempList.add(parts[1]); - passMap.put(parts[0], tempList); - } - } - } - return passMap; - } -} diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java index 031f1063..6a601a3b 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java @@ -11,34 +11,41 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import com.zeapo.pwdstore.crypto.PgpHandler; +import com.zeapo.pwdstore.utils.PasswordItem; +import com.zeapo.pwdstore.utils.PasswordRepository; + import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.FileFilterUtils; import org.eclipse.jgit.lib.Repository; -import org.openintents.openpgp.util.OpenPgpListPreference; +import org.eclipse.jgit.transport.CredentialItem; import java.io.File; import java.io.FileFilter; import java.io.IOException; -import java.io.InputStream; +import java.util.Stack; public class PasswordStore extends Activity implements ToCloneOrNot.OnFragmentInteractionListener, PasswordFragment.OnFragmentInteractionListener { private int listState = 0; + private Stack scrollPositions; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pwdstore); + scrollPositions = new Stack(); } @Override public void onResume(){ super.onResume(); + + // create the repository static variable in PasswordRepository + PasswordRepository.getRepository(new File(getFilesDir() + "/store/.git")); + // re-check that there was no change with the repository state checkLocalRepository(); - Repository repository = PasswordRepository.getRepository(new File(getFilesDir() + "/store/.git")); - PasswordRepository.getFilesList(); - PasswordRepository.getPasswords(); } @Override @@ -54,16 +61,29 @@ public class PasswordStore extends Activity implements ToCloneOrNot.OnFragmentI // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); - if (id == R.id.user_pref) { - try { - Intent intent = new Intent(this, UserPreference.class); - startActivity(intent); - } catch (Exception e) { - System.out.println("Exception caught :("); - e.printStackTrace(); - } - return true; + switch (id) { + case R.id.user_pref: + try { + Intent intent = new Intent(this, UserPreference.class); + startActivity(intent); + } catch (Exception e) { + System.out.println("Exception caught :("); + e.printStackTrace(); + } + return true; + + case R.id.referesh: + PasswordFragment plist; + if (null != + (plist = (PasswordFragment) getFragmentManager().findFragmentByTag("PasswordsList"))) { + plist.updateAdapter(); + } + return true; + + default: + break; } + return super.onOptionsItemSelected(item); } @@ -78,62 +98,83 @@ public class PasswordStore extends Activity implements ToCloneOrNot.OnFragmentI } private void checkLocalRepository() { +// final File localDir = new File(getFilesDir() + "/store/.git"); + checkLocalRepository(PasswordRepository.getWorkTree()); + } + + private void checkLocalRepository(File localDir) { int status = 0; - final File localDir = new File(getFilesDir() + "/store/.git"); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); if (localDir.exists()) { - File[] folders = localDir.listFiles((FileFilter) FileFilterUtils.directoryFileFilter()); + File[] folders = localDir.listFiles(); status = folders.length; } // either the repo is empty or it was not correctly cloned switch (status) { case 0: + if(!localDir.equals(PasswordRepository.getWorkTree())) + break; + ToCloneOrNot cloneFrag = new ToCloneOrNot(); fragmentTransaction.replace(R.id.main_layout, cloneFrag, "ToCloneOrNot"); fragmentTransaction.commit(); break; - case 1: - // empty - break; default: PasswordFragment passFrag = new PasswordFragment(); + Bundle args = new Bundle(); + args.putString("Path", localDir.getAbsolutePath()); - if (fragmentManager.findFragmentByTag("ToCloneOrNot") == null) { - fragmentTransaction.add(R.id.main_layout, passFrag); - } else { - fragmentTransaction.replace(R.id.main_layout, passFrag); - } + if (!scrollPositions.isEmpty()) + args.putInt("Position", scrollPositions.pop()); + else + args.putInt("Position", 0); + + passFrag.setArguments(args); + + if (fragmentManager.findFragmentByTag("PasswordsList") != null) + fragmentTransaction.addToBackStack("passlist"); + + fragmentTransaction.replace(R.id.main_layout, passFrag, "PasswordsList"); fragmentTransaction.commit(); } } - /* If an item is clicked in the list of passwords, this will be triggered */ + /** Stack the positions the different fragments were at */ @Override - public void onFragmentInteraction(String id) { - + public void savePosition(Integer position) { + this.scrollPositions.push(position); + } - try { - byte[] data = new byte[0]; + /* If an item is clicked in the list of passwords, this will be triggered */ + @Override + public void onFragmentInteraction(PasswordItem item) { + if (item.getType() == PasswordItem.TYPE_CATEGORY) { + checkLocalRepository(item.getFile()); + } else { try { - data = FileUtils.readFileToByteArray(PasswordRepository.getFile(id)); + byte[] data = new byte[0]; + try { + data = FileUtils.readFileToByteArray(PasswordRepository.getFile(item.getName())); + + Intent intent = new Intent(this, PgpHandler.class); + intent.putExtra("PGP-ID", FileUtils.readFileToString(PasswordRepository.getFile("/.gpg-id"))); + intent.putExtra("NAME", item.getName()); + intent.putExtra("FILE_PATH", PasswordRepository.getFile(item.getName()).getAbsolutePath()); + startActivity(intent); + + } catch (IOException e) { + e.printStackTrace(); + } - Intent intent = new Intent(this, PgpHandler.class); - intent.putExtra("FILE_CONTENT", data); - intent.putExtra("FILE_PATH", PasswordRepository.getFile(id).getAbsolutePath()); - startActivity(intent); - } catch (IOException e) { + } catch (Exception e) { +// TODO handle problems e.printStackTrace(); } - - - } catch (Exception e) { -// TODO handle problems - e.printStackTrace(); } } diff --git a/app/src/main/java/com/zeapo/pwdstore/PgpHandler.java b/app/src/main/java/com/zeapo/pwdstore/PgpHandler.java deleted file mode 100644 index c97e0156..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/PgpHandler.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.zeapo.pwdstore; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Intent; -import android.content.IntentSender; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.text.TextUtils; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.TextView; -import android.widget.Toast; - -import com.zeapo.pwdstore.R; - -import org.apache.commons.io.FileUtils; -import org.openintents.openpgp.OpenPgpError; -import org.openintents.openpgp.OpenPgpSignatureResult; -import org.openintents.openpgp.util.OpenPgpApi; -import org.openintents.openpgp.util.OpenPgpServiceConnection; -import org.openintents.openpgp.util.OpenPgpUtils; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; - -public class PgpHandler extends Activity { - - - private OpenPgpServiceConnection mServiceConnection; - - public static final int REQUEST_CODE_SIGN = 9910; - public static final int REQUEST_CODE_ENCRYPT = 9911; - public static final int REQUEST_CODE_SIGN_AND_ENCRYPT = 9912; - public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913; - public static final int REQUEST_CODE_GET_KEY = 9914; - public static final int REQUEST_CODE_GET_KEY_IDS = 9915; - - public final class Constants { - public static final String TAG = "Keychain"; - } - - private String filePath; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_pgp_handler); - - // some persistance - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this); - String providerPackageName = settings.getString("openpgp_provider_list", ""); - - System.out.println(getIntent().getExtras().getString("FILE_PATH")); - Bundle extra = getIntent().getExtras(); - ((TextView) findViewById(R.id.hhh)).setText(extra.getString("FILE_PATH")); - - if (TextUtils.isEmpty(providerPackageName)) { - Toast.makeText(this, "No OpenPGP Provider selected!", Toast.LENGTH_LONG).show(); - finish(); - } else { - // bind to service - mServiceConnection = new OpenPgpServiceConnection( - PgpHandler.this, providerPackageName); - mServiceConnection.bindToService(); - } - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.pgp_handler, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - if (id == R.id.action_settings) { - return true; - } - return super.onOptionsItemSelected(item); - } - - - private void handleError(final OpenPgpError error) { - runOnUiThread(new Runnable() { - - @Override - public void run() { - Toast.makeText(PgpHandler.this, - "onError id:" + error.getErrorId() + "\n\n" + error.getMessage(), - Toast.LENGTH_LONG).show(); - Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId()); - Log.e(Constants.TAG, "onError getMessage:" + error.getMessage()); - } - }); - } - - private void showToast(final String message) { - runOnUiThread(new Runnable() { - - @Override - public void run() { - Toast.makeText(PgpHandler.this, - message, - Toast.LENGTH_SHORT).show(); - } - }); - } - - private class MyCallback implements OpenPgpApi.IOpenPgpCallback { - boolean returnToCiphertextField; - ByteArrayOutputStream os; - int requestCode; - - private MyCallback(boolean returnToCiphertextField, ByteArrayOutputStream os, int requestCode) { - this.returnToCiphertextField = returnToCiphertextField; - this.os = os; - this.requestCode = requestCode; - } - - @Override - public void onReturn(Intent result) { - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: { - showToast("RESULT_CODE_SUCCESS"); - - // encrypt/decrypt/sign/verify - if (os != null) { - try { - Log.d(OpenPgpApi.TAG, "result: " + os.toByteArray().length - + " str=" + os.toString("UTF-8")); - } catch (UnsupportedEncodingException e) { - Log.e(Constants.TAG, "UnsupportedEncodingException", e); - } - } - - // verify - if (result.hasExtra(OpenPgpApi.RESULT_SIGNATURE)) { - OpenPgpSignatureResult sigResult - = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); - showToast(sigResult.toString()); - } - - // get key ids - if (result.hasExtra(OpenPgpApi.RESULT_KEY_IDS)) { - long[] keyIds = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS); - String out = "keyIds: "; - for (int i = 0; i < keyIds.length; i++) { - out += OpenPgpUtils.convertKeyIdToHex(keyIds[i]) + ", "; - } - - showToast(out); - } - break; - } - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: { - showToast("RESULT_CODE_USER_INTERACTION_REQUIRED"); - - PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); - try { - PgpHandler.this.startIntentSenderForResult(pi.getIntentSender(), - requestCode, null, 0, 0, 0); - } catch (IntentSender.SendIntentException e) { - Log.e(Constants.TAG, "SendIntentException", e); - } - break; - } - case OpenPgpApi.RESULT_CODE_ERROR: { - showToast("RESULT_CODE_ERROR"); - - OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); - handleError(error); - break; - } - } - } - } - - public void getKey(View view) { - decryptAndVerify(new Intent()); - } - - public void decryptAndVerify(Intent data) { - data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); - data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, "Mohamed Zenadi"); - - try { - InputStream is = FileUtils.openInputStream(new File(getIntent().getExtras().getString("FILE_PATH"))); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); - api.executeApiAsync(data, is, os, new MyCallback(false, os, REQUEST_CODE_DECRYPT_AND_VERIFY)); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/app/src/main/java/com/zeapo/pwdstore/UserPreference.java b/app/src/main/java/com/zeapo/pwdstore/UserPreference.java index cc6c1495..a0c5d49c 100644 --- a/app/src/main/java/com/zeapo/pwdstore/UserPreference.java +++ b/app/src/main/java/com/zeapo/pwdstore/UserPreference.java @@ -9,5 +9,7 @@ public class UserPreference extends PreferenceActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preference); + + getActionBar().setDisplayHomeAsUpEnabled(true); } } diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/OpenPgpProviderActivity.java b/app/src/main/java/com/zeapo/pwdstore/crypto/OpenPgpProviderActivity.java new file mode 100644 index 00000000..8bc4b9d3 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/crypto/OpenPgpProviderActivity.java @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2013-2014 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.zeapo.pwdstore.crypto; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.IntentSender; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.zeapo.pwdstore.R; + +import org.openintents.openpgp.OpenPgpError; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.openintents.openpgp.util.OpenPgpApi; +import org.openintents.openpgp.util.OpenPgpServiceConnection; +import org.openintents.openpgp.util.OpenPgpUtils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +public class OpenPgpProviderActivity extends Activity { + private EditText mMessage; + private EditText mCiphertext; + private EditText mEncryptUserIds; + private Button mSign; + private Button mEncrypt; + private Button mSignAndEncrypt; + private Button mDecryptAndVerify; + private EditText mAccount; + private EditText mGetKeyEdit; + private EditText mGetKeyIdsEdit; + private Button mGetKey; + private Button mGetKeyIds; + + private OpenPgpServiceConnection mServiceConnection; + + public static final int REQUEST_CODE_SIGN = 9910; + public static final int REQUEST_CODE_ENCRYPT = 9911; + public static final int REQUEST_CODE_SIGN_AND_ENCRYPT = 9912; + public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913; + public static final int REQUEST_CODE_GET_KEY = 9914; + public static final int REQUEST_CODE_GET_KEY_IDS = 9915; + + public final class Constants { + public static final String TAG = "Keychain"; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.openpgp_provider); + + mMessage = (EditText) findViewById(R.id.crypto_provider_demo_message); + mCiphertext = (EditText) findViewById(R.id.crypto_provider_demo_ciphertext); + mEncryptUserIds = (EditText) findViewById(R.id.crypto_provider_demo_encrypt_user_id); + mSign = (Button) findViewById(R.id.crypto_provider_demo_sign); + mEncrypt = (Button) findViewById(R.id.crypto_provider_demo_encrypt); + mSignAndEncrypt = (Button) findViewById(R.id.crypto_provider_demo_sign_and_encrypt); + mDecryptAndVerify = (Button) findViewById(R.id.crypto_provider_demo_decrypt_and_verify); + mAccount = (EditText) findViewById(R.id.crypto_provider_demo_account); + mGetKeyEdit = (EditText) findViewById(R.id.crypto_provider_demo_get_key_edit); + mGetKeyIdsEdit = (EditText) findViewById(R.id.crypto_provider_demo_get_key_ids_edit); + mGetKey = (Button) findViewById(R.id.crypto_provider_demo_get_key); + mGetKeyIds = (Button) findViewById(R.id.crypto_provider_demo_get_key_ids); + + mSign.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + sign(new Intent()); + } + }); + mEncrypt.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + encrypt(new Intent()); + } + }); + mSignAndEncrypt.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + signAndEncrypt(new Intent()); + } + }); + mDecryptAndVerify.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + decryptAndVerify(new Intent()); + } + }); + mGetKey.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + getKey(new Intent()); + } + }); + mGetKeyIds.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + getKeyIds(new Intent()); + } + }); + + SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this); + String providerPackageName = settings.getString("openpgp_provider_list", ""); + if (TextUtils.isEmpty(providerPackageName)) { + Toast.makeText(this, "No OpenPGP Provider selected!", Toast.LENGTH_LONG).show(); + finish(); + } else { + // bind to service + mServiceConnection = new OpenPgpServiceConnection( + OpenPgpProviderActivity.this, providerPackageName); + mServiceConnection.bindToService(); + } + } + + private void handleError(final OpenPgpError error) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + Toast.makeText(OpenPgpProviderActivity.this, + "onError id:" + error.getErrorId() + "\n\n" + error.getMessage(), + Toast.LENGTH_LONG).show(); + Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId()); + Log.e(Constants.TAG, "onError getMessage:" + error.getMessage()); + } + }); + } + + private void showToast(final String message) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + Toast.makeText(OpenPgpProviderActivity.this, + message, + Toast.LENGTH_SHORT).show(); + } + }); + } + + /** + * Takes input from message or ciphertext EditText and turns it into a ByteArrayInputStream + * + * @param ciphertext + * @return + */ + private InputStream getInputstream(boolean ciphertext) { + InputStream is = null; + try { + String inputStr; + if (ciphertext) { + inputStr = mCiphertext.getText().toString(); + } else { + inputStr = mMessage.getText().toString(); + } + is = new ByteArrayInputStream(inputStr.getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + Log.e(Constants.TAG, "UnsupportedEncodingException", e); + } + + return is; + } + + private class MyCallback implements OpenPgpApi.IOpenPgpCallback { + boolean returnToCiphertextField; + ByteArrayOutputStream os; + int requestCode; + + private MyCallback(boolean returnToCiphertextField, ByteArrayOutputStream os, int requestCode) { + this.returnToCiphertextField = returnToCiphertextField; + this.os = os; + this.requestCode = requestCode; + } + + @Override + public void onReturn(Intent result) { + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: { + showToast("RESULT_CODE_SUCCESS"); + + // encrypt/decrypt/sign/verify + if (os != null) { + try { + Log.d(OpenPgpApi.TAG, "result: " + os.toByteArray().length + + " str=" + os.toString("UTF-8")); + + if (returnToCiphertextField) { + mCiphertext.setText(os.toString("UTF-8")); + } else { + mMessage.setText(os.toString("UTF-8")); + } + } catch (UnsupportedEncodingException e) { + Log.e(Constants.TAG, "UnsupportedEncodingException", e); + } + } + + // verify + if (result.hasExtra(OpenPgpApi.RESULT_SIGNATURE)) { + OpenPgpSignatureResult sigResult + = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); + showToast(sigResult.toString()); + } + + // get key ids + if (result.hasExtra(OpenPgpApi.RESULT_KEY_IDS)) { + long[] keyIds = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS); + String out = "keyIds: "; + for (int i = 0; i < keyIds.length; i++) { + out += OpenPgpUtils.convertKeyIdToHex(keyIds[i]) + ", "; + } + + showToast(out); + } + break; + } + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: { + showToast("RESULT_CODE_USER_INTERACTION_REQUIRED"); + + PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); + try { + OpenPgpProviderActivity.this.startIntentSenderForResult(pi.getIntentSender(), + requestCode, null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + Log.e(Constants.TAG, "SendIntentException", e); + } + break; + } + case OpenPgpApi.RESULT_CODE_ERROR: { + showToast("RESULT_CODE_ERROR"); + + OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); + handleError(error); + break; + } + } + } + } + + public void sign(Intent data) { + data.setAction(OpenPgpApi.ACTION_SIGN); + data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); + + InputStream is = getInputstream(false); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); + api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN)); + } + + public void encrypt(Intent data) { + data.setAction(OpenPgpApi.ACTION_ENCRYPT); + data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(",")); + data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); + + InputStream is = getInputstream(false); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); + api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_ENCRYPT)); + } + + public void signAndEncrypt(Intent data) { + data.setAction(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT); + data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(",")); + data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); + + InputStream is = getInputstream(false); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); + api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN_AND_ENCRYPT)); + } + + public void decryptAndVerify(Intent data) { + data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); + data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); + + InputStream is = getInputstream(true); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); + api.executeApiAsync(data, is, os, new MyCallback(false, os, REQUEST_CODE_DECRYPT_AND_VERIFY)); + } + + public void getKey(Intent data) { + data.setAction(OpenPgpApi.ACTION_GET_KEY); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); + data.putExtra(OpenPgpApi.EXTRA_KEY_ID, Long.decode(mGetKeyEdit.getText().toString())); + + OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); + api.executeApiAsync(data, null, null, new MyCallback(false, null, REQUEST_CODE_GET_KEY)); + } + + public void getKeyIds(Intent data) { + data.setAction(OpenPgpApi.ACTION_GET_KEY_IDS); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); + data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mGetKeyIdsEdit.getText().toString().split(",")); + + OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); + api.executeApiAsync(data, null, null, new MyCallback(false, null, REQUEST_CODE_GET_KEY_IDS)); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Log.d(Constants.TAG, "onActivityResult resultCode: " + resultCode); + + // try again after user interaction + if (resultCode == RESULT_OK) { + /* + * The data originally given to one of the methods above, is again + * returned here to be used when calling the method again after user + * interaction. The Intent now also contains results from the user + * interaction, for example selected key ids. + */ + switch (requestCode) { + case REQUEST_CODE_SIGN: { + sign(data); + break; + } + case REQUEST_CODE_ENCRYPT: { + encrypt(data); + break; + } + case REQUEST_CODE_SIGN_AND_ENCRYPT: { + signAndEncrypt(data); + break; + } + case REQUEST_CODE_DECRYPT_AND_VERIFY: { + decryptAndVerify(data); + break; + } + case REQUEST_CODE_GET_KEY: { + getKey(data); + break; + } + case REQUEST_CODE_GET_KEY_IDS: { + getKeyIds(data); + break; + } + } + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + + if (mServiceConnection != null) { + mServiceConnection.unbindFromService(); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpHandler.java b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpHandler.java new file mode 100644 index 00000000..0132c4cd --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpHandler.java @@ -0,0 +1,276 @@ +package com.zeapo.pwdstore.crypto; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.IntentSender; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.zeapo.pwdstore.R; + +import org.apache.commons.io.FileUtils; +import org.openintents.openpgp.OpenPgpError; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.openintents.openpgp.util.OpenPgpApi; +import org.openintents.openpgp.util.OpenPgpServiceConnection; +import org.openintents.openpgp.util.OpenPgpUtils; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +public class PgpHandler extends Activity { + + + private OpenPgpServiceConnection mServiceConnection; + + public static final int REQUEST_CODE_SIGN = 9910; + public static final int REQUEST_CODE_ENCRYPT = 9911; + public static final int REQUEST_CODE_SIGN_AND_ENCRYPT = 9912; + public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913; + public static final int REQUEST_CODE_GET_KEY = 9914; + public static final int REQUEST_CODE_GET_KEY_IDS = 9915; + + public final class Constants { + public static final String TAG = "Keychain"; + } + + private String filePath; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_pgp_handler); + + // Setup action buttons + getActionBar().setDisplayHomeAsUpEnabled(true); + getActionBar().setHomeButtonEnabled(true); + + Bundle extra = getIntent().getExtras(); + ((TextView) findViewById(R.id.crypto_handler_name)).setText(extra.getString("NAME")); + + // some persistance + SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this); + String providerPackageName = settings.getString("openpgp_provider_list", ""); + String accountName = settings.getString("openpgp_account_name", ""); + + if (accountName.isEmpty()) { + ((TextView) findViewById(R.id.crypto_account_name)).setText("No account selected"); + } + + if (TextUtils.isEmpty(providerPackageName)) { + Toast.makeText(this, "No OpenPGP Provider selected!", Toast.LENGTH_LONG).show(); + finish(); + } else { + // bind to service + mServiceConnection = new OpenPgpServiceConnection( + PgpHandler.this, providerPackageName); + mServiceConnection.bindToService(); + } + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.pgp_handler, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + if (id == R.id.action_settings) { + return true; + } + return super.onOptionsItemSelected(item); + } + + public void decrypt(View view) { + decryptAndVerify(new Intent()); +// getKeyIds(new Intent()); + } + + private void handleError(final OpenPgpError error) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + Toast.makeText(PgpHandler.this, + "onError id:" + error.getErrorId() + "\n\n" + error.getMessage(), + Toast.LENGTH_LONG).show(); + Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId()); + Log.e(Constants.TAG, "onError getMessage:" + error.getMessage()); + } + }); + } + + private void showToast(final String message) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + Toast.makeText(PgpHandler.this, + message, + Toast.LENGTH_SHORT).show(); + } + }); + } + + private class MyCallback implements OpenPgpApi.IOpenPgpCallback { + boolean returnToCiphertextField; + ByteArrayOutputStream os; + int requestCode; + + private MyCallback(boolean returnToCiphertextField, ByteArrayOutputStream os, int requestCode) { + this.returnToCiphertextField = returnToCiphertextField; + this.os = os; + this.requestCode = requestCode; + } + + @Override + public void onReturn(Intent result) { + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: { + showToast("RESULT_CODE_SUCCESS"); + + // encrypt/decrypt/sign/verify + if (os != null) { + try { + Log.d(OpenPgpApi.TAG, "result: " + os.toByteArray().length + + " str=" + os.toString("UTF-8")); + showToast(os.toString("UTF-8")); + if (returnToCiphertextField) { +// mCiphertext.setText(os.toString("UTF-8")); + } else { +// mMessage.setText(os.toString("UTF-8")); + } + } catch (UnsupportedEncodingException e) { + Log.e(Constants.TAG, "UnsupportedEncodingException", e); + } + } + + // verify + if (result.hasExtra(OpenPgpApi.RESULT_SIGNATURE)) { + OpenPgpSignatureResult sigResult + = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); + showToast(sigResult.toString()); + } + + // get key ids + if (result.hasExtra(OpenPgpApi.RESULT_KEY_IDS)) { + long[] keyIds = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS); + String out = "keyIds: "; + for (int i = 0; i < keyIds.length; i++) { + out += OpenPgpUtils.convertKeyIdToHex(keyIds[i]) + ", "; + } + + showToast(out); + } + break; + } + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: { + showToast("RESULT_CODE_USER_INTERACTION_REQUIRED"); + + PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); + try { + PgpHandler.this.startIntentSenderForResult(pi.getIntentSender(), + requestCode, null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + Log.e(Constants.TAG, "SendIntentException", e); + } + break; + } + case OpenPgpApi.RESULT_CODE_ERROR: { + showToast("RESULT_CODE_ERROR"); + + OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); + handleError(error); + break; + } + } + } + } + + + public void decryptAndVerify(Intent data) { + data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); + data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + + try { + InputStream is = FileUtils.openInputStream(new File(getIntent().getExtras().getString("FILE_PATH"))); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); + api.executeApiAsync(data, is, os, new MyCallback(false, os, REQUEST_CODE_DECRYPT_AND_VERIFY)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public void getKeyIds(Intent data) { + data.setAction(OpenPgpApi.ACTION_GET_KEY_IDS); + data.putExtra(OpenPgpApi.EXTRA_USER_IDS, new String[]{getIntent().getExtras().getString("PGP-ID")}); + + OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService()); + api.executeApiAsync(data, null, null, new MyCallback(false, null, REQUEST_CODE_GET_KEY_IDS)); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Log.d(Constants.TAG, "onActivityResult resultCode: " + resultCode); + + // try again after user interaction + if (resultCode == RESULT_OK) { + /* + * The data originally given to one of the methods above, is again + * returned here to be used when calling the method again after user + * interaction. The Intent now also contains results from the user + * interaction, for example selected key ids. + */ + switch (requestCode) { +// case REQUEST_CODE_SIGN: { +// sign(data); +// break; +// } +// case REQUEST_CODE_ENCRYPT: { +// encrypt(data); +// break; +// } +// case REQUEST_CODE_SIGN_AND_ENCRYPT: { +// signAndEncrypt(data); +// break; +// } + case REQUEST_CODE_DECRYPT_AND_VERIFY: { + decryptAndVerify(data); + break; + } +// case REQUEST_CODE_GET_KEY: { +// getKey(data); +// break; +// } + case REQUEST_CODE_GET_KEY_IDS: { + getKeyIds(data); + break; + } + } + } + } + +} diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordAdapter.java b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordAdapter.java new file mode 100644 index 00000000..b0cdb5a5 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordAdapter.java @@ -0,0 +1,63 @@ +package com.zeapo.pwdstore.utils; + +import android.content.Context; +import android.graphics.Typeface; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import com.zeapo.pwdstore.R; + +import java.util.ArrayList; + +public class PasswordAdapter extends ArrayAdapter { + private final Context context; + private final ArrayList values; + + static class ViewHolder { + public TextView name; + public TextView type; + } + + public PasswordAdapter(Context context, ArrayList values) { + super(context, R.layout.password_row_layout, values); + this.context = context; + this.values = values; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View rowView = convertView; + PasswordItem pass = values.get(position); + + // reuse for performance, holder pattern! + if (rowView == null) { + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + rowView = inflater.inflate(R.layout.password_row_layout, null); + + ViewHolder viewHolder = new ViewHolder(); + viewHolder.name = (TextView) rowView.findViewById(R.id.label); + viewHolder.type = (TextView) rowView.findViewById(R.id.type); + rowView.setTag(viewHolder); + } + + ViewHolder holder = (ViewHolder) rowView.getTag(); + + holder.name.setText(pass.toString()); + + if (pass.getType() == PasswordItem.TYPE_CATEGORY) { + holder.name.setTextColor(this.context.getResources().getColor(android.R.color.holo_blue_dark)); + holder.name.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); + holder.type.setText("Category: "); + } else { + holder.type.setText("Password: "); + holder.name.setTextColor(this.context.getResources().getColor(android.R.color.holo_orange_dark)); + holder.name.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)); + } + + return rowView; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.java b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.java new file mode 100644 index 00000000..4403d16b --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.java @@ -0,0 +1,103 @@ +package com.zeapo.pwdstore.utils; + +import java.io.File; + +public class PasswordItem implements Comparable{ + + public final static char TYPE_CATEGORY = 'c'; + public final static char TYPE_PASSWORD = 'p'; + + private char type; + private String name; + private PasswordItem parent; + private File file; + + /** Create a password item + * + * Make it protected so that we use a builder + * @param name + * @param parent + * @param type + */ + protected PasswordItem(String name, PasswordItem parent, char type, File file) { + this.name = name; + this.parent = parent; + this.type = type; + this.file = file; + } + + /** Create a new Category item + * + * @param name + * @param parent + * @return + */ + public static PasswordItem newCategory(String name, File file, PasswordItem parent) { + return new PasswordItem(name, parent, TYPE_CATEGORY, file); + } + + /** Create a new parentless category item + * + * @param name + * @return + */ + public static PasswordItem newCategory(String name, File file) { + return new PasswordItem(name, null, TYPE_CATEGORY, file); + } + + /** Create a new password item + * + * @param name + * @param parent + * @return + */ + public static PasswordItem newPassword(String name, File file, PasswordItem parent) { + return new PasswordItem(name, parent, TYPE_PASSWORD, file); + } + + /** Create a new parentless password item + * + * @param name + * @return + */ + public static PasswordItem newPassword(String name, File file) { + return new PasswordItem(name, null, TYPE_PASSWORD, file); + } + + public char getType(){ + return this.type; + } + + public String getName(){ + return this.name; + } + + public PasswordItem getParent() { + return this.parent; + } + + public File getFile() { + return this.file; + } + + @Override + public String toString(){ + return this.getName().replace(".gpg", ""); + } + + @Override + public boolean equals(Object o){ + PasswordItem other = (PasswordItem) o; + // Makes it possible to have a category and a password with the same name + return (other.getType() + other.getName()) + .equals(this.getType() + this.getName()); + } + + @Override + public int compareTo(Object o) { + PasswordItem other = (PasswordItem) o; + // Appending the type will make the sort type dependent + return (this.getType() + this.getName()) + .compareTo(other.getType() + other.getName()); + } +} diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.java b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.java new file mode 100644 index 00000000..5f42b956 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.java @@ -0,0 +1,88 @@ +package com.zeapo.pwdstore.utils; + +import org.apache.commons.io.FileUtils; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static java.util.Collections.sort; + +public class PasswordRepository { + + private static Repository repository; + + protected PasswordRepository(){ } + + public static Repository getRepository(File localDir) { + if (repository == null) { + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + try { + repository = builder.setGitDir(localDir) + .readEnvironment() + .findGitDir() + .build(); + + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + return repository; + } + + public static void closeRepository() { + repository.close(); + } + + public static ArrayList getFilesList(){ + return getFilesList(repository.getWorkTree()); + } + + public static ArrayList getPasswords() { + return getPasswords(repository.getWorkTree()); + } + + public static File getWorkTree() { + return repository.getWorkTree(); + } + + public static File getFile(String name) { + return new File(repository.getWorkTree() + "/" + name); + } + + public static ArrayList getFilesList(File path){ + if (!path.exists()) return new ArrayList(); + + List files = (List) FileUtils.listFiles(path, new String[] {"gpg"}, true); + return new ArrayList(files); + } + + public static ArrayList getPasswords(File path) { + //We need to recover the passwords then parse the files + ArrayList passList = getFilesList(path); + + if (passList.size() == 0) return new ArrayList(); + + // TODO replace with a set + ArrayList passwordList = new ArrayList(); + + for (File file : passList) { + String fileName = file.getAbsolutePath().replace(path.getAbsolutePath() + "/", ""); + + String[] parts = fileName.split("/"); + if (parts.length == 1) { + passwordList.add(PasswordItem.newPassword(parts[0], file.getParentFile())); + } else { + if (!passwordList.contains(PasswordItem.newCategory(parts[0], file.getParentFile()))) { + passwordList.add(PasswordItem.newCategory(parts[0], file.getParentFile())); + } + } + } + sort(passwordList); + return passwordList; + } +} diff --git a/app/src/main/res/layout/activity_pgp_handler.xml b/app/src/main/res/layout/activity_pgp_handler.xml index 159a6a9b..5095b32c 100644 --- a/app/src/main/res/layout/activity_pgp_handler.xml +++ b/app/src/main/res/layout/activity_pgp_handler.xml @@ -6,18 +6,58 @@ android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" - tools:context="com.zeapo.pwdstore.PgpHandler"> + tools:context="com.zeapo.pwdstore.crypto.PgpHandler"> - + -