2014-02-28 17:46:01 +00:00
package eu.siacs.conversations.persistance ;
2014-01-24 22:58:51 +00:00
2015-06-25 14:56:34 +00:00
import android.content.ContentValues ;
import android.content.Context ;
import android.database.Cursor ;
2015-07-20 12:56:41 +00:00
import android.database.DatabaseUtils ;
2015-06-25 14:56:34 +00:00
import android.database.sqlite.SQLiteCantOpenDatabaseException ;
import android.database.sqlite.SQLiteDatabase ;
import android.database.sqlite.SQLiteOpenHelper ;
2017-01-25 12:22:20 +00:00
import android.os.Environment ;
2015-06-25 14:56:34 +00:00
import android.util.Base64 ;
import android.util.Log ;
2015-12-11 12:52:04 +00:00
import android.util.Pair ;
2015-06-25 14:56:34 +00:00
2016-09-16 09:07:52 +00:00
import org.json.JSONObject ;
2017-06-18 14:35:30 +00:00
import org.whispersystems.libsignal.SignalProtocolAddress ;
import org.whispersystems.libsignal.IdentityKey ;
import org.whispersystems.libsignal.IdentityKeyPair ;
import org.whispersystems.libsignal.InvalidKeyException ;
import org.whispersystems.libsignal.state.PreKeyRecord ;
import org.whispersystems.libsignal.state.SessionRecord ;
import org.whispersystems.libsignal.state.SignedPreKeyRecord ;
2015-06-25 14:56:34 +00:00
2015-12-23 18:18:53 +00:00
import java.io.ByteArrayInputStream ;
2017-01-25 12:22:20 +00:00
import java.io.File ;
2015-05-29 09:17:26 +00:00
import java.io.IOException ;
2015-12-23 16:41:26 +00:00
import java.security.cert.CertificateEncodingException ;
2015-12-23 18:18:53 +00:00
import java.security.cert.CertificateException ;
import java.security.cert.CertificateFactory ;
2015-12-23 16:41:26 +00:00
import java.security.cert.X509Certificate ;
2014-01-24 22:58:51 +00:00
import java.util.ArrayList ;
2016-11-14 21:27:41 +00:00
import java.util.HashMap ;
2015-06-29 11:53:39 +00:00
import java.util.HashSet ;
2015-09-20 21:17:32 +00:00
import java.util.Iterator ;
2014-01-24 22:58:51 +00:00
import java.util.List ;
2016-11-14 21:27:41 +00:00
import java.util.Map ;
2015-06-29 11:53:39 +00:00
import java.util.Set ;
2014-03-11 16:47:05 +00:00
import java.util.concurrent.CopyOnWriteArrayList ;
2016-01-13 02:53:38 +00:00
import org.json.JSONException ;
2014-01-24 22:58:51 +00:00
2015-05-14 13:25:52 +00:00
import eu.siacs.conversations.Config ;
2015-05-29 09:17:26 +00:00
import eu.siacs.conversations.crypto.axolotl.AxolotlService ;
2016-11-14 21:27:41 +00:00
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus ;
2015-07-28 20:00:54 +00:00
import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.entities.Account ;
import eu.siacs.conversations.entities.Contact ;
import eu.siacs.conversations.entities.Conversation ;
import eu.siacs.conversations.entities.Message ;
2016-04-22 19:25:06 +00:00
import eu.siacs.conversations.entities.PresenceTemplate ;
2014-05-19 13:15:09 +00:00
import eu.siacs.conversations.entities.Roster ;
2016-01-13 02:53:38 +00:00
import eu.siacs.conversations.entities.ServiceDiscoveryResult ;
2017-05-31 14:45:51 +00:00
import eu.siacs.conversations.services.ShortcutService ;
2017-06-25 16:18:13 +00:00
import eu.siacs.conversations.utils.CryptoHelper ;
2017-01-25 12:22:20 +00:00
import eu.siacs.conversations.utils.MimeUtils ;
2015-05-14 13:25:52 +00:00
import eu.siacs.conversations.xmpp.jid.InvalidJidException ;
2014-11-05 20:55:47 +00:00
import eu.siacs.conversations.xmpp.jid.Jid ;
2017-05-07 19:05:35 +00:00
import eu.siacs.conversations.xmpp.mam.MamReference ;
2014-11-05 20:55:47 +00:00
2014-01-24 22:58:51 +00:00
public class DatabaseBackend extends SQLiteOpenHelper {
2014-01-26 02:27:55 +00:00
2014-01-24 22:58:51 +00:00
private static DatabaseBackend instance = null ;
2014-01-26 02:27:55 +00:00
2014-01-24 22:58:51 +00:00
private static final String DATABASE_NAME = " history " ;
2017-02-07 20:17:08 +00:00
private static final int DATABASE_VERSION = 35 ;
2014-05-19 13:15:09 +00:00
private static String CREATE_CONTATCS_STATEMENT = " create table "
2014-07-29 12:42:17 +00:00
+ Contact . TABLENAME + " ( " + Contact . ACCOUNT + " TEXT, "
+ Contact . SERVERNAME + " TEXT, " + Contact . SYSTEMNAME + " TEXT, "
+ Contact . JID + " TEXT, " + Contact . KEYS + " TEXT, "
+ Contact . PHOTOURI + " TEXT, " + Contact . OPTIONS + " NUMBER, "
2014-08-24 18:53:13 +00:00
+ Contact . SYSTEMACCOUNT + " NUMBER, " + Contact . AVATAR + " TEXT, "
2015-06-26 13:41:02 +00:00
+ Contact . LAST_PRESENCE + " TEXT, " + Contact . LAST_TIME + " NUMBER, "
2014-11-16 16:21:21 +00:00
+ Contact . GROUPS + " TEXT, FOREIGN KEY( " + Contact . ACCOUNT + " ) REFERENCES "
2014-08-24 18:53:13 +00:00
+ Account . TABLENAME + " ( " + Account . UUID
+ " ) ON DELETE CASCADE, UNIQUE( " + Contact . ACCOUNT + " , "
+ Contact . JID + " ) ON CONFLICT REPLACE); " ;
2014-01-24 22:58:51 +00:00
2016-01-13 02:53:38 +00:00
private static String CREATE_DISCOVERY_RESULTS_STATEMENT = " create table "
+ ServiceDiscoveryResult . TABLENAME + " ( "
+ ServiceDiscoveryResult . HASH + " TEXT, "
+ ServiceDiscoveryResult . VER + " TEXT, "
+ ServiceDiscoveryResult . RESULT + " TEXT, "
+ " UNIQUE( " + ServiceDiscoveryResult . HASH + " , "
+ ServiceDiscoveryResult . VER + " ) ON CONFLICT REPLACE); " ;
2016-04-22 19:25:06 +00:00
private static String CREATE_PRESENCE_TEMPLATES_STATEMENT = " CREATE TABLE "
+ PresenceTemplate . TABELNAME + " ( "
+ PresenceTemplate . UUID + " TEXT, "
+ PresenceTemplate . LAST_USED + " NUMBER, "
+ PresenceTemplate . MESSAGE + " TEXT, "
+ PresenceTemplate . STATUS + " TEXT, "
+ " UNIQUE( " + PresenceTemplate . MESSAGE + " , " + PresenceTemplate . STATUS + " ) ON CONFLICT REPLACE); " ;
2015-05-29 09:17:26 +00:00
private static String CREATE_PREKEYS_STATEMENT = " CREATE TABLE "
2015-07-28 20:00:54 +00:00
+ SQLiteAxolotlStore . PREKEY_TABLENAME + " ( "
2015-11-28 19:11:38 +00:00
+ SQLiteAxolotlStore . ACCOUNT + " TEXT, "
+ SQLiteAxolotlStore . ID + " INTEGER, "
+ SQLiteAxolotlStore . KEY + " TEXT, FOREIGN KEY( "
+ SQLiteAxolotlStore . ACCOUNT
+ " ) REFERENCES " + Account . TABLENAME + " ( " + Account . UUID + " ) ON DELETE CASCADE, "
+ " UNIQUE( " + SQLiteAxolotlStore . ACCOUNT + " , "
+ SQLiteAxolotlStore . ID
+ " ) ON CONFLICT REPLACE "
+ " ); " ;
2015-05-29 09:17:26 +00:00
private static String CREATE_SIGNED_PREKEYS_STATEMENT = " CREATE TABLE "
2015-07-28 20:00:54 +00:00
+ SQLiteAxolotlStore . SIGNED_PREKEY_TABLENAME + " ( "
2015-11-28 19:11:38 +00:00
+ SQLiteAxolotlStore . ACCOUNT + " TEXT, "
+ SQLiteAxolotlStore . ID + " INTEGER, "
+ SQLiteAxolotlStore . KEY + " TEXT, FOREIGN KEY( "
+ SQLiteAxolotlStore . ACCOUNT
+ " ) REFERENCES " + Account . TABLENAME + " ( " + Account . UUID + " ) ON DELETE CASCADE, "
+ " UNIQUE( " + SQLiteAxolotlStore . ACCOUNT + " , "
+ SQLiteAxolotlStore . ID
+ " ) ON CONFLICT REPLACE " +
2015-05-29 09:17:26 +00:00
" ); " ;
private static String CREATE_SESSIONS_STATEMENT = " CREATE TABLE "
2015-07-28 20:00:54 +00:00
+ SQLiteAxolotlStore . SESSION_TABLENAME + " ( "
2015-11-28 19:11:38 +00:00
+ SQLiteAxolotlStore . ACCOUNT + " TEXT, "
+ SQLiteAxolotlStore . NAME + " TEXT, "
+ SQLiteAxolotlStore . DEVICE_ID + " INTEGER, "
+ SQLiteAxolotlStore . KEY + " TEXT, FOREIGN KEY( "
+ SQLiteAxolotlStore . ACCOUNT
+ " ) REFERENCES " + Account . TABLENAME + " ( " + Account . UUID + " ) ON DELETE CASCADE, "
+ " UNIQUE( " + SQLiteAxolotlStore . ACCOUNT + " , "
+ SQLiteAxolotlStore . NAME + " , "
+ SQLiteAxolotlStore . DEVICE_ID
+ " ) ON CONFLICT REPLACE "
+ " ); " ;
2015-05-29 09:17:26 +00:00
2015-06-29 11:53:39 +00:00
private static String CREATE_IDENTITIES_STATEMENT = " CREATE TABLE "
2015-07-28 20:00:54 +00:00
+ SQLiteAxolotlStore . IDENTITIES_TABLENAME + " ( "
+ SQLiteAxolotlStore . ACCOUNT + " TEXT, "
+ SQLiteAxolotlStore . NAME + " TEXT, "
+ SQLiteAxolotlStore . OWN + " INTEGER, "
+ SQLiteAxolotlStore . FINGERPRINT + " TEXT, "
2015-12-23 16:41:26 +00:00
+ SQLiteAxolotlStore . CERTIFICATE + " BLOB, "
2016-11-14 21:27:41 +00:00
+ SQLiteAxolotlStore . TRUST + " TEXT, "
+ SQLiteAxolotlStore . ACTIVE + " NUMBER, "
2016-11-19 12:34:54 +00:00
+ SQLiteAxolotlStore . LAST_ACTIVATION + " NUMBER, "
2015-07-28 20:00:54 +00:00
+ SQLiteAxolotlStore . KEY + " TEXT, FOREIGN KEY( "
+ SQLiteAxolotlStore . ACCOUNT
2015-07-10 00:24:33 +00:00
+ " ) REFERENCES " + Account . TABLENAME + " ( " + Account . UUID + " ) ON DELETE CASCADE, "
2015-07-28 20:00:54 +00:00
+ " UNIQUE( " + SQLiteAxolotlStore . ACCOUNT + " , "
2015-11-28 19:11:38 +00:00
+ SQLiteAxolotlStore . NAME + " , "
+ SQLiteAxolotlStore . FINGERPRINT
2015-07-10 00:24:33 +00:00
+ " ) ON CONFLICT IGNORE "
2015-11-28 19:11:38 +00:00
+ " ); " ;
2015-06-29 11:53:39 +00:00
2016-11-08 20:37:44 +00:00
private static String START_TIMES_TABLE = " start_times " ;
private static String CREATE_START_TIMES_TABLE = " create table " + START_TIMES_TABLE + " (timestamp NUMBER); " ;
2016-11-08 11:20:07 +00:00
2017-01-25 12:22:20 +00:00
private static String CREATE_MESSAGE_TIME_INDEX = " create INDEX message_time_index ON " + Message . TABLENAME + " ( " + Message . TIME_SENT + " ) " ;
2017-02-07 20:17:08 +00:00
private static String CREATE_MESSAGE_CONVERSATION_INDEX = " create INDEX message_conversation_index ON " + Message . TABLENAME + " ( " + Message . CONVERSATION + " ) " ;
2017-01-25 12:22:20 +00:00
2014-09-19 15:21:33 +00:00
private DatabaseBackend ( Context context ) {
2014-01-24 22:58:51 +00:00
super ( context , DATABASE_NAME , null , DATABASE_VERSION ) ;
}
@Override
public void onCreate ( SQLiteDatabase db ) {
2014-01-28 23:15:38 +00:00
db . execSQL ( " PRAGMA foreign_keys=ON; " ) ;
2016-04-25 09:06:17 +00:00
db . execSQL ( " create table " + Account . TABLENAME + " ( " + Account . UUID + " TEXT PRIMARY KEY, "
+ Account . USERNAME + " TEXT, "
+ Account . SERVER + " TEXT, "
+ Account . PASSWORD + " TEXT, "
2015-10-29 12:41:08 +00:00
+ Account . DISPLAY_NAME + " TEXT, "
2016-04-25 09:06:17 +00:00
+ Account . STATUS + " TEXT, "
+ Account . STATUS_MESSAGE + " TEXT, "
+ Account . ROSTERVERSION + " TEXT, "
+ Account . OPTIONS + " NUMBER, "
+ Account . AVATAR + " TEXT, "
+ Account . KEYS + " TEXT, "
+ Account . HOSTNAME + " TEXT, "
+ Account . PORT + " NUMBER DEFAULT 5222) " ) ;
2014-01-26 02:27:55 +00:00
db . execSQL ( " create table " + Conversation . TABLENAME + " ( "
2014-01-27 19:40:42 +00:00
+ Conversation . UUID + " TEXT PRIMARY KEY, " + Conversation . NAME
2014-02-10 14:24:34 +00:00
+ " TEXT, " + Conversation . CONTACT + " TEXT, "
+ Conversation . ACCOUNT + " TEXT, " + Conversation . CONTACTJID
2014-01-27 19:40:42 +00:00
+ " TEXT, " + Conversation . CREATED + " NUMBER, "
2014-09-27 16:16:31 +00:00
+ Conversation . STATUS + " NUMBER, " + Conversation . MODE
2014-09-28 13:21:56 +00:00
+ " NUMBER, " + Conversation . ATTRIBUTES + " TEXT, FOREIGN KEY( "
+ Conversation . ACCOUNT + " ) REFERENCES " + Account . TABLENAME
+ " ( " + Account . UUID + " ) ON DELETE CASCADE); " ) ;
2014-01-27 19:40:42 +00:00
db . execSQL ( " create table " + Message . TABLENAME + " ( " + Message . UUID
2014-01-28 23:15:38 +00:00
+ " TEXT PRIMARY KEY, " + Message . CONVERSATION + " TEXT, "
+ Message . TIME_SENT + " NUMBER, " + Message . COUNTERPART
2014-07-29 12:42:17 +00:00
+ " TEXT, " + Message . TRUE_COUNTERPART + " TEXT, "
+ Message . BODY + " TEXT, " + Message . ENCRYPTION + " NUMBER, "
2014-08-24 18:53:13 +00:00
+ Message . STATUS + " NUMBER, " + Message . TYPE + " NUMBER, "
2014-11-13 20:04:05 +00:00
+ Message . RELATIVE_FILE_PATH + " TEXT, "
2014-12-09 20:41:49 +00:00
+ Message . SERVER_MSG_ID + " TEXT, "
2015-07-09 12:23:17 +00:00
+ Message . FINGERPRINT + " TEXT, "
2015-07-31 11:08:35 +00:00
+ Message . CARBON + " INTEGER, "
2016-02-15 22:15:04 +00:00
+ Message . EDITED + " TEXT, "
2015-10-14 19:18:34 +00:00
+ Message . READ + " NUMBER DEFAULT 1, "
2016-03-04 19:09:21 +00:00
+ Message . OOB + " INTEGER, "
2016-10-26 10:26:04 +00:00
+ Message . ERROR_MESSAGE + " TEXT, "
2014-08-24 18:53:13 +00:00
+ Message . REMOTE_MSG_ID + " TEXT, FOREIGN KEY( "
+ Message . CONVERSATION + " ) REFERENCES "
+ Conversation . TABLENAME + " ( " + Conversation . UUID
+ " ) ON DELETE CASCADE); " ) ;
2017-01-25 12:22:20 +00:00
db . execSQL ( CREATE_MESSAGE_TIME_INDEX ) ;
2017-02-07 20:17:08 +00:00
db . execSQL ( CREATE_MESSAGE_CONVERSATION_INDEX ) ;
2014-05-19 13:15:09 +00:00
db . execSQL ( CREATE_CONTATCS_STATEMENT ) ;
2016-01-13 02:53:38 +00:00
db . execSQL ( CREATE_DISCOVERY_RESULTS_STATEMENT ) ;
2015-06-29 11:40:56 +00:00
db . execSQL ( CREATE_SESSIONS_STATEMENT ) ;
db . execSQL ( CREATE_PREKEYS_STATEMENT ) ;
db . execSQL ( CREATE_SIGNED_PREKEYS_STATEMENT ) ;
2015-06-29 11:53:39 +00:00
db . execSQL ( CREATE_IDENTITIES_STATEMENT ) ;
2016-04-22 19:25:06 +00:00
db . execSQL ( CREATE_PRESENCE_TEMPLATES_STATEMENT ) ;
2016-11-08 11:20:07 +00:00
db . execSQL ( CREATE_START_TIMES_TABLE ) ;
2014-01-24 22:58:51 +00:00
}
@Override
2014-04-03 20:28:37 +00:00
public void onUpgrade ( SQLiteDatabase db , int oldVersion , int newVersion ) {
if ( oldVersion < 2 & & newVersion > = 2 ) {
2014-05-19 13:15:09 +00:00
db . execSQL ( " update " + Account . TABLENAME + " set "
+ Account . OPTIONS + " = " + Account . OPTIONS + " | 8 " ) ;
2014-04-03 20:28:37 +00:00
}
2014-04-06 13:34:08 +00:00
if ( oldVersion < 3 & & newVersion > = 3 ) {
2014-05-19 13:15:09 +00:00
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN "
+ Message . TYPE + " NUMBER " ) ;
}
if ( oldVersion < 5 & & newVersion > = 5 ) {
2014-07-29 12:42:17 +00:00
db . execSQL ( " DROP TABLE " + Contact . TABLENAME ) ;
2014-05-19 13:15:09 +00:00
db . execSQL ( CREATE_CONTATCS_STATEMENT ) ;
2014-07-29 12:42:17 +00:00
db . execSQL ( " UPDATE " + Account . TABLENAME + " SET "
+ Account . ROSTERVERSION + " = NULL " ) ;
}
if ( oldVersion < 6 & & newVersion > = 6 ) {
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN "
+ Message . TRUE_COUNTERPART + " TEXT " ) ;
2014-04-06 13:34:08 +00:00
}
2014-08-21 10:32:50 +00:00
if ( oldVersion < 7 & & newVersion > = 7 ) {
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN "
+ Message . REMOTE_MSG_ID + " TEXT " ) ;
db . execSQL ( " ALTER TABLE " + Contact . TABLENAME + " ADD COLUMN "
+ Contact . AVATAR + " TEXT " ) ;
db . execSQL ( " ALTER TABLE " + Account . TABLENAME + " ADD COLUMN "
+ Account . AVATAR + " TEXT " ) ;
}
2014-09-27 16:16:31 +00:00
if ( oldVersion < 8 & & newVersion > = 8 ) {
db . execSQL ( " ALTER TABLE " + Conversation . TABLENAME + " ADD COLUMN "
+ Conversation . ATTRIBUTES + " TEXT " ) ;
}
2015-06-26 13:41:02 +00:00
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 " ) ;
}
2014-11-13 20:04:05 +00:00
if ( oldVersion < 10 & & newVersion > = 10 ) {
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN "
+ Message . RELATIVE_FILE_PATH + " TEXT " ) ;
}
2014-11-16 16:21:21 +00:00
if ( oldVersion < 11 & & newVersion > = 11 ) {
db . execSQL ( " ALTER TABLE " + Contact . TABLENAME + " ADD COLUMN "
+ Contact . GROUPS + " TEXT " ) ;
2015-11-28 19:11:38 +00:00
db . execSQL ( " delete from " + Contact . TABLENAME ) ;
db . execSQL ( " update " + Account . TABLENAME + " set " + Account . ROSTERVERSION + " = NULL " ) ;
2014-11-16 16:21:21 +00:00
}
2014-12-09 20:41:49 +00:00
if ( oldVersion < 12 & & newVersion > = 12 ) {
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN "
+ Message . SERVER_MSG_ID + " TEXT " ) ;
}
2015-01-02 00:21:14 +00:00
if ( oldVersion < 13 & & newVersion > = 13 ) {
2015-11-28 19:11:38 +00:00
db . execSQL ( " delete from " + Contact . TABLENAME ) ;
db . execSQL ( " update " + Account . TABLENAME + " set " + Account . ROSTERVERSION + " = NULL " ) ;
2015-01-02 00:21:14 +00:00
}
2015-05-14 13:25:52 +00:00
if ( oldVersion < 14 & & newVersion > = 14 ) {
2016-09-12 20:48:51 +00:00
canonicalizeJids ( db ) ;
2015-05-14 13:25:52 +00:00
}
2015-11-28 19:11:38 +00:00
if ( oldVersion < 15 & & newVersion > = 15 ) {
2015-07-20 11:15:49 +00:00
recreateAxolotlDb ( db ) ;
2015-07-09 12:23:17 +00:00
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN "
+ Message . FINGERPRINT + " TEXT " ) ;
2015-06-25 14:56:34 +00:00
}
2015-07-29 14:41:58 +00:00
if ( oldVersion < 16 & & newVersion > = 16 ) {
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN "
+ Message . CARBON + " INTEGER " ) ;
}
2015-11-06 13:50:55 +00:00
if ( oldVersion < 19 & & newVersion > = 19 ) {
2015-11-28 19:11:38 +00:00
db . execSQL ( " ALTER TABLE " + Account . TABLENAME + " ADD COLUMN " + Account . DISPLAY_NAME + " TEXT " ) ;
}
if ( oldVersion < 20 & & newVersion > = 20 ) {
db . execSQL ( " ALTER TABLE " + Account . TABLENAME + " ADD COLUMN " + Account . HOSTNAME + " TEXT " ) ;
db . execSQL ( " ALTER TABLE " + Account . TABLENAME + " ADD COLUMN " + Account . PORT + " NUMBER DEFAULT 5222 " ) ;
2015-11-06 13:50:55 +00:00
}
2016-04-22 19:25:06 +00:00
if ( oldVersion < 26 & & newVersion > = 26 ) {
db . execSQL ( " ALTER TABLE " + Account . TABLENAME + " ADD COLUMN " + Account . STATUS + " TEXT " ) ;
db . execSQL ( " ALTER TABLE " + Account . TABLENAME + " ADD COLUMN " + Account . STATUS_MESSAGE + " TEXT " ) ;
}
2015-11-06 13:50:55 +00:00
/ * Any migrations that alter the Account table need to happen BEFORE this migration , as it
* depends on account de - serialization .
* /
2017-07-14 08:34:01 +00:00
if ( oldVersion < 17 & & newVersion > = 17 & & newVersion < 31 ) {
2015-09-05 15:25:46 +00:00
List < Account > accounts = getAccounts ( db ) ;
for ( Account account : accounts ) {
String ownDeviceIdString = account . getKey ( SQLiteAxolotlStore . JSONKEY_REGISTRATION_ID ) ;
2015-11-28 19:11:38 +00:00
if ( ownDeviceIdString = = null ) {
2015-09-05 15:25:46 +00:00
continue ;
}
int ownDeviceId = Integer . valueOf ( ownDeviceIdString ) ;
2017-06-18 14:35:30 +00:00
SignalProtocolAddress ownAddress = new SignalProtocolAddress ( account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , ownDeviceId ) ;
2015-09-05 15:25:46 +00:00
deleteSession ( db , account , ownAddress ) ;
IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair ( db , account ) ;
2015-09-05 16:47:37 +00:00
if ( identityKeyPair ! = null ) {
2016-11-14 21:27:41 +00:00
String [ ] selectionArgs = {
account . getUuid ( ) ,
2017-06-25 16:18:13 +00:00
CryptoHelper . bytesToHex ( identityKeyPair . getPublicKey ( ) . serialize ( ) )
2016-11-14 21:27:41 +00:00
} ;
ContentValues values = new ContentValues ( ) ;
values . put ( SQLiteAxolotlStore . TRUSTED , 2 ) ;
db . update ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , values ,
SQLiteAxolotlStore . ACCOUNT + " = ? AND "
+ SQLiteAxolotlStore . FINGERPRINT + " = ? " ,
selectionArgs ) ;
2015-09-05 16:47:37 +00:00
} else {
2015-11-28 19:11:38 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : could not load own identity key pair " ) ;
2015-09-05 16:47:37 +00:00
}
2015-09-05 15:25:46 +00:00
}
}
2015-10-14 19:18:34 +00:00
if ( oldVersion < 18 & & newVersion > = 18 ) {
2015-11-28 19:11:38 +00:00
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN " + Message . READ + " NUMBER DEFAULT 1 " ) ;
2015-10-14 19:18:34 +00:00
}
2015-12-02 15:43:55 +00:00
if ( oldVersion < 21 & & newVersion > = 21 ) {
List < Account > accounts = getAccounts ( db ) ;
for ( Account account : accounts ) {
account . unsetPgpSignature ( ) ;
db . update ( Account . TABLENAME , account . getContentValues ( ) , Account . UUID
+ " =? " , new String [ ] { account . getUuid ( ) } ) ;
}
}
2015-12-23 16:41:26 +00:00
2017-07-13 17:28:16 +00:00
if ( oldVersion > = 15 & & oldVersion < 22 & & newVersion > = 22 ) {
db . execSQL ( " ALTER TABLE " + SQLiteAxolotlStore . IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore . CERTIFICATE ) ;
}
2016-01-13 02:53:38 +00:00
if ( oldVersion < 23 & & newVersion > = 23 ) {
db . execSQL ( CREATE_DISCOVERY_RESULTS_STATEMENT ) ;
}
2016-02-15 22:15:04 +00:00
if ( oldVersion < 24 & & newVersion > = 24 ) {
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN " + Message . EDITED + " TEXT " ) ;
}
2016-03-04 19:09:21 +00:00
if ( oldVersion < 25 & & newVersion > = 25 ) {
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN " + Message . OOB + " INTEGER " ) ;
}
2016-04-22 19:25:06 +00:00
if ( oldVersion < 26 & & newVersion > = 26 ) {
db . execSQL ( CREATE_PRESENCE_TEMPLATES_STATEMENT ) ;
}
2016-05-19 08:41:56 +00:00
if ( oldVersion < 27 & & newVersion > = 27 ) {
db . execSQL ( " DELETE FROM " + ServiceDiscoveryResult . TABLENAME ) ;
}
2016-09-12 20:48:51 +00:00
if ( oldVersion < 28 & & newVersion > = 28 ) {
canonicalizeJids ( db ) ;
}
2016-10-26 10:26:04 +00:00
if ( oldVersion < 29 & & newVersion > = 29 ) {
db . execSQL ( " ALTER TABLE " + Message . TABLENAME + " ADD COLUMN " + Message . ERROR_MESSAGE + " TEXT " ) ;
}
2016-11-08 11:20:07 +00:00
if ( oldVersion < 30 & & newVersion > = 30 ) {
db . execSQL ( CREATE_START_TIMES_TABLE ) ;
}
2017-07-13 17:28:16 +00:00
if ( oldVersion > = 15 & & oldVersion < 31 & & newVersion > = 31 ) {
2016-11-14 21:27:41 +00:00
db . execSQL ( " ALTER TABLE " + SQLiteAxolotlStore . IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore . TRUST + " TEXT " ) ;
db . execSQL ( " ALTER TABLE " + SQLiteAxolotlStore . IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore . ACTIVE + " NUMBER " ) ;
HashMap < Integer , ContentValues > migration = new HashMap < > ( ) ;
2016-11-25 16:04:23 +00:00
migration . put ( 0 , createFingerprintStatusContentValues ( FingerprintStatus . Trust . TRUSTED , true ) ) ;
2016-11-14 21:27:41 +00:00
migration . put ( 1 , createFingerprintStatusContentValues ( FingerprintStatus . Trust . TRUSTED , true ) ) ;
migration . put ( 2 , createFingerprintStatusContentValues ( FingerprintStatus . Trust . UNTRUSTED , true ) ) ;
migration . put ( 3 , createFingerprintStatusContentValues ( FingerprintStatus . Trust . COMPROMISED , false ) ) ;
migration . put ( 4 , createFingerprintStatusContentValues ( FingerprintStatus . Trust . TRUSTED , false ) ) ;
2016-11-25 16:04:23 +00:00
migration . put ( 5 , createFingerprintStatusContentValues ( FingerprintStatus . Trust . TRUSTED , false ) ) ;
2016-11-14 21:27:41 +00:00
migration . put ( 6 , createFingerprintStatusContentValues ( FingerprintStatus . Trust . UNTRUSTED , false ) ) ;
migration . put ( 7 , createFingerprintStatusContentValues ( FingerprintStatus . Trust . VERIFIED_X509 , true ) ) ;
migration . put ( 8 , createFingerprintStatusContentValues ( FingerprintStatus . Trust . VERIFIED_X509 , false ) ) ;
for ( Map . Entry < Integer , ContentValues > entry : migration . entrySet ( ) ) {
String whereClause = SQLiteAxolotlStore . TRUSTED + " =? " ;
String [ ] where = { String . valueOf ( entry . getKey ( ) ) } ;
db . update ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , entry . getValue ( ) , whereClause , where ) ;
}
}
2017-07-13 17:28:16 +00:00
if ( oldVersion > = 15 & & oldVersion < 32 & & newVersion > = 32 ) {
2016-11-19 12:34:54 +00:00
db . execSQL ( " ALTER TABLE " + SQLiteAxolotlStore . IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore . LAST_ACTIVATION + " NUMBER " ) ;
ContentValues defaults = new ContentValues ( ) ;
defaults . put ( SQLiteAxolotlStore . LAST_ACTIVATION , System . currentTimeMillis ( ) ) ;
db . update ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , defaults , null , null ) ;
}
2017-07-13 17:28:16 +00:00
if ( oldVersion > = 15 & & oldVersion < 33 & & newVersion > = 33 ) {
2016-11-25 16:04:23 +00:00
String whereClause = SQLiteAxolotlStore . OWN + " =1 " ;
db . update ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , createFingerprintStatusContentValues ( FingerprintStatus . Trust . VERIFIED , true ) , whereClause , null ) ;
}
2017-01-25 12:22:20 +00:00
if ( oldVersion < 34 & & newVersion > = 34 ) {
db . execSQL ( CREATE_MESSAGE_TIME_INDEX ) ;
final File oldPicturesDirectory = new File ( Environment . getExternalStoragePublicDirectory ( Environment . DIRECTORY_PICTURES ) + " /Conversations/ " ) ;
final File oldFilesDirectory = new File ( Environment . getExternalStorageDirectory ( ) + " /Conversations/ " ) ;
final File newFilesDirectory = new File ( Environment . getExternalStorageDirectory ( ) + " /Conversations/Media/Conversations Files/ " ) ;
final File newVideosDirectory = new File ( Environment . getExternalStorageDirectory ( ) + " /Conversations/Media/Conversations Videos/ " ) ;
if ( oldPicturesDirectory . exists ( ) & & oldPicturesDirectory . isDirectory ( ) ) {
final File newPicturesDirectory = new File ( Environment . getExternalStorageDirectory ( ) + " /Conversations/Media/Conversations Images/ " ) ;
newPicturesDirectory . getParentFile ( ) . mkdirs ( ) ;
if ( oldPicturesDirectory . renameTo ( newPicturesDirectory ) ) {
Log . d ( Config . LOGTAG , " moved " + oldPicturesDirectory . getAbsolutePath ( ) + " to " + newPicturesDirectory . getAbsolutePath ( ) ) ;
}
}
if ( oldFilesDirectory . exists ( ) & & oldFilesDirectory . isDirectory ( ) ) {
newFilesDirectory . mkdirs ( ) ;
newVideosDirectory . mkdirs ( ) ;
2017-02-07 09:20:13 +00:00
final File [ ] files = oldFilesDirectory . listFiles ( ) ;
if ( files = = null ) {
return ;
}
for ( File file : files ) {
2017-01-25 12:22:20 +00:00
if ( file . getName ( ) . equals ( " .nomedia " ) ) {
if ( file . delete ( ) ) {
Log . d ( Config . LOGTAG , " deleted nomedia file in " + oldFilesDirectory . getAbsolutePath ( ) ) ;
}
} else if ( file . isFile ( ) ) {
final String name = file . getName ( ) ;
boolean isVideo = false ;
int start = name . lastIndexOf ( '.' ) + 1 ;
if ( start < name . length ( ) ) {
String mime = MimeUtils . guessMimeTypeFromExtension ( name . substring ( start ) ) ;
isVideo = mime ! = null & & mime . startsWith ( " video/ " ) ;
}
File dst = new File ( ( isVideo ? newVideosDirectory : newFilesDirectory ) . getAbsolutePath ( ) + " / " + file . getName ( ) ) ;
if ( file . renameTo ( dst ) ) {
Log . d ( Config . LOGTAG , " moved " + file + " to " + dst ) ;
}
}
}
}
}
2017-02-07 20:17:08 +00:00
if ( oldVersion < 35 & & newVersion > = 35 ) {
db . execSQL ( CREATE_MESSAGE_CONVERSATION_INDEX ) ;
}
2016-11-14 21:27:41 +00:00
}
private static ContentValues createFingerprintStatusContentValues ( FingerprintStatus . Trust trust , boolean active ) {
ContentValues values = new ContentValues ( ) ;
values . put ( SQLiteAxolotlStore . TRUST , trust . toString ( ) ) ;
values . put ( SQLiteAxolotlStore . ACTIVE , active ? 1 : 0 ) ;
return values ;
2016-09-12 20:48:51 +00:00
}
private void canonicalizeJids ( SQLiteDatabase db ) {
// migrate db to new, canonicalized JID domainpart representation
// Conversation table
Cursor cursor = db . rawQuery ( " select * from " + Conversation . TABLENAME , new String [ 0 ] ) ;
while ( cursor . moveToNext ( ) ) {
String newJid ;
try {
newJid = Jid . fromString (
cursor . getString ( cursor . getColumnIndex ( Conversation . CONTACTJID ) )
2016-10-20 15:31:46 +00:00
) . toPreppedString ( ) ;
2016-09-12 20:48:51 +00:00
} catch ( InvalidJidException ignored ) {
Log . e ( Config . LOGTAG , " Failed to migrate Conversation CONTACTJID "
+ cursor . getString ( cursor . getColumnIndex ( Conversation . CONTACTJID ) )
+ " : " + ignored + " . Skipping... " ) ;
continue ;
}
String updateArgs [ ] = {
newJid ,
cursor . getString ( cursor . getColumnIndex ( Conversation . UUID ) ) ,
} ;
db . execSQL ( " update " + Conversation . TABLENAME
+ " set " + Conversation . CONTACTJID + " = ? "
+ " where " + Conversation . UUID + " = ? " , updateArgs ) ;
}
cursor . close ( ) ;
// Contact table
cursor = db . rawQuery ( " select * from " + Contact . TABLENAME , new String [ 0 ] ) ;
while ( cursor . moveToNext ( ) ) {
String newJid ;
try {
newJid = Jid . fromString (
cursor . getString ( cursor . getColumnIndex ( Contact . JID ) )
2016-10-20 15:31:46 +00:00
) . toPreppedString ( ) ;
2016-09-12 20:48:51 +00:00
} catch ( InvalidJidException ignored ) {
Log . e ( Config . LOGTAG , " Failed to migrate Contact JID "
+ cursor . getString ( cursor . getColumnIndex ( Contact . JID ) )
+ " : " + ignored + " . Skipping... " ) ;
continue ;
}
String updateArgs [ ] = {
newJid ,
cursor . getString ( cursor . getColumnIndex ( Contact . ACCOUNT ) ) ,
cursor . getString ( cursor . getColumnIndex ( Contact . JID ) ) ,
} ;
db . execSQL ( " update " + Contact . TABLENAME
+ " set " + Contact . JID + " = ? "
+ " where " + Contact . ACCOUNT + " = ? "
+ " AND " + Contact . JID + " = ? " , updateArgs ) ;
}
cursor . close ( ) ;
// Account table
cursor = db . rawQuery ( " select * from " + Account . TABLENAME , new String [ 0 ] ) ;
while ( cursor . moveToNext ( ) ) {
String newServer ;
try {
newServer = Jid . fromParts (
cursor . getString ( cursor . getColumnIndex ( Account . USERNAME ) ) ,
cursor . getString ( cursor . getColumnIndex ( Account . SERVER ) ) ,
" mobile "
) . getDomainpart ( ) ;
} catch ( InvalidJidException ignored ) {
Log . e ( Config . LOGTAG , " Failed to migrate Account SERVER "
+ cursor . getString ( cursor . getColumnIndex ( Account . SERVER ) )
+ " : " + ignored + " . Skipping... " ) ;
continue ;
}
String updateArgs [ ] = {
newServer ,
cursor . getString ( cursor . getColumnIndex ( Account . UUID ) ) ,
} ;
db . execSQL ( " update " + Account . TABLENAME
+ " set " + Account . SERVER + " = ? "
+ " where " + Account . UUID + " = ? " , updateArgs ) ;
}
cursor . close ( ) ;
2014-01-24 22:58:51 +00:00
}
2014-01-26 02:27:55 +00:00
2014-01-24 22:58:51 +00:00
public static synchronized DatabaseBackend getInstance ( Context context ) {
if ( instance = = null ) {
2014-01-25 18:33:12 +00:00
instance = new DatabaseBackend ( context ) ;
2014-01-24 22:58:51 +00:00
}
return instance ;
}
2014-01-26 02:27:55 +00:00
2014-01-27 19:40:42 +00:00
public void createConversation ( Conversation conversation ) {
2014-01-24 22:58:51 +00:00
SQLiteDatabase db = this . getWritableDatabase ( ) ;
2014-01-27 19:40:42 +00:00
db . insert ( Conversation . TABLENAME , null , conversation . getContentValues ( ) ) ;
}
public void createMessage ( Message message ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
db . insert ( Message . TABLENAME , null , message . getContentValues ( ) ) ;
2014-01-24 22:58:51 +00:00
}
2014-01-28 23:15:38 +00:00
2014-01-28 18:21:54 +00:00
public void createAccount ( Account account ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
2014-01-28 23:15:38 +00:00
db . insert ( Account . TABLENAME , null , account . getContentValues ( ) ) ;
2014-01-28 18:21:54 +00:00
}
2014-05-19 13:15:09 +00:00
2016-01-13 02:53:38 +00:00
public void insertDiscoveryResult ( ServiceDiscoveryResult result ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
db . insert ( ServiceDiscoveryResult . TABLENAME , null , result . getContentValues ( ) ) ;
}
public ServiceDiscoveryResult findDiscoveryResult ( final String hash , final String ver ) {
SQLiteDatabase db = this . getReadableDatabase ( ) ;
String [ ] selectionArgs = { hash , ver } ;
Cursor cursor = db . query ( ServiceDiscoveryResult . TABLENAME , null ,
ServiceDiscoveryResult . HASH + " =? AND " + ServiceDiscoveryResult . VER + " =? " ,
selectionArgs , null , null , null ) ;
2016-03-11 08:01:27 +00:00
if ( cursor . getCount ( ) = = 0 ) {
cursor . close ( ) ;
2016-01-13 02:53:38 +00:00
return null ;
2016-03-11 08:01:27 +00:00
}
2016-01-13 02:53:38 +00:00
cursor . moveToFirst ( ) ;
ServiceDiscoveryResult result = null ;
try {
result = new ServiceDiscoveryResult ( cursor ) ;
} catch ( JSONException e ) { /* result is still null */ }
cursor . close ( ) ;
return result ;
}
2016-04-22 19:25:06 +00:00
public void insertPresenceTemplate ( PresenceTemplate template ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
db . insert ( PresenceTemplate . TABELNAME , null , template . getContentValues ( ) ) ;
}
public List < PresenceTemplate > getPresenceTemplates ( ) {
ArrayList < PresenceTemplate > templates = new ArrayList < > ( ) ;
SQLiteDatabase db = this . getReadableDatabase ( ) ;
Cursor cursor = db . query ( PresenceTemplate . TABELNAME , null , null , null , null , null , PresenceTemplate . LAST_USED + " desc " ) ;
while ( cursor . moveToNext ( ) ) {
templates . add ( PresenceTemplate . fromCursor ( cursor ) ) ;
}
cursor . close ( ) ;
return templates ;
}
public void deletePresenceTemplate ( PresenceTemplate template ) {
Log . d ( Config . LOGTAG , " deleting presence template with uuid " + template . getUuid ( ) ) ;
SQLiteDatabase db = this . getWritableDatabase ( ) ;
String where = PresenceTemplate . UUID + " =? " ;
String [ ] whereArgs = { template . getUuid ( ) } ;
db . delete ( PresenceTemplate . TABELNAME , where , whereArgs ) ;
}
2014-07-12 10:41:37 +00:00
public CopyOnWriteArrayList < Conversation > getConversations ( int status ) {
2014-11-05 20:55:47 +00:00
CopyOnWriteArrayList < Conversation > list = new CopyOnWriteArrayList < > ( ) ;
2014-01-24 22:58:51 +00:00
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2015-11-28 19:11:38 +00:00
String [ ] selectionArgs = { Integer . toString ( status ) } ;
2014-01-27 19:40:42 +00:00
Cursor cursor = db . rawQuery ( " select * from " + Conversation . TABLENAME
+ " where " + Conversation . STATUS + " = ? order by "
+ Conversation . CREATED + " desc " , selectionArgs ) ;
2014-01-26 02:27:55 +00:00
while ( cursor . moveToNext ( ) ) {
2014-01-24 22:58:51 +00:00
list . add ( Conversation . fromCursor ( cursor ) ) ;
}
2014-11-15 23:34:16 +00:00
cursor . close ( ) ;
2014-01-24 22:58:51 +00:00
return list ;
}
2014-07-29 12:42:17 +00:00
2014-10-20 19:08:33 +00:00
public ArrayList < Message > getMessages ( Conversation conversations , int limit ) {
2014-07-29 12:42:17 +00:00
return getMessages ( conversations , limit , - 1 ) ;
2014-06-14 14:59:07 +00:00
}
2014-01-24 22:58:51 +00:00
2014-10-20 19:08:33 +00:00
public ArrayList < Message > getMessages ( Conversation conversation , int limit ,
2015-11-28 19:11:38 +00:00
long timestamp ) {
2014-11-05 20:55:47 +00:00
ArrayList < Message > list = new ArrayList < > ( ) ;
2014-01-27 19:40:42 +00:00
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2014-06-14 14:59:07 +00:00
Cursor cursor ;
2014-07-29 12:42:17 +00:00
if ( timestamp = = - 1 ) {
2015-11-28 19:11:38 +00:00
String [ ] selectionArgs = { conversation . getUuid ( ) } ;
2014-06-14 14:59:07 +00:00
cursor = db . query ( Message . TABLENAME , null , Message . CONVERSATION
2014-07-29 12:42:17 +00:00
+ " =? " , selectionArgs , null , null , Message . TIME_SENT
+ " DESC " , String . valueOf ( limit ) ) ;
2014-06-14 14:59:07 +00:00
} else {
2015-11-28 19:11:38 +00:00
String [ ] selectionArgs = { conversation . getUuid ( ) ,
Long . toString ( timestamp ) } ;
2014-06-14 14:59:07 +00:00
cursor = db . query ( Message . TABLENAME , null , Message . CONVERSATION
2015-11-28 19:11:38 +00:00
+ " =? and " + Message . TIME_SENT + " <? " , selectionArgs ,
2014-07-29 12:42:17 +00:00
null , null , Message . TIME_SENT + " DESC " ,
2014-06-14 14:59:07 +00:00
String . valueOf ( limit ) ) ;
}
2014-01-28 23:15:38 +00:00
if ( cursor . getCount ( ) > 0 ) {
2014-01-27 19:40:42 +00:00
cursor . moveToLast ( ) ;
do {
2017-04-26 10:43:52 +00:00
Message message = Message . fromCursor ( cursor , conversation ) ;
2017-01-30 05:03:51 +00:00
if ( message ! = null ) {
list . add ( message ) ;
}
2014-01-27 19:40:42 +00:00
} while ( cursor . moveToPrevious ( ) ) ;
}
2014-11-15 23:34:16 +00:00
cursor . close ( ) ;
2014-01-27 19:40:42 +00:00
return list ;
}
2015-11-28 19:11:38 +00:00
public Iterable < Message > getMessagesIterable ( final Conversation conversation ) {
2015-09-20 21:17:32 +00:00
return new Iterable < Message > ( ) {
@Override
public Iterator < Message > iterator ( ) {
2015-11-28 19:11:38 +00:00
class MessageIterator implements Iterator < Message > {
2015-09-20 21:17:32 +00:00
SQLiteDatabase db = getReadableDatabase ( ) ;
2015-11-28 19:11:38 +00:00
String [ ] selectionArgs = { conversation . getUuid ( ) } ;
2015-09-20 21:17:32 +00:00
Cursor cursor = db . query ( Message . TABLENAME , null , Message . CONVERSATION
+ " =? " , selectionArgs , null , null , Message . TIME_SENT
+ " ASC " , null ) ;
public MessageIterator ( ) {
cursor . moveToFirst ( ) ;
}
@Override
public boolean hasNext ( ) {
return ! cursor . isAfterLast ( ) ;
}
@Override
public Message next ( ) {
2017-04-26 10:43:52 +00:00
Message message = Message . fromCursor ( cursor , conversation ) ;
2015-09-20 21:17:32 +00:00
cursor . moveToNext ( ) ;
return message ;
}
@Override
public void remove ( ) {
throw new UnsupportedOperationException ( ) ;
}
}
return new MessageIterator ( ) ;
}
} ;
}
2014-11-05 20:55:47 +00:00
public Conversation findConversation ( final Account account , final Jid contactJid ) {
2014-01-27 19:40:42 +00:00
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2015-11-28 19:11:38 +00:00
String [ ] selectionArgs = { account . getUuid ( ) ,
2016-10-20 15:31:46 +00:00
contactJid . toBareJid ( ) . toPreppedString ( ) + " /% " ,
contactJid . toBareJid ( ) . toPreppedString ( )
2015-11-28 19:11:38 +00:00
} ;
2014-01-28 23:15:38 +00:00
Cursor cursor = db . query ( Conversation . TABLENAME , null ,
2015-01-21 15:18:57 +00:00
Conversation . ACCOUNT + " =? AND ( " + Conversation . CONTACTJID
2015-05-29 09:17:26 +00:00
+ " like ? OR " + Conversation . CONTACTJID + " =?) " , selectionArgs , null , null , null ) ;
2016-03-31 11:55:46 +00:00
if ( cursor . getCount ( ) = = 0 ) {
cursor . close ( ) ;
2014-01-28 23:15:38 +00:00
return null ;
2016-03-31 11:55:46 +00:00
}
2014-01-27 19:40:42 +00:00
cursor . moveToFirst ( ) ;
2014-11-15 23:34:16 +00:00
Conversation conversation = Conversation . fromCursor ( cursor ) ;
cursor . close ( ) ;
return conversation ;
2014-01-27 19:40:42 +00:00
}
2014-12-21 20:43:58 +00:00
public void updateConversation ( final Conversation conversation ) {
final SQLiteDatabase db = this . getWritableDatabase ( ) ;
2015-11-28 19:11:38 +00:00
final String [ ] args = { conversation . getUuid ( ) } ;
2014-01-28 23:15:38 +00:00
db . update ( Conversation . TABLENAME , conversation . getContentValues ( ) ,
Conversation . UUID + " =? " , args ) ;
2014-01-27 19:40:42 +00:00
}
2014-01-28 23:15:38 +00:00
2014-01-28 18:21:54 +00:00
public List < Account > getAccounts ( ) {
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2015-09-05 15:25:46 +00:00
return getAccounts ( db ) ;
}
private List < Account > getAccounts ( SQLiteDatabase db ) {
List < Account > list = new ArrayList < > ( ) ;
2014-01-28 23:15:38 +00:00
Cursor cursor = db . query ( Account . TABLENAME , null , null , null , null ,
null , null ) ;
2014-01-28 18:21:54 +00:00
while ( cursor . moveToNext ( ) ) {
list . add ( Account . fromCursor ( cursor ) ) ;
}
2014-10-08 12:10:37 +00:00
cursor . close ( ) ;
2014-01-28 18:21:54 +00:00
return list ;
}
2016-09-07 12:34:58 +00:00
public boolean updateAccount ( Account account ) {
2014-01-28 18:21:54 +00:00
SQLiteDatabase db = this . getWritableDatabase ( ) ;
2015-11-28 19:11:38 +00:00
String [ ] args = { account . getUuid ( ) } ;
2016-09-07 12:34:58 +00:00
final int rows = db . update ( Account . TABLENAME , account . getContentValues ( ) , Account . UUID + " =? " , args ) ;
return rows = = 1 ;
2014-01-28 18:21:54 +00:00
}
2016-09-07 12:34:58 +00:00
public boolean deleteAccount ( Account account ) {
2014-01-28 18:21:54 +00:00
SQLiteDatabase db = this . getWritableDatabase ( ) ;
2015-11-28 19:11:38 +00:00
String [ ] args = { account . getUuid ( ) } ;
2016-09-07 12:34:58 +00:00
final int rows = db . delete ( Account . TABLENAME , Account . UUID + " =? " , args ) ;
return rows = = 1 ;
2014-01-28 18:21:54 +00:00
}
2014-09-28 13:21:56 +00:00
2014-09-19 15:21:33 +00:00
public boolean hasEnabledAccounts ( ) {
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2014-09-28 13:21:56 +00:00
Cursor cursor = db . rawQuery ( " select count( " + Account . UUID + " ) from "
+ Account . TABLENAME + " where not options & (1 <<1) " , null ) ;
2014-10-14 16:26:45 +00:00
try {
cursor . moveToFirst ( ) ;
int count = cursor . getInt ( 0 ) ;
return ( count > 0 ) ;
} catch ( SQLiteCantOpenDatabaseException e ) {
2014-10-15 17:32:12 +00:00
return true ; // better safe than sorry
2015-02-28 11:03:53 +00:00
} catch ( RuntimeException e ) {
return true ; // better safe than sorry
2016-03-31 11:55:46 +00:00
} finally {
if ( cursor ! = null ) {
cursor . close ( ) ;
}
2014-10-14 16:26:45 +00:00
}
2014-09-19 15:21:33 +00:00
}
2014-01-27 19:40:42 +00:00
2014-01-28 23:15:38 +00:00
@Override
public SQLiteDatabase getWritableDatabase ( ) {
SQLiteDatabase db = super . getWritableDatabase ( ) ;
db . execSQL ( " PRAGMA foreign_keys=ON; " ) ;
return db ;
}
2014-02-02 15:05:15 +00:00
public void updateMessage ( Message message ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
2015-11-28 19:11:38 +00:00
String [ ] args = { message . getUuid ( ) } ;
2014-02-02 15:05:15 +00:00
db . update ( Message . TABLENAME , message . getContentValues ( ) , Message . UUID
+ " =? " , args ) ;
}
2014-02-05 21:33:39 +00:00
2016-02-15 22:15:04 +00:00
public void updateMessage ( Message message , String uuid ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
String [ ] args = { uuid } ;
db . update ( Message . TABLENAME , message . getContentValues ( ) , Message . UUID
+ " =? " , args ) ;
}
2014-05-19 13:15:09 +00:00
public void readRoster ( Roster roster ) {
2014-02-05 21:33:39 +00:00
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2014-02-10 02:34:00 +00:00
Cursor cursor ;
2015-11-28 19:11:38 +00:00
String args [ ] = { roster . getAccount ( ) . getUuid ( ) } ;
2015-07-03 19:32:46 +00:00
cursor = db . query ( Contact . TABLENAME , null , Contact . ACCOUNT + " =? " , args , null , null , null ) ;
2014-02-05 21:33:39 +00:00
while ( cursor . moveToNext ( ) ) {
2014-05-19 13:15:09 +00:00
roster . initContact ( Contact . fromCursor ( cursor ) ) ;
2014-02-05 21:33:39 +00:00
}
2014-10-08 12:10:37 +00:00
cursor . close ( ) ;
2014-02-05 21:33:39 +00:00
}
2014-07-29 12:42:17 +00:00
2014-11-05 20:55:47 +00:00
public void writeRoster ( final Roster roster ) {
final Account account = roster . getAccount ( ) ;
final SQLiteDatabase db = this . getWritableDatabase ( ) ;
2016-01-13 07:55:46 +00:00
db . beginTransaction ( ) ;
2014-07-29 12:42:17 +00:00
for ( Contact contact : roster . getContacts ( ) ) {
2014-05-19 19:05:17 +00:00
if ( contact . getOption ( Contact . Options . IN_ROSTER ) ) {
db . insert ( Contact . TABLENAME , null , contact . getContentValues ( ) ) ;
} else {
2014-07-29 12:42:17 +00:00
String where = Contact . ACCOUNT + " =? AND " + Contact . JID + " =? " ;
2016-10-20 15:31:46 +00:00
String [ ] whereArgs = { account . getUuid ( ) , contact . getJid ( ) . toPreppedString ( ) } ;
2014-05-19 19:05:17 +00:00
db . delete ( Contact . TABLENAME , where , whereArgs ) ;
}
}
2016-01-13 07:55:46 +00:00
db . setTransactionSuccessful ( ) ;
db . endTransaction ( ) ;
2014-05-19 19:05:17 +00:00
account . setRosterVersion ( roster . getVersion ( ) ) ;
updateAccount ( account ) ;
2014-02-05 21:33:39 +00:00
}
2014-02-11 22:55:03 +00:00
2014-04-20 18:48:16 +00:00
public void deleteMessagesInConversation ( Conversation conversation ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
2015-11-28 19:11:38 +00:00
String [ ] args = { conversation . getUuid ( ) } ;
2014-04-20 18:48:16 +00:00
db . delete ( Message . TABLENAME , Message . CONVERSATION + " =? " , args ) ;
}
2014-02-19 00:35:23 +00:00
2017-01-23 16:14:30 +00:00
public boolean expireOldMessages ( long timestamp ) {
String where = Message . TIME_SENT + " <? " ;
String [ ] whereArgs = { String . valueOf ( timestamp ) } ;
SQLiteDatabase db = this . getReadableDatabase ( ) ;
return db . delete ( Message . TABLENAME , where , whereArgs ) > 0 ;
}
2017-05-07 19:05:35 +00:00
public MamReference getLastMessageReceived ( Account account ) {
2016-03-31 11:55:46 +00:00
Cursor cursor = null ;
2016-01-15 22:46:52 +00:00
try {
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2017-03-08 19:54:13 +00:00
String sql = " select messages.timeSent,messages.serverMsgId from accounts join conversations on accounts.uuid=conversations.accountUuid join messages on conversations.uuid=messages.conversationUuid where accounts.uuid=? and (messages.status=0 or messages.carbon=1 or messages.serverMsgId not null) and conversations.mode=0 order by messages.timesent desc limit 1 " ;
2016-01-15 22:46:52 +00:00
String [ ] args = { account . getUuid ( ) } ;
2016-03-31 11:55:46 +00:00
cursor = db . rawQuery ( sql , args ) ;
2016-01-15 22:46:52 +00:00
if ( cursor . getCount ( ) = = 0 ) {
return null ;
} else {
cursor . moveToFirst ( ) ;
2017-05-07 19:05:35 +00:00
return new MamReference ( cursor . getLong ( 0 ) , cursor . getString ( 1 ) ) ;
2016-01-15 22:46:52 +00:00
}
} catch ( Exception e ) {
2015-12-11 12:52:04 +00:00
return null ;
2016-03-31 11:55:46 +00:00
} finally {
if ( cursor ! = null ) {
cursor . close ( ) ;
}
2015-12-11 12:52:04 +00:00
}
}
2016-11-19 20:39:16 +00:00
public long getLastTimeFingerprintUsed ( Account account , String fingerprint ) {
String SQL = " select messages.timeSent from accounts join conversations on accounts.uuid=conversations.accountUuid join messages on conversations.uuid=messages.conversationUuid where accounts.uuid=? and messages.axolotl_fingerprint=? order by messages.timesent desc limit 1 " ;
String [ ] args = { account . getUuid ( ) , fingerprint } ;
Cursor cursor = getReadableDatabase ( ) . rawQuery ( SQL , args ) ;
long time ;
if ( cursor . moveToFirst ( ) ) {
time = cursor . getLong ( 0 ) ;
} else {
time = 0 ;
}
cursor . close ( ) ;
return time ;
}
2017-05-07 19:05:35 +00:00
public MamReference getLastClearDate ( Account account ) {
2016-09-16 09:07:52 +00:00
SQLiteDatabase db = this . getReadableDatabase ( ) ;
String [ ] columns = { Conversation . ATTRIBUTES } ;
String selection = Conversation . ACCOUNT + " =? " ;
String [ ] args = { account . getUuid ( ) } ;
Cursor cursor = db . query ( Conversation . TABLENAME , columns , selection , args , null , null , null ) ;
2017-05-07 19:05:35 +00:00
MamReference maxClearDate = new MamReference ( 0 ) ;
2016-09-16 09:07:52 +00:00
while ( cursor . moveToNext ( ) ) {
try {
2017-05-07 19:05:35 +00:00
final JSONObject o = new JSONObject ( cursor . getString ( 0 ) ) ;
maxClearDate = MamReference . max ( maxClearDate , MamReference . fromAttribute ( o . getString ( Conversation . ATTRIBUTE_LAST_CLEAR_HISTORY ) ) ) ;
2016-09-16 09:07:52 +00:00
} catch ( Exception e ) {
//ignored
}
}
cursor . close ( ) ;
2017-05-07 19:05:35 +00:00
return maxClearDate ;
2016-09-16 09:07:52 +00:00
}
2017-06-18 14:35:30 +00:00
private Cursor getCursorForSession ( Account account , SignalProtocolAddress contact ) {
2015-05-29 09:17:26 +00:00
final SQLiteDatabase db = this . getReadableDatabase ( ) ;
String [ ] selectionArgs = { account . getUuid ( ) ,
contact . getName ( ) ,
Integer . toString ( contact . getDeviceId ( ) ) } ;
2016-09-16 09:07:52 +00:00
return db . query ( SQLiteAxolotlStore . SESSION_TABLENAME ,
null ,
2015-07-28 20:00:54 +00:00
SQLiteAxolotlStore . ACCOUNT + " = ? AND "
+ SQLiteAxolotlStore . NAME + " = ? AND "
+ SQLiteAxolotlStore . DEVICE_ID + " = ? " ,
2015-05-29 09:17:26 +00:00
selectionArgs ,
null , null , null ) ;
}
2017-06-18 14:35:30 +00:00
public SessionRecord loadSession ( Account account , SignalProtocolAddress contact ) {
2015-05-29 09:17:26 +00:00
SessionRecord session = null ;
2015-06-26 13:41:02 +00:00
Cursor cursor = getCursorForSession ( account , contact ) ;
2015-11-28 19:11:38 +00:00
if ( cursor . getCount ( ) ! = 0 ) {
2015-05-29 09:17:26 +00:00
cursor . moveToFirst ( ) ;
try {
2015-11-28 19:11:38 +00:00
session = new SessionRecord ( Base64 . decode ( cursor . getString ( cursor . getColumnIndex ( SQLiteAxolotlStore . KEY ) ) , Base64 . DEFAULT ) ) ;
2015-05-29 09:17:26 +00:00
} catch ( IOException e ) {
2015-06-29 11:40:56 +00:00
cursor . close ( ) ;
2015-05-29 09:17:26 +00:00
throw new AssertionError ( e ) ;
}
2015-06-26 13:41:02 +00:00
}
cursor . close ( ) ;
2015-05-29 09:17:26 +00:00
return session ;
}
2017-06-18 14:35:30 +00:00
public List < Integer > getSubDeviceSessions ( Account account , SignalProtocolAddress contact ) {
2015-05-29 09:17:26 +00:00
final SQLiteDatabase db = this . getReadableDatabase ( ) ;
2015-09-05 15:25:46 +00:00
return getSubDeviceSessions ( db , account , contact ) ;
}
2017-06-18 14:35:30 +00:00
private List < Integer > getSubDeviceSessions ( SQLiteDatabase db , Account account , SignalProtocolAddress contact ) {
2015-09-05 15:25:46 +00:00
List < Integer > devices = new ArrayList < > ( ) ;
2015-07-28 20:00:54 +00:00
String [ ] columns = { SQLiteAxolotlStore . DEVICE_ID } ;
2015-05-29 09:17:26 +00:00
String [ ] selectionArgs = { account . getUuid ( ) ,
contact . getName ( ) } ;
2015-07-28 20:00:54 +00:00
Cursor cursor = db . query ( SQLiteAxolotlStore . SESSION_TABLENAME ,
2015-05-29 09:17:26 +00:00
columns ,
2015-07-28 20:00:54 +00:00
SQLiteAxolotlStore . ACCOUNT + " = ? AND "
+ SQLiteAxolotlStore . NAME + " = ? " ,
2015-05-29 09:17:26 +00:00
selectionArgs ,
null , null , null ) ;
2015-11-28 19:11:38 +00:00
while ( cursor . moveToNext ( ) ) {
2015-05-29 09:17:26 +00:00
devices . add ( cursor . getInt (
2015-07-28 20:00:54 +00:00
cursor . getColumnIndex ( SQLiteAxolotlStore . DEVICE_ID ) ) ) ;
2015-05-29 09:17:26 +00:00
}
cursor . close ( ) ;
return devices ;
}
2017-06-29 12:17:45 +00:00
public List < String > getKnownSignalAddresses ( Account account ) {
List < String > addresses = new ArrayList < > ( ) ;
String [ ] colums = { SQLiteAxolotlStore . NAME } ;
String [ ] selectionArgs = { account . getUuid ( ) } ;
Cursor cursor = getReadableDatabase ( ) . query ( SQLiteAxolotlStore . SESSION_TABLENAME ,
colums ,
SQLiteAxolotlStore . ACCOUNT + " = ? " ,
selectionArgs ,
null , null , null
) ;
while ( cursor . moveToNext ( ) ) {
addresses . add ( cursor . getString ( cursor . getColumnIndex ( SQLiteAxolotlStore . NAME ) ) ) ;
}
cursor . close ( ) ;
return addresses ;
}
2017-06-18 14:35:30 +00:00
public boolean containsSession ( Account account , SignalProtocolAddress contact ) {
2015-05-29 09:17:26 +00:00
Cursor cursor = getCursorForSession ( account , contact ) ;
int count = cursor . getCount ( ) ;
cursor . close ( ) ;
return count ! = 0 ;
}
2017-06-18 14:35:30 +00:00
public void storeSession ( Account account , SignalProtocolAddress contact , SessionRecord session ) {
2015-05-29 09:17:26 +00:00
SQLiteDatabase db = this . getWritableDatabase ( ) ;
ContentValues values = new ContentValues ( ) ;
2015-07-28 20:00:54 +00:00
values . put ( SQLiteAxolotlStore . NAME , contact . getName ( ) ) ;
values . put ( SQLiteAxolotlStore . DEVICE_ID , contact . getDeviceId ( ) ) ;
2015-11-28 19:11:38 +00:00
values . put ( SQLiteAxolotlStore . KEY , Base64 . encodeToString ( session . serialize ( ) , Base64 . DEFAULT ) ) ;
2015-07-28 20:00:54 +00:00
values . put ( SQLiteAxolotlStore . ACCOUNT , account . getUuid ( ) ) ;
db . insert ( SQLiteAxolotlStore . SESSION_TABLENAME , null , values ) ;
2015-05-29 09:17:26 +00:00
}
2017-06-18 14:35:30 +00:00
public void deleteSession ( Account account , SignalProtocolAddress contact ) {
2015-05-29 09:17:26 +00:00
SQLiteDatabase db = this . getWritableDatabase ( ) ;
2015-09-05 15:25:46 +00:00
deleteSession ( db , account , contact ) ;
}
2017-06-18 14:35:30 +00:00
private void deleteSession ( SQLiteDatabase db , Account account , SignalProtocolAddress contact ) {
2015-05-29 09:17:26 +00:00
String [ ] args = { account . getUuid ( ) ,
contact . getName ( ) ,
Integer . toString ( contact . getDeviceId ( ) ) } ;
2015-07-28 20:00:54 +00:00
db . delete ( SQLiteAxolotlStore . SESSION_TABLENAME ,
SQLiteAxolotlStore . ACCOUNT + " = ? AND "
+ SQLiteAxolotlStore . NAME + " = ? AND "
+ SQLiteAxolotlStore . DEVICE_ID + " = ? " ,
2015-05-29 09:17:26 +00:00
args ) ;
}
2017-06-18 14:35:30 +00:00
public void deleteAllSessions ( Account account , SignalProtocolAddress contact ) {
2015-05-29 09:17:26 +00:00
SQLiteDatabase db = this . getWritableDatabase ( ) ;
String [ ] args = { account . getUuid ( ) , contact . getName ( ) } ;
2015-07-28 20:00:54 +00:00
db . delete ( SQLiteAxolotlStore . SESSION_TABLENAME ,
SQLiteAxolotlStore . ACCOUNT + " =? AND "
+ SQLiteAxolotlStore . NAME + " = ? " ,
2015-05-29 09:17:26 +00:00
args ) ;
}
private Cursor getCursorForPreKey ( Account account , int preKeyId ) {
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2015-07-28 20:00:54 +00:00
String [ ] columns = { SQLiteAxolotlStore . KEY } ;
2015-05-29 09:17:26 +00:00
String [ ] selectionArgs = { account . getUuid ( ) , Integer . toString ( preKeyId ) } ;
2015-07-28 20:00:54 +00:00
Cursor cursor = db . query ( SQLiteAxolotlStore . PREKEY_TABLENAME ,
2015-05-29 09:17:26 +00:00
columns ,
2015-07-28 20:00:54 +00:00
SQLiteAxolotlStore . ACCOUNT + " =? AND "
+ SQLiteAxolotlStore . ID + " =? " ,
2015-05-29 09:17:26 +00:00
selectionArgs ,
null , null , null ) ;
return cursor ;
}
public PreKeyRecord loadPreKey ( Account account , int preKeyId ) {
PreKeyRecord record = null ;
Cursor cursor = getCursorForPreKey ( account , preKeyId ) ;
2015-11-28 19:11:38 +00:00
if ( cursor . getCount ( ) ! = 0 ) {
2015-05-29 09:17:26 +00:00
cursor . moveToFirst ( ) ;
try {
2015-11-28 19:11:38 +00:00
record = new PreKeyRecord ( Base64 . decode ( cursor . getString ( cursor . getColumnIndex ( SQLiteAxolotlStore . KEY ) ) , Base64 . DEFAULT ) ) ;
} catch ( IOException e ) {
2015-05-29 09:17:26 +00:00
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 ( ) ;
2015-07-28 20:00:54 +00:00
values . put ( SQLiteAxolotlStore . ID , record . getId ( ) ) ;
2015-11-28 19:11:38 +00:00
values . put ( SQLiteAxolotlStore . KEY , Base64 . encodeToString ( record . serialize ( ) , Base64 . DEFAULT ) ) ;
2015-07-28 20:00:54 +00:00
values . put ( SQLiteAxolotlStore . ACCOUNT , account . getUuid ( ) ) ;
db . insert ( SQLiteAxolotlStore . PREKEY_TABLENAME , null , values ) ;
2015-05-29 09:17:26 +00:00
}
public void deletePreKey ( Account account , int preKeyId ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
String [ ] args = { account . getUuid ( ) , Integer . toString ( preKeyId ) } ;
2015-07-28 20:00:54 +00:00
db . delete ( SQLiteAxolotlStore . PREKEY_TABLENAME ,
SQLiteAxolotlStore . ACCOUNT + " =? AND "
+ SQLiteAxolotlStore . ID + " =? " ,
2015-05-29 09:17:26 +00:00
args ) ;
}
private Cursor getCursorForSignedPreKey ( Account account , int signedPreKeyId ) {
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2015-07-28 20:00:54 +00:00
String [ ] columns = { SQLiteAxolotlStore . KEY } ;
2015-05-29 09:17:26 +00:00
String [ ] selectionArgs = { account . getUuid ( ) , Integer . toString ( signedPreKeyId ) } ;
2015-07-28 20:00:54 +00:00
Cursor cursor = db . query ( SQLiteAxolotlStore . SIGNED_PREKEY_TABLENAME ,
2015-05-29 09:17:26 +00:00
columns ,
2015-07-28 20:00:54 +00:00
SQLiteAxolotlStore . ACCOUNT + " =? AND " + SQLiteAxolotlStore . ID + " =? " ,
2015-05-29 09:17:26 +00:00
selectionArgs ,
null , null , null ) ;
return cursor ;
}
public SignedPreKeyRecord loadSignedPreKey ( Account account , int signedPreKeyId ) {
SignedPreKeyRecord record = null ;
2015-06-25 14:56:34 +00:00
Cursor cursor = getCursorForSignedPreKey ( account , signedPreKeyId ) ;
2015-11-28 19:11:38 +00:00
if ( cursor . getCount ( ) ! = 0 ) {
2015-05-29 09:17:26 +00:00
cursor . moveToFirst ( ) ;
try {
2015-11-28 19:11:38 +00:00
record = new SignedPreKeyRecord ( Base64 . decode ( cursor . getString ( cursor . getColumnIndex ( SQLiteAxolotlStore . KEY ) ) , Base64 . DEFAULT ) ) ;
} catch ( IOException e ) {
2015-05-29 09:17:26 +00:00
throw new AssertionError ( e ) ;
}
}
cursor . close ( ) ;
return record ;
}
public List < SignedPreKeyRecord > loadSignedPreKeys ( Account account ) {
List < SignedPreKeyRecord > prekeys = new ArrayList < > ( ) ;
SQLiteDatabase db = this . getReadableDatabase ( ) ;
2015-07-28 20:00:54 +00:00
String [ ] columns = { SQLiteAxolotlStore . KEY } ;
2015-05-29 09:17:26 +00:00
String [ ] selectionArgs = { account . getUuid ( ) } ;
2015-07-28 20:00:54 +00:00
Cursor cursor = db . query ( SQLiteAxolotlStore . SIGNED_PREKEY_TABLENAME ,
2015-05-29 09:17:26 +00:00
columns ,
2015-07-28 20:00:54 +00:00
SQLiteAxolotlStore . ACCOUNT + " =? " ,
2015-05-29 09:17:26 +00:00
selectionArgs ,
null , null , null ) ;
2015-11-28 19:11:38 +00:00
while ( cursor . moveToNext ( ) ) {
2015-05-29 09:17:26 +00:00
try {
2015-07-28 20:00:54 +00:00
prekeys . add ( new SignedPreKeyRecord ( Base64 . decode ( cursor . getString ( cursor . getColumnIndex ( SQLiteAxolotlStore . KEY ) ) , Base64 . DEFAULT ) ) ) ;
2015-05-29 09:17:26 +00:00
} catch ( IOException ignored ) {
}
}
2015-06-29 11:40:56 +00:00
cursor . close ( ) ;
2015-05-29 09:17:26 +00:00
return prekeys ;
}
2017-05-04 09:03:58 +00:00
public int getSignedPreKeysCount ( Account account ) {
String [ ] columns = { " count( " + SQLiteAxolotlStore . KEY + " ) " } ;
String [ ] selectionArgs = { account . getUuid ( ) } ;
SQLiteDatabase db = this . getReadableDatabase ( ) ;
Cursor cursor = db . query ( SQLiteAxolotlStore . SIGNED_PREKEY_TABLENAME ,
columns ,
SQLiteAxolotlStore . ACCOUNT + " =? " ,
selectionArgs ,
null , null , null ) ;
final int count ;
if ( cursor . moveToFirst ( ) ) {
count = cursor . getInt ( 0 ) ;
} else {
count = 0 ;
}
cursor . close ( ) ;
return count ;
}
2015-05-29 09:17:26 +00:00
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 ( ) ;
2015-07-28 20:00:54 +00:00
values . put ( SQLiteAxolotlStore . ID , record . getId ( ) ) ;
2015-11-28 19:11:38 +00:00
values . put ( SQLiteAxolotlStore . KEY , Base64 . encodeToString ( record . serialize ( ) , Base64 . DEFAULT ) ) ;
2015-07-28 20:00:54 +00:00
values . put ( SQLiteAxolotlStore . ACCOUNT , account . getUuid ( ) ) ;
db . insert ( SQLiteAxolotlStore . SIGNED_PREKEY_TABLENAME , null , values ) ;
2015-05-29 09:17:26 +00:00
}
public void deleteSignedPreKey ( Account account , int signedPreKeyId ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
String [ ] args = { account . getUuid ( ) , Integer . toString ( signedPreKeyId ) } ;
2015-07-28 20:00:54 +00:00
db . delete ( SQLiteAxolotlStore . SIGNED_PREKEY_TABLENAME ,
SQLiteAxolotlStore . ACCOUNT + " =? AND "
+ SQLiteAxolotlStore . ID + " =? " ,
2015-05-29 09:17:26 +00:00
args ) ;
}
2015-06-29 11:53:39 +00:00
private Cursor getIdentityKeyCursor ( Account account , String name , boolean own ) {
2015-09-05 15:25:46 +00:00
final SQLiteDatabase db = this . getReadableDatabase ( ) ;
return getIdentityKeyCursor ( db , account , name , own ) ;
2015-07-09 12:23:17 +00:00
}
2015-09-05 15:25:46 +00:00
private Cursor getIdentityKeyCursor ( SQLiteDatabase db , Account account , String name , boolean own ) {
2015-11-28 19:11:38 +00:00
return getIdentityKeyCursor ( db , account , name , own , null ) ;
2015-07-15 14:32:42 +00:00
}
2015-09-05 15:25:46 +00:00
private Cursor getIdentityKeyCursor ( Account account , String fingerprint ) {
2015-06-29 11:53:39 +00:00
final SQLiteDatabase db = this . getReadableDatabase ( ) ;
2015-09-05 15:25:46 +00:00
return getIdentityKeyCursor ( db , account , fingerprint ) ;
}
private Cursor getIdentityKeyCursor ( SQLiteDatabase db , Account account , String fingerprint ) {
return getIdentityKeyCursor ( db , account , null , null , fingerprint ) ;
}
private Cursor getIdentityKeyCursor ( SQLiteDatabase db , Account account , String name , Boolean own , String fingerprint ) {
2016-11-14 21:27:41 +00:00
String [ ] columns = { SQLiteAxolotlStore . TRUST ,
SQLiteAxolotlStore . ACTIVE ,
2016-11-19 12:34:54 +00:00
SQLiteAxolotlStore . LAST_ACTIVATION ,
2015-07-28 20:00:54 +00:00
SQLiteAxolotlStore . KEY } ;
2015-07-09 12:23:17 +00:00
ArrayList < String > selectionArgs = new ArrayList < > ( 4 ) ;
selectionArgs . add ( account . getUuid ( ) ) ;
2015-07-28 20:00:54 +00:00
String selectionString = SQLiteAxolotlStore . ACCOUNT + " = ? " ;
2015-11-28 19:11:38 +00:00
if ( name ! = null ) {
2015-07-15 14:32:42 +00:00
selectionArgs . add ( name ) ;
2015-07-28 20:00:54 +00:00
selectionString + = " AND " + SQLiteAxolotlStore . NAME + " = ? " ;
2015-07-15 14:32:42 +00:00
}
2015-11-28 19:11:38 +00:00
if ( fingerprint ! = null ) {
2015-07-09 12:23:17 +00:00
selectionArgs . add ( fingerprint ) ;
2015-07-28 20:00:54 +00:00
selectionString + = " AND " + SQLiteAxolotlStore . FINGERPRINT + " = ? " ;
2015-07-15 14:32:42 +00:00
}
2015-11-28 19:11:38 +00:00
if ( own ! = null ) {
selectionArgs . add ( own ? " 1 " : " 0 " ) ;
2015-07-28 20:00:54 +00:00
selectionString + = " AND " + SQLiteAxolotlStore . OWN + " = ? " ;
2015-07-09 12:23:17 +00:00
}
2015-07-28 20:00:54 +00:00
Cursor cursor = db . query ( SQLiteAxolotlStore . IDENTITIES_TABLENAME ,
2015-06-29 11:53:39 +00:00
columns ,
2015-07-09 12:23:17 +00:00
selectionString ,
selectionArgs . toArray ( new String [ selectionArgs . size ( ) ] ) ,
2015-06-29 11:53:39 +00:00
null , null , null ) ;
return cursor ;
}
2015-09-05 15:25:46 +00:00
public IdentityKeyPair loadOwnIdentityKeyPair ( Account account ) {
SQLiteDatabase db = getReadableDatabase ( ) ;
return loadOwnIdentityKeyPair ( db , account ) ;
}
private IdentityKeyPair loadOwnIdentityKeyPair ( SQLiteDatabase db , Account account ) {
2016-10-20 15:31:46 +00:00
String name = account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) ;
2015-06-29 11:53:39 +00:00
IdentityKeyPair identityKeyPair = null ;
2015-09-05 15:25:46 +00:00
Cursor cursor = getIdentityKeyCursor ( db , account , name , true ) ;
2015-11-28 19:11:38 +00:00
if ( cursor . getCount ( ) ! = 0 ) {
2015-06-29 11:53:39 +00:00
cursor . moveToFirst ( ) ;
try {
2015-11-28 19:11:38 +00:00
identityKeyPair = new IdentityKeyPair ( Base64 . decode ( cursor . getString ( cursor . getColumnIndex ( SQLiteAxolotlStore . KEY ) ) , Base64 . DEFAULT ) ) ;
2015-06-29 11:53:39 +00:00
} catch ( InvalidKeyException e ) {
2015-11-28 19:11:38 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Encountered invalid IdentityKey in database for account " + account . getJid ( ) . toBareJid ( ) + " , address: " + name ) ;
2015-06-29 11:53:39 +00:00
}
}
cursor . close ( ) ;
return identityKeyPair ;
}
public Set < IdentityKey > loadIdentityKeys ( Account account , String name ) {
2015-07-19 16:36:28 +00:00
return loadIdentityKeys ( account , name , null ) ;
}
2016-11-14 21:27:41 +00:00
public Set < IdentityKey > loadIdentityKeys ( Account account , String name , FingerprintStatus status ) {
2015-06-29 11:53:39 +00:00
Set < IdentityKey > identityKeys = new HashSet < > ( ) ;
Cursor cursor = getIdentityKeyCursor ( account , name , false ) ;
2015-11-28 19:11:38 +00:00
while ( cursor . moveToNext ( ) ) {
2016-11-14 21:27:41 +00:00
if ( status ! = null & & ! FingerprintStatus . fromCursor ( cursor ) . equals ( status ) ) {
2015-07-19 16:36:28 +00:00
continue ;
}
2015-06-29 11:53:39 +00:00
try {
2016-11-17 19:09:42 +00:00
String key = cursor . getString ( cursor . getColumnIndex ( SQLiteAxolotlStore . KEY ) ) ;
if ( key ! = null ) {
identityKeys . add ( new IdentityKey ( Base64 . decode ( key , Base64 . DEFAULT ) , 0 ) ) ;
} else {
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Missing key (possibly preverified) in database for account " + account . getJid ( ) . toBareJid ( ) + " , address: " + name ) ;
}
2015-06-29 11:53:39 +00:00
} catch ( InvalidKeyException e ) {
2015-11-28 19:11:38 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Encountered invalid IdentityKey in database for account " + account . getJid ( ) . toBareJid ( ) + " , address: " + name ) ;
2015-06-29 11:53:39 +00:00
}
}
cursor . close ( ) ;
return identityKeys ;
}
2015-07-20 12:56:41 +00:00
public long numTrustedKeys ( Account account , String name ) {
SQLiteDatabase db = getReadableDatabase ( ) ;
String [ ] args = {
account . getUuid ( ) ,
2015-07-20 20:02:54 +00:00
name ,
2016-11-14 21:27:41 +00:00
FingerprintStatus . Trust . TRUSTED . toString ( ) ,
FingerprintStatus . Trust . VERIFIED . toString ( ) ,
FingerprintStatus . Trust . VERIFIED_X509 . toString ( )
2015-07-20 12:56:41 +00:00
} ;
2015-07-28 20:00:54 +00:00
return DatabaseUtils . queryNumEntries ( db , SQLiteAxolotlStore . IDENTITIES_TABLENAME ,
SQLiteAxolotlStore . ACCOUNT + " = ? "
2015-11-28 19:11:38 +00:00
+ " AND " + SQLiteAxolotlStore . NAME + " = ? "
2016-11-14 21:27:41 +00:00
+ " AND ( " + SQLiteAxolotlStore . TRUST + " = ? OR " + SQLiteAxolotlStore . TRUST + " = ? OR " + SQLiteAxolotlStore . TRUST + " = ?) "
+ " AND " + SQLiteAxolotlStore . ACTIVE + " > 0 " ,
2015-07-20 12:56:41 +00:00
args
) ;
}
2016-11-14 21:27:41 +00:00
private void storeIdentityKey ( Account account , String name , boolean own , String fingerprint , String base64Serialized , FingerprintStatus status ) {
2015-06-29 11:53:39 +00:00
SQLiteDatabase db = this . getWritableDatabase ( ) ;
ContentValues values = new ContentValues ( ) ;
2015-07-28 20:00:54 +00:00
values . put ( SQLiteAxolotlStore . ACCOUNT , account . getUuid ( ) ) ;
values . put ( SQLiteAxolotlStore . NAME , name ) ;
values . put ( SQLiteAxolotlStore . OWN , own ? 1 : 0 ) ;
values . put ( SQLiteAxolotlStore . FINGERPRINT , fingerprint ) ;
values . put ( SQLiteAxolotlStore . KEY , base64Serialized ) ;
2016-11-14 21:27:41 +00:00
values . putAll ( status . toContentValues ( ) ) ;
2016-11-17 19:09:42 +00:00
String where = SQLiteAxolotlStore . ACCOUNT + " =? AND " + SQLiteAxolotlStore . NAME + " =? AND " + SQLiteAxolotlStore . FINGERPRINT + " =? " ;
String [ ] whereArgs = { account . getUuid ( ) , name , fingerprint } ;
int rows = db . update ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , values , where , whereArgs ) ;
if ( rows = = 0 ) {
db . insert ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , null , values ) ;
}
}
public void storePreVerification ( Account account , String name , String fingerprint , FingerprintStatus status ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
ContentValues values = new ContentValues ( ) ;
values . put ( SQLiteAxolotlStore . ACCOUNT , account . getUuid ( ) ) ;
values . put ( SQLiteAxolotlStore . NAME , name ) ;
values . put ( SQLiteAxolotlStore . OWN , 0 ) ;
values . put ( SQLiteAxolotlStore . FINGERPRINT , fingerprint ) ;
values . putAll ( status . toContentValues ( ) ) ;
2015-07-28 20:00:54 +00:00
db . insert ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , null , values ) ;
2015-06-29 11:53:39 +00:00
}
2016-11-14 21:27:41 +00:00
public FingerprintStatus getFingerprintStatus ( Account account , String fingerprint ) {
2015-07-15 14:32:42 +00:00
Cursor cursor = getIdentityKeyCursor ( account , fingerprint ) ;
2016-11-14 21:27:41 +00:00
final FingerprintStatus status ;
2015-07-09 12:23:17 +00:00
if ( cursor . getCount ( ) > 0 ) {
cursor . moveToFirst ( ) ;
2016-11-14 21:27:41 +00:00
status = FingerprintStatus . fromCursor ( cursor ) ;
} else {
status = null ;
2015-07-09 12:23:17 +00:00
}
cursor . close ( ) ;
2016-11-14 21:27:41 +00:00
return status ;
2015-07-09 12:23:17 +00:00
}
2016-11-14 21:27:41 +00:00
public boolean setIdentityKeyTrust ( Account account , String fingerprint , FingerprintStatus fingerprintStatus ) {
2015-07-09 12:23:17 +00:00
SQLiteDatabase db = this . getWritableDatabase ( ) ;
2016-11-14 21:27:41 +00:00
return setIdentityKeyTrust ( db , account , fingerprint , fingerprintStatus ) ;
2015-09-05 15:25:46 +00:00
}
2016-11-14 21:27:41 +00:00
private boolean setIdentityKeyTrust ( SQLiteDatabase db , Account account , String fingerprint , FingerprintStatus status ) {
2015-07-09 12:23:17 +00:00
String [ ] selectionArgs = {
account . getUuid ( ) ,
fingerprint
} ;
2016-11-14 21:27:41 +00:00
int rows = db . update ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , status . toContentValues ( ) ,
2015-07-28 20:00:54 +00:00
SQLiteAxolotlStore . ACCOUNT + " = ? AND "
2015-11-28 19:11:38 +00:00
+ SQLiteAxolotlStore . FINGERPRINT + " = ? " ,
2015-07-09 12:23:17 +00:00
selectionArgs ) ;
return rows = = 1 ;
}
2015-12-23 16:41:26 +00:00
public boolean setIdentityKeyCertificate ( Account account , String fingerprint , X509Certificate x509Certificate ) {
SQLiteDatabase db = this . getWritableDatabase ( ) ;
String [ ] selectionArgs = {
account . getUuid ( ) ,
fingerprint
} ;
try {
ContentValues values = new ContentValues ( ) ;
values . put ( SQLiteAxolotlStore . CERTIFICATE , x509Certificate . getEncoded ( ) ) ;
return db . update ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , values ,
SQLiteAxolotlStore . ACCOUNT + " = ? AND "
+ SQLiteAxolotlStore . FINGERPRINT + " = ? " ,
selectionArgs ) = = 1 ;
} catch ( CertificateEncodingException e ) {
2015-12-23 18:18:53 +00:00
Log . d ( Config . LOGTAG , " could not encode certificate " ) ;
2015-12-23 16:41:26 +00:00
return false ;
}
}
2015-12-23 18:18:53 +00:00
public X509Certificate getIdentityKeyCertifcate ( Account account , String fingerprint ) {
SQLiteDatabase db = this . getReadableDatabase ( ) ;
String [ ] selectionArgs = {
account . getUuid ( ) ,
fingerprint
} ;
String [ ] colums = { SQLiteAxolotlStore . CERTIFICATE } ;
String selection = SQLiteAxolotlStore . ACCOUNT + " = ? AND " + SQLiteAxolotlStore . FINGERPRINT + " = ? " ;
Cursor cursor = db . query ( SQLiteAxolotlStore . IDENTITIES_TABLENAME , colums , selection , selectionArgs , null , null , null ) ;
if ( cursor . getCount ( ) < 1 ) {
return null ;
} else {
cursor . moveToFirst ( ) ;
byte [ ] certificate = cursor . getBlob ( cursor . getColumnIndex ( SQLiteAxolotlStore . CERTIFICATE ) ) ;
2016-06-12 10:50:31 +00:00
cursor . close ( ) ;
2015-12-23 18:18:53 +00:00
if ( certificate = = null | | certificate . length = = 0 ) {
return null ;
}
try {
CertificateFactory certificateFactory = CertificateFactory . getInstance ( " X.509 " ) ;
return ( X509Certificate ) certificateFactory . generateCertificate ( new ByteArrayInputStream ( certificate ) ) ;
} catch ( CertificateException e ) {
Log . d ( Config . LOGTAG , " certificate exception " + e . getMessage ( ) ) ;
return null ;
}
}
}
2016-11-17 19:09:42 +00:00
public void storeIdentityKey ( Account account , String name , IdentityKey identityKey , FingerprintStatus status ) {
2017-06-25 16:18:13 +00:00
storeIdentityKey ( account , name , false , CryptoHelper . bytesToHex ( identityKey . getPublicKey ( ) . serialize ( ) ) , Base64 . encodeToString ( identityKey . serialize ( ) , Base64 . DEFAULT ) , status ) ;
2015-06-29 11:53:39 +00:00
}
2015-09-05 15:25:46 +00:00
public void storeOwnIdentityKeyPair ( Account account , IdentityKeyPair identityKeyPair ) {
2017-06-25 16:18:13 +00:00
storeIdentityKey ( account , account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , true , CryptoHelper . bytesToHex ( identityKeyPair . getPublicKey ( ) . serialize ( ) ) , Base64 . encodeToString ( identityKeyPair . serialize ( ) , Base64 . DEFAULT ) , FingerprintStatus . createActiveVerified ( false ) ) ;
2015-06-29 11:53:39 +00:00
}
2015-07-07 17:30:08 +00:00
2015-07-20 11:15:49 +00:00
public void recreateAxolotlDb ( SQLiteDatabase db ) {
2015-11-28 19:11:38 +00:00
Log . d ( Config . LOGTAG , AxolotlService . LOGPREFIX + " : " + " >>> (RE)CREATING AXOLOTL DATABASE <<< " ) ;
2015-07-28 20:00:54 +00:00
db . execSQL ( " DROP TABLE IF EXISTS " + SQLiteAxolotlStore . SESSION_TABLENAME ) ;
2015-07-07 17:30:08 +00:00
db . execSQL ( CREATE_SESSIONS_STATEMENT ) ;
2015-07-28 20:00:54 +00:00
db . execSQL ( " DROP TABLE IF EXISTS " + SQLiteAxolotlStore . PREKEY_TABLENAME ) ;
2015-07-07 17:30:08 +00:00
db . execSQL ( CREATE_PREKEYS_STATEMENT ) ;
2015-07-28 20:00:54 +00:00
db . execSQL ( " DROP TABLE IF EXISTS " + SQLiteAxolotlStore . SIGNED_PREKEY_TABLENAME ) ;
2015-07-07 17:30:08 +00:00
db . execSQL ( CREATE_SIGNED_PREKEYS_STATEMENT ) ;
2015-07-28 20:00:54 +00:00
db . execSQL ( " DROP TABLE IF EXISTS " + SQLiteAxolotlStore . IDENTITIES_TABLENAME ) ;
2015-07-07 17:30:08 +00:00
db . execSQL ( CREATE_IDENTITIES_STATEMENT ) ;
}
2015-11-28 19:11:38 +00:00
2015-07-07 17:36:22 +00:00
public void wipeAxolotlDb ( Account account ) {
String accountName = account . getUuid ( ) ;
2015-11-28 19:11:38 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " >>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<< " ) ;
2015-07-07 17:36:22 +00:00
SQLiteDatabase db = this . getWritableDatabase ( ) ;
2015-11-28 19:11:38 +00:00
String [ ] deleteArgs = {
2015-07-07 17:36:22 +00:00
accountName
} ;
2015-07-28 20:00:54 +00:00
db . delete ( SQLiteAxolotlStore . SESSION_TABLENAME ,
SQLiteAxolotlStore . ACCOUNT + " = ? " ,
2015-07-07 17:36:22 +00:00
deleteArgs ) ;
2015-07-28 20:00:54 +00:00
db . delete ( SQLiteAxolotlStore . PREKEY_TABLENAME ,
SQLiteAxolotlStore . ACCOUNT + " = ? " ,
2015-07-07 17:36:22 +00:00
deleteArgs ) ;
2015-07-28 20:00:54 +00:00
db . delete ( SQLiteAxolotlStore . SIGNED_PREKEY_TABLENAME ,
SQLiteAxolotlStore . ACCOUNT + " = ? " ,
2015-07-07 17:36:22 +00:00
deleteArgs ) ;
2015-07-28 20:00:54 +00:00
db . delete ( SQLiteAxolotlStore . IDENTITIES_TABLENAME ,
SQLiteAxolotlStore . ACCOUNT + " = ? " ,
2015-07-07 17:36:22 +00:00
deleteArgs ) ;
}
2016-11-08 11:20:07 +00:00
2017-05-31 14:45:51 +00:00
public List < ShortcutService . FrequentContact > getFrequentContacts ( int days ) {
SQLiteDatabase db = this . getReadableDatabase ( ) ;
final String SQL = " select " + Conversation . TABLENAME + " . " + Conversation . ACCOUNT + " , " + Conversation . TABLENAME + " . " + Conversation . CONTACTJID + " from " + Conversation . TABLENAME + " join " + Message . TABLENAME + " on conversations.uuid=messages.conversationUuid where messages.status!=0 and carbon==0 and conversations.mode=0 and messages.timeSent>=? group by conversations.uuid order by count(body) desc limit 4; " ;
String [ ] whereArgs = new String [ ] { String . valueOf ( System . currentTimeMillis ( ) - ( Config . MILLISECONDS_IN_DAY * days ) ) } ;
Cursor cursor = db . rawQuery ( SQL , whereArgs ) ;
ArrayList < ShortcutService . FrequentContact > contacts = new ArrayList < > ( ) ;
while ( cursor . moveToNext ( ) ) {
try {
contacts . add ( new ShortcutService . FrequentContact ( cursor . getString ( 0 ) , Jid . fromString ( cursor . getString ( 1 ) ) ) ) ;
} catch ( Exception e ) {
Log . d ( Config . LOGTAG , e . getMessage ( ) ) ;
}
}
cursor . close ( ) ;
return contacts ;
}
2014-01-24 22:58:51 +00:00
}