2019-07-16 10:43:07 +00:00
package statusproto
import (
2019-07-30 16:02:45 +00:00
"bytes"
2019-07-16 10:43:07 +00:00
"context"
"database/sql"
2019-07-30 16:02:45 +00:00
"encoding/gob"
"encoding/hex"
2019-07-16 10:43:07 +00:00
"time"
2019-07-30 16:02:45 +00:00
"github.com/ethereum/go-ethereum/crypto"
2019-07-16 10:43:07 +00:00
"github.com/pkg/errors"
protocol "github.com/status-im/status-protocol-go/v1"
)
const (
uniqueIDContstraint = "UNIQUE constraint failed: user_messages.id"
)
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-17 08:45:14 +00:00
err := db . db . QueryRow ( "SELECT max(clock) FROM user_messages WHERE chat_id = ?" , chatID ) . Scan ( & last )
2019-07-16 10:43:07 +00:00
if err != nil {
return 0 , err
}
return last . Int64 , nil
}
2019-07-30 16:02:45 +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-10-28 09:06:54 +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 16:02:45 +00:00
if err != nil {
return err
}
defer stmt . Close ( )
_ , err = stmt . Exec (
2019-08-13 13:09:46 +00:00
chat . ID ,
2019-07-30 16:02:45 +00:00
chat . Name ,
chat . Color ,
chat . Active ,
chat . ChatType ,
chat . Timestamp ,
chat . DeletedAtClockValue ,
pkey ,
chat . UnviewedMessagesCount ,
chat . LastClockValue ,
chat . LastMessageContentType ,
chat . LastMessageContent ,
2019-08-30 13:06:58 +00:00
chat . LastMessageTimestamp ,
2019-10-28 09:06:54 +00:00
chat . LastMessageClockValue ,
2019-07-30 16:02:45 +00:00
encodedMembers . Bytes ( ) ,
encodedMembershipUpdates . Bytes ( ) ,
)
if err != nil {
return err
}
return err
}
2019-08-13 13:09:46 +00:00
func ( db sqlitePersistence ) DeleteChat ( chatID string ) error {
_ , err := db . db . Exec ( "DELETE FROM chats WHERE id = ?" , chatID )
2019-08-02 07:51:08 +00:00
return err
}
2019-08-25 15:48:37 +00:00
func ( db sqlitePersistence ) Chats ( ) ( [ ] * Chat , error ) {
return db . chats ( nil )
2019-08-13 13:09:46 +00:00
}
2019-07-30 16:02:45 +00:00
2019-08-25 15:48:37 +00:00
func ( db sqlitePersistence ) chats ( tx * sql . Tx ) ( [ ] * Chat , error ) {
2019-08-13 13:09:46 +00:00
var err error
if tx == nil {
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return nil , err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
}
rows , err := tx . Query ( ` SELECT
2019-08-25 15:48:37 +00:00
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 ,
2019-08-30 13:06:58 +00:00
last_message_timestamp ,
2019-10-28 09:06:54 +00:00
last_message_clock_value ,
2019-08-25 15:48:37 +00:00
members ,
membership_updates
2019-07-30 16:02:45 +00:00
FROM chats
2019-08-25 15:48:37 +00:00
ORDER BY chats . timestamp DESC ` )
2019-07-30 16:02:45 +00:00
if err != nil {
return nil , err
}
defer rows . Close ( )
var response [ ] * Chat
for rows . Next ( ) {
2019-08-13 13:09:46 +00:00
var lastMessageContentType sql . NullString
var lastMessageContent sql . NullString
2019-08-30 13:06:58 +00:00
var lastMessageTimestamp sql . NullInt64
2019-10-28 09:06:54 +00:00
var lastMessageClockValue sql . NullInt64
2019-08-13 13:09:46 +00:00
2019-07-30 16:02:45 +00:00
chat := & Chat { }
encodedMembers := [ ] byte { }
encodedMembershipUpdates := [ ] byte { }
pkey := [ ] byte { }
err := rows . Scan (
& chat . ID ,
& chat . Name ,
& chat . Color ,
& chat . Active ,
& chat . ChatType ,
& chat . Timestamp ,
& chat . DeletedAtClockValue ,
& pkey ,
& chat . UnviewedMessagesCount ,
& chat . LastClockValue ,
2019-08-13 13:09:46 +00:00
& lastMessageContentType ,
& lastMessageContent ,
2019-08-30 13:06:58 +00:00
& lastMessageTimestamp ,
2019-10-28 09:06:54 +00:00
& lastMessageClockValue ,
2019-07-30 16:02:45 +00:00
& encodedMembers ,
& encodedMembershipUpdates ,
)
if err != nil {
return nil , err
}
2019-08-13 13:09:46 +00:00
chat . LastMessageContent = lastMessageContent . String
chat . LastMessageContentType = lastMessageContentType . String
2019-08-30 13:06:58 +00:00
chat . LastMessageTimestamp = lastMessageTimestamp . Int64
2019-10-28 09:06:54 +00:00
chat . LastMessageClockValue = lastMessageClockValue . Int64
2019-07-30 16:02:45 +00:00
// Restore members
membersDecoder := gob . NewDecoder ( bytes . NewBuffer ( encodedMembers ) )
if err := membersDecoder . Decode ( & chat . Members ) ; err != nil {
return nil , err
}
// Restore membership updates
membershipUpdatesDecoder := gob . NewDecoder ( bytes . NewBuffer ( encodedMembershipUpdates ) )
if err := membershipUpdatesDecoder . Decode ( & chat . MembershipUpdates ) ; err != nil {
return nil , err
}
if len ( pkey ) != 0 {
chat . PublicKey , err = crypto . UnmarshalPubkey ( pkey )
if err != nil {
return nil , err
}
}
response = append ( response , chat )
}
return response , nil
}
2019-08-02 07:51:08 +00:00
func ( db sqlitePersistence ) Contacts ( ) ( [ ] * Contact , error ) {
rows , err := db . db . Query ( ` SELECT
id ,
address ,
name ,
2019-09-16 15:05:58 +00:00
alias ,
identicon ,
2019-08-02 07:51:08 +00:00
photo ,
last_updated ,
system_tags ,
device_info ,
2019-10-08 12:09:37 +00:00
ens_verified ,
ens_verified_at ,
2019-08-02 07:51:08 +00:00
tribute_to_talk
FROM contacts ` )
if err != nil {
return nil , err
}
defer rows . Close ( )
var response [ ] * Contact
for rows . Next ( ) {
contact := & Contact { }
encodedDeviceInfo := [ ] byte { }
encodedSystemTags := [ ] byte { }
err := rows . Scan (
& contact . ID ,
& contact . Address ,
& contact . Name ,
2019-09-16 15:05:58 +00:00
& contact . Alias ,
& contact . Identicon ,
2019-08-02 07:51:08 +00:00
& contact . Photo ,
& contact . LastUpdated ,
& encodedSystemTags ,
& encodedDeviceInfo ,
2019-10-08 12:09:37 +00:00
& contact . ENSVerified ,
& contact . ENSVerifiedAt ,
2019-08-02 07:51:08 +00:00
& contact . TributeToTalk ,
)
if err != nil {
return nil , err
}
2019-09-16 15:05:58 +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-08-02 07:51:08 +00:00
}
2019-09-16 15:05:58 +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-08-02 07:51:08 +00:00
}
response = append ( response , contact )
}
return response , nil
}
2019-10-08 12:09:37 +00:00
func ( db sqlitePersistence ) SetContactsENSData ( contacts [ ] Contact ) error {
2019-09-16 15:05:58 +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-10-08 12:09:37 +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
func ( db sqlitePersistence ) SetContactsGeneratedData ( contacts [ ] Contact , tx * sql . Tx ) error {
var err error
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-16 15:05:58 +00:00
for _ , contact := range contacts {
_ , err := tx . Exec ( ` INSERT OR IGNORE INTO contacts (
id ,
address ,
name ,
alias ,
identicon ,
photo ,
last_updated ,
tribute_to_talk )
VALUES ( ? , ? , "" , ? , ? , "" , 0 , "" ) ` ,
contact . ID ,
contact . Address ,
contact . Alias ,
contact . Identicon ,
)
if err != nil {
return err
}
}
return nil
}
2019-08-13 13:09:46 +00:00
func ( db sqlitePersistence ) SaveContact ( contact Contact , tx * sql . Tx ) error {
var err error
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-08-02 07:51:08 +00:00
// Encode device info
var encodedDeviceInfo bytes . Buffer
deviceInfoEncoder := gob . NewEncoder ( & encodedDeviceInfo )
if err := deviceInfoEncoder . Encode ( contact . DeviceInfo ) ; err != nil {
return err
}
// Encoded system tags
var encodedSystemTags bytes . Buffer
systemTagsEncoder := gob . NewEncoder ( & encodedSystemTags )
if err := systemTagsEncoder . Encode ( contact . SystemTags ) ; err != nil {
return err
}
// Insert record
2019-08-13 13:09:46 +00:00
stmt , err := tx . Prepare ( ` INSERT INTO contacts (
2019-08-02 07:51:08 +00:00
id ,
address ,
name ,
2019-09-16 15:05:58 +00:00
alias ,
identicon ,
2019-08-02 07:51:08 +00:00
photo ,
last_updated ,
system_tags ,
device_info ,
2019-10-08 12:09:37 +00:00
ens_verified ,
ens_verified_at ,
2019-08-02 07:51:08 +00:00
tribute_to_talk
)
2019-10-08 12:09:37 +00:00
VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? ) ` )
2019-08-02 07:51:08 +00:00
if err != nil {
return err
}
defer stmt . Close ( )
_ , err = stmt . Exec (
contact . ID ,
contact . Address ,
contact . Name ,
2019-09-16 15:05:58 +00:00
contact . Alias ,
contact . Identicon ,
2019-08-02 07:51:08 +00:00
contact . Photo ,
contact . LastUpdated ,
encodedSystemTags . Bytes ( ) ,
encodedDeviceInfo . Bytes ( ) ,
2019-10-08 12:09:37 +00:00
contact . ENSVerified ,
contact . ENSVerifiedAt ,
2019-08-02 07:51:08 +00:00
contact . TributeToTalk ,
)
return err
}
2019-07-16 10:43:07 +00:00
// Messages returns messages for a given contact, in a given period. Ordered by a timestamp.
2019-08-29 07:01:59 +00:00
func ( db sqlitePersistence ) Messages ( from , to time . Time ) ( result [ ] * protocol . Message , err error ) {
2019-07-16 10:43:07 +00:00
rows , err := db . db . Query ( ` SELECT
2019-09-20 06:41:53 +00:00
id ,
chat_id ,
2019-08-29 07:01:59 +00:00
content_type ,
message_type ,
2019-09-20 06:41:53 +00:00
text ,
clock ,
timestamp ,
content_chat_id ,
content_text ,
public_key ,
2019-08-29 07:01:59 +00:00
flags
FROM user_messages
WHERE timestamp >= ? AND timestamp <= ?
ORDER BY timestamp ` ,
protocol . TimestampInMsFromTime ( from ) ,
protocol . TimestampInMsFromTime ( to ) ,
)
2019-07-16 10:43:07 +00:00
if err != nil {
return nil , err
}
2019-07-30 16:02:45 +00:00
defer rows . Close ( )
2019-08-29 07:01:59 +00:00
var rst [ ] * protocol . Message
2019-07-16 10:43:07 +00:00
for rows . Next ( ) {
msg := protocol . Message {
Content : protocol . Content { } ,
}
pkey := [ ] byte { }
err = rows . Scan (
2019-09-20 06:41:53 +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-16 10:43:07 +00:00
if err != nil {
return nil , err
}
if len ( pkey ) != 0 {
2019-10-18 15:01:49 +00:00
msg . SigPubKey , err = crypto . UnmarshalPubkey ( pkey )
2019-07-16 10:43:07 +00:00
if err != nil {
return nil , err
}
}
rst = append ( rst , & msg )
}
return rst , nil
}
2019-08-29 07:01:59 +00:00
func ( db sqlitePersistence ) SaveMessages ( messages [ ] * protocol . Message ) ( last int64 , err error ) {
2019-07-31 07:50:08 +00:00
var (
tx * sql . Tx
stmt * sql . Stmt
)
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
2019-09-20 06:41:53 +00:00
stmt , err = tx . Prepare ( ` INSERT INTO
user_messages (
id ,
chat_id ,
content_type ,
message_type ,
text ,
clock ,
timestamp ,
content_chat_id ,
content_text ,
public_key ,
flags
) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )
` )
if err != nil {
return
}
2019-07-31 07:50:08 +00:00
var rst sql . Result
for _ , msg := range messages {
2019-10-18 15:01:49 +00:00
var pkey [ ] byte
2019-07-31 07:50:08 +00:00
if msg . SigPubKey != nil {
2019-10-18 15:01:49 +00:00
pkey = crypto . FromECDSAPub ( msg . SigPubKey )
2019-07-31 07:50:08 +00:00
}
rst , err = stmt . Exec (
2019-09-20 06:41:53 +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-31 07:50:08 +00:00
if err != nil {
if err . Error ( ) == uniqueIDContstraint {
// skip duplicated messages
err = nil
continue
}
return
}
last , err = rst . LastInsertId ( )
if err != nil {
return
}
}
return
}