2021-07-09 05:25:57 +02:00
package org.thoughtcrime.securesms.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-03-15 06:44:44 +01:00
import org.session.libsession.utilities.TextSecurePreferences
2021-05-18 01:21:56 +02:00
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
import org.session.libsignal.crypto.ecc.DjbECPublicKey
import org.session.libsignal.crypto.ecc.ECKeyPair
2021-05-18 01:50:16 +02:00
import org.session.libsignal.database.LokiAPIDatabaseProtocol
2022-05-18 02:20:57 +02:00
import org.session.libsignal.utilities.ForkInfo
import org.session.libsignal.utilities.Hex
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.PublicKeyValidation
import org.session.libsignal.utilities.Snode
import org.session.libsignal.utilities.removing05PrefixIfNeeded
import org.session.libsignal.utilities.toHexString
2021-06-07 03:53:17 +02:00
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
2021-03-15 06:44:44 +01:00
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
2022-05-18 02:20:57 +02:00
import java.util.Date
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
2022-05-18 02:20:57 +02:00
private const val publicKey = " public_key "
private const val timestamp = " timestamp "
private const val snode = " snode "
2020-08-10 03:40:43 +02:00
// Snode pool
2021-05-06 07:46:22 +02:00
public 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
2021-05-06 07:46:22 +02:00
public 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
2022-05-18 02:20:57 +02:00
private const val legacyLastMessageHashValueTable2 = " last_message_hash_value_table "
private const val lastMessageHashValueTable2 = " session_last_message_hash_value_table "
private const val lastMessageHashValue = " last_message_hash_value "
private const val lastMessageHashNamespace = " last_message_namespace "
2020-08-10 03:40:43 +02:00
@JvmStatic val createLastMessageHashValueTable2Command
2022-05-18 02:20:57 +02:00
= " CREATE TABLE $legacyLastMessageHashValueTable2 ( $snode TEXT, $publicKey TEXT, $lastMessageHashValue TEXT, PRIMARY KEY ( $snode , $publicKey )); "
2020-08-10 03:40:43 +02:00
// Received message hash values
2022-05-18 02:20:57 +02:00
private const val legacyReceivedMessageHashValuesTable3 = " received_message_hash_values_table_3 "
private const val receivedMessageHashValuesTable = " session_received_message_hash_values_table "
private const val receivedMessageHashValues = " received_message_hash_values "
private const val receivedMessageHashNamespace = " received_message_namespace "
2020-08-20 02:32:31 +02:00
@JvmStatic val createReceivedMessageHashValuesTable3Command
2022-05-18 02:20:57 +02:00
= " CREATE TABLE $legacyReceivedMessageHashValuesTable3 ( $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) "
2022-05-18 02:20:57 +02:00
// Hard fork service node info
const val FORK _INFO _TABLE = " fork_info "
const val DUMMY _KEY = " dummy_key "
const val DUMMY _VALUE = " 1 "
const val HF _VALUE = " hf_value "
const val SF _VALUE = " sf_value "
const val CREATE _FORK _INFO _TABLE _COMMAND = " CREATE TABLE $FORK _INFO_TABLE ( $DUMMY _KEY INTEGER PRIMARY KEY, $HF _VALUE INTEGER, $SF _VALUE INTEGER); "
const val CREATE _DEFAULT _FORK _INFO _COMMAND = " INSERT INTO $FORK _INFO_TABLE ( $DUMMY _KEY, $HF _VALUE, $SF _VALUE) VALUES ( $DUMMY _VALUE, 18, 1); "
const val UPDATE _HASHES _INCLUDE _NAMESPACE _COMMAND = """
CREATE TABLE IF NOT EXISTS $ lastMessageHashValueTable2 (
$ snode TEXT , $ publicKey TEXT , $ lastMessageHashValue TEXT , $ lastMessageHashNamespace INTEGER DEFAULT 0 , PRIMARY KEY ( $ snode , $ publicKey , $ lastMessageHashNamespace )
) ;
INSERT INTO $ lastMessageHashValueTable2 ( $ snode , $ publicKey , $ lastMessageHashValue ) SELECT $ snode , $ publicKey , $ lastMessageHashValue FROM $ legacyLastMessageHashValueTable2 ) ;
DROP TABLE $ legacyLastMessageHashValueTable2 ;
"""
const val UPDATE _RECEIVED _INCLUDE _NAMESPACE _COMMAND = """
CREATE TABLE IF NOT EXISTS $ receivedMessageHashValuesTable (
$ publicKey STRING , $ receivedMessageHashValues TEXT , $ receivedMessageHashNamespace INTEGER DEFAULT 0 , PRIMARY KEY ( $ publicKey , $ receivedMessageHashNamespace )
) ;
INSERT INTO $ receivedMessageHashValuesTable ( $ publicKey , $ receivedMessageHashValues ) SELECT $ publicKey , $ receivedMessageHashValues FROM $ legacyReceivedMessageHashValuesTable3 ;
DROP TABLE $ legacyReceivedMessageHashValuesTable3 ;
"""
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
}
2022-05-18 02:20:57 +02:00
override fun getLastMessageHashValue ( snode : Snode , publicKey : String , namespace : Int ) : String ? {
2019-06-04 04:12:40 +02:00
val database = databaseHelper . readableDatabase
2022-05-18 02:20:57 +02:00
val query = " ${Companion.snode} = ? AND ${Companion.publicKey} = ? AND $lastMessageHashNamespace = ? "
return database . get ( lastMessageHashValueTable2 , query , arrayOf ( snode . toString ( ) , publicKey , namespace . toString ( ) ) ) { cursor ->
2019-06-04 03:05:03 +02:00
cursor . getString ( cursor . getColumnIndexOrThrow ( lastMessageHashValue ) )
}
2019-06-04 01:35:18 +02:00
}
2022-05-18 02:20:57 +02:00
override fun setLastMessageHashValue ( snode : Snode , publicKey : String , newValue : String , namespace : Int ) {
2019-06-04 03:05:03 +02:00
val database = databaseHelper . writableDatabase
2022-05-18 02:20:57 +02:00
val row = wrap ( mapOf (
Companion . snode to snode . toString ( ) ,
Companion . publicKey to publicKey ,
lastMessageHashValue to newValue ,
lastMessageHashNamespace to namespace . toString ( )
) )
val query = " ${Companion.snode} = ? AND ${Companion.publicKey} = ? AND $lastMessageHashNamespace = ? "
database . insertOrUpdate ( lastMessageHashValueTable2 , row , query , arrayOf ( snode . toString ( ) , publicKey , namespace . toString ( ) ) )
2019-06-04 01:35:18 +02:00
}
2022-05-18 02:20:57 +02:00
override fun getReceivedMessageHashValues ( publicKey : String , namespace : Int ) : Set < String > ? {
2019-06-04 04:12:40 +02:00
val database = databaseHelper . readableDatabase
2022-05-18 02:20:57 +02:00
val query = " ${Companion.publicKey} = ? AND ${Companion.receivedMessageHashNamespace} = ? "
return database . get ( receivedMessageHashValuesTable , query , arrayOf ( publicKey , namespace . toString ( ) ) ) { 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
}
2022-05-18 02:20:57 +02:00
override fun setReceivedMessageHashValues ( publicKey : String , newValue : Set < String > , namespace : Int ) {
2019-06-04 03:05:03 +02:00
val database = databaseHelper . writableDatabase
2020-08-12 02:00:35 +02:00
val receivedMessageHashValuesAsString = newValue . joinToString ( " - " )
2022-05-18 02:20:57 +02:00
val row = wrap ( mapOf (
Companion . publicKey to publicKey ,
Companion . receivedMessageHashValues to receivedMessageHashValuesAsString ,
Companion . receivedMessageHashNamespace to namespace . toString ( )
) )
val query = " ${Companion.publicKey} = ? AND $receivedMessageHashNamespace = ? "
database . insertOrUpdate ( receivedMessageHashValuesTable , row , query , arrayOf ( publicKey , namespace . toString ( ) ) )
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
2021-04-13 09:17:16 +02:00
override fun getLastMessageServerID ( room : String , server : String ) : Long ? {
val database = databaseHelper . writableDatabase
val index = " $server . $room "
return database . get ( lastMessageServerIDTable , " $lastMessageServerIDTableIndex = ? " , wrap ( index ) ) { cursor ->
cursor . getInt ( lastMessageServerID )
} ?. toLong ( )
}
override fun setLastMessageServerID ( room : String , server : String , newValue : Long ) {
val database = databaseHelper . writableDatabase
val index = " $server . $room "
val row = wrap ( mapOf ( lastMessageServerIDTableIndex to index , lastMessageServerID to newValue . toString ( ) ) )
database . insertOrUpdate ( lastMessageServerIDTable , row , " $lastMessageServerIDTableIndex = ? " , wrap ( index ) )
}
fun removeLastMessageServerID ( room : String , server : String ) {
val database = databaseHelper . writableDatabase
2021-04-19 02:16:38 +02:00
val index = " $server . $room "
2021-04-13 09:17:16 +02:00
database . delete ( lastMessageServerIDTable , " $lastMessageServerIDTableIndex = ? " , wrap ( index ) )
}
override fun getLastDeletionServerID ( room : String , server : String ) : Long ? {
val database = databaseHelper . readableDatabase
val index = " $server . $room "
return database . get ( lastDeletionServerIDTable , " $lastDeletionServerIDTableIndex = ? " , wrap ( index ) ) { cursor ->
cursor . getInt ( lastDeletionServerID )
} ?. toLong ( )
}
override fun setLastDeletionServerID ( room : String , server : String , newValue : Long ) {
val database = databaseHelper . writableDatabase
val index = " $server . $room "
val row = wrap ( mapOf ( lastDeletionServerIDTableIndex to index , lastDeletionServerID to newValue . toString ( ) ) )
database . insertOrUpdate ( lastDeletionServerIDTable , row , " $lastDeletionServerIDTableIndex = ? " , wrap ( index ) )
}
2021-04-19 02:16:38 +02:00
fun removeLastDeletionServerID ( room : String , server : String ) {
val database = databaseHelper . writableDatabase
val index = " $server . $room "
2021-04-28 09:41:30 +02:00
database . delete ( lastDeletionServerIDTable , " $lastDeletionServerIDTableIndex = ? " , wrap ( index ) )
2021-04-19 02:16:38 +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 ( )
}
2021-04-13 09:17:16 +02:00
fun getUserCount ( room : String , server : String ) : Int ? {
val database = databaseHelper . readableDatabase
val index = " $server . $room "
return database . get ( userCountTable , " $publicChatID = ? " , wrap ( index ) ) { cursor ->
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
2021-05-05 05:52:15 +02:00
override fun setUserCount ( room : String , server : String , newValue : Int ) {
2021-04-13 09:17:16 +02:00
val database = databaseHelper . writableDatabase
val index = " $server . $room "
val row = wrap ( mapOf ( publicChatID to index , userCount to newValue . toString ( ) ) )
database . insertOrUpdate ( userCountTable , row , " $publicChatID = ? " , wrap ( index ) )
}
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-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 ( )
2021-03-15 06:44:44 +01:00
val row = wrap ( mapOf ( closedGroupsEncryptionKeyPairIndex to index , Companion . encryptionKeyPairPublicKey to encryptionKeyPairPublicKey ,
2021-01-13 06:13:49 +01:00
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 ) )
}
2022-05-18 02:20:57 +02:00
override fun getForkInfo ( ) : ForkInfo {
val database = databaseHelper . readableDatabase
val queryCursor = database . query ( FORK _INFO _TABLE , arrayOf ( HF _VALUE , SF _VALUE ) , " $DUMMY _KEY = $DUMMY _VALUE " , null , null , null , null )
val forkInfo = queryCursor . use { cursor ->
if ( ! cursor . moveToNext ( ) ) {
ForkInfo ( 18 , 1 ) // no HF info, none set will at least be the version
} else {
ForkInfo ( cursor . getInt ( 0 ) , cursor . getInt ( 1 ) )
}
}
return forkInfo
}
override fun setForkInfo ( forkInfo : ForkInfo ) {
val database = databaseHelper . writableDatabase
val query = " $DUMMY _KEY = $DUMMY _VALUE "
val contentValues = ContentValues ( 3 )
contentValues . put ( DUMMY _KEY , DUMMY _VALUE )
contentValues . put ( HF _VALUE , forkInfo . hf )
contentValues . put ( SF _VALUE , forkInfo . sf )
database . insertOrUpdate ( FORK _INFO _TABLE , contentValues , query , emptyArray ( ) )
}
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 )
2022-02-07 07:06:27 +01:00
x . iterator ( ) . forEach { result . put ( it . key , it . value ) }
2019-06-04 03:05:03 +02:00
return result
}
// endregion