2019-11-21 16:19:22 +00:00
package protocol
2019-07-17 22:25:42 +00:00
import (
2019-07-30 18:39:16 +00:00
"bytes"
2019-07-17 22:25:42 +00:00
"context"
"database/sql"
2019-07-30 18:39:16 +00:00
"encoding/gob"
"encoding/hex"
2019-07-17 22:25:42 +00:00
"time"
2019-07-30 18:39:16 +00:00
"github.com/ethereum/go-ethereum/crypto"
2019-07-17 22:25:42 +00:00
"github.com/pkg/errors"
2019-11-21 16:19:22 +00:00
v1protocol "github.com/status-im/status-go/protocol/v1"
2019-07-17 22:25:42 +00:00
)
var (
// ErrMsgAlreadyExist returned if msg already exist.
ErrMsgAlreadyExist = errors . New ( "message with given ID already exist" )
)
// sqlitePersistence wrapper around sql db with operations common for a client.
type sqlitePersistence struct {
db * sql . DB
}
func ( db sqlitePersistence ) LastMessageClock ( chatID string ) ( int64 , error ) {
if chatID == "" {
return 0 , errors . New ( "chat ID is empty" )
}
var last sql . NullInt64
2019-07-26 07:17:29 +00:00
err := db . db . QueryRow ( "SELECT max(clock) FROM user_messages WHERE chat_id = ?" , chatID ) . Scan ( & last )
2019-07-17 22:25:42 +00:00
if err != nil {
return 0 , err
}
return last . Int64 , nil
}
2019-07-30 18:39:16 +00:00
func ( db sqlitePersistence ) SaveChat ( chat Chat ) error {
var err error
pkey := [ ] byte { }
// For one to one chatID is an encoded public key
if chat . ChatType == ChatTypeOneToOne {
pkey , err = hex . DecodeString ( chat . ID [ 2 : ] )
if err != nil {
return err
}
// Safety check, make sure is well formed
_ , err := crypto . UnmarshalPubkey ( pkey )
if err != nil {
return err
}
}
// Encode members
var encodedMembers bytes . Buffer
memberEncoder := gob . NewEncoder ( & encodedMembers )
if err := memberEncoder . Encode ( chat . Members ) ; err != nil {
return err
}
// Encode membership updates
var encodedMembershipUpdates bytes . Buffer
membershipUpdatesEncoder := gob . NewEncoder ( & encodedMembershipUpdates )
if err := membershipUpdatesEncoder . Encode ( chat . MembershipUpdates ) ; err != nil {
return err
}
// Insert record
2019-11-04 10:08:22 +00:00
stmt , err := db . db . Prepare ( ` INSERT INTO chats ( id , name , color , active , type , timestamp , deleted_at_clock_value , public_key , unviewed_message_count , last_clock_value , last_message_content_type , last_message_content , last_message_timestamp , last_message_clock_value , members , membership_updates )
VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? ) ` )
2019-07-30 18:39:16 +00:00
if err != nil {
return err
}
defer stmt . Close ( )
_ , err = stmt . Exec (
2019-08-20 11:20:25 +00:00
chat . ID ,
2019-07-30 18:39:16 +00:00
chat . Name ,
chat . Color ,
chat . Active ,
chat . ChatType ,
chat . Timestamp ,
chat . DeletedAtClockValue ,
pkey ,
chat . UnviewedMessagesCount ,
chat . LastClockValue ,
chat . LastMessageContentType ,
chat . LastMessageContent ,
2019-09-02 09:29:06 +00:00
chat . LastMessageTimestamp ,
2019-11-04 10:08:22 +00:00
chat . LastMessageClockValue ,
2019-07-30 18:39:16 +00:00
encodedMembers . Bytes ( ) ,
encodedMembershipUpdates . Bytes ( ) ,
)
if err != nil {
return err
}
return err
}
2019-08-20 11:20:25 +00:00
func ( db sqlitePersistence ) DeleteChat ( chatID string ) error {
_ , err := db . db . Exec ( "DELETE FROM chats WHERE id = ?" , chatID )
2019-07-30 18:39:16 +00:00
return err
}
2019-08-29 06:33:46 +00:00
func ( db sqlitePersistence ) Chats ( ) ( [ ] * Chat , error ) {
return db . chats ( nil )
2019-08-20 11:20:25 +00:00
}
2019-07-30 18:39:16 +00:00
2019-11-15 08:52:28 +00:00
func ( db sqlitePersistence ) chats ( tx * sql . Tx ) ( chats [ ] * Chat , err error ) {
2019-08-20 11:20:25 +00:00
if tx == nil {
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-08-20 11:20:25 +00:00
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
}
2019-11-15 08:52:28 +00:00
rows , err := tx . Query ( `
SELECT
id ,
name ,
color ,
active ,
type ,
timestamp ,
deleted_at_clock_value ,
public_key ,
unviewed_message_count ,
last_clock_value ,
last_message_content_type ,
last_message_content ,
last_message_timestamp ,
last_message_clock_value ,
members ,
membership_updates
FROM chats
ORDER BY chats . timestamp DESC
` )
2019-07-30 18:39:16 +00:00
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-07-30 18:39:16 +00:00
}
defer rows . Close ( )
for rows . Next ( ) {
2019-11-15 08:52:28 +00:00
var (
lastMessageContentType sql . NullString
lastMessageContent sql . NullString
lastMessageTimestamp sql . NullInt64
lastMessageClockValue sql . NullInt64
chat Chat
encodedMembers [ ] byte
encodedMembershipUpdates [ ] byte
pkey [ ] byte
)
err = rows . Scan (
2019-07-30 18:39:16 +00:00
& chat . ID ,
& chat . Name ,
& chat . Color ,
& chat . Active ,
& chat . ChatType ,
& chat . Timestamp ,
& chat . DeletedAtClockValue ,
& pkey ,
& chat . UnviewedMessagesCount ,
& chat . LastClockValue ,
2019-08-20 11:20:25 +00:00
& lastMessageContentType ,
& lastMessageContent ,
2019-09-02 09:29:06 +00:00
& lastMessageTimestamp ,
2019-11-04 10:08:22 +00:00
& lastMessageClockValue ,
2019-07-30 18:39:16 +00:00
& encodedMembers ,
& encodedMembershipUpdates ,
)
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-07-30 18:39:16 +00:00
}
2019-08-20 11:20:25 +00:00
chat . LastMessageContent = lastMessageContent . String
chat . LastMessageContentType = lastMessageContentType . String
2019-09-02 09:29:06 +00:00
chat . LastMessageTimestamp = lastMessageTimestamp . Int64
2019-11-04 10:08:22 +00:00
chat . LastMessageClockValue = lastMessageClockValue . Int64
2019-07-30 18:39:16 +00:00
// Restore members
membersDecoder := gob . NewDecoder ( bytes . NewBuffer ( encodedMembers ) )
2019-11-15 08:52:28 +00:00
err = membersDecoder . Decode ( & chat . Members )
if err != nil {
return
2019-07-30 18:39:16 +00:00
}
// Restore membership updates
membershipUpdatesDecoder := gob . NewDecoder ( bytes . NewBuffer ( encodedMembershipUpdates ) )
2019-11-15 08:52:28 +00:00
err = membershipUpdatesDecoder . Decode ( & chat . MembershipUpdates )
if err != nil {
return
2019-07-30 18:39:16 +00:00
}
if len ( pkey ) != 0 {
chat . PublicKey , err = crypto . UnmarshalPubkey ( pkey )
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-07-30 18:39:16 +00:00
}
}
2019-11-15 08:52:28 +00:00
chats = append ( chats , & chat )
2019-07-30 18:39:16 +00:00
}
2019-11-15 08:52:28 +00:00
return
2019-07-30 18:39:16 +00:00
}
func ( db sqlitePersistence ) Contacts ( ) ( [ ] * Contact , error ) {
2019-11-15 08:52:28 +00:00
rows , err := db . db . Query ( `
SELECT
id ,
address ,
name ,
alias ,
identicon ,
photo ,
last_updated ,
system_tags ,
device_info ,
ens_verified ,
ens_verified_at ,
tribute_to_talk
FROM contacts
` )
2019-07-30 18:39:16 +00:00
if err != nil {
return nil , err
}
defer rows . Close ( )
var response [ ] * Contact
for rows . Next ( ) {
2019-11-15 08:52:28 +00:00
var (
contact Contact
encodedDeviceInfo [ ] byte
encodedSystemTags [ ] byte
)
2019-07-30 18:39:16 +00:00
err := rows . Scan (
& contact . ID ,
& contact . Address ,
& contact . Name ,
2019-09-26 07:01:17 +00:00
& contact . Alias ,
& contact . Identicon ,
2019-07-30 18:39:16 +00:00
& contact . Photo ,
& contact . LastUpdated ,
& encodedSystemTags ,
& encodedDeviceInfo ,
2019-11-04 10:08:22 +00:00
& contact . ENSVerified ,
& contact . ENSVerifiedAt ,
2019-07-30 18:39:16 +00:00
& contact . TributeToTalk ,
)
if err != nil {
return nil , err
}
2019-09-26 07:01:17 +00:00
if encodedDeviceInfo != nil {
// Restore device info
deviceInfoDecoder := gob . NewDecoder ( bytes . NewBuffer ( encodedDeviceInfo ) )
if err := deviceInfoDecoder . Decode ( & contact . DeviceInfo ) ; err != nil {
return nil , err
}
2019-07-30 18:39:16 +00:00
}
2019-09-26 07:01:17 +00:00
if encodedSystemTags != nil {
// Restore system tags
systemTagsDecoder := gob . NewDecoder ( bytes . NewBuffer ( encodedSystemTags ) )
if err := systemTagsDecoder . Decode ( & contact . SystemTags ) ; err != nil {
return nil , err
}
2019-07-30 18:39:16 +00:00
}
2019-11-15 08:52:28 +00:00
response = append ( response , & contact )
2019-07-30 18:39:16 +00:00
}
return response , nil
}
2019-11-04 10:08:22 +00:00
func ( db sqlitePersistence ) SetContactsENSData ( contacts [ ] Contact ) error {
2019-09-26 07:01:17 +00:00
tx , err := db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
2019-11-04 10:08:22 +00:00
// Ensure contacts exists
err = db . SetContactsGeneratedData ( contacts , tx )
if err != nil {
return err
}
// Update ens data
for _ , contact := range contacts {
_ , err := tx . Exec ( ` UPDATE contacts SET name = ?, ens_verified = ? , ens_verified_at = ? WHERE id = ? ` , contact . Name , contact . ENSVerified , contact . ENSVerifiedAt , contact . ID )
if err != nil {
return err
}
}
return nil
}
// SetContactsGeneratedData sets a contact generated data if not existing already
// in the database
2019-11-15 08:52:28 +00:00
func ( db sqlitePersistence ) SetContactsGeneratedData ( contacts [ ] Contact , tx * sql . Tx ) ( err error ) {
2019-11-04 10:08:22 +00:00
if tx == nil {
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
}
2019-09-26 07:01:17 +00:00
for _ , contact := range contacts {
2019-11-15 08:52:28 +00:00
_ , err = tx . Exec ( `
INSERT OR IGNORE INTO contacts (
id ,
address ,
name ,
alias ,
identicon ,
photo ,
last_updated ,
tribute_to_talk
) VALUES ( ? , ? , "" , ? , ? , "" , 0 , "" ) ` ,
2019-09-26 07:01:17 +00:00
contact . ID ,
contact . Address ,
contact . Alias ,
contact . Identicon ,
)
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-09-26 07:01:17 +00:00
}
}
2019-11-15 08:52:28 +00:00
return
2019-09-26 07:01:17 +00:00
}
2019-11-15 08:52:28 +00:00
func ( db sqlitePersistence ) SaveContact ( contact Contact , tx * sql . Tx ) ( err error ) {
2019-08-20 11:20:25 +00:00
if tx == nil {
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-08-20 11:20:25 +00:00
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
}
2019-07-30 18:39:16 +00:00
// Encode device info
var encodedDeviceInfo bytes . Buffer
deviceInfoEncoder := gob . NewEncoder ( & encodedDeviceInfo )
2019-11-15 08:52:28 +00:00
err = deviceInfoEncoder . Encode ( contact . DeviceInfo )
if err != nil {
return
2019-07-30 18:39:16 +00:00
}
// Encoded system tags
var encodedSystemTags bytes . Buffer
systemTagsEncoder := gob . NewEncoder ( & encodedSystemTags )
2019-11-15 08:52:28 +00:00
err = systemTagsEncoder . Encode ( contact . SystemTags )
if err != nil {
return
2019-07-30 18:39:16 +00:00
}
// Insert record
2019-11-15 08:52:28 +00:00
stmt , err := tx . Prepare ( `
INSERT INTO contacts (
id ,
address ,
name ,
alias ,
identicon ,
photo ,
last_updated ,
system_tags ,
device_info ,
ens_verified ,
ens_verified_at ,
tribute_to_talk
) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )
` )
2019-07-30 18:39:16 +00:00
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-07-30 18:39:16 +00:00
}
defer stmt . Close ( )
_ , err = stmt . Exec (
contact . ID ,
contact . Address ,
contact . Name ,
2019-09-26 07:01:17 +00:00
contact . Alias ,
contact . Identicon ,
2019-07-30 18:39:16 +00:00
contact . Photo ,
contact . LastUpdated ,
encodedSystemTags . Bytes ( ) ,
encodedDeviceInfo . Bytes ( ) ,
2019-11-04 10:08:22 +00:00
contact . ENSVerified ,
contact . ENSVerifiedAt ,
2019-07-30 18:39:16 +00:00
contact . TributeToTalk ,
)
2019-11-15 08:52:28 +00:00
return
2019-07-30 18:39:16 +00:00
}
2019-07-17 22:25:42 +00:00
// Messages returns messages for a given contact, in a given period. Ordered by a timestamp.
2019-11-21 16:19:22 +00:00
func ( db sqlitePersistence ) Messages ( from , to time . Time ) ( result [ ] * v1protocol . Message , err error ) {
2019-11-15 08:52:28 +00:00
rows , err := db . db . Query ( `
SELECT
2019-09-26 07:01:17 +00:00
id ,
chat_id ,
2019-11-21 16:19:22 +00:00
content_type ,
message_type ,
2019-09-26 07:01:17 +00:00
text ,
clock ,
timestamp ,
content_chat_id ,
content_text ,
public_key ,
2019-09-02 09:29:06 +00:00
flags
2019-11-15 08:52:28 +00:00
FROM user_messages
2019-11-21 16:19:22 +00:00
WHERE timestamp >= ? AND timestamp <= ?
2019-09-02 09:29:06 +00:00
ORDER BY timestamp ` ,
2019-11-21 16:19:22 +00:00
v1protocol . TimestampInMsFromTime ( from ) ,
v1protocol . TimestampInMsFromTime ( to ) ,
2019-09-02 09:29:06 +00:00
)
2019-07-17 22:25:42 +00:00
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-07-17 22:25:42 +00:00
}
2019-07-30 18:39:16 +00:00
defer rows . Close ( )
2019-07-17 22:25:42 +00:00
for rows . Next ( ) {
2019-11-21 16:19:22 +00:00
msg := v1protocol . Message {
Content : v1protocol . Content { } ,
2019-07-17 22:25:42 +00:00
}
2019-11-15 08:52:28 +00:00
var pkey [ ] byte
2019-07-17 22:25:42 +00:00
err = rows . Scan (
2019-09-26 07:01:17 +00:00
& msg . ID , & msg . ChatID , & msg . ContentT , & msg . MessageT , & msg . Text , & msg . Clock ,
& msg . Timestamp , & msg . Content . ChatID , & msg . Content . Text , & pkey , & msg . Flags ,
)
2019-07-17 22:25:42 +00:00
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-07-17 22:25:42 +00:00
}
if len ( pkey ) != 0 {
2019-11-04 10:08:22 +00:00
msg . SigPubKey , err = crypto . UnmarshalPubkey ( pkey )
2019-07-17 22:25:42 +00:00
if err != nil {
2019-11-15 08:52:28 +00:00
return
2019-07-17 22:25:42 +00:00
}
}
2019-11-15 08:52:28 +00:00
result = append ( result , & msg )
2019-07-17 22:25:42 +00:00
}
2019-11-15 08:52:28 +00:00
return
2019-07-17 22:25:42 +00:00
}
2019-11-21 16:19:22 +00:00
func ( db sqlitePersistence ) SaveMessages ( messages [ ] * v1protocol . Message ) ( last int64 , err error ) {
2019-11-15 08:52:28 +00:00
tx , err := db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
2019-08-06 21:50:13 +00:00
if err != nil {
return
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
2019-11-15 08:52:28 +00:00
stmt , err := tx . Prepare ( `
INSERT OR IGNORE INTO user_messages (
2019-09-26 07:01:17 +00:00
id ,
2019-11-21 16:19:22 +00:00
chat_id ,
content_type ,
2019-09-26 07:01:17 +00:00
message_type ,
text ,
clock ,
timestamp ,
content_chat_id ,
content_text ,
public_key ,
flags
) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )
` )
if err != nil {
return
}
2019-08-06 21:50:13 +00:00
var rst sql . Result
for _ , msg := range messages {
2019-11-04 10:08:22 +00:00
var pkey [ ] byte
2019-08-06 21:50:13 +00:00
if msg . SigPubKey != nil {
2019-11-04 10:08:22 +00:00
pkey = crypto . FromECDSAPub ( msg . SigPubKey )
2019-08-06 21:50:13 +00:00
}
rst , err = stmt . Exec (
2019-09-26 07:01:17 +00:00
msg . ID , msg . ChatID , msg . ContentT , msg . MessageT , msg . Text , msg . Clock , msg . Timestamp ,
msg . Content . ChatID , msg . Content . Text , pkey , msg . Flags ,
)
2019-08-06 21:50:13 +00:00
if err != nil {
return
}
last , err = rst . LastInsertId ( )
if err != nil {
return
}
}
2019-11-15 08:52:28 +00:00
2019-08-06 21:50:13 +00:00
return
}