2020-05-11 08:19:26 +02:00
package org.thoughtcrime.securesms.loki.database
2019-06-04 01:35:18 +02:00
2019-06-04 03:05:03 +02:00
import android.content.ContentValues
2019-06-04 01:35:18 +02:00
import android.content.Context
2021-01-13 06:13:49 +01:00
import org.session.libsignal.libsignal.ecc.DjbECPrivateKey
import org.session.libsignal.libsignal.ecc.DjbECPublicKey
import org.session.libsignal.libsignal.ecc.ECKeyPair
2021-02-03 02:22:40 +01:00
import org.session.libsignal.utilities.logging.Log
2019-06-04 01:35:18 +02:00
import org.thoughtcrime.securesms.database.Database
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
2020-05-11 08:19:26 +02:00
import org.thoughtcrime.securesms.loki.utilities.*
2020-11-26 00:07:45 +01:00
import org.session.libsignal.service.loki.api.Snode
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
2021-01-13 06:13:49 +01:00
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
import org.session.libsignal.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
2021-02-01 00:25:19 +01:00
import org.session.libsignal.utilities.Hex
2021-01-20 01:18:00 +01:00
import org.session.libsession.utilities.TextSecurePreferences
2021-02-18 04:14:05 +01:00
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
2020-12-06 23:02:48 +01:00
import java.util.*
2019-06-04 01:35:18 +02:00
2019-06-11 01:46:42 +02:00
class LokiAPIDatabase ( context : Context , helper : SQLCipherOpenHelper ) : Database ( context , helper ) , LokiAPIDatabaseProtocol {
2019-06-04 01:35:18 +02:00
companion object {
2020-07-16 01:46:10 +02:00
// Shared
private val publicKey = " public_key "
private val timestamp = " timestamp "
2020-08-10 03:40:43 +02:00
private val snode = " snode "
// Snode pool
private val snodePoolTable = " loki_snode_pool_cache "
2020-05-29 04:01:43 +02:00
private val dummyKey = " dummy_key "
2020-06-01 02:33:47 +02:00
private val snodePool = " snode_pool_key "
2020-08-10 05:38:07 +02:00
@JvmStatic val createSnodePoolTableCommand = " CREATE TABLE $snodePoolTable ( $dummyKey TEXT PRIMARY KEY, $snodePool TEXT); "
2020-08-10 03:40:43 +02:00
// Onion request paths
private val onionRequestPathTable = " loki_path_cache "
2020-05-29 05:57:56 +02:00
private val indexPath = " index_path "
2020-08-10 05:38:07 +02:00
@JvmStatic val createOnionRequestPathTableCommand = " CREATE TABLE $onionRequestPathTable ( $indexPath TEXT PRIMARY KEY, $snode TEXT); "
2020-08-10 03:40:43 +02:00
// Swarms
private val swarmTable = " loki_api_swarm_cache "
2020-07-15 04:24:43 +02:00
private val swarmPublicKey = " hex_encoded_public_key "
2019-06-04 03:05:03 +02:00
private val swarm = " swarm "
2020-08-10 05:38:07 +02:00
@JvmStatic val createSwarmTableCommand = " CREATE TABLE $swarmTable ( $swarmPublicKey TEXT PRIMARY KEY, $swarm TEXT); "
2020-08-10 03:40:43 +02:00
// Last message hash values
private val lastMessageHashValueTable2 = " last_message_hash_value_table "
2019-06-04 03:05:03 +02:00
private val lastMessageHashValue = " last_message_hash_value "
2020-08-10 03:40:43 +02:00
@JvmStatic val createLastMessageHashValueTable2Command
2020-08-12 02:00:35 +02:00
= " CREATE TABLE $lastMessageHashValueTable2 ( $snode TEXT, $publicKey TEXT, $lastMessageHashValue TEXT, PRIMARY KEY ( $snode , $publicKey )); "
2020-08-10 03:40:43 +02:00
// Received message hash values
2020-08-20 02:32:31 +02:00
private val receivedMessageHashValuesTable3 = " received_message_hash_values_table_3 "
2019-06-04 03:05:03 +02:00
private val receivedMessageHashValues = " received_message_hash_values "
2020-08-20 02:32:31 +02:00
@JvmStatic val createReceivedMessageHashValuesTable3Command
= " CREATE TABLE $receivedMessageHashValuesTable3 ( $publicKey STRING PRIMARY KEY, $receivedMessageHashValues TEXT); "
2020-08-10 03:40:43 +02:00
// Open group auth tokens
private val openGroupAuthTokenTable = " loki_api_group_chat_auth_token_database "
2019-08-28 04:09:05 +02:00
private val server = " server "
2019-08-23 08:57:26 +02:00
private val token = " token "
2020-08-10 05:38:07 +02:00
@JvmStatic val createOpenGroupAuthTokenTableCommand = " CREATE TABLE $openGroupAuthTokenTable ( $server TEXT PRIMARY KEY, $token TEXT); "
2020-08-10 03:40:43 +02:00
// Last message server IDs
private val lastMessageServerIDTable = " loki_api_last_message_server_id_cache "
private val lastMessageServerIDTableIndex = " loki_api_last_message_server_id_cache_index "
2019-08-26 05:55:45 +02:00
private val lastMessageServerID = " last_message_server_id "
2020-08-10 03:40:43 +02:00
@JvmStatic val createLastMessageServerIDTableCommand = " CREATE TABLE $lastMessageServerIDTable ( $lastMessageServerIDTableIndex STRING PRIMARY KEY, $lastMessageServerID INTEGER DEFAULT 0); "
// Last deletion server IDs
private val lastDeletionServerIDTable = " loki_api_last_deletion_server_id_cache "
private val lastDeletionServerIDTableIndex = " loki_api_last_deletion_server_id_cache_index "
2019-08-30 09:08:46 +02:00
private val lastDeletionServerID = " last_deletion_server_id "
2020-08-10 03:40:43 +02:00
@JvmStatic val createLastDeletionServerIDTableCommand = " CREATE TABLE $lastDeletionServerIDTable ( $lastDeletionServerIDTableIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0); "
// User counts
private val userCountTable = " loki_user_count_cache "
2020-01-22 00:46:04 +01:00
private val publicChatID = " public_chat_id "
private val userCount = " user_count "
2020-08-10 03:40:43 +02:00
@JvmStatic val createUserCountTableCommand = " CREATE TABLE $userCountTable ( $publicChatID STRING PRIMARY KEY, $userCount INTEGER DEFAULT 0); "
// Session request sent timestamps
private val sessionRequestSentTimestampTable = " session_request_sent_timestamp_cache "
@JvmStatic val createSessionRequestSentTimestampTableCommand = " CREATE TABLE $sessionRequestSentTimestampTable ( $publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0); "
2020-07-16 01:44:00 +02:00
// Session request processed timestamp cache
2020-08-10 03:40:43 +02:00
private val sessionRequestProcessedTimestampTable = " session_request_processed_timestamp_cache "
@JvmStatic val createSessionRequestProcessedTimestampTableCommand = " CREATE TABLE $sessionRequestProcessedTimestampTable ( $publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0); "
2020-07-29 01:04:24 +02:00
// Open group public keys
2020-08-10 03:40:43 +02:00
private val openGroupPublicKeyTable = " open_group_public_keys "
@JvmStatic val createOpenGroupPublicKeyTableCommand = " CREATE TABLE $openGroupPublicKeyTable ( $server STRING PRIMARY KEY, $publicKey INTEGER DEFAULT 0); "
2020-10-19 06:12:06 +02:00
// Open group profile picture cache
2020-11-04 13:29:47 +01:00
public val openGroupProfilePictureTable = " open_group_avatar_cache "
2020-10-19 06:12:06 +02:00
private val openGroupProfilePicture = " open_group_avatar "
@JvmStatic val createOpenGroupProfilePictureTableCommand = " CREATE TABLE $openGroupProfilePictureTable ( $publicChatID STRING PRIMARY KEY, $openGroupProfilePicture TEXT NULLABLE DEFAULT NULL); "
2021-01-13 06:13:49 +01:00
// Closed groups (V2)
public val closedGroupEncryptionKeyPairsTable = " closed_group_encryption_key_pairs_table "
public val closedGroupsEncryptionKeyPairIndex = " closed_group_encryption_key_pair_index "
public val encryptionKeyPairPublicKey = " encryption_key_pair_public_key "
public val encryptionKeyPairPrivateKey = " encryption_key_pair_private_key "
@JvmStatic
val createClosedGroupEncryptionKeyPairsTable = " CREATE TABLE $closedGroupEncryptionKeyPairsTable ( $closedGroupsEncryptionKeyPairIndex STRING PRIMARY KEY, $encryptionKeyPairPublicKey STRING, $encryptionKeyPairPrivateKey STRING) "
public val closedGroupPublicKeysTable = " closed_group_public_keys_table "
public val groupPublicKey = " group_public_key "
@JvmStatic
val createClosedGroupPublicKeysTable = " CREATE TABLE $closedGroupPublicKeysTable ( $groupPublicKey STRING PRIMARY KEY) "
2020-07-16 01:44:00 +02:00
// region Deprecated
2020-08-10 03:40:43 +02:00
private val deviceLinkCache = " loki_pairing_authorisation_cache "
private val masterPublicKey = " primary_device "
private val slavePublicKey = " secondary_device "
private val requestSignature = " request_signature "
private val authorizationSignature = " grant_signature "
@JvmStatic val createDeviceLinkCacheCommand = " CREATE TABLE $deviceLinkCache ( $masterPublicKey STRING, $slavePublicKey STRING, " +
" $requestSignature STRING NULLABLE DEFAULT NULL, $authorizationSignature STRING NULLABLE DEFAULT NULL, PRIMARY KEY ( $masterPublicKey , $slavePublicKey )); "
2020-05-22 02:41:31 +02:00
private val sessionRequestTimestampCache = " session_request_timestamp_cache "
2020-07-29 01:04:24 +02:00
@JvmStatic val createSessionRequestTimestampCacheCommand = " CREATE TABLE $sessionRequestTimestampCache ( $publicKey STRING PRIMARY KEY, $timestamp STRING); "
2020-07-16 01:44:00 +02:00
// endregion
2019-06-04 01:35:18 +02:00
}
2020-07-08 09:05:26 +02:00
override fun getSnodePool ( ) : Set < Snode > {
2020-05-29 04:01:43 +02:00
val database = databaseHelper . readableDatabase
2020-08-10 03:40:43 +02:00
return database . get ( snodePoolTable , " ${Companion.dummyKey} = ? " , wrap ( " dummy_key " ) ) { cursor ->
2020-06-01 02:33:47 +02:00
val snodePoolAsString = cursor . getString ( cursor . getColumnIndexOrThrow ( snodePool ) )
2020-05-29 04:01:43 +02:00
snodePoolAsString . split ( " , " ) . mapNotNull { snodeAsString ->
val components = snodeAsString . split ( " - " )
val address = components [ 0 ]
val port = components . getOrNull ( 1 ) ?. toIntOrNull ( ) ?: return @mapNotNull null
val ed25519Key = components . getOrNull ( 2 ) ?: return @mapNotNull null
2020-05-29 05:57:56 +02:00
val x25519Key = components . getOrNull ( 3 ) ?: return @mapNotNull null
2020-07-08 09:05:26 +02:00
Snode ( address , port , Snode . KeySet ( ed25519Key , x25519Key ) )
2020-05-29 04:01:43 +02:00
}
} ?. toSet ( ) ?: setOf ( )
}
2020-07-08 09:05:26 +02:00
override fun setSnodePool ( newValue : Set < Snode > ) {
2020-05-29 04:01:43 +02:00
val database = databaseHelper . writableDatabase
val snodePoolAsString = newValue . joinToString ( " , " ) { snode ->
var string = " ${snode.address} - ${snode.port} "
val keySet = snode . publicKeySet
if ( keySet != null ) {
string += " - ${keySet.ed25519Key} - ${keySet.x25519Key} "
}
string
}
2020-08-12 02:00:35 +02:00
val row = wrap ( mapOf ( Companion . dummyKey to " dummy_key " , snodePool to snodePoolAsString ) )
2020-08-10 03:40:43 +02:00
database . insertOrUpdate ( snodePoolTable , row , " ${Companion.dummyKey} = ? " , wrap ( " dummy_key " ) )
2020-05-29 04:01:43 +02:00
}
2020-10-09 04:57:52 +02:00
override fun setOnionRequestPaths ( newValue : List < List < Snode > > ) {
// FIXME: This approach assumes either 1 or 2 paths of length 3 each. We should do better than this.
val database = databaseHelper . writableDatabase
fun set ( indexPath : String , snode : Snode ) {
var snodeAsString = " ${snode.address} - ${snode.port} "
val keySet = snode . publicKeySet
if ( keySet != null ) {
snodeAsString += " - ${keySet.ed25519Key} - ${keySet.x25519Key} "
}
val row = wrap ( mapOf ( Companion . indexPath to indexPath , Companion . snode to snodeAsString ) )
database . insertOrUpdate ( onionRequestPathTable , row , " ${Companion.indexPath} = ? " , wrap ( indexPath ) )
}
Log . d ( " Loki " , " Persisting onion request paths to database. " )
2020-10-09 05:38:29 +02:00
clearOnionRequestPaths ( )
2020-10-09 04:57:52 +02:00
if ( newValue . count ( ) < 1 ) { return }
val path0 = newValue [ 0 ]
if ( path0 . count ( ) != 3 ) { return }
set ( " 0-0 " , path0 [ 0 ] ) ; set ( " 0-1 " , path0 [ 1 ] ) ; set ( " 0-2 " , path0 [ 2 ] )
if ( newValue . count ( ) < 2 ) { return }
val path1 = newValue [ 1 ]
if ( path1 . count ( ) != 3 ) { return }
set ( " 1-0 " , path1 [ 0 ] ) ; set ( " 1-1 " , path1 [ 1 ] ) ; set ( " 1-2 " , path1 [ 2 ] )
}
2020-07-08 09:05:26 +02:00
override fun getOnionRequestPaths ( ) : List < List < Snode > > {
2020-05-29 05:57:56 +02:00
val database = databaseHelper . readableDatabase
2020-07-08 09:05:26 +02:00
fun get ( indexPath : String ) : Snode ? {
2020-08-10 03:40:43 +02:00
return database . get ( onionRequestPathTable , " ${Companion.indexPath} = ? " , wrap ( indexPath ) ) { cursor ->
2020-05-29 05:57:56 +02:00
val snodeAsString = cursor . getString ( cursor . getColumnIndexOrThrow ( snode ) )
val components = snodeAsString . split ( " - " )
val address = components [ 0 ]
val port = components . getOrNull ( 1 ) ?. toIntOrNull ( )
val ed25519Key = components . getOrNull ( 2 )
val x25519Key = components . getOrNull ( 3 )
if ( port != null && ed25519Key != null && x25519Key != null ) {
2020-07-08 09:05:26 +02:00
Snode ( address , port , Snode . KeySet ( ed25519Key , x25519Key ) )
2020-05-29 05:57:56 +02:00
} else {
null
}
}
}
2020-10-09 04:57:52 +02:00
val result = mutableListOf < List < Snode > > ( )
val path0Snode0 = get ( " 0-0 " ) ; val path0Snode1 = get ( " 0-1 " ) ; val path0Snode2 = get ( " 0-2 " )
if ( path0Snode0 != null && path0Snode1 != null && path0Snode2 != null ) {
result . add ( listOf ( path0Snode0 , path0Snode1 , path0Snode2 ) )
}
val path1Snode0 = get ( " 1-0 " ) ; val path1Snode1 = get ( " 1-1 " ) ; val path1Snode2 = get ( " 1-2 " )
if ( path1Snode0 != null && path1Snode1 != null && path1Snode2 != null ) {
result . add ( listOf ( path1Snode0 , path1Snode1 , path1Snode2 ) )
}
return result
2020-05-29 05:57:56 +02:00
}
2020-09-01 10:30:54 +02:00
override fun clearOnionRequestPaths ( ) {
2020-05-29 05:57:56 +02:00
val database = databaseHelper . writableDatabase
fun delete ( indexPath : String ) {
2020-08-10 03:40:43 +02:00
database . delete ( onionRequestPathTable , " ${Companion.indexPath} = ? " , wrap ( indexPath ) )
2020-05-29 05:57:56 +02:00
}
delete ( " 0-0 " ) ; delete ( " 0-1 " )
delete ( " 0-2 " ) ; delete ( " 1-0 " )
delete ( " 1-1 " ) ; delete ( " 1-2 " )
}
2020-07-15 04:24:43 +02:00
override fun getSwarm ( publicKey : String ) : Set < Snode > ? {
2019-06-04 04:12:40 +02:00
val database = databaseHelper . readableDatabase
2020-08-10 03:40:43 +02:00
return database . get ( swarmTable , " ${Companion.swarmPublicKey} = ? " , wrap ( publicKey ) ) { cursor ->
2019-06-04 03:05:03 +02:00
val swarmAsString = cursor . getString ( cursor . getColumnIndexOrThrow ( swarm ) )
2020-01-17 02:51:37 +01:00
swarmAsString . split ( " , " ) . mapNotNull { targetAsString ->
val components = targetAsString . split ( " - " )
val address = components [ 0 ]
2020-01-28 00:18:18 +01:00
val port = components . getOrNull ( 1 ) ?. toIntOrNull ( ) ?: return @mapNotNull null
2020-05-11 08:19:26 +02:00
val ed25519Key = components . getOrNull ( 2 ) ?: return @mapNotNull null
2020-05-29 05:57:56 +02:00
val x25519Key = components . getOrNull ( 3 ) ?: return @mapNotNull null
2020-07-08 09:05:26 +02:00
Snode ( address , port , Snode . KeySet ( ed25519Key , x25519Key ) )
2019-06-04 03:23:44 +02:00
}
2019-07-15 05:19:58 +02:00
} ?. toSet ( )
2019-06-04 01:35:18 +02:00
}
2020-07-15 04:24:43 +02:00
override fun setSwarm ( publicKey : String , newValue : Set < Snode > ) {
2019-06-04 03:05:03 +02:00
val database = databaseHelper . writableDatabase
2019-06-19 07:45:40 +02:00
val swarmAsString = newValue . joinToString ( " , " ) { target ->
2020-01-17 02:51:37 +01:00
var string = " ${target.address} - ${target.port} "
2020-01-28 00:18:18 +01:00
val keySet = target . publicKeySet
if ( keySet != null ) {
2020-04-09 05:38:15 +02:00
string += " - ${keySet.ed25519Key} - ${keySet.x25519Key} "
2020-01-17 02:51:37 +01:00
}
string
2019-06-04 03:23:44 +02:00
}
2020-08-12 02:00:35 +02:00
val row = wrap ( mapOf ( Companion . swarmPublicKey to publicKey , swarm to swarmAsString ) )
2020-08-10 03:40:43 +02:00
database . insertOrUpdate ( swarmTable , row , " ${Companion.swarmPublicKey} = ? " , wrap ( publicKey ) )
2019-06-04 01:35:18 +02:00
}
2020-08-10 03:40:43 +02:00
override fun getLastMessageHashValue ( snode : Snode , publicKey : String ) : String ? {
2019-06-04 04:12:40 +02:00
val database = databaseHelper . readableDatabase
2020-08-10 03:40:43 +02:00
val query = " ${Companion.snode} = ? AND ${Companion.publicKey} = ? "
2020-08-12 02:00:35 +02:00
return database . get ( lastMessageHashValueTable2 , query , arrayOf ( snode . toString ( ) , publicKey ) ) { cursor ->
2019-06-04 03:05:03 +02:00
cursor . getString ( cursor . getColumnIndexOrThrow ( lastMessageHashValue ) )
}
2019-06-04 01:35:18 +02:00
}
2020-08-10 03:40:43 +02:00
override fun setLastMessageHashValue ( snode : Snode , publicKey : String , newValue : String ) {
2019-06-04 03:05:03 +02:00
val database = databaseHelper . writableDatabase
2020-08-12 02:00:35 +02:00
val row = wrap ( mapOf ( Companion . snode to snode . toString ( ) , Companion . publicKey to publicKey , lastMessageHashValue to newValue ) )
2020-08-10 03:40:43 +02:00
val query = " ${Companion.snode} = ? AND ${Companion.publicKey} = ? "
2020-08-12 02:00:35 +02:00
database . insertOrUpdate ( lastMessageHashValueTable2 , row , query , arrayOf ( snode . toString ( ) , publicKey ) )
2019-06-04 01:35:18 +02:00
}
2020-08-10 03:40:43 +02:00
override fun getReceivedMessageHashValues ( publicKey : String ) : Set < String > ? {
2019-06-04 04:12:40 +02:00
val database = databaseHelper . readableDatabase
2020-08-20 01:19:00 +02:00
val query = " ${Companion.publicKey} = ? "
2020-08-20 02:32:31 +02:00
return database . get ( receivedMessageHashValuesTable3 , query , arrayOf ( publicKey ) ) { cursor ->
2020-08-20 01:19:00 +02:00
val receivedMessageHashValuesAsString = cursor . getString ( cursor . getColumnIndexOrThrow ( Companion . receivedMessageHashValues ) )
2020-08-12 02:00:35 +02:00
receivedMessageHashValuesAsString . split ( " - " ) . toSet ( )
2019-06-04 03:05:03 +02:00
}
2019-06-04 01:35:18 +02:00
}
2020-08-10 03:40:43 +02:00
override fun setReceivedMessageHashValues ( publicKey : String , newValue : Set < String > ) {
2019-06-04 03:05:03 +02:00
val database = databaseHelper . writableDatabase
2020-08-12 02:00:35 +02:00
val receivedMessageHashValuesAsString = newValue . joinToString ( " - " )
2020-08-20 01:19:00 +02:00
val row = wrap ( mapOf ( Companion . publicKey to publicKey , Companion . receivedMessageHashValues to receivedMessageHashValuesAsString ) )
val query = " ${Companion.publicKey} = ? "
2020-08-20 02:32:31 +02:00
database . insertOrUpdate ( receivedMessageHashValuesTable3 , row , query , arrayOf ( publicKey ) )
2019-06-04 01:35:18 +02:00
}
2019-08-23 08:57:26 +02:00
2019-10-07 06:30:20 +02:00
override fun getAuthToken ( server : String ) : String ? {
2019-08-23 08:57:26 +02:00
val database = databaseHelper . readableDatabase
2020-08-10 03:40:43 +02:00
return database . get ( openGroupAuthTokenTable , " ${Companion.server} = ? " , wrap ( server ) ) { cursor ->
2019-08-23 08:57:26 +02:00
cursor . getString ( cursor . getColumnIndexOrThrow ( token ) )
}
}
2019-10-07 06:30:20 +02:00
override fun setAuthToken ( server : String , newValue : String ? ) {
2019-08-23 08:57:26 +02:00
val database = databaseHelper . writableDatabase
2019-08-26 05:55:45 +02:00
if ( newValue != null ) {
2020-08-12 02:00:35 +02:00
val row = wrap ( mapOf ( Companion . server to server , token to newValue ) )
2020-08-10 03:40:43 +02:00
database . insertOrUpdate ( openGroupAuthTokenTable , row , " ${Companion.server} = ? " , wrap ( server ) )
2019-08-23 08:57:26 +02:00
} else {
2020-08-10 03:40:43 +02:00
database . delete ( openGroupAuthTokenTable , " ${Companion.server} = ? " , wrap ( server ) )
2019-08-23 08:57:26 +02:00
}
}
2019-08-26 05:55:45 +02:00
2019-08-28 04:09:05 +02:00
override fun getLastMessageServerID ( group : Long , server : String ) : Long ? {
2019-08-26 05:55:45 +02:00
val database = databaseHelper . readableDatabase
2019-08-28 04:09:05 +02:00
val index = " $server . $group "
2020-08-10 03:40:43 +02:00
return database . get ( lastMessageServerIDTable , " $lastMessageServerIDTableIndex = ? " , wrap ( index ) ) { cursor ->
2019-08-26 05:55:45 +02:00
cursor . getInt ( lastMessageServerID )
} ?. toLong ( )
}
2019-08-28 04:09:05 +02:00
override fun setLastMessageServerID ( group : Long , server : String , newValue : Long ) {
2019-08-26 05:55:45 +02:00
val database = databaseHelper . writableDatabase
2019-08-28 04:09:05 +02:00
val index = " $server . $group "
2020-08-12 02:00:35 +02:00
val row = wrap ( mapOf ( lastMessageServerIDTableIndex to index , lastMessageServerID to newValue . toString ( ) ) )
2020-08-10 03:40:43 +02:00
database . insertOrUpdate ( lastMessageServerIDTable , row , " $lastMessageServerIDTableIndex = ? " , wrap ( index ) )
2019-08-26 05:55:45 +02:00
}
2019-10-10 02:38:43 +02:00
fun removeLastMessageServerID ( group : Long , server : String ) {
val database = databaseHelper . writableDatabase
val index = " $server . $group "
2020-08-10 03:40:43 +02:00
database . delete ( lastMessageServerIDTable , " $lastMessageServerIDTableIndex = ? " , wrap ( index ) )
2019-10-10 02:38:43 +02:00
}
2019-08-30 09:08:46 +02:00
override fun getLastDeletionServerID ( group : Long , server : String ) : Long ? {
2019-08-26 05:55:45 +02:00
val database = databaseHelper . readableDatabase
2019-08-28 04:09:05 +02:00
val index = " $server . $group "
2020-08-10 03:40:43 +02:00
return database . get ( lastDeletionServerIDTable , " $lastDeletionServerIDTableIndex = ? " , wrap ( index ) ) { cursor ->
2019-08-30 09:08:46 +02:00
cursor . getInt ( lastDeletionServerID )
2019-08-26 05:55:45 +02:00
} ?. toLong ( )
}
2019-08-30 09:08:46 +02:00
override fun setLastDeletionServerID ( group : Long , server : String , newValue : Long ) {
2019-08-26 05:55:45 +02:00
val database = databaseHelper . writableDatabase
2019-08-28 04:09:05 +02:00
val index = " $server . $group "
2020-08-12 02:00:35 +02:00
val row = wrap ( mapOf ( lastDeletionServerIDTableIndex to index , lastDeletionServerID to newValue . toString ( ) ) )
2020-08-10 03:40:43 +02:00
database . insertOrUpdate ( lastDeletionServerIDTable , row , " $lastDeletionServerIDTableIndex = ? " , wrap ( index ) )
2019-08-26 05:55:45 +02:00
}
2019-09-24 05:33:42 +02:00
2019-10-10 02:38:43 +02:00
fun removeLastDeletionServerID ( group : Long , server : String ) {
val database = databaseHelper . writableDatabase
val index = " $server . $group "
2020-08-10 03:40:43 +02:00
database . delete ( lastDeletionServerIDTable , " $lastDeletionServerIDTableIndex = ? " , wrap ( index ) )
2019-11-19 23:50:40 +01:00
}
2020-01-22 00:46:04 +01:00
fun getUserCount ( group : Long , server : String ) : Int ? {
val database = databaseHelper . readableDatabase
val index = " $server . $group "
2020-08-10 03:40:43 +02:00
return database . get ( userCountTable , " $publicChatID = ? " , wrap ( index ) ) { cursor ->
2020-01-22 00:46:04 +01:00
cursor . getInt ( userCount )
} ?. toInt ( )
}
2020-07-29 01:04:24 +02:00
override fun setUserCount ( group : Long , server : String , newValue : Int ) {
2020-01-22 00:46:04 +01:00
val database = databaseHelper . writableDatabase
val index = " $server . $group "
2020-08-12 02:00:35 +02:00
val row = wrap ( mapOf ( publicChatID to index , Companion . userCount to newValue . toString ( ) ) )
2020-08-10 03:40:43 +02:00
database . insertOrUpdate ( userCountTable , row , " $publicChatID = ? " , wrap ( index ) )
2020-01-22 00:46:04 +01:00
}
2020-05-22 02:41:31 +02:00
2020-07-16 01:44:00 +02:00
override fun getSessionRequestSentTimestamp ( publicKey : String ) : Long ? {
val database = databaseHelper . readableDatabase
2020-08-10 03:40:43 +02:00
return database . get ( sessionRequestSentTimestampTable , " ${LokiAPIDatabase.publicKey} = ? " , wrap ( publicKey ) ) { cursor ->
2020-08-21 07:39:45 +02:00
cursor . getLong ( LokiAPIDatabase . timestamp )
2020-07-16 01:44:00 +02:00
} ?. toLong ( )
}
2020-07-29 01:04:24 +02:00
override fun setSessionRequestSentTimestamp ( publicKey : String , newValue : Long ) {
2020-07-16 01:44:00 +02:00
val database = databaseHelper . writableDatabase
2020-08-12 02:00:35 +02:00
val row = wrap ( mapOf ( LokiAPIDatabase . publicKey to publicKey , LokiAPIDatabase . timestamp to newValue . toString ( ) ) )
2020-08-10 03:40:43 +02:00
database . insertOrUpdate ( sessionRequestSentTimestampTable , row , " ${LokiAPIDatabase.publicKey} = ? " , wrap ( publicKey ) )
2020-07-16 01:44:00 +02:00
}
override fun getSessionRequestProcessedTimestamp ( publicKey : String ) : Long ? {
2020-05-22 02:41:31 +02:00
val database = databaseHelper . readableDatabase
2020-08-10 03:40:43 +02:00
return database . get ( sessionRequestProcessedTimestampTable , " ${LokiAPIDatabase.publicKey} = ? " , wrap ( publicKey ) ) { cursor ->
2020-07-16 01:46:10 +02:00
cursor . getInt ( LokiAPIDatabase . timestamp )
2020-05-22 02:41:31 +02:00
} ?. toLong ( )
}
2020-07-29 01:04:24 +02:00
override fun setSessionRequestProcessedTimestamp ( publicKey : String , newValue : Long ) {
2020-05-22 02:41:31 +02:00
val database = databaseHelper . writableDatabase
2020-07-29 01:04:24 +02:00
val row = wrap ( mapOf ( LokiAPIDatabase . publicKey to publicKey , LokiAPIDatabase . timestamp to newValue . toString ( ) ) )
2020-08-10 03:40:43 +02:00
database . insertOrUpdate ( sessionRequestProcessedTimestampTable , row , " ${LokiAPIDatabase.publicKey} = ? " , wrap ( publicKey ) )
2020-05-22 02:41:31 +02:00
}
2020-07-29 01:04:24 +02:00
override fun getOpenGroupPublicKey ( server : String ) : String ? {
val database = databaseHelper . readableDatabase
2020-08-10 03:40:43 +02:00
return database . get ( openGroupPublicKeyTable , " ${LokiAPIDatabase.server} = ? " , wrap ( server ) ) { cursor ->
2020-07-29 01:04:24 +02:00
cursor . getString ( LokiAPIDatabase . publicKey )
}
}
override fun setOpenGroupPublicKey ( server : String , newValue : String ) {
val database = databaseHelper . writableDatabase
2020-08-12 02:00:35 +02:00
val row = wrap ( mapOf ( LokiAPIDatabase . server to server , LokiAPIDatabase . publicKey to newValue ) )
2020-08-10 03:40:43 +02:00
database . insertOrUpdate ( openGroupPublicKeyTable , row , " ${LokiAPIDatabase.server} = ? " , wrap ( server ) )
}
2020-10-19 06:12:06 +02:00
override fun getOpenGroupProfilePictureURL ( group : Long , server : String ) : String ? {
2020-09-25 13:11:55 +02:00
val database = databaseHelper . readableDatabase
val index = " $server . $group "
2020-10-19 06:12:06 +02:00
return database . get ( openGroupProfilePictureTable , " $publicChatID = ? " , wrap ( index ) ) { cursor ->
cursor . getString ( openGroupProfilePicture )
2020-09-25 13:11:55 +02:00
} ?. toString ( )
}
2020-10-19 06:12:06 +02:00
override fun setOpenGroupProfilePictureURL ( group : Long , server : String , newValue : String ) {
2020-09-25 13:11:55 +02:00
val database = databaseHelper . writableDatabase
val index = " $server . $group "
2020-10-19 06:12:06 +02:00
val row = wrap ( mapOf ( publicChatID to index , openGroupProfilePicture to newValue ) )
database . insertOrUpdate ( openGroupProfilePictureTable , row , " $publicChatID = ? " , wrap ( index ) )
2020-09-25 13:11:55 +02:00
}
2020-10-19 06:12:06 +02:00
fun clearOpenGroupProfilePictureURL ( group : Long , server : String ) : Boolean {
2020-09-28 11:56:40 +02:00
val database = databaseHelper . writableDatabase
val index = " $server . $group "
2020-10-19 06:12:06 +02:00
return database . delete ( openGroupProfilePictureTable , " $publicChatID = ? " , arrayOf ( index ) ) > 0
2020-09-28 11:56:40 +02:00
}
2020-12-06 23:02:48 +01:00
override fun getLastSnodePoolRefreshDate ( ) : Date ? {
val time = TextSecurePreferences . getLastSnodePoolRefreshDate ( context )
if ( time <= 0 ) { return null }
return Date ( time )
}
override fun setLastSnodePoolRefreshDate ( date : Date ) {
TextSecurePreferences . setLastSnodePoolRefreshDate ( context , date )
}
2021-01-13 06:13:49 +01:00
override fun getUserX25519KeyPair ( ) : ECKeyPair {
val keyPair = IdentityKeyUtil . getIdentityKeyPair ( context )
return ECKeyPair ( DjbECPublicKey ( keyPair . publicKey . serialize ( ) . removing05PrefixIfNeeded ( ) ) , DjbECPrivateKey ( keyPair . privateKey . serialize ( ) ) )
}
fun addClosedGroupEncryptionKeyPair ( encryptionKeyPair : ECKeyPair , groupPublicKey : String ) {
val database = databaseHelper . writableDatabase
val timestamp = Date ( ) . time . toString ( )
val index = " $groupPublicKey - $timestamp "
val encryptionKeyPairPublicKey = encryptionKeyPair . publicKey . serialize ( ) . toHexString ( ) . removing05PrefixIfNeeded ( )
val encryptionKeyPairPrivateKey = encryptionKeyPair . privateKey . serialize ( ) . toHexString ( )
val row = wrap ( mapOf ( Companion . closedGroupsEncryptionKeyPairIndex to index , Companion . encryptionKeyPairPublicKey to encryptionKeyPairPublicKey ,
Companion . encryptionKeyPairPrivateKey to encryptionKeyPairPrivateKey ) )
database . insertOrUpdate ( closedGroupEncryptionKeyPairsTable , row , " ${Companion.closedGroupsEncryptionKeyPairIndex} = ? " , wrap ( index ) )
}
override fun getClosedGroupEncryptionKeyPairs ( groupPublicKey : String ) : List < ECKeyPair > {
val database = databaseHelper . readableDatabase
val timestampsAndKeyPairs = database . getAll ( closedGroupEncryptionKeyPairsTable , " ${Companion.closedGroupsEncryptionKeyPairIndex} LIKE ? " , wrap ( " $groupPublicKey % " ) ) { cursor ->
val timestamp = cursor . getString ( cursor . getColumnIndexOrThrow ( Companion . closedGroupsEncryptionKeyPairIndex ) ) . split ( " - " ) . last ( )
val encryptionKeyPairPublicKey = cursor . getString ( cursor . getColumnIndexOrThrow ( Companion . encryptionKeyPairPublicKey ) )
val encryptionKeyPairPrivateKey = cursor . getString ( cursor . getColumnIndexOrThrow ( Companion . encryptionKeyPairPrivateKey ) )
val keyPair = ECKeyPair ( DjbECPublicKey ( Hex . fromStringCondensed ( encryptionKeyPairPublicKey ) ) , DjbECPrivateKey ( Hex . fromStringCondensed ( encryptionKeyPairPrivateKey ) ) )
Pair ( timestamp , keyPair )
}
return timestampsAndKeyPairs . sortedBy { it . first . toLong ( ) } . map { it . second }
}
override fun getLatestClosedGroupEncryptionKeyPair ( groupPublicKey : String ) : ECKeyPair ? {
return getClosedGroupEncryptionKeyPairs ( groupPublicKey ) . lastOrNull ( )
}
fun removeAllClosedGroupEncryptionKeyPairs ( groupPublicKey : String ) {
val database = databaseHelper . writableDatabase
database . delete ( closedGroupEncryptionKeyPairsTable , " ${Companion.closedGroupsEncryptionKeyPairIndex} LIKE ? " , wrap ( " $groupPublicKey % " ) )
}
fun addClosedGroupPublicKey ( groupPublicKey : String ) {
val database = databaseHelper . writableDatabase
val row = wrap ( mapOf ( Companion . groupPublicKey to groupPublicKey ) )
database . insertOrUpdate ( closedGroupPublicKeysTable , row , " ${Companion.groupPublicKey} = ? " , wrap ( groupPublicKey ) )
}
fun getAllClosedGroupPublicKeys ( ) : Set < String > {
val database = databaseHelper . readableDatabase
return database . getAll ( closedGroupPublicKeysTable , null , null ) { cursor ->
cursor . getString ( cursor . getColumnIndexOrThrow ( Companion . groupPublicKey ) )
} . toSet ( )
}
2021-02-18 04:14:05 +01:00
override fun isClosedGroup ( groupPublicKey : String ) : Boolean {
if ( ! PublicKeyValidation . isValid ( groupPublicKey ) ) { return false }
return getAllClosedGroupPublicKeys ( ) . contains ( groupPublicKey )
}
2021-01-13 06:13:49 +01:00
fun removeClosedGroupPublicKey ( groupPublicKey : String ) {
val database = databaseHelper . writableDatabase
database . delete ( closedGroupPublicKeysTable , " ${Companion.groupPublicKey} = ? " , wrap ( groupPublicKey ) )
}
2019-06-04 03:05:03 +02:00
}
2019-06-04 04:12:40 +02:00
// region Convenience
2019-06-04 03:05:03 +02:00
private inline fun < reified T > wrap ( x : T ) : Array < T > {
return Array ( 1 ) { x }
}
private fun wrap ( x : Map < String , String > ) : ContentValues {
val result = ContentValues ( x . size )
x . forEach { result . put ( it . key , it . value ) }
return result
}
// endregion