2020-07-22 07:41:40 +00:00
package pushnotificationserver
2020-06-30 08:30:58 +00:00
import (
2020-07-13 10:39:33 +00:00
"crypto/ecdsa"
2020-06-30 08:30:58 +00:00
"database/sql"
2020-07-02 13:57:50 +00:00
"strings"
2020-07-01 10:09:40 +00:00
2020-07-01 12:04:09 +00:00
"github.com/golang/protobuf/proto"
2023-06-07 05:58:01 +00:00
sqlite3 "github.com/mutecomm/go-sqlcipher/v4"
2020-07-01 12:04:09 +00:00
2020-07-13 10:39:33 +00:00
"github.com/status-im/status-go/eth-node/crypto"
2020-07-01 10:09:40 +00:00
"github.com/status-im/status-go/protocol/protobuf"
2020-06-30 08:30:58 +00:00
)
2020-07-01 10:09:40 +00:00
type Persistence interface {
2020-07-02 13:57:50 +00:00
// GetPushNotificationRegistrationByPublicKeyAndInstallationID retrieve a push notification registration from storage given a public key and installation id
2020-07-03 08:02:28 +00:00
GetPushNotificationRegistrationByPublicKeyAndInstallationID ( publicKey [ ] byte , installationID string ) ( * protobuf . PushNotificationRegistration , error )
2020-07-02 13:57:50 +00:00
// GetPushNotificationRegistrationByPublicKey retrieve all the push notification registrations from storage given a public key
GetPushNotificationRegistrationByPublicKeys ( publicKeys [ ] [ ] byte ) ( [ ] * PushNotificationIDAndRegistration , error )
2020-07-30 14:47:24 +00:00
// GetPushNotificationRegistrationPublicKeys return all the public keys stored
2020-07-10 13:26:06 +00:00
GetPushNotificationRegistrationPublicKeys ( ) ( [ ] [ ] byte , error )
2020-07-02 13:57:50 +00:00
2020-07-30 14:47:24 +00:00
//GetPushNotificationRegistrationVersion returns the latest version or 0 for a given pk and installationID
GetPushNotificationRegistrationVersion ( publicKey [ ] byte , installationID string ) ( uint64 , error )
// UnregisterPushNotificationRegistration unregister a given pk/installationID
UnregisterPushNotificationRegistration ( publicKey [ ] byte , installationID string , version uint64 ) error
2020-07-02 08:08:19 +00:00
// DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id
2020-07-03 08:02:28 +00:00
DeletePushNotificationRegistration ( publicKey [ ] byte , installationID string ) error
2020-07-02 08:08:19 +00:00
// SavePushNotificationRegistration saves a push notification option to the db
2020-07-03 08:02:28 +00:00
SavePushNotificationRegistration ( publicKey [ ] byte , registration * protobuf . PushNotificationRegistration ) error
2020-07-13 10:39:33 +00:00
// GetIdentity returns the server identity key
GetIdentity ( ) ( * ecdsa . PrivateKey , error )
// SaveIdentity saves the server identity key
SaveIdentity ( * ecdsa . PrivateKey ) error
2020-08-26 05:54:00 +00:00
// PushNotificationExists checks whether a push notification exists and inserts it otherwise
PushNotificationExists ( [ ] byte ) ( bool , error )
2020-07-01 10:09:40 +00:00
}
type SQLitePersistence struct {
2020-06-30 08:30:58 +00:00
db * sql . DB
}
2020-07-01 10:09:40 +00:00
func NewSQLitePersistence ( db * sql . DB ) Persistence {
return & SQLitePersistence { db : db }
}
2020-07-03 08:02:28 +00:00
func ( p * SQLitePersistence ) GetPushNotificationRegistrationByPublicKeyAndInstallationID ( publicKey [ ] byte , installationID string ) ( * protobuf . PushNotificationRegistration , error ) {
2020-07-02 08:08:19 +00:00
var marshaledRegistration [ ] byte
2020-07-30 14:47:24 +00:00
err := p . db . QueryRow ( ` SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ? AND registration IS NOT NULL ` , publicKey , installationID ) . Scan ( & marshaledRegistration )
2020-07-01 12:04:09 +00:00
if err == sql . ErrNoRows {
return nil , nil
} else if err != nil {
return nil , err
}
2020-07-30 14:47:24 +00:00
registration := & protobuf . PushNotificationRegistration { }
if err := proto . Unmarshal ( marshaledRegistration , registration ) ; err != nil {
return nil , err
2020-07-30 13:37:32 +00:00
}
2020-07-30 14:47:24 +00:00
return registration , nil
}
2020-07-30 13:37:32 +00:00
2020-07-30 14:47:24 +00:00
func ( p * SQLitePersistence ) GetPushNotificationRegistrationVersion ( publicKey [ ] byte , installationID string ) ( uint64 , error ) {
2020-07-31 08:56:26 +00:00
var version uint64
err := p . db . QueryRow ( ` SELECT version FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ? ` , publicKey , installationID ) . Scan ( & version )
2020-07-01 12:04:09 +00:00
2020-07-31 08:56:26 +00:00
if err == sql . ErrNoRows {
2020-07-30 14:47:24 +00:00
return 0 , nil
2020-07-31 08:56:26 +00:00
} else if err != nil {
return 0 , err
2020-07-01 12:04:09 +00:00
}
2020-07-31 08:56:26 +00:00
return version , nil
2020-07-01 12:04:09 +00:00
}
2020-07-02 13:57:50 +00:00
type PushNotificationIDAndRegistration struct {
ID [ ] byte
Registration * protobuf . PushNotificationRegistration
}
func ( p * SQLitePersistence ) GetPushNotificationRegistrationByPublicKeys ( publicKeys [ ] [ ] byte ) ( [ ] * PushNotificationIDAndRegistration , error ) {
// TODO: check for a max number of keys
publicKeyArgs := make ( [ ] interface { } , 0 , len ( publicKeys ) )
for _ , pk := range publicKeys {
publicKeyArgs = append ( publicKeyArgs , pk )
}
inVector := strings . Repeat ( "?, " , len ( publicKeys ) - 1 ) + "?"
2020-07-30 14:47:24 +00:00
rows , err := p . db . Query ( ` SELECT public_key,registration FROM push_notification_server_registrations WHERE registration IS NOT NULL AND public_key IN ( ` + inVector + ` ) ` , publicKeyArgs ... ) // nolint: gosec
2020-07-02 13:57:50 +00:00
if err != nil {
return nil , err
}
defer rows . Close ( )
var registrations [ ] * PushNotificationIDAndRegistration
for rows . Next ( ) {
response := & PushNotificationIDAndRegistration { }
var marshaledRegistration [ ] byte
err := rows . Scan ( & response . ID , & marshaledRegistration )
if err != nil {
return nil , err
}
registration := & protobuf . PushNotificationRegistration { }
2020-07-30 13:37:32 +00:00
// Skip if there's no registration
if marshaledRegistration == nil {
continue
}
2020-07-02 13:57:50 +00:00
if err := proto . Unmarshal ( marshaledRegistration , registration ) ; err != nil {
return nil , err
}
response . Registration = registration
registrations = append ( registrations , response )
}
return registrations , nil
}
2020-07-10 13:26:06 +00:00
func ( p * SQLitePersistence ) GetPushNotificationRegistrationPublicKeys ( ) ( [ ] [ ] byte , error ) {
2020-07-30 13:37:32 +00:00
rows , err := p . db . Query ( ` SELECT public_key FROM push_notification_server_registrations WHERE registration IS NOT NULL ` )
2020-07-10 13:26:06 +00:00
if err != nil {
return nil , err
}
defer rows . Close ( )
var publicKeys [ ] [ ] byte
for rows . Next ( ) {
var publicKey [ ] byte
err := rows . Scan ( & publicKey )
if err != nil {
return nil , err
}
publicKeys = append ( publicKeys , publicKey )
}
return publicKeys , nil
}
2020-07-03 08:02:28 +00:00
func ( p * SQLitePersistence ) SavePushNotificationRegistration ( publicKey [ ] byte , registration * protobuf . PushNotificationRegistration ) error {
2020-07-02 08:08:19 +00:00
marshaledRegistration , err := proto . Marshal ( registration )
2020-07-01 12:04:09 +00:00
if err != nil {
return err
}
2020-07-03 08:02:28 +00:00
_ , err = p . db . Exec ( ` INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?) ` , publicKey , registration . InstallationId , registration . Version , marshaledRegistration )
2020-07-01 12:04:09 +00:00
return err
}
2020-07-30 14:47:24 +00:00
func ( p * SQLitePersistence ) UnregisterPushNotificationRegistration ( publicKey [ ] byte , installationID string , version uint64 ) error {
_ , err := p . db . Exec ( ` UPDATE push_notification_server_registrations SET registration = NULL, version = ? WHERE public_key = ? AND installation_id = ? ` , version , publicKey , installationID )
return err
}
2020-07-03 08:02:28 +00:00
func ( p * SQLitePersistence ) DeletePushNotificationRegistration ( publicKey [ ] byte , installationID string ) error {
_ , err := p . db . Exec ( ` DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ? ` , publicKey , installationID )
2020-07-01 12:04:09 +00:00
return err
2020-06-30 08:30:58 +00:00
}
2020-07-13 10:39:33 +00:00
func ( p * SQLitePersistence ) SaveIdentity ( privateKey * ecdsa . PrivateKey ) error {
_ , err := p . db . Exec ( ` INSERT INTO push_notification_server_identity (private_key) VALUES (?) ` , crypto . FromECDSA ( privateKey ) )
return err
}
func ( p * SQLitePersistence ) GetIdentity ( ) ( * ecdsa . PrivateKey , error ) {
var pkBytes [ ] byte
err := p . db . QueryRow ( ` SELECT private_key FROM push_notification_server_identity LIMIT 1 ` ) . Scan ( & pkBytes )
if err == sql . ErrNoRows {
return nil , nil
}
if err != nil {
return nil , err
}
pk , err := crypto . ToECDSA ( pkBytes )
if err != nil {
return nil , err
}
return pk , nil
}
2020-08-26 05:54:00 +00:00
func ( p * SQLitePersistence ) PushNotificationExists ( messageID [ ] byte ) ( bool , error ) {
_ , err := p . db . Exec ( ` INSERT INTO push_notification_server_notifications VALUES (?) ` , messageID )
if err != nil && err . ( sqlite3 . Error ) . ExtendedCode == sqlite3 . ErrConstraintUnique {
return true , nil
} else if err != nil {
return false , err
}
return false , nil
}