diff options
author | Andreas Straub <andy@strb.org> | 2015-07-20 15:13:14 +0200 |
---|---|---|
committer | Andreas Straub <andy@strb.org> | 2015-07-20 15:13:14 +0200 |
commit | aaa8fe8978ac0513a013a9c77efef663f32eda47 (patch) | |
tree | 16cc60f67a4fbded2eeeb7ba1001ebb1b1a004a5 /src/main/java/eu/siacs/conversations/persistance | |
parent | 9c91b9036aa57c610c26b4cdf52ee295eaa0dc95 (diff) | |
parent | 19a0ae42d667644ee3400c92c53ad0ad093c52fe (diff) |
Merge branch 'CryptoNextBeta' into development
* CryptoNextBeta: (60 commits)
Lock TrustKeys if no trusted keys are available
Optimize imports
Use MD style for key trust toggle switch
Fix set/remove OnUpdateBlocklistListener
Fix axolotl database migration
Remove device list from EditAccount
Add clear devices to overflow menu in EditAccount
Ask for key trust when sending messages
Encrypt files for HTTP upload in encrypted chats
Refactor trust key ui and show in account details
Send correct body for HTTP files
Handle file transmission properly in axolotl
Remove unneccessary code
Fix trust status for outgoing messages
Don't merge messages with different trust statuses
Fix copying of axolotl keys to clipboard
Add refresh icon to v21 theme
Disable Axolotl option if not usable
Show trust status of messages' originating session
Add key trust toggle to ContactDetailsActivity
...
Diffstat (limited to '')
-rw-r--r-- | src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java | 510 | ||||
-rw-r--r-- | src/main/java/eu/siacs/conversations/persistance/FileBackend.java | 30 |
2 files changed, 509 insertions, 31 deletions
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index d11b02fa..71b4cba9 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -1,10 +1,32 @@ package eu.siacs.conversations.persistance; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteCantOpenDatabaseException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Base64; +import android.util.Log; + +import org.whispersystems.libaxolotl.AxolotlAddress; +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.SessionRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; + +import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import eu.siacs.conversations.Config; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; @@ -13,19 +35,12 @@ import eu.siacs.conversations.entities.Roster; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteCantOpenDatabaseException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - public class DatabaseBackend extends SQLiteOpenHelper { private static DatabaseBackend instance = null; private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 14; + private static final int DATABASE_VERSION = 15; private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -33,12 +48,66 @@ public class DatabaseBackend extends SQLiteOpenHelper { + Contact.JID + " TEXT," + Contact.KEYS + " TEXT," + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER," + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, " - + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, " + + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, " + Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", " + Contact.JID + ") ON CONFLICT REPLACE);"; + private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE " + + AxolotlService.SQLiteAxolotlStore.PREKEY_TABLENAME + "(" + + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " TEXT, " + + AxolotlService.SQLiteAxolotlStore.ID + " INTEGER, " + + AxolotlService.SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" + + AxolotlService.SQLiteAxolotlStore.ACCOUNT + + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " + + "UNIQUE( " + AxolotlService.SQLiteAxolotlStore.ACCOUNT + ", " + + AxolotlService.SQLiteAxolotlStore.ID + + ") ON CONFLICT REPLACE" + +");"; + + private static String CREATE_SIGNED_PREKEYS_STATEMENT = "CREATE TABLE " + + AxolotlService.SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME + "(" + + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " TEXT, " + + AxolotlService.SQLiteAxolotlStore.ID + " INTEGER, " + + AxolotlService.SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" + + AxolotlService.SQLiteAxolotlStore.ACCOUNT + + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " + + "UNIQUE( " + AxolotlService.SQLiteAxolotlStore.ACCOUNT + ", " + + AxolotlService.SQLiteAxolotlStore.ID + + ") ON CONFLICT REPLACE"+ + ");"; + + private static String CREATE_SESSIONS_STATEMENT = "CREATE TABLE " + + AxolotlService.SQLiteAxolotlStore.SESSION_TABLENAME + "(" + + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " TEXT, " + + AxolotlService.SQLiteAxolotlStore.NAME + " TEXT, " + + AxolotlService.SQLiteAxolotlStore.DEVICE_ID + " INTEGER, " + + AxolotlService.SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" + + AxolotlService.SQLiteAxolotlStore.ACCOUNT + + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " + + "UNIQUE( " + AxolotlService.SQLiteAxolotlStore.ACCOUNT + ", " + + AxolotlService.SQLiteAxolotlStore.NAME + ", " + + AxolotlService.SQLiteAxolotlStore.DEVICE_ID + + ") ON CONFLICT REPLACE" + +");"; + + private static String CREATE_IDENTITIES_STATEMENT = "CREATE TABLE " + + AxolotlService.SQLiteAxolotlStore.IDENTITIES_TABLENAME + "(" + + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " TEXT, " + + AxolotlService.SQLiteAxolotlStore.NAME + " TEXT, " + + AxolotlService.SQLiteAxolotlStore.OWN + " INTEGER, " + + AxolotlService.SQLiteAxolotlStore.FINGERPRINT + " TEXT, " + + AxolotlService.SQLiteAxolotlStore.TRUSTED + " INTEGER, " + + AxolotlService.SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" + + AxolotlService.SQLiteAxolotlStore.ACCOUNT + + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " + + "UNIQUE( " + AxolotlService.SQLiteAxolotlStore.ACCOUNT + ", " + + AxolotlService.SQLiteAxolotlStore.NAME + ", " + + AxolotlService.SQLiteAxolotlStore.FINGERPRINT + + ") ON CONFLICT IGNORE" + +");"; + private DatabaseBackend(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @@ -69,12 +138,17 @@ public class DatabaseBackend extends SQLiteOpenHelper { + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, " + Message.RELATIVE_FILE_PATH + " TEXT, " + Message.SERVER_MSG_ID + " TEXT, " + + Message.FINGERPRINT + " TEXT, " + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY(" + Message.CONVERSATION + ") REFERENCES " + Conversation.TABLENAME + "(" + Conversation.UUID + ") ON DELETE CASCADE);"); db.execSQL(CREATE_CONTATCS_STATEMENT); + db.execSQL(CREATE_SESSIONS_STATEMENT); + db.execSQL(CREATE_PREKEYS_STATEMENT); + db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT); + db.execSQL(CREATE_IDENTITIES_STATEMENT); } @Override @@ -109,12 +183,12 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN " + Conversation.ATTRIBUTES + " TEXT"); } - if (oldVersion < 9 && newVersion >= 9) { - db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " - + Contact.LAST_TIME + " NUMBER"); - db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " - + Contact.LAST_PRESENCE + " TEXT"); - } + if (oldVersion < 9 && newVersion >= 9) { + db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + + Contact.LAST_TIME + " NUMBER"); + db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + + Contact.LAST_PRESENCE + " TEXT"); + } if (oldVersion < 10 && newVersion >= 10) { db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.RELATIVE_FILE_PATH + " TEXT"); @@ -215,6 +289,11 @@ public class DatabaseBackend extends SQLiteOpenHelper { } cursor.close(); } + if (oldVersion < 15 && newVersion >= 15) { + recreateAxolotlDb(db); + db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + + Message.FINGERPRINT + " TEXT"); + } } public static synchronized DatabaseBackend getInstance(Context context) { @@ -311,7 +390,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { }; Cursor cursor = db.query(Conversation.TABLENAME, null, Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID - + " like ? OR "+Conversation.CONTACTJID+"=?)", selectionArgs, null, null, null); + + " like ? OR " + Conversation.CONTACTJID + "=?)", selectionArgs, null, null, null); if (cursor.getCount() == 0) return null; cursor.moveToFirst(); @@ -481,4 +560,403 @@ public class DatabaseBackend extends SQLiteOpenHelper { cursor.close(); return list; } + + private Cursor getCursorForSession(Account account, AxolotlAddress contact) { + final SQLiteDatabase db = this.getReadableDatabase(); + String[] columns = null; + String[] selectionArgs = {account.getUuid(), + contact.getName(), + Integer.toString(contact.getDeviceId())}; + Cursor cursor = db.query(AxolotlService.SQLiteAxolotlStore.SESSION_TABLENAME, + columns, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ? AND " + + AxolotlService.SQLiteAxolotlStore.NAME + " = ? AND " + + AxolotlService.SQLiteAxolotlStore.DEVICE_ID + " = ? ", + selectionArgs, + null, null, null); + + return cursor; + } + + public SessionRecord loadSession(Account account, AxolotlAddress contact) { + SessionRecord session = null; + Cursor cursor = getCursorForSession(account, contact); + if(cursor.getCount() != 0) { + cursor.moveToFirst(); + try { + session = new SessionRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(AxolotlService.SQLiteAxolotlStore.KEY)),Base64.DEFAULT)); + } catch (IOException e) { + cursor.close(); + throw new AssertionError(e); + } + } + cursor.close(); + return session; + } + + public List<Integer> getSubDeviceSessions(Account account, AxolotlAddress contact) { + List<Integer> devices = new ArrayList<>(); + final SQLiteDatabase db = this.getReadableDatabase(); + String[] columns = {AxolotlService.SQLiteAxolotlStore.DEVICE_ID}; + String[] selectionArgs = {account.getUuid(), + contact.getName()}; + Cursor cursor = db.query(AxolotlService.SQLiteAxolotlStore.SESSION_TABLENAME, + columns, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ? AND " + + AxolotlService.SQLiteAxolotlStore.NAME + " = ?", + selectionArgs, + null, null, null); + + while(cursor.moveToNext()) { + devices.add(cursor.getInt( + cursor.getColumnIndex(AxolotlService.SQLiteAxolotlStore.DEVICE_ID))); + } + + cursor.close(); + return devices; + } + + public boolean containsSession(Account account, AxolotlAddress contact) { + Cursor cursor = getCursorForSession(account, contact); + int count = cursor.getCount(); + cursor.close(); + return count != 0; + } + + public void storeSession(Account account, AxolotlAddress contact, SessionRecord session) { + SQLiteDatabase db = this.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(AxolotlService.SQLiteAxolotlStore.NAME, contact.getName()); + values.put(AxolotlService.SQLiteAxolotlStore.DEVICE_ID, contact.getDeviceId()); + values.put(AxolotlService.SQLiteAxolotlStore.KEY, Base64.encodeToString(session.serialize(),Base64.DEFAULT)); + values.put(AxolotlService.SQLiteAxolotlStore.ACCOUNT, account.getUuid()); + db.insert(AxolotlService.SQLiteAxolotlStore.SESSION_TABLENAME, null, values); + } + + public void deleteSession(Account account, AxolotlAddress contact) { + SQLiteDatabase db = this.getWritableDatabase(); + String[] args = {account.getUuid(), + contact.getName(), + Integer.toString(contact.getDeviceId())}; + db.delete(AxolotlService.SQLiteAxolotlStore.SESSION_TABLENAME, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ? AND " + + AxolotlService.SQLiteAxolotlStore.NAME + " = ? AND " + + AxolotlService.SQLiteAxolotlStore.DEVICE_ID + " = ? ", + args); + } + + public void deleteAllSessions(Account account, AxolotlAddress contact) { + SQLiteDatabase db = this.getWritableDatabase(); + String[] args = {account.getUuid(), contact.getName()}; + db.delete(AxolotlService.SQLiteAxolotlStore.SESSION_TABLENAME, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + "=? AND " + + AxolotlService.SQLiteAxolotlStore.NAME + " = ?", + args); + } + + private Cursor getCursorForPreKey(Account account, int preKeyId) { + SQLiteDatabase db = this.getReadableDatabase(); + String[] columns = {AxolotlService.SQLiteAxolotlStore.KEY}; + String[] selectionArgs = {account.getUuid(), Integer.toString(preKeyId)}; + Cursor cursor = db.query(AxolotlService.SQLiteAxolotlStore.PREKEY_TABLENAME, + columns, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + "=? AND " + + AxolotlService.SQLiteAxolotlStore.ID + "=?", + selectionArgs, + null, null, null); + + return cursor; + } + + public PreKeyRecord loadPreKey(Account account, int preKeyId) { + PreKeyRecord record = null; + Cursor cursor = getCursorForPreKey(account, preKeyId); + if(cursor.getCount() != 0) { + cursor.moveToFirst(); + try { + record = new PreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(AxolotlService.SQLiteAxolotlStore.KEY)),Base64.DEFAULT)); + } catch (IOException e ) { + throw new AssertionError(e); + } + } + cursor.close(); + return record; + } + + public boolean containsPreKey(Account account, int preKeyId) { + Cursor cursor = getCursorForPreKey(account, preKeyId); + int count = cursor.getCount(); + cursor.close(); + return count != 0; + } + + public void storePreKey(Account account, PreKeyRecord record) { + SQLiteDatabase db = this.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(AxolotlService.SQLiteAxolotlStore.ID, record.getId()); + values.put(AxolotlService.SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(),Base64.DEFAULT)); + values.put(AxolotlService.SQLiteAxolotlStore.ACCOUNT, account.getUuid()); + db.insert(AxolotlService.SQLiteAxolotlStore.PREKEY_TABLENAME, null, values); + } + + public void deletePreKey(Account account, int preKeyId) { + SQLiteDatabase db = this.getWritableDatabase(); + String[] args = {account.getUuid(), Integer.toString(preKeyId)}; + db.delete(AxolotlService.SQLiteAxolotlStore.PREKEY_TABLENAME, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + "=? AND " + + AxolotlService.SQLiteAxolotlStore.ID + "=?", + args); + } + + private Cursor getCursorForSignedPreKey(Account account, int signedPreKeyId) { + SQLiteDatabase db = this.getReadableDatabase(); + String[] columns = {AxolotlService.SQLiteAxolotlStore.KEY}; + String[] selectionArgs = {account.getUuid(), Integer.toString(signedPreKeyId)}; + Cursor cursor = db.query(AxolotlService.SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, + columns, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + "=? AND " + AxolotlService.SQLiteAxolotlStore.ID + "=?", + selectionArgs, + null, null, null); + + return cursor; + } + + public SignedPreKeyRecord loadSignedPreKey(Account account, int signedPreKeyId) { + SignedPreKeyRecord record = null; + Cursor cursor = getCursorForSignedPreKey(account, signedPreKeyId); + if(cursor.getCount() != 0) { + cursor.moveToFirst(); + try { + record = new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(AxolotlService.SQLiteAxolotlStore.KEY)),Base64.DEFAULT)); + } catch (IOException e ) { + throw new AssertionError(e); + } + } + cursor.close(); + return record; + } + + public List<SignedPreKeyRecord> loadSignedPreKeys(Account account) { + List<SignedPreKeyRecord> prekeys = new ArrayList<>(); + SQLiteDatabase db = this.getReadableDatabase(); + String[] columns = {AxolotlService.SQLiteAxolotlStore.KEY}; + String[] selectionArgs = {account.getUuid()}; + Cursor cursor = db.query(AxolotlService.SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, + columns, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + "=?", + selectionArgs, + null, null, null); + + while(cursor.moveToNext()) { + try { + prekeys.add(new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(AxolotlService.SQLiteAxolotlStore.KEY)), Base64.DEFAULT))); + } catch (IOException ignored) { + } + } + cursor.close(); + return prekeys; + } + + public boolean containsSignedPreKey(Account account, int signedPreKeyId) { + Cursor cursor = getCursorForPreKey(account, signedPreKeyId); + int count = cursor.getCount(); + cursor.close(); + return count != 0; + } + + public void storeSignedPreKey(Account account, SignedPreKeyRecord record) { + SQLiteDatabase db = this.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(AxolotlService.SQLiteAxolotlStore.ID, record.getId()); + values.put(AxolotlService.SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(),Base64.DEFAULT)); + values.put(AxolotlService.SQLiteAxolotlStore.ACCOUNT, account.getUuid()); + db.insert(AxolotlService.SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, null, values); + } + + public void deleteSignedPreKey(Account account, int signedPreKeyId) { + SQLiteDatabase db = this.getWritableDatabase(); + String[] args = {account.getUuid(), Integer.toString(signedPreKeyId)}; + db.delete(AxolotlService.SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + "=? AND " + + AxolotlService.SQLiteAxolotlStore.ID + "=?", + args); + } + + private Cursor getIdentityKeyCursor(Account account, String name, boolean own) { + return getIdentityKeyCursor(account, name, own, null); + } + + private Cursor getIdentityKeyCursor(Account account, String fingerprint) { + return getIdentityKeyCursor(account, null, null, fingerprint); + } + + private Cursor getIdentityKeyCursor(Account account, String name, Boolean own, String fingerprint) { + final SQLiteDatabase db = this.getReadableDatabase(); + String[] columns = {AxolotlService.SQLiteAxolotlStore.TRUSTED, + AxolotlService.SQLiteAxolotlStore.KEY}; + ArrayList<String> selectionArgs = new ArrayList<>(4); + selectionArgs.add(account.getUuid()); + String selectionString = AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ?"; + if (name != null){ + selectionArgs.add(name); + selectionString += " AND " +AxolotlService.SQLiteAxolotlStore.NAME + " = ?"; + } + if (fingerprint != null){ + selectionArgs.add(fingerprint); + selectionString += " AND " +AxolotlService.SQLiteAxolotlStore.FINGERPRINT + " = ?"; + } + if (own != null){ + selectionArgs.add(own?"1":"0"); + selectionString += " AND " +AxolotlService.SQLiteAxolotlStore.OWN + " = ?"; + } + Cursor cursor = db.query(AxolotlService.SQLiteAxolotlStore.IDENTITIES_TABLENAME, + columns, + selectionString, + selectionArgs.toArray(new String[selectionArgs.size()]), + null, null, null); + + return cursor; + } + + public IdentityKeyPair loadOwnIdentityKeyPair(Account account, String name) { + IdentityKeyPair identityKeyPair = null; + Cursor cursor = getIdentityKeyCursor(account, name, true); + if(cursor.getCount() != 0) { + cursor.moveToFirst(); + try { + identityKeyPair = new IdentityKeyPair(Base64.decode(cursor.getString(cursor.getColumnIndex(AxolotlService.SQLiteAxolotlStore.KEY)),Base64.DEFAULT)); + } catch (InvalidKeyException e) { + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name); + } + } + cursor.close(); + + return identityKeyPair; + } + + public Set<IdentityKey> loadIdentityKeys(Account account, String name) { + return loadIdentityKeys(account, name, null); + } + + public Set<IdentityKey> loadIdentityKeys(Account account, String name, AxolotlService.SQLiteAxolotlStore.Trust trust) { + Set<IdentityKey> identityKeys = new HashSet<>(); + Cursor cursor = getIdentityKeyCursor(account, name, false); + + while(cursor.moveToNext()) { + if ( trust != null && + cursor.getInt(cursor.getColumnIndex(AxolotlService.SQLiteAxolotlStore.TRUSTED)) + != trust.ordinal()) { + continue; + } + try { + identityKeys.add(new IdentityKey(Base64.decode(cursor.getString(cursor.getColumnIndex(AxolotlService.SQLiteAxolotlStore.KEY)),Base64.DEFAULT),0)); + } catch (InvalidKeyException e) { + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Encountered invalid IdentityKey in database for account"+account.getJid().toBareJid()+", address: "+name); + } + } + cursor.close(); + + return identityKeys; + } + + public long numTrustedKeys(Account account, String name) { + SQLiteDatabase db = getReadableDatabase(); + String[] args = { + account.getUuid(), + name + }; + return DatabaseUtils.queryNumEntries(db, AxolotlService.SQLiteAxolotlStore.IDENTITIES_TABLENAME, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ?" + + " AND " + AxolotlService.SQLiteAxolotlStore.NAME + " = ?", + args + ); + } + + private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized) { + storeIdentityKey(account, name, own, fingerprint, base64Serialized, AxolotlService.SQLiteAxolotlStore.Trust.UNDECIDED); + } + + private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized, AxolotlService.SQLiteAxolotlStore.Trust trusted) { + SQLiteDatabase db = this.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(AxolotlService.SQLiteAxolotlStore.ACCOUNT, account.getUuid()); + values.put(AxolotlService.SQLiteAxolotlStore.NAME, name); + values.put(AxolotlService.SQLiteAxolotlStore.OWN, own ? 1 : 0); + values.put(AxolotlService.SQLiteAxolotlStore.FINGERPRINT, fingerprint); + values.put(AxolotlService.SQLiteAxolotlStore.KEY, base64Serialized); + values.put(AxolotlService.SQLiteAxolotlStore.TRUSTED, trusted.ordinal()); + db.insert(AxolotlService.SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values); + } + + public AxolotlService.SQLiteAxolotlStore.Trust isIdentityKeyTrusted(Account account, String fingerprint) { + Cursor cursor = getIdentityKeyCursor(account, fingerprint); + AxolotlService.SQLiteAxolotlStore.Trust trust = null; + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + int trustValue = cursor.getInt(cursor.getColumnIndex(AxolotlService.SQLiteAxolotlStore.TRUSTED)); + trust = AxolotlService.SQLiteAxolotlStore.Trust.values()[trustValue]; + } + cursor.close(); + return trust; + } + + public boolean setIdentityKeyTrust(Account account, String fingerprint, AxolotlService.SQLiteAxolotlStore.Trust trust) { + SQLiteDatabase db = this.getWritableDatabase(); + String[] selectionArgs = { + account.getUuid(), + fingerprint + }; + ContentValues values = new ContentValues(); + values.put(AxolotlService.SQLiteAxolotlStore.TRUSTED, trust.ordinal()); + int rows = db.update(AxolotlService.SQLiteAxolotlStore.IDENTITIES_TABLENAME, values, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ? AND " + + AxolotlService.SQLiteAxolotlStore.FINGERPRINT + " = ? ", + selectionArgs); + return rows == 1; + } + + public void storeIdentityKey(Account account, String name, IdentityKey identityKey) { + storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT)); + } + + public void storeOwnIdentityKeyPair(Account account, String name, IdentityKeyPair identityKeyPair) { + storeIdentityKey(account, name, true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), AxolotlService.SQLiteAxolotlStore.Trust.TRUSTED); + } + + public void recreateAxolotlDb() { + recreateAxolotlDb(getWritableDatabase()); + } + + public void recreateAxolotlDb(SQLiteDatabase db) { + Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+">>> (RE)CREATING AXOLOTL DATABASE <<<"); + db.execSQL("DROP TABLE IF EXISTS " + AxolotlService.SQLiteAxolotlStore.SESSION_TABLENAME); + db.execSQL(CREATE_SESSIONS_STATEMENT); + db.execSQL("DROP TABLE IF EXISTS " + AxolotlService.SQLiteAxolotlStore.PREKEY_TABLENAME); + db.execSQL(CREATE_PREKEYS_STATEMENT); + db.execSQL("DROP TABLE IF EXISTS " + AxolotlService.SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME); + db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT); + db.execSQL("DROP TABLE IF EXISTS " + AxolotlService.SQLiteAxolotlStore.IDENTITIES_TABLENAME); + db.execSQL(CREATE_IDENTITIES_STATEMENT); + } + + public void wipeAxolotlDb(Account account) { + String accountName = account.getUuid(); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<"); + SQLiteDatabase db = this.getWritableDatabase(); + String[] deleteArgs= { + accountName + }; + db.delete(AxolotlService.SQLiteAxolotlStore.SESSION_TABLENAME, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ?", + deleteArgs); + db.delete(AxolotlService.SQLiteAxolotlStore.PREKEY_TABLENAME, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ?", + deleteArgs); + db.delete(AxolotlService.SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ?", + deleteArgs); + db.delete(AxolotlService.SQLiteAxolotlStore.IDENTITIES_TABLENAME, + AxolotlService.SQLiteAxolotlStore.ACCOUNT + " = ?", + deleteArgs); + } } diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index ab191285..d607345e 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -1,5 +1,19 @@ package eu.siacs.conversations.persistance; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.util.Base64; +import android.util.Base64OutputStream; +import android.util.Log; +import android.webkit.MimeTypeMap; + import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; @@ -17,25 +31,11 @@ import java.util.Arrays; import java.util.Date; import java.util.Locale; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.RectF; -import android.net.Uri; -import android.os.Environment; -import android.provider.MediaStore; -import android.util.Base64; -import android.util.Base64OutputStream; -import android.util.Log; -import android.webkit.MimeTypeMap; - import eu.siacs.conversations.Config; import eu.siacs.conversations.R; -import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.ExifHelper; |