Files
Igor Sirotin f3e4363808 refactor: move protocol to internal/protocol
Part of the Go project layout migration, item 27.

Pure move plus import-path rewrite across 502 files. No API or behaviour
change. `internal/` keeps the messaging application logic unimportable
from outside the module, which is what the issue asks for -- status-go is
consumed through the C-bindings in mobile/, not as a Go library.

Things that had to follow the move, beyond the Go imports:

- tools/generate-handlers/template.txt. messenger_handlers.go is
  generated, and the template hard-codes the imports it emits, so the
  generated file kept importing protocol/common and failed typecheck.
- .gitignore. The ignore rule for that generated file was pinned to the
  old path; without moving it, a 1486-line generated file starts being
  tracked.
- Makefile: the logosstorage and torrent test targets (both the archive
  packages and ./protocol itself), the archive README, migration-protocol.
- scripts/run_unit_tests.sh, which names the protocol package explicitly
  to shard its tests.
- scripts/cleanup_generated_files.sh and .golangci.yml.

scripts/migration_check.sh also needed a fix that is not specific to this
move: it validated every file the branch touched under a migration dir
against the timestamp naming rule, and a directory rename makes every
migration in it look newly added. It now excludes renames, so moving a
migration is not mistaken for adding one.

refs #7067
2026-08-21 10:11:05 +02:00

195 lines
7.2 KiB
Go

package pushnotificationserver
import (
"crypto/ecdsa"
"database/sql"
"strings"
"github.com/golang/protobuf/proto"
sqlite3 "github.com/mutecomm/go-sqlcipher/v4"
"github.com/status-im/status-go/internal/crypto"
"github.com/status-im/status-go/internal/protocol/protobuf"
)
type Persistence interface {
// GetPushNotificationRegistrationByPublicKeyAndInstallationID retrieve a push notification registration from storage given a public key and installation id
GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error)
// GetPushNotificationRegistrationByPublicKey retrieve all the push notification registrations from storage given a public key
GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error)
// GetPushNotificationRegistrationPublicKeys return all the public keys stored
GetPushNotificationRegistrationPublicKeys() ([][]byte, error)
//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
// DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id
DeletePushNotificationRegistration(publicKey []byte, installationID string) error
// SavePushNotificationRegistration saves a push notification option to the db
SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error
// GetIdentity returns the server identity key
GetIdentity() (*ecdsa.PrivateKey, error)
// SaveIdentity saves the server identity key
SaveIdentity(*ecdsa.PrivateKey) error
// PushNotificationExists checks whether a push notification exists and inserts it otherwise
PushNotificationExists([]byte) (bool, error)
}
type SQLitePersistence struct {
db *sql.DB
}
func NewSQLitePersistence(db *sql.DB) Persistence {
return &SQLitePersistence{db: db}
}
func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error) {
var marshaledRegistration []byte
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)
if err == sql.ErrNoRows {
return nil, nil
} else if err != nil {
return nil, err
}
registration := &protobuf.PushNotificationRegistration{}
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
return nil, err
}
return registration, nil
}
func (p *SQLitePersistence) GetPushNotificationRegistrationVersion(publicKey []byte, installationID string) (uint64, error) {
var version uint64
err := p.db.QueryRow(`SELECT version FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID).Scan(&version)
if err == sql.ErrNoRows {
return 0, nil
} else if err != nil {
return 0, err
}
return version, nil
}
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) + "?"
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
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{}
// Skip if there's no registration
if marshaledRegistration == nil {
continue
}
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
return nil, err
}
response.Registration = registration
registrations = append(registrations, response)
}
return registrations, nil
}
func (p *SQLitePersistence) GetPushNotificationRegistrationPublicKeys() ([][]byte, error) {
rows, err := p.db.Query(`SELECT public_key FROM push_notification_server_registrations WHERE registration IS NOT NULL`)
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
}
func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error {
marshaledRegistration, err := proto.Marshal(registration)
if err != nil {
return err
}
_, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, publicKey, registration.InstallationId, registration.Version, marshaledRegistration)
return err
}
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
}
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)
return err
}
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
}
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
}