<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.openintents.safe" \r
- android:versionCode="4" \r
- android:versionName="1.0.0">
+ android:versionName="1.1.0" android:versionCode="5">
<application android:icon="@drawable/icon_safe" android:allowClearUserData="true" android:debuggable="true" android:label="@string/app_name">\r
\r
<!-- aTrackDog metadata -->\r
<activity class=".PassList" android:name="PassList" android:label="@string/app_name" />
<activity class=".PassEdit" android:name="PassEdit" android:label="@string/app_name" />
<activity class=".CategoryList" android:name="CategoryList" android:label="@string/app_name" />
- <activity class=".AskPassword" android:name="AskPassword" android:label="@string/app_name" />\r
+ <activity class=".AskPassword" android:name="AskPassword" android:label="@string/app_name"/>\r
<activity class=".Help" android:name="Help" android:label="@string/app_name" />
<activity class=".ChangePass" android:name="ChangePass" android:label="@string/app_name" />
<activity class=".Restore" android:name="Restore" android:label="@string/app_name" />
<string name="dialog_title_first_time_warning">New master key</string>\r
<string name="dialog_summary_first_time_warning">A new random master key has been created. Use menu > backup and store this key in a safe place. Without this key you may lose encrypted data.</string>\r
<string name="switch_mode">Switch mode</string>\r
+ <string name="decrypt_progress">Decrypting...</string>\r
\r
\r
<!-- ***************************\r
boolean isLocal = thisIntent.getBooleanExtra (EXTRA_IS_LOCAL, false);
pbeKey = (EditText) findViewById(R.id.password);
+ pbeKey.requestFocus();
introText = (TextView) findViewById(R.id.first_time);
remoteAsk = (TextView) findViewById(R.id.remote);
confirmPass = (EditText) findViewById(R.id.pass_confirm);
if (dbHelper == null) {
dbHelper = new DBHelper(this);
}
+ if (viewMode==VIEW_NORMAL) {
+ // clear pbeKey in case user had typed it, strayed
+ // to something else, then another person opened
+ // the app. Wouldn't want the password already typed
+ pbeKey.setText("");
+ }
}
masterKey=decryptedMasterKey;
return true;
}
- masterKey=null;
return false;
}
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
-import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import android.content.Context;
import android.util.Log;
serializer.attribute(null, "date", dateOut);
- DBHelper dbHelper=new DBHelper(myCtx);
-
- String masterKeyEncrypted = dbHelper.fetchMasterKey();
+ String masterKeyEncrypted = Passwords.fetchMasterKeyEncrypted();
serializer.startTag(null, "MasterKey");
serializer.text(masterKeyEncrypted);
serializer.endTag(null, "MasterKey");
- String salt = dbHelper.fetchSalt();
+ String salt = Passwords.fetchSalt();
serializer.startTag(null, "Salt");
serializer.text(salt);
serializer.endTag(null, "Salt");
List<CategoryEntry> crows;
- crows = dbHelper.fetchAllCategoryRows();
- HashMap<Long, ArrayList<String>> packageAccess=dbHelper.fetchPackageAccessAll();
+ crows = Passwords.getCategoryEntries();
int totalPasswords=0;
serializer.attribute(null, "name", crow.name);
List<PassEntry> rows;
- rows = dbHelper.fetchAllRows(crow.id);
+ rows = Passwords.getPassEntries(crow.id, false, false);
for (PassEntry row : rows) {
totalPasswords++;
serializer.endTag(null, "UniqueName");
}
- if(packageAccess.containsKey(row.id)) {
+ ArrayList<PackageAccessEntry> packageAccess=Passwords.getPackageAccessEntries(row.id);
+ if(packageAccess!=null) {
serializer.startTag(null, "PackageAccess");
- serializer.text(packageAccess.get(row.id).toString());
+ String entry="";
+ Iterator<PackageAccessEntry> packIter=packageAccess.iterator();
+ while (packIter.hasNext()) {
+ if (entry.length()!=0) {
+ entry += ",";
+ }
+ entry += packIter.next().packageAccess;
+ }
+ entry = "[" + entry + "]";
+ serializer.text(entry);
serializer.endTag(null, "PackageAccess");
}
serializer.endTag(null, "OISafe");
serializer.endDocument();
- dbHelper.close();
-
result=myCtx.getString(R.string.backup_complete)+" "+
Integer.toString(totalPasswords);
} catch (IOException e) {
private EditText nameText;
private Long RowId;
- private DBHelper dbHelper=null;
- private CryptoHelper ch;
-
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
if (debug) Log.d(TAG, "onCreate");
- ch = new CryptoHelper();
- try {
- ch.init(CryptoHelper.EncryptionMedium,PassList.getSalt());
- ch.setPassword(PassList.getMasterKey());
- } catch (CryptoHelperException e1) {
- e1.printStackTrace();
- Toast.makeText(this,getString(R.string.crypto_error)
- + e1.getMessage(), Toast.LENGTH_SHORT).show();
- }
-
- if (dbHelper == null){
- dbHelper = new DBHelper(this);
- }
-
String title = getResources().getString(R.string.app_name) + " - " +
getResources().getString(R.string.edit_entry);
setTitle(title);
protected void onPause() {
super.onPause();
if (debug) Log.d(TAG, "onPause");
- dbHelper.close();
- dbHelper = null;
}
@Override
protected void onResume() {
super.onResume();
if (debug) Log.d(TAG, "onResume");
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
- }
if (!CategoryList.isSignedIn()) {
Intent frontdoor = new Intent(this, FrontDoor.class);
startActivity(frontdoor);
String namePlain = nameText.getText().toString();
if (debug) Log.d(TAG, "name: " + namePlain);
+ entry.plainName=namePlain;
- try {
- entry.name = ch.encrypt(namePlain);
- } catch(CryptoHelperException e) {
- Log.e(TAG,e.toString());
- }
-
-
if(RowId == null || RowId == -1) {
- if (debug) Log.d(TAG, "addCategory");
- dbHelper.addCategory(entry);
+ entry.id=-1;
} else {
- if (debug) Log.d(TAG, "updateCategory");
- if (debug) Log.d(TAG, "RowId: " + String.valueOf(RowId));
- dbHelper.updateCategory(RowId, entry);
+ entry.id=RowId;
}
+ if (debug) Log.d(TAG, "addCategory");
+ RowId=Passwords.putCategoryEntry(entry);
}
/**
private void populateFields() {
if (debug) Log.d(TAG, "populateFields");
if (RowId != null) {
- CategoryEntry row = dbHelper.fetchCategory(RowId);
- if (row.id > -1) {
- String cryptName = row.name;
- try {
- nameText.setText(ch.decrypt(cryptName));
- } catch (CryptoHelperException e) {
- Log.e(TAG,e.toString());
- }
- }
+ CategoryEntry catEntry = Passwords.getCategoryEntry(RowId);
+ nameText.setText(catEntry.plainName);
}
}
}
* @author Randy McEoin
*/
public class CategoryEntry extends Object {
- public long id;
+ public long id=-1;
public String name;
+ public boolean nameNeedsDecrypt;
public String plainName;
+ public boolean plainNameNeedsEncrypt=true;
}
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public static final String KEY_ID = "id"; // Intent keys
- private CryptoHelper ch=null;
- private DBHelper dbHelper=null;
-
private String importMessage="";
private int importedEntries=0;
private Thread importThread=null;
private List<CategoryEntry> rows;
private Intent restartTimerIntent;
+ private int lastPosition=0;
BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
finish();
}
+ try {
+ Passwords.InitCrypto(CryptoHelper.EncryptionMedium, salt, masterKey);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Toast.makeText(CategoryList.this, "CategoryList: " + getString(R.string.crypto_error),
+ Toast.LENGTH_SHORT).show();
+ }
+
setContentView(R.layout.cat_list);
String title = getResources().getString(R.string.app_name) + " - " +
getResources().getString(R.string.categories);
setTitle(title);
- if (dbHelper==null) {
- dbHelper = new DBHelper(this);
- if (dbHelper.getPrePopulate()==true)
- {
- prePopulate();
- dbHelper.clearPrePopulate();
- }
+ if (Passwords.getPrePopulate()==true)
+ {
+ prePopulate();
+ Passwords.clearPrePopulate();
}
IntentFilter filter = new IntentFilter();
super.onResume();
if (debug) Log.d(TAG,"onResume()");
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
- }
if (!isSignedIn()) {
Intent frontdoor = new Intent(this, FrontDoor.class);
try { backupThread.join(maxWaitToDie); }
catch(InterruptedException e){} // ignore
}
- dbHelper.close();
- dbHelper = null;
}
@Override
* @return True if signed in
*/
public static boolean isSignedIn() {
- if (masterKey != null) {
+ if ((salt != null) && (masterKey != null)) {
return true;
}
return false;
*/
private void fillData() {
if (debug) Log.d(TAG,"fillData()");
- // initialize crypto so that we can display readable descriptions in
- // the list view
- ch = new CryptoHelper();
- if(masterKey == null) {
- masterKey = "";
- }
- try {
- ch.init(CryptoHelper.EncryptionMedium,salt);
- ch.setPassword(masterKey);
- } catch (CryptoHelperException e1) {
- e1.printStackTrace();
- Toast.makeText(this,getString(R.string.crypto_error)
- + e1.getMessage(), Toast.LENGTH_SHORT).show();
- return;
- }
-
- List<String> items = new ArrayList<String>();
- if (dbHelper==null) {
- return;
- }
- rows = dbHelper.fetchAllCategoryRows();
-
- for (CategoryEntry row : rows) {
- String cryptDesc = row.name;
- row.plainName = "";
- try {
- row.plainName = ch.decrypt(cryptDesc);
- } catch (CryptoHelperException e) {
- Log.e(TAG,e.toString());
- }
- }
- Collections.sort(rows, new Comparator<CategoryEntry>() {
- public int compare(CategoryEntry o1, CategoryEntry o2) {
- return o1.plainName.compareToIgnoreCase(o2.plainName);
- }});
- for (CategoryEntry row : rows) {
- items.add(row.plainName);
- }
-
+ List<String> categoryNames=Passwords.getCategoryNames();
+
+ rows=Passwords.getCategoryEntries();
+
ArrayAdapter<String> entries =
- new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
+ new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
+ categoryNames);
setListAdapter(entries);
}
}
private void delCategory(long Id) {
- if (dbHelper.countPasswords(Id)>0) {
+ if (Passwords.countPasswords(Id)>0) {
Toast.makeText(CategoryList.this, R.string.category_not_empty,
Toast.LENGTH_SHORT).show();
return;
}
- dbHelper.deleteCategory(Id);
+ Passwords.deleteCategoryEntry(Id);
fillData();
}
launchPassList(rows.get(info.position).id);
break;
case EDIT_CATEGORY_INDEX:
- Intent i = new Intent(this, CategoryEdit.class);
if (position > -1) {
+ Intent i = new Intent(this, CategoryEdit.class);
i.putExtra(KEY_ID, rows.get(position).id);
startActivityForResult(i,REQUEST_EDIT_CATEGORY);
+
+ lastPosition=position;
}
break;
case ADD_CATEGORY_INDEX:
try {
if (position > -1) {
delCategory(rows.get(position).id);
+ if (position>2) {
+ setSelection(position-1);
+ }
}
} catch (IndexOutOfBoundsException e) {
// This should only happen when there are no
protected void onActivityResult(int requestCode, int resultCode, Intent i) {
super.onActivityResult(requestCode, resultCode, i);
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
- }
-
if (resultCode == RESULT_OK) {
fillData();
+ if (requestCode==REQUEST_EDIT_CATEGORY) {
+ setSelection(lastPosition);
+ }
}
}
if (debug) Log.d(TAG,"addCategory("+name+")");
if ((name==null) || (name=="")) return -1;
CategoryEntry entry = new CategoryEntry();
+ entry.plainName=name;
- sendBroadcast (restartTimerIntent);
- String namePlain = name;
-
- try {
- ch = new CryptoHelper();
- if(masterKey == null) {
- masterKey = "";
- }
- ch.init(CryptoHelper.EncryptionMedium,salt);
- ch.setPassword(masterKey);
-
- entry.name = ch.encrypt(namePlain);
- } catch(CryptoHelperException e) {
- Log.e(TAG,e.toString());
- Toast.makeText(this,getString(R.string.crypto_error)
- + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
- return dbHelper.addCategory(entry);
+ return Passwords.putCategoryEntry(entry);
}
public boolean exportDatabase(){
+ sendBroadcast (restartTimerIntent);
String filename=EXPORT_FILENAME;
try {
CSVWriter writer = new CSVWriter(new FileWriter(filename), ',');
};
writer.writeNext(header);
- ch = new CryptoHelper();
- if(masterKey == null) {
- masterKey = "";
- }
- try {
- ch.init(CryptoHelper.EncryptionMedium,salt);
- ch.setPassword(masterKey);
- } catch (CryptoHelperException e1) {
- e1.printStackTrace();
- Toast.makeText(this,getString(R.string.crypto_error)
- + e1.getMessage(), Toast.LENGTH_SHORT).show();
- return false;
- }
-
- HashMap<Long, String> categories = new HashMap<Long, String>();
+ HashMap<Long, String> categories = Passwords.getCategoryIdToName();
- List<CategoryEntry> crows;
- crows = dbHelper.fetchAllCategoryRows();
-
- for (CategoryEntry row : crows) {
- String cryptDesc = row.name;
- row.plainName = "";
- try {
- row.plainName = ch.decrypt(cryptDesc);
- categories.put(row.id, row.plainName);
- } catch (CryptoHelperException e) {
- Log.e(TAG,e.toString());
- Toast.makeText(CategoryList.this, R.string.cannot_decrypt_category,
- Toast.LENGTH_SHORT).show();
- return false;
- }
- }
-
List<PassEntry> rows;
- rows = dbHelper.fetchAllRows(new Long(0));
+ rows = Passwords.getPassEntries(new Long(0), true, false);
for (PassEntry row : rows) {
- String cryptDesc = row.description;
- String cryptWebsite = row.website;
- String cryptUsername = row.username;
- String cryptPassword = row.password;
- String cryptNote = row.note;
- row.plainDescription = "";
- row.plainWebsite = "";
- row.plainUsername = "";
- row.plainPassword = "";
- row.plainNote = "";
- try {
- row.plainDescription = ch.decrypt(cryptDesc);
- row.plainWebsite = ch.decrypt(cryptWebsite);
- row.plainUsername = ch.decrypt(cryptUsername);
- row.plainPassword = ch.decrypt(cryptPassword);
- row.plainNote = ch.decrypt(cryptNote);
- } catch (CryptoHelperException e) {
- Log.e(TAG,e.toString());
- Toast.makeText(CategoryList.this, R.string.cannot_decrypt_password,
- Toast.LENGTH_SHORT).show();
- return false;
- }
String[] rowEntries = { categories.get(row.category),
row.plainDescription,
row.plainWebsite,
}
private void deleteDatabaseNow(){
- dbHelper.deleteDatabase();
+ Passwords.deleteAll();
}
public void deleteDatabase4Import(final String filename){
*/
private void importDatabaseThreadStart(final String filename){
showDialog(IMPORT_PROGRESS_KEY);
+
importThread = new Thread(new Runnable() {
public void run() {
importDatabaseFromCSV(filename);
* into the database.
*/
private void importDatabaseFromCSV(String filename){
+ sendBroadcast (restartTimerIntent);
try {
importMessage="";
importedEntries=0;
}
// Log.i(TAG,"first line is valid");
- HashMap<String, Long> categoryToId=getCategoryToId(dbHelper);
+ HashMap<String, Long> categoryToId=Passwords.getCategoryNameToId();
//
// take a pass through the CSV and collect any new Categories
//
}
reader.close();
- categoryToId=getCategoryToId(dbHelper); // re-read the categories to get id's of new categories
+ // re-read the categories to get id's of new categories
+ categoryToId=Passwords.getCategoryNameToId();
//
// read the whole file again to import the actual fields
//
}
continue;
}
-
+
PassEntry entry=new PassEntry();
- try {
- entry.category = categoryToId.get(nextLine[0]);
- entry.description = ch.encrypt(description);
- entry.website = ch.encrypt(nextLine[2]);
- entry.username = ch.encrypt(nextLine[3]);
- entry.password = ch.encrypt(nextLine[4]);
- entry.note = ch.encrypt(nextLine[5]);
- } catch(CryptoHelperException e) {
- Log.e(TAG,e.toString());
- continue;
- }
+ entry.category = categoryToId.get(nextLine[0]);
+ entry.plainDescription = description;
+ entry.plainWebsite = nextLine[2];
+ entry.plainUsername = nextLine[3];
+ entry.plainPassword = nextLine[4];
+ entry.plainNote = nextLine[5];
entry.id=0;
- dbHelper.addPassword(entry);
+ Passwords.putPassEntry(entry);
newEntries++;
}
reader.close();
importMessage=getString(R.string.import_file_error);
}
}
-
- public static HashMap<String, Long> getCategoryToId(DBHelper dbHelper)
- {
- CryptoHelper ch = new CryptoHelper();
- if(masterKey == null) {
- masterKey = "";
- }
- try {
- ch.init(CryptoHelper.EncryptionMedium,salt);
- ch.setPassword(masterKey);
- } catch (CryptoHelperException e1) {
- e1.printStackTrace();
- return null;
- }
-
- HashMap<String,Long> categories = new HashMap<String,Long>();
- List<CategoryEntry> rows;
- if (dbHelper==null) {
- if (debug) Log.d(TAG, "getCategoryToId: dbHelper is null");
- return categories;
- }
- rows = dbHelper.fetchAllCategoryRows();
-
- for (CategoryEntry row : rows) {
- String cryptDesc = row.name;
- row.plainName = "";
- try {
- row.plainName = ch.decrypt(cryptDesc);
- categories.put(row.plainName, row.id);
- } catch (CryptoHelperException e) {
- Log.e(TAG,e.toString());
- }
- }
- return categories;
- }
}
\ No newline at end of file
throw new CryptoHelperException(msg);\r
}\r
byte[] ciphertext = {};\r
+ if (plaintext==null) {\r
+ return "";\r
+ }\r
\r
try {\r
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);\r
throw new CryptoHelperException(msg);\r
}\r
\r
- if ((ciphertext==null) || (ciphertext=="")) {\r
+ if ((ciphertext==null) || (ciphertext.length()==0)) {\r
return "";\r
}\r
byte[] byteCiphertext=hexStringToBytes(ciphertext);\r
+ "expires integer not null, "
+ "dateadded text not null);";
- private static final String CIPHER_ACCESS_DROP =
- "drop table " + TABLE_CIPHER_ACCESS + ";";
+// private static final String CIPHER_ACCESS_DROP =
+// "drop table " + TABLE_CIPHER_ACCESS + ";";
private SQLiteDatabase db;
private static boolean needsPrePopulation=false;
private static final int REQUEST_CODE_ASK_PASSWORD = 1;\r
private static final int REQUEST_CODE_ALLOW_EXTERNAL_ACCESS = 2;\r
\r
-\r
- private DBHelper dbHelper;\r
private String salt;\r
private String masterKey;\r
private CryptoHelper ch;\r
super.onCreate(icicle);\r
mServiceIntent = null;\r
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);\r
- \r
+\r
+ Passwords.Initialize(this);\r
+\r
// The service is launched in onResume()\r
}\r
\r
CategoryList.setSalt(salt);\r
PassList.setMasterKey(masterKey);\r
CategoryList.setMasterKey(masterKey);\r
+ if ((salt==null) || (salt=="")) {\r
+ return;\r
+ }\r
if (ch == null) {\r
ch = new CryptoHelper();\r
}\r
\r
if (clearUniqueName == null) throw new Exception ("EXTRA_UNIQUE_NAME not set.");\r
\r
- if (dbHelper == null) {\r
- // Need to open DBHelper here, because\r
- // onResume() is called after onActivityResult()\r
- dbHelper = new DBHelper(this);\r
- }\r
-\r
- String uniqueName = ch.encrypt(clearUniqueName);\r
- PassEntry row = dbHelper.fetchPassword(uniqueName);\r
- boolean passExists = row.id > 1;\r
+ PassEntry row = Passwords.findPassWithUniqueName(clearUniqueName);\r
+ boolean passExists = (row!=null);\r
\r
- String clearCallingPackage = getCallingPackage();\r
- String callingPackage = ch.encrypt (clearCallingPackage);\r
+ String callingPackage = getCallingPackage();\r
if (passExists) { // check for permission to access this password.\r
- ArrayList<String> packageAccess = dbHelper.fetchPackageAccess(row.id);\r
- if (! PassEntry.checkPackageAccess(packageAccess, callingPackage)) {\r
+ ArrayList<String> packageAccess = Passwords.getPackageAccess(row.id);\r
+ if ((packageAccess==null) ||\r
+ (! PassEntry.checkPackageAccess(packageAccess, callingPackage))) {\r
throw new Exception ("It is currently not permissible for this application to request this password.");\r
}\r
/*TODO: check if this package is in the package_access table corresponding to this password:\r
[ ] Always grant access to all passwords in org.syntaxpolice.ServiceTest category?\r
[ ] Don't grant access"\r
*/\r
+ } else {\r
+ row = new PassEntry();\r
}\r
\r
if (action.equals (CryptoIntents.ACTION_GET_PASSWORD)) {\r
if (passExists) {\r
- username = ch.decrypt(row.username);\r
- password = ch.decrypt(row.password);\r
+ username = row.plainUsername;\r
+ password = row.plainPassword;\r
} else throw new Exception ("Could not find password with the unique name: " + clearUniqueName);\r
\r
// stashing the return values:\r
if (clearPassword == null) {\r
throw new Exception ("PASSWORD extra must be set.");\r
} \r
- row.username = ch.encrypt(clearUsername == null ? "" : clearUsername);\r
- row.password = ch.encrypt(clearPassword);\r
+ row.plainUsername = clearUsername == null ? "" : clearUsername;\r
+ row.plainPassword = clearPassword;\r
// since this package is setting the password, it automatically gets access to it:\r
if (passExists) { //exists already \r
if (clearUsername.equals("") && clearPassword.equals("")) {\r
- dbHelper.deletePassword(row.id);\r
+ Passwords.deletePassEntry(row.id);\r
} else {\r
- dbHelper.updatePassword(row.id, row);\r
+ Passwords.putPassEntry(row);\r
}\r
} else {// add a new one\r
- row.uniqueName = uniqueName;\r
- row.description=uniqueName; //for display purposes\r
+ row.plainUniqueName = clearUniqueName;\r
+ row.plainDescription=clearUniqueName; //for display purposes\r
// TODO: Should we send these fields in extras also? If so, probably not using \r
// the openintents namespace? If another application were to implement a keystore\r
// they might not want to use these.\r
- row.website = ""; \r
- row.note = "";\r
-\r
- String category = ch.encrypt("Application Data");\r
- CategoryEntry c = new CategoryEntry();\r
- c.name = category;\r
- row.category = dbHelper.addCategory(c); //doesn't add category if it already exists\r
+ row.plainWebsite = ""; \r
+ row.plainNote = "";\r
+\r
+ String category="Application Data";\r
+ CategoryEntry c = Passwords.getCategoryEntryByName(category);\r
+ if (c==null) {\r
+ c = new CategoryEntry();\r
+ c.plainName = "Application Data";\r
+ c.id = Passwords.putCategoryEntry(c); //doesn't add category if it already exists\r
+ }\r
+ row.category = c.id;\r
row.id = 0; // entry is truly new\r
- row.id = dbHelper.addPassword(row);\r
+ row.id = Passwords.putPassEntry(row);\r
} \r
- ArrayList<String> packageAccess = dbHelper.fetchPackageAccess(row.id);\r
- if (! PassEntry.checkPackageAccess(packageAccess, callingPackage)) {\r
- dbHelper.addPackageAccess(row.id, callingPackage);\r
+ ArrayList<String> packageAccess = Passwords.getPackageAccess(row.id);\r
+ if ((packageAccess==null) ||\r
+ (! PassEntry.checkPackageAccess(packageAccess, callingPackage))) {\r
+ Passwords.addPackageAccess(row.id, callingPackage);\r
}\r
\r
}\r
Log.d(TAG, "onPause()");\r
\r
releaseService();\r
- dbHelper.close();\r
- dbHelper = null;\r
}\r
\r
@Override\r
\r
if (debug)\r
Log.d(TAG, "onResume()");\r
- if (dbHelper == null) {\r
- dbHelper = new DBHelper(this);\r
- }\r
\r
initService(); // start up the PWS service so other applications can query.\r
}\r
--- /dev/null
+/* $Id$
+ *
+ * Copyright (C) 2009 OpenIntents.org
+ *
+ * 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 org.openintents.safe;
+
+public class PackageAccessEntry extends Object {
+ public boolean needsDecrypt;
+ public boolean needsEncrypt=true;
+ public String packageAccess;
+ public String plainPackageAccess;
+}
package org.openintents.safe;
+import org.openintents.intents.CryptoIntents;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
public static final int GEN_PASSWORD_INDEX = Menu.FIRST + 3;
public static final int RESULT_DELETED = RESULT_FIRST_USER;
-
+
+ private Intent restartTimerIntent;
+
private EditText descriptionText;
private EditText passwordText;
private EditText usernameText;
private EditText websiteText;
private EditText noteText;
private Long RowId;
- private DBHelper dbHelper = null;
- private CryptoHelper ch;
private boolean pass_gen_ret = false;
private boolean discardEntry = false;
public static boolean entryEdited = false;
+ boolean populated = false;
+ Intent frontdoor;
+
+ BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(CryptoIntents.ACTION_CRYPTO_LOGGED_OUT)) {
+ if (debug) Log.d(TAG,"caught ACTION_CRYPTO_LOGGED_OUT");
+ frontdoor.setAction(Intent.ACTION_MAIN);
+ startActivity(frontdoor);
+ }
+ }
+ };
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
if (debug) Log.d(TAG,"onCreate()");
+
+ frontdoor = new Intent(this, FrontDoor.class);
+ if (!CategoryList.isSignedIn()) {
+ startActivity(frontdoor);
+ // normally we'd do a finish() here, but
+ // by starting frontdoor from here the user could
+ // potentially find this activity and continue editing
+ }
+ restartTimerIntent = new Intent (CryptoIntents.ACTION_RESTART_TIMER);
+
String title = getResources().getString(R.string.app_name) + " - "
+ getResources().getString(R.string.edit_entry);
setTitle(title);
- ch = new CryptoHelper();
- try {
- ch.init(CryptoHelper.EncryptionMedium,PassList.getSalt());
- ch.setPassword(PassList.getMasterKey());
- } catch (CryptoHelperException e1) {
- e1.printStackTrace();
- Toast.makeText(this,getString(R.string.crypto_error)
- + e1.getMessage(), Toast.LENGTH_SHORT).show();
- }
-
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
- }
setContentView(R.layout.pass_edit);
}
}
});
+ restoreMe();
+ sendBroadcast (restartTimerIntent);
}
@Override
if (isFinishing() && discardEntry==false) {
savePassword();
}
- dbHelper.close();
- dbHelper = null;
}
@Override
if (debug) Log.d(TAG,"onResume()");
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
- }
if (CategoryList.isSignedIn() == false) {
saveState();
finish();
private void saveState() {
PassEntry entry = new PassEntry();
- String passwordPlain = passwordText.getText().toString();
- String notePlain = noteText.getText().toString();
- String usernamePlain = usernameText.getText().toString();
- String websitePlain = websiteText.getText().toString();
- String descPlain = descriptionText.getText().toString();
-
- try {
- entry.category = PassList.getCategoryId();
- entry.description = ch.encrypt(descPlain);
- entry.username = ch.encrypt(usernamePlain);
- entry.password = ch.encrypt(passwordPlain);
- entry.note = ch.encrypt(notePlain);
- entry.website = ch.encrypt(websitePlain);
- } catch (CryptoHelperException e) {
- Log.e(TAG, e.toString());
- }
+ entry.category = PassList.getCategoryId();
+ entry.plainDescription = descriptionText.getText().toString();
+ entry.plainWebsite = websiteText.getText().toString();
+ entry.plainUsername = usernameText.getText().toString();
+ entry.plainPassword = passwordText.getText().toString();
+ entry.plainNote = noteText.getText().toString();
entryEdited = true;
if (RowId == null || RowId == -1) {
entry.id = 0; // brand new entry
- RowId = dbHelper.addPassword(entry);
+ RowId = Passwords.putPassEntry(entry);
} else {
- PassEntry storedEntry = dbHelper.fetchPassword (RowId);
+ entry.id=RowId;
+ PassEntry storedEntry = Passwords.getPassEntry(RowId, true, false);
//update fields that aren't set in the UI:
entry.uniqueName = storedEntry.uniqueName;
- dbHelper.updatePassword(RowId, entry);
+ Passwords.putPassEntry(entry);
}
}
* @param Id
*/
private void delPassword(long Id) {
- dbHelper.deletePassword(Id);
+ Passwords.deletePassEntry(Id);
discardEntry=true;
setResult(RESULT_DELETED);
finish();
pass_gen_ret = false;
return;
}
- if (RowId != null) {
- PassEntry row = dbHelper.fetchPassword(RowId);
- if (row.id > -1) {
- String cryptDesc = row.description;
- String cryptWebsite = row.website;
- String cryptUsername = row.username;
- String cryptPass = row.password;
- String cryptNote = row.note;
- try {
- descriptionText.setText(ch.decrypt(cryptDesc));
- websiteText.setText(ch.decrypt(cryptWebsite));
- usernameText.setText(ch.decrypt(cryptUsername));
- passwordText.setText(ch.decrypt(cryptPass));
- noteText.setText(ch.decrypt(cryptNote));
- } catch (CryptoHelperException e) {
- Log.e(TAG, e.toString());
- }
- }
+ if (debug) Log.d(TAG,"populateFields: populated="+populated);
+ if (populated) {
+ return;
+ }
+ if ((RowId != null) && (RowId != -1)) {
+ PassEntry passEntry = Passwords.getPassEntry(RowId, true, false);
+ descriptionText.setText(passEntry.plainDescription);
+ websiteText.setText(passEntry.plainWebsite);
+ usernameText.setText(passEntry.plainUsername);
+ passwordText.setText(passEntry.plainPassword);
+ noteText.setText(passEntry.plainNote);
}
+ populated=true;
}
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ String nonConfig;
+ if (populated==true) {
+ nonConfig="true";
+ }else {
+ nonConfig="false";
+ }
+ return(nonConfig);
+ }
+
+ private void restoreMe() {
+ String nonConfig;
+
+ if (getLastNonConfigurationInstance()!=null) {
+ nonConfig=(String) getLastNonConfigurationInstance();
+ if (nonConfig.compareTo("true")==0) {
+ populated=true;
+ }
+ }
+ }
}
* @author Steven Osborn - http://steven.bitsetters.com\r
*/\r
public class PassEntry extends Object {\r
+ public long id=-1;\r
+ public boolean needsDecryptDescription;\r
+ public boolean needsDecrypt;\r
+ public boolean needsEncrypt=true;\r
public String password;\r
- public long id;\r
public long category;\r
public String categoryName;\r
public String description;\r
public String plainUsername;\r
public String plainWebsite;\r
public String plainNote;\r
+ public String plainUniqueName;\r
public String lastEdited;\r
\r
public static boolean checkPackageAccess (ArrayList<String> packageAccess, String packageName) {\r
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
+import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
*/
public class PassList extends ListActivity {
- private static final boolean debug= false;
+ private static final boolean debug = false;
private static final String TAG = "PassList";
// Menu Item order
public static final int REQUEST_EDIT_PASSWORD = 2;
public static final int REQUEST_ADD_PASSWORD = 3;
public static final int REQUEST_MOVE_PASSWORD = 4;
+
+ protected static final int MSG_UPDATE_LIST = 0x101;
+
+ private static final int DECRYPT_PROGRESS_KEY = 0;
public static final String KEY_ID = "id"; // Intent keys
public static final String KEY_CATEGORY_ID = "categoryId"; // Intent keys
- private CryptoHelper ch;
- private DBHelper dbHelper=null;
private static Long CategoryId=null;
private Intent restartTimerIntent;
private static String salt;
- private static String masterKey;
+ private static String masterKey;
+
+ private Thread fillerThread=null;
private List<PassEntry> rows;
-
+ private int lastPosition=0;
+ List<String> passDescriptions=new ArrayList<String>();
+
+ public Handler myViewUpdateHandler = new Handler(){
+ // @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case PassList.MSG_UPDATE_LIST:
+ ArrayAdapter<String> entries =
+ new ArrayAdapter<String>(PassList.this, android.R.layout.simple_list_item_1,
+ passDescriptions);
+ setListAdapter(entries);
+
+ if (debug) Log.d(TAG,"lastPosition="+lastPosition);
+ if (lastPosition>2) {
+ setSelection(lastPosition-1);
+ lastPosition=0;
+ }
+ break;
+ }
+ super.handleMessage(msg);
+ }
+ };
+
/**
* Called when the activity is first created.
*/
super.onCreate(icicle);
if (debug) Log.d(TAG,"onCreate()");
+ if (!CategoryList.isSignedIn()) {
+ finish();
+ }
restartTimerIntent = new Intent (CryptoIntents.ACTION_RESTART_TIMER);
+
setContentView(R.layout.pass_list);
- if (dbHelper==null) {
- dbHelper = new DBHelper(this);
- }
CategoryId = icicle != null ? icicle.getLong(CategoryList.KEY_ID) : null;
if (CategoryId == null) {
Bundle extras = getIntent().getExtras();
finish(); // no valid category less than one
}
- String categoryName=getCategoryName(CategoryId);
+ String categoryName=Passwords.getCategoryEntry(CategoryId).plainName;
String title = getResources().getString(R.string.app_name) + " - " +
getResources().getString(R.string.passwords) + " -" +
categoryName;
list.setFocusable(true);
list.setOnCreateContextMenuListener(this);
registerForContextMenu(list);
+
+ sendBroadcast (restartTimerIntent);
}
@Override
super.onPause();
if (debug) Log.d(TAG,"onPause()");
- if (dbHelper != null) {
- dbHelper.close();
- dbHelper = null;
+ if ((fillerThread != null) && (fillerThread.isAlive())) {
+ if (debug) Log.d(TAG,"wait for thread");
+ int maxWaitToDie=500000;
+ try { fillerThread.join(maxWaitToDie); }
+ catch(InterruptedException e){} // ignore
}
}
if (CategoryList.isSignedIn()==false) {
finish();
}
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
- }
}
@Override
super.onStop();
if (debug) Log.d(TAG,"onStop()");
- if (dbHelper != null) {
- dbHelper.close();
- dbHelper=null;
- }
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view,
return true;
}
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case DECRYPT_PROGRESS_KEY: {
+ ProgressDialog dialog = new ProgressDialog(this);
+ dialog.setMessage(getString(R.string.decrypt_progress));
+ dialog.setIndeterminate(false);
+ dialog.setCancelable(true);
+ return dialog;
+ }
+ }
+ return null;
+ }
+
/**
* Populates the password ListView
*/
- private void fillData() {
- // initialize crypto so that we can display readable descriptions in
- // the list view
- ch = new CryptoHelper();
- if(masterKey == null) {
- masterKey = "";
- }
- try {
- ch.init(CryptoHelper.EncryptionMedium, salt);
- ch.setPassword(masterKey);
- } catch (CryptoHelperException e1) {
- e1.printStackTrace();
- Toast.makeText(this,getString(R.string.crypto_error)
- + e1.getMessage(), Toast.LENGTH_SHORT).show();
- return;
- }
-
- List<String> items = new ArrayList<String>();
- rows = dbHelper.fetchAllRows(CategoryId);
-
- for (PassEntry row : rows) {
- String cryptDesc = row.description;
- row.plainDescription = "";
- try {
- row.plainDescription = ch.decrypt(cryptDesc);
- } catch (CryptoHelperException e) {
- Log.e(TAG,e.toString());
- }
- }
- Collections.sort(rows, new Comparator<PassEntry>() {
- public int compare(PassEntry o1, PassEntry o2) {
- return o1.plainDescription.compareToIgnoreCase(o2.plainDescription);
- }});
- for (PassEntry row : rows) {
- items.add(row.plainDescription);
- }
-
- ArrayAdapter<String> entries =
- new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
- setListAdapter(entries);
-
- }
+ private void fillData() {
+ showDialog(DECRYPT_PROGRESS_KEY);
+
+ fillerThread = new Thread(new Runnable() {
+ public void run(){
+ rows=Passwords.getPassEntries(CategoryId, true, true);
+ passDescriptions.clear();
+ Iterator<PassEntry> passIter=rows.iterator();
+ while (passIter.hasNext()) {
+ PassEntry passEntry=passIter.next();
+ passDescriptions.add(passEntry.plainDescription);
+ }
+// dismissDialog(DECRYPT_PROGRESS_KEY);
+ // forced to removeDialog(), without it
+ // after an orientation change dismissDialog()
+ // would crash
+ removeDialog(DECRYPT_PROGRESS_KEY);
+
+ Message mu = new Message();
+ mu.what = PassList.MSG_UPDATE_LIST;
+ PassList.this.myViewUpdateHandler.sendMessage(mu);
+ }
+ });
+ fillerThread.start();
+ }
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
+ sendBroadcast (restartTimerIntent);
+
if (menu == null) {
return super.onMenuOpened(featureId, menu);
}
*/
public void deletePassword2(int position){
try {
+ lastPosition=position;
delPassword(rows.get(position).id);
} catch (IndexOutOfBoundsException e) {
// This should only happen when there are no
}
private void delPassword(long Id) {
- dbHelper.deletePassword(Id);
+ Passwords.deletePassEntry(Id);
fillData();
}
* @param passwordId
*/
private void movePassword(final long passwordId) {
- final HashMap<String, Long> categoryToId=CategoryList.getCategoryToId(dbHelper);
- String categoryName=getCategoryName(CategoryId);
+ final HashMap<String, Long> categoryToId=Passwords.getCategoryNameToId();
+ String categoryName=Passwords.getCategoryEntry(CategoryId).plainName;
categoryToId.remove(categoryName);
Set<String> categories=categoryToId.keySet();
final String[] items=(String[])categories.toArray(new String[categories.size()]);
public void onClick(DialogInterface dialog, int which) {
long newCategoryId=categoryToId.get(items[which]);
- dbHelper.updatePasswordCategory(passwordId, newCategoryId);
+ Passwords.updatePassCategory(passwordId, newCategoryId);
String result=getString(R.string.moved_to) + " " + items[which];
Toast.makeText(PassList.this, result,
Toast.LENGTH_LONG).show();
vi.putExtra(KEY_ID, rows.get(position).id);
vi.putExtra(KEY_CATEGORY_ID, CategoryId);
startActivityForResult(vi,REQUEST_VIEW_PASSWORD);
+ lastPosition=position;
break;
case EDIT_PASSWORD_INDEX:
Intent i = new Intent(this, PassEdit.class);
i.putExtra(KEY_ID, rows.get(position).id);
i.putExtra(KEY_CATEGORY_ID, CategoryId);
startActivityForResult(i,REQUEST_EDIT_PASSWORD);
+ lastPosition=position;
break;
case DEL_PASSWORD_INDEX:
deletePassword(position);
break;
case MOVE_PASSWORD_INDEX:
movePassword(rows.get(position).id);
+ lastPosition=position;
break;
}
return super.onOptionsItemSelected(item);
super.onActivityResult(requestCode, resultCode, i);
//Log.d(TAG, "onActivityResult. requestCode: " + requestCode + ", resultCode: " + resultCode);
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
- }
if (((requestCode==REQUEST_VIEW_PASSWORD)&&(PassView.entryEdited)) ||
((requestCode==REQUEST_EDIT_PASSWORD)&&(PassEdit.entryEdited)) ||
((requestCode==REQUEST_ADD_PASSWORD)&&(PassEdit.entryEdited)) ||
fillData();
}
}
-
- /**
- * Retrieve the decrypted category name based on the provided id.
- *
- * @param Id category id
- * @return decrypted category name
- */
- private String getCategoryName(long Id) {
- CategoryEntry category=dbHelper.fetchCategory(Id);
- category.plainName="";
- if (ch==null) {
- ch=new CryptoHelper();
- }
- try {
- ch.init(CryptoHelper.EncryptionMedium, salt);
- ch.setPassword(masterKey);
- category.plainName = ch.decrypt(category.name);
- } catch (CryptoHelperException e) {
- Log.e(TAG,e.toString());
- Toast.makeText(this,getString(R.string.crypto_error)
- + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
- return category.plainName;
- }
}
private TextView packageAccessText;
private Long RowId;
private Long CategoryId;
- private DBHelper dbHelper = null;
- private CryptoHelper ch;
public static boolean entryEdited=false;
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
if (debug) Log.d(TAG,"onCreate()");
+ if (!CategoryList.isSignedIn()) {
+ finish();
+ }
+
String title = getResources().getString(R.string.app_name) + " - "
+ getResources().getString(R.string.view_entry);
setTitle(title);
- ch = new CryptoHelper();
- try {
- ch.init(CryptoHelper.EncryptionMedium, PassList.getSalt());
- ch.setPassword(PassList.getMasterKey());
- } catch (CryptoHelperException e1) {
- e1.printStackTrace();
- Toast.makeText(this,getString(R.string.crypto_error)
- + e1.getMessage(), Toast.LENGTH_SHORT).show();
- }
-
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
- }
-
setContentView(R.layout.pass_view);
descriptionText = (TextView) findViewById(R.id.description);
@Override
protected void onPause() {
super.onPause();
- dbHelper.close();
- dbHelper = null;
}
@Override
if (debug) Log.d(TAG,"onResume()");
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
- }
if (CategoryList.isSignedIn() == false) {
finish();
}
-// populateFields();
}
@Override
* @param Id
*/
private void delPassword(long Id) {
- dbHelper.deletePassword(Id);
+ Passwords.deletePassEntry(Id);
setResult(RESULT_OK);
finish();
}
private void populateFields() {
if (debug) Log.d(TAG,"populateFields()");
if (RowId != null) {
- if (dbHelper == null) {
- dbHelper = new DBHelper(this);
+ PassEntry row = Passwords.getPassEntry(RowId, true, false);
+ ArrayList<String> packageAccess = Passwords.getPackageAccess(RowId);
+ descriptionText.setText(row.plainDescription);
+ websiteText.setText(row.plainWebsite);
+ usernameText.setText(row.plainUsername);
+ passwordText.setText(row.plainPassword);
+ noteText.setText(row.plainNote);
+ String lastEdited;
+ if (row.lastEdited!=null) {
+ lastEdited=row.lastEdited;
+ } else {
+ lastEdited=getString(R.string.last_edited_unknown);
}
- PassEntry row = dbHelper.fetchPassword(RowId);
- if (row.id > -1) {
- String cryptDesc = row.description;
- String cryptWebsite = row.website;
- String cryptUsername = row.username;
- String cryptPass = row.password;
- String cryptNote = row.note;
- String cryptUniqueName = row.uniqueName;
- ArrayList<String> packageAccess = dbHelper.fetchPackageAccess(row.id);
- try {
- descriptionText.setText(ch.decrypt(cryptDesc));
- websiteText.setText(ch.decrypt(cryptWebsite));
- usernameText.setText(ch.decrypt(cryptUsername));
- passwordText.setText(ch.decrypt(cryptPass));
- noteText.setText(ch.decrypt(cryptNote));
- String lastEdited;
- if (row.lastEdited!=null) {
- lastEdited=row.lastEdited;
- } else {
- lastEdited=getString(R.string.last_edited_unknown);
- }
- lastEditedText.setText(getString(R.string.last_edited)+": "+lastEdited);
- if (cryptUniqueName!=null) {
- String plainUniqueName=ch.decrypt(cryptUniqueName);
- if (plainUniqueName!="") {
- uniqueNameText.setText(getString(R.string.uniquename)+
- ": "+plainUniqueName);
- }
- }
- String packages="";
- if (packageAccess!=null) {
- for (String packageName : packageAccess) {
- String plainPackageName=ch.decrypt(packageName);
- PackageManager pm=getPackageManager();
- String appLabel="";
- try {
- ApplicationInfo ai=pm.getApplicationInfo(plainPackageName,0);
- appLabel=pm.getApplicationLabel(ai).toString();
- } catch (NameNotFoundException e) {
- appLabel="("+getString(R.string.not_found)+")";
- }
- packages+=plainPackageName+" "+appLabel+" ";
- }
- }
- if (packages!="") {
- packageAccessText.setText(getString(R.string.package_access)+
- ": "+packages);
+ lastEditedText.setText(getString(R.string.last_edited)+": "+lastEdited);
+ if (row.plainUniqueName!=null) {
+ uniqueNameText.setText(getString(R.string.uniquename)+
+ ": "+row.plainUniqueName);
+ }
+ String packages="";
+ if (packageAccess!=null) {
+ for (String packageName : packageAccess) {
+ if (debug) Log.d(TAG,"packageName="+packageName);
+ PackageManager pm=getPackageManager();
+ String appLabel="";
+ try {
+ ApplicationInfo ai=pm.getApplicationInfo(packageName,0);
+ appLabel=pm.getApplicationLabel(ai).toString();
+ } catch (NameNotFoundException e) {
+ appLabel="("+getString(R.string.not_found)+")";
}
- } catch (CryptoHelperException e) {
- Log.e(TAG, e.toString());
+ packages+=packageName+" "+appLabel+" ";
}
}
+ if (packages!="") {
+ packageAccessText.setText(getString(R.string.package_access)+
+ ": "+packages);
+ }
}
}
}
--- /dev/null
+/*
+ * Copyright (C) 2009 OpenIntents.org
+ *
+ * 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 org.openintents.safe;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * Abstraction layer for storing encrypted and decrypted versions
+ * of all PassEntry and CategoryEntry data.
+ * Handles fetching and storing with DBHelper
+ * and encrypt/decrypt of data.
+ *
+ * @author Randy McEoin
+ *
+ */
+public class Passwords {
+
+ private static final boolean debug = false;
+ private static final String TAG = "Passwords";
+
+ private static HashMap<Long, PassEntry> passEntries=null;
+
+ private static HashMap<Long, CategoryEntry> categoryEntries=null;
+
+ private static HashMap<Long, ArrayList<PackageAccessEntry>> packageAccessEntries=null;
+
+ private static CryptoHelper ch=null;
+ private static DBHelper dbHelper=null;
+
+ public static void Initialize(Context ctx) {
+ if (debug) Log.d(TAG,"Initialize()");
+
+ if (ch==null) {
+ ch = new CryptoHelper();
+ }
+ if (dbHelper==null) {
+ dbHelper = new DBHelper(ctx);
+ }
+ if (passEntries==null) {
+ passEntries = new HashMap<Long, PassEntry>();
+ InitPassEntries();
+ }
+ if (categoryEntries==null) {
+ categoryEntries = new HashMap<Long, CategoryEntry>();
+ InitCategoryEntries();
+ }
+ if (packageAccessEntries==null) {
+ packageAccessEntries = new HashMap<Long, ArrayList<PackageAccessEntry>>();
+ InitPackageAccess();
+ }
+ }
+
+ /**
+ * Force a fresh load from the database.
+ */
+ public static void Reset() {
+ categoryEntries.clear();
+ InitCategoryEntries();
+ passEntries.clear();
+ InitPassEntries();
+ packageAccessEntries.clear();
+ InitPackageAccess();
+ }
+
+ public static void InitCrypto(int strength, String salt, String masterKey)
+ throws Exception {
+ try {
+ ch.init(strength,salt);
+ ch.setPassword(masterKey);
+ } catch (CryptoHelperException e1) {
+ e1.printStackTrace();
+ throw new Exception("Error with Passwords.InitCrypto: "+
+ e1.getLocalizedMessage());
+ }
+ }
+
+ public static void deleteAll() {
+ dbHelper.deleteDatabase();
+ Reset();
+ }
+
+ public static boolean getPrePopulate() {
+ return dbHelper.getPrePopulate();
+ }
+
+ public static void clearPrePopulate() {
+ dbHelper.clearPrePopulate();
+ }
+
+ public static String fetchSalt() {
+ return dbHelper.fetchSalt();
+ }
+
+ public static String fetchMasterKeyEncrypted() {
+ return dbHelper.fetchMasterKey();
+ }
+ ///////////////////////////////////////////////////
+ ///////////// Category Functions //////////////////
+ ///////////////////////////////////////////////////
+
+ private static void InitCategoryEntries() {
+ List<CategoryEntry> catRows;
+ catRows = dbHelper.fetchAllCategoryRows();
+ for (CategoryEntry catRow : catRows) {
+ catRow.nameNeedsDecrypt=true;
+ catRow.plainNameNeedsEncrypt=false;
+ categoryEntries.put(catRow.id, catRow);
+ }
+ }
+
+ public static List<CategoryEntry> getCategoryEntries() {
+ Collection<CategoryEntry> categories=categoryEntries.values();
+ Iterator<CategoryEntry> catIter=categories.iterator();
+ while (catIter.hasNext()) {
+ CategoryEntry catEntry=catIter.next();
+ // run through and ensure all entries are decrypted
+ getCategoryEntry(catEntry.id);
+ }
+ List<CategoryEntry> catList=new ArrayList<CategoryEntry>(categories);
+ Collections.sort(catList, new Comparator<CategoryEntry>() {
+ public int compare(CategoryEntry o1, CategoryEntry o2) {
+ return o1.plainName.compareToIgnoreCase(o2.plainName);
+ }});
+ return catList;
+ }
+
+ public static List<String> getCategoryNames() {
+ List<String> items = new ArrayList<String>();
+
+ List<CategoryEntry> catList=getCategoryEntries();
+ for (CategoryEntry row : catList) {
+ items.add(row.plainName);
+ }
+ return items;
+ }
+
+ public static HashMap<Long, String> getCategoryIdToName() {
+ HashMap<Long, String> categoryMap = new HashMap<Long, String>();
+ Collection<CategoryEntry> categories=categoryEntries.values();
+ Iterator<CategoryEntry> catIter=categories.iterator();
+ while (catIter.hasNext()) {
+ CategoryEntry catEntry=catIter.next();
+ // run through and ensure all entries are decrypted
+ getCategoryEntry(catEntry.id);
+ categoryMap.put(catEntry.id, catEntry.plainName);
+ }
+ return categoryMap;
+ }
+
+ public static HashMap<String, Long> getCategoryNameToId() {
+ HashMap<String, Long> categoryMap = new HashMap<String, Long>();
+ Collection<CategoryEntry> categories=categoryEntries.values();
+ Iterator<CategoryEntry> catIter=categories.iterator();
+ while (catIter.hasNext()) {
+ CategoryEntry catEntry=catIter.next();
+ // run through and ensure all entries are decrypted
+ getCategoryEntry(catEntry.id);
+ categoryMap.put(catEntry.plainName, catEntry.id);
+ if (debug) Log.d(TAG,"map "+catEntry.plainName+" to "+catEntry.id);
+ }
+ return categoryMap;
+ }
+
+ public static CategoryEntry getCategoryEntryByName(String category) {
+ Collection<CategoryEntry> categories=categoryEntries.values();
+ Iterator<CategoryEntry> catIter=categories.iterator();
+ while (catIter.hasNext()) {
+ CategoryEntry catEntry=catIter.next();
+ if (catEntry.name.compareTo(category)==0) {
+ return catEntry;
+ }
+ }
+ return null;
+ }
+
+ public static CategoryEntry getCategoryEntry(Long id) {
+ CategoryEntry catEntry=categoryEntries.get(id);
+ if (catEntry.nameNeedsDecrypt) {
+ if (debug) Log.d(TAG,"decrypt cat");
+ try {
+ catEntry.plainName = ch.decrypt(catEntry.name);
+ } catch (CryptoHelperException e) {
+ Log.e(TAG,e.toString());
+ }
+ catEntry.nameNeedsDecrypt=false;
+ categoryEntries.put(id, catEntry);
+ }
+ return catEntry;
+ }
+
+ public static long putCategoryEntry(CategoryEntry catEntry) {
+ if (catEntry.plainNameNeedsEncrypt) {
+ if (debug) Log.d(TAG,"encrypt cat");
+ try {
+ catEntry.name = ch.encrypt(catEntry.plainName);
+ } catch (CryptoHelperException e) {
+ Log.e(TAG,e.toString());
+ }
+ catEntry.plainNameNeedsEncrypt=false;
+ }
+ if (catEntry.id==-1) {
+ catEntry.id=dbHelper.addCategory(catEntry);
+ } else {
+ dbHelper.updateCategory(catEntry.id, catEntry);
+ }
+ categoryEntries.put(catEntry.id, catEntry);
+ return catEntry.id;
+ }
+
+ public static void deleteCategoryEntry(long id) {
+ if (debug) Log.d(TAG,"deleteCategoryEntry("+id+")");
+ dbHelper.deleteCategory(id);
+ categoryEntries.remove(id);
+ }
+
+ ///////////////////////////////////////////////////
+ ///////////// Password Functions //////////////////
+ ///////////////////////////////////////////////////
+
+ private static void InitPassEntries() {
+ List<PassEntry> passRows;
+ passRows = dbHelper.fetchAllRows(new Long(0));
+ for (PassEntry passRow : passRows) {
+ passRow.needsDecryptDescription=true;
+ passRow.needsDecrypt=true;
+ passRow.needsEncrypt=false;
+ passEntries.put(passRow.id, passRow);
+ }
+ }
+
+ public static List<PassEntry> getPassEntries(long categoryId, boolean decrypt, boolean descriptionOnly) {
+ Collection<PassEntry> passwords=passEntries.values();
+ List<PassEntry> passList=new ArrayList<PassEntry>();
+ Iterator<PassEntry> passIter=passwords.iterator();
+ while (passIter.hasNext()) {
+ PassEntry passEntry=passIter.next();
+ if ((categoryId==0) || (passEntry.category==categoryId)) {
+ getPassEntry(passEntry.id, decrypt, descriptionOnly);
+ passList.add(passEntry);
+ }
+ }
+ if (decrypt==true) {
+ Collections.sort(passList, new Comparator<PassEntry>() {
+ public int compare(PassEntry o1, PassEntry o2) {
+ return o1.plainDescription.compareToIgnoreCase(o2.plainDescription);
+ }});
+ }
+ return passList;
+ }
+
+ public static HashMap<Long, String> getPassIdToDescriptionOLD() {
+ HashMap<Long, String> passMap = new HashMap<Long, String>();
+ Collection<PassEntry> passwords=passEntries.values();
+ Iterator<PassEntry> passIter=passwords.iterator();
+ while (passIter.hasNext()) {
+ PassEntry passEntry=passIter.next();
+ // run through and ensure all entries are decrypted
+ getPassEntry(passEntry.id, true, true);
+ passMap.put(passEntry.id, passEntry.plainDescription);
+ }
+ return passMap;
+ }
+
+ public static HashMap<String, Long> getPassDescriptionToIdOLD() {
+ HashMap<String, Long> passMap = new HashMap<String, Long>();
+ Collection<PassEntry> passwords=passEntries.values();
+ Iterator<PassEntry> passIter=passwords.iterator();
+ while (passIter.hasNext()) {
+ PassEntry passEntry=passIter.next();
+ // run through and ensure all entries are decrypted
+ getPassEntry(passEntry.id, true, true);
+ passMap.put(passEntry.plainDescription, passEntry.id);
+ }
+ return passMap;
+ }
+
+ public static PassEntry getPassEntry(Long id, boolean decrypt, boolean descriptionOnly) {
+ if (debug) Log.d(TAG,"getPassEntry("+id+")");
+ PassEntry passEntry=passEntries.get(id);
+ if (passEntry==null) {
+ return null;
+ }
+ if (decrypt==false) {
+ return passEntry;
+ }
+ if (passEntry.needsDecryptDescription) {
+ //if (debug) Log.d(TAG,"decrypt pass description");
+ try {
+ passEntry.plainDescription = ch.decrypt(passEntry.description);
+ } catch (CryptoHelperException e) {
+ Log.e(TAG,e.toString());
+ }
+ passEntry.needsDecryptDescription=false;
+ passEntries.put(id, passEntry);
+ }
+ if (!descriptionOnly && passEntry.needsDecrypt) {
+ if (debug) Log.d(TAG,"decrypt pass");
+ try {
+ passEntry.plainDescription = ch.decrypt(passEntry.description);
+ passEntry.plainWebsite=ch.decrypt(passEntry.website);
+ passEntry.plainUsername=ch.decrypt(passEntry.username);
+ passEntry.plainPassword=ch.decrypt(passEntry.password);
+ passEntry.plainNote=ch.decrypt(passEntry.note);
+ passEntry.plainUniqueName=ch.decrypt(passEntry.uniqueName);
+ } catch (CryptoHelperException e) {
+ Log.e(TAG,e.toString());
+ }
+ passEntry.needsDecrypt=false;
+ passEntries.put(id, passEntry);
+ }
+ return passEntry;
+ }
+
+ public static PassEntry findPassWithUniqueName(String plainUniqueName) {
+ String uniqueName="";
+ try {
+ uniqueName = ch.encrypt(plainUniqueName);
+ } catch (CryptoHelperException e) {
+ Log.e(TAG,e.toString());
+ return null;
+ }
+ Collection<PassEntry> passwords=passEntries.values();
+ Iterator<PassEntry> passIter=passwords.iterator();
+ while (passIter.hasNext()) {
+ PassEntry passEntry=passIter.next();
+ if (passEntry.uniqueName.compareTo(uniqueName)==0) {
+ passEntry=getPassEntry(passEntry.id,true,false);
+ return passEntry;
+ }
+ }
+ return null;
+ }
+ public static long putPassEntry(PassEntry passEntry) {
+ if (debug) Log.d(TAG,"putPassEntry("+passEntry.id+")");
+ if (passEntry.needsEncrypt) {
+ if (debug) Log.d(TAG,"encrypt pass");
+ try {
+ passEntry.description = ch.encrypt(passEntry.plainDescription);
+ passEntry.website = ch.encrypt(passEntry.plainWebsite);
+ passEntry.username = ch.encrypt(passEntry.plainUsername);
+ passEntry.password = ch.encrypt(passEntry.plainPassword);
+ passEntry.note = ch.encrypt(passEntry.plainNote);
+ passEntry.uniqueName = ch.encrypt(passEntry.plainUniqueName);
+ } catch (CryptoHelperException e) {
+ Log.e(TAG,e.toString());
+ }
+ passEntry.needsEncrypt=false;
+ }
+ if (passEntry.id==0) {
+ passEntry.id=dbHelper.addPassword(passEntry);
+ } else {
+ dbHelper.updatePassword(passEntry.id, passEntry);
+ }
+ passEntries.put(passEntry.id, passEntry);
+ return passEntry.id;
+ }
+
+ public static void deletePassEntry(long id) {
+ if (debug) Log.d(TAG,"deletePassEntry("+id+")");
+ dbHelper.deletePassword(id);
+ passEntries.remove(id);
+ }
+
+ public static void updatePassCategory(long passId, long categoryId) {
+ PassEntry passEntry=passEntries.get(passId);
+ passEntry.category=categoryId;
+ dbHelper.updatePasswordCategory(passId, categoryId);
+ }
+
+ public static int countPasswords(long categoryId) {
+ return dbHelper.countPasswords(categoryId);
+ }
+
+ ///////////////////////////////////////////////////
+ ////////// Package Access Functions ///////////////
+ ///////////////////////////////////////////////////
+
+ private static void InitPackageAccess() {
+ HashMap<Long, ArrayList<String>> dbPackageAccess=dbHelper.fetchPackageAccessAll();
+ if (!dbPackageAccess.isEmpty()) {
+ Set<Long> keys=dbPackageAccess.keySet();
+ Iterator<Long> keysIter=keys.iterator();
+ while (keysIter.hasNext()) {
+ Long key=keysIter.next();
+ ArrayList<PackageAccessEntry> packageNames=new ArrayList<PackageAccessEntry>();
+ ArrayList<String> dbPackageNames=dbPackageAccess.get(key);
+ Iterator<String> packIter=dbPackageNames.iterator();
+ while (packIter.hasNext()) {
+ String packageName=packIter.next();
+ PackageAccessEntry packageAccess = new PackageAccessEntry();
+ packageAccess.packageAccess=packageName;
+ packageAccess.needsDecrypt=true;
+ packageAccess.needsEncrypt=false;
+ packageNames.add(packageAccess);
+ }
+ packageAccessEntries.put(key, packageNames);
+ }
+ }
+ }
+
+ public static ArrayList<String> getPackageAccess(Long id) {
+ ArrayList<String> packageAccess=null;
+ if (packageAccessEntries.containsKey(id)) {
+ ArrayList<PackageAccessEntry> packageAccessEntry=packageAccessEntries.get(id);
+ Iterator<PackageAccessEntry> packIter=packageAccessEntry.iterator();
+ packageAccess=new ArrayList<String>();
+ while(packIter.hasNext()) {
+ PackageAccessEntry packEntry=packIter.next();
+ if (packEntry.needsDecrypt) {
+ try {
+ packEntry.plainPackageAccess=ch.decrypt(packEntry.packageAccess);
+ } catch (CryptoHelperException e) {
+ Log.e(TAG,e.toString());
+ }
+ }
+ packageAccess.add(packEntry.plainPackageAccess);
+ }
+ }
+ return packageAccess;
+ }
+
+ public static ArrayList<PackageAccessEntry> getPackageAccessEntries(Long id) {
+ if (packageAccessEntries.containsKey(id)) {
+ return packageAccessEntries.get(id);
+ }
+ return null;
+ }
+
+ public static void addPackageAccess(Long id, String packageName) {
+ String encryptedPackageName="";
+ try {
+ encryptedPackageName = ch.encrypt(packageName);
+ dbHelper.addPackageAccess(id, encryptedPackageName);
+ } catch (CryptoHelperException e) {
+ Log.e(TAG,e.toString());
+ return;
+ }
+
+ ArrayList<PackageAccessEntry> packageNames;
+ if (packageAccessEntries.containsKey(id)) {
+ packageNames=packageAccessEntries.get(id);
+ } else {
+ packageNames=new ArrayList<PackageAccessEntry>();
+ }
+ PackageAccessEntry newPackageAccessEntry=new PackageAccessEntry();
+ newPackageAccessEntry.plainPackageAccess=packageName;
+ newPackageAccessEntry.packageAccess=encryptedPackageName;
+ newPackageAccessEntry.needsDecrypt=false;
+ newPackageAccessEntry.needsEncrypt=false;
+ packageNames.add(newPackageAccessEntry);
+ packageAccessEntries.put(id, packageNames);
+ }
+}
}
dbHelper.commit();
dbHelper.close();
+ Passwords.Reset();
Toast.makeText(Restore.this, getString(R.string.restore_complete,
Integer.toString(totalPasswords)),