mirror of
https://github.com/status-im/status-go.git
synced 2025-02-16 16:56:53 +00:00
Merge branch 'feature/mute-chats-2' into develop
This commit is contained in:
commit
717ed3e1cf
6
PUSH-NOTIFICATIONS-TODO.txt
Normal file
6
PUSH-NOTIFICATIONS-TODO.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Specs changes:
|
||||||
|
- Use application metadata wrapper
|
||||||
|
- Encrypt of payload instead of signature + public key of server
|
||||||
|
- Removed preferencs + each device registers individually
|
||||||
|
- Add grant
|
||||||
|
- Add version in PushNotificationInfo
|
@ -12,6 +12,7 @@
|
|||||||
// 0005_waku_mode.up.sql (146B)
|
// 0005_waku_mode.up.sql (146B)
|
||||||
// 0006_appearance.up.sql (67B)
|
// 0006_appearance.up.sql (67B)
|
||||||
// 0007_enable_waku_default.up.sql (38B)
|
// 0007_enable_waku_default.up.sql (38B)
|
||||||
|
// 0008_add_push_notifications.up.sql (349B)
|
||||||
// doc.go (74B)
|
// doc.go (74B)
|
||||||
|
|
||||||
package migrations
|
package migrations
|
||||||
@ -216,7 +217,7 @@ func _0004_pending_stickersDownSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "0004_pending_stickers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
info := bindataFileInfo{name: "0004_pending_stickers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -236,7 +237,7 @@ func _0004_pending_stickersUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "0004_pending_stickers.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
info := bindataFileInfo{name: "0004_pending_stickers.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3c, 0xed, 0x25, 0xdf, 0x75, 0x2, 0x6c, 0xf0, 0xa2, 0xa8, 0x37, 0x62, 0x65, 0xad, 0xfd, 0x98, 0xa0, 0x9d, 0x63, 0x94, 0xdf, 0x6b, 0x46, 0xe0, 0x68, 0xec, 0x9c, 0x7f, 0x77, 0xdd, 0xb3, 0x6}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3c, 0xed, 0x25, 0xdf, 0x75, 0x2, 0x6c, 0xf0, 0xa2, 0xa8, 0x37, 0x62, 0x65, 0xad, 0xfd, 0x98, 0xa0, 0x9d, 0x63, 0x94, 0xdf, 0x6b, 0x46, 0xe0, 0x68, 0xec, 0x9c, 0x7f, 0x77, 0xdd, 0xb3, 0x6}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -256,7 +257,7 @@ func _0005_waku_modeDownSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "0005_waku_mode.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
info := bindataFileInfo{name: "0005_waku_mode.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -276,7 +277,7 @@ func _0005_waku_modeUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "0005_waku_mode.up.sql", size: 146, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
info := bindataFileInfo{name: "0005_waku_mode.up.sql", size: 146, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa6, 0x91, 0xc, 0xd7, 0x89, 0x61, 0x2e, 0x4c, 0x5a, 0xb6, 0x67, 0xd1, 0xc1, 0x42, 0x24, 0x38, 0xd6, 0x1b, 0x75, 0x41, 0x9c, 0x23, 0xb0, 0xca, 0x5c, 0xf1, 0x5c, 0xd0, 0x13, 0x92, 0x3e, 0xe1}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa6, 0x91, 0xc, 0xd7, 0x89, 0x61, 0x2e, 0x4c, 0x5a, 0xb6, 0x67, 0xd1, 0xc1, 0x42, 0x24, 0x38, 0xd6, 0x1b, 0x75, 0x41, 0x9c, 0x23, 0xb0, 0xca, 0x5c, 0xf1, 0x5c, 0xd0, 0x13, 0x92, 0x3e, 0xe1}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -296,7 +297,7 @@ func _0006_appearanceUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "0006_appearance.up.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1585895847, 0)}
|
info := bindataFileInfo{name: "0006_appearance.up.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xae, 0x6, 0x25, 0x6c, 0xe4, 0x9d, 0xa7, 0x72, 0xe8, 0xbc, 0xe4, 0x1f, 0x1e, 0x2d, 0x7c, 0xb7, 0xf6, 0xa3, 0xec, 0x3b, 0x4e, 0x93, 0x2e, 0xa4, 0xec, 0x6f, 0xe5, 0x95, 0x94, 0xe8, 0x4, 0xfb}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xae, 0x6, 0x25, 0x6c, 0xe4, 0x9d, 0xa7, 0x72, 0xe8, 0xbc, 0xe4, 0x1f, 0x1e, 0x2d, 0x7c, 0xb7, 0xf6, 0xa3, 0xec, 0x3b, 0x4e, 0x93, 0x2e, 0xa4, 0xec, 0x6f, 0xe5, 0x95, 0x94, 0xe8, 0x4, 0xfb}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -316,11 +317,31 @@ func _0007_enable_waku_defaultUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "0007_enable_waku_default.up.sql", size: 38, mode: os.FileMode(0644), modTime: time.Unix(1585895900, 0)}
|
info := bindataFileInfo{name: "0007_enable_waku_default.up.sql", size: 38, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd4, 0x42, 0xb6, 0xe5, 0x48, 0x41, 0xeb, 0xc0, 0x7e, 0x3b, 0xe6, 0x8e, 0x96, 0x33, 0x20, 0x92, 0x24, 0x5a, 0x60, 0xfa, 0xa0, 0x3, 0x5e, 0x76, 0x4b, 0x89, 0xaa, 0x37, 0x66, 0xbc, 0x26, 0x11}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd4, 0x42, 0xb6, 0xe5, 0x48, 0x41, 0xeb, 0xc0, 0x7e, 0x3b, 0xe6, 0x8e, 0x96, 0x33, 0x20, 0x92, 0x24, 0x5a, 0x60, 0xfa, 0xa0, 0x3, 0x5e, 0x76, 0x4b, 0x89, 0xaa, 0x37, 0x66, 0xbc, 0x26, 0x11}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var __0008_add_push_notificationsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\xce\x51\x0e\x82\x30\x0c\x00\xd0\x7f\x4f\xd1\x7b\xf8\x35\x64\x7c\x55\x48\x70\x7c\x2f\x08\x45\x97\x40\x6b\xd6\x6a\xe2\xed\x3d\x80\x33\x9a\x78\x81\x97\xe7\x30\xf8\x1e\x82\xab\xd0\x83\x92\x59\xe2\x8b\x82\xab\x6b\x38\x74\x38\x1c\x5b\xc8\xb4\x89\x51\xbc\xdd\xf5\x1a\x59\x2c\x2d\x69\x1a\x2d\x09\x6b\x24\x1e\xcf\x2b\xcd\x50\x75\x1d\x7a\xd7\x42\xed\x1b\x37\x60\x80\xc6\xe1\xc9\xef\x77\xdf\x60\x25\x9e\x0b\xec\x1b\x17\xfa\xe1\x07\xad\xf0\x53\xca\x0f\xca\xff\x36\x0b\xf0\x92\x65\x8b\x93\xb0\x8d\x93\x69\x14\x5e\x9f\x9f\xf0\x57\x00\x00\x00\xff\xff\x30\xc0\x56\xbd\x5d\x01\x00\x00")
|
||||||
|
|
||||||
|
func _0008_add_push_notificationsUpSqlBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
__0008_add_push_notificationsUpSql,
|
||||||
|
"0008_add_push_notifications.up.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _0008_add_push_notificationsUpSql() (*asset, error) {
|
||||||
|
bytes, err := _0008_add_push_notificationsUpSqlBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "0008_add_push_notifications.up.sql", size: 349, mode: os.FileMode(0644), modTime: time.Unix(1595832401, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5a, 0x0, 0xbf, 0xd0, 0xdd, 0xcd, 0x73, 0xe0, 0x7c, 0x56, 0xef, 0xdc, 0x57, 0x61, 0x94, 0x64, 0x70, 0xb9, 0xfa, 0xa1, 0x2a, 0x36, 0xc, 0x2f, 0xf8, 0x95, 0xa, 0x57, 0x3e, 0x7a, 0xd7, 0x12}}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
|
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
|
||||||
|
|
||||||
func docGoBytes() ([]byte, error) {
|
func docGoBytes() ([]byte, error) {
|
||||||
@ -456,6 +477,8 @@ var _bindata = map[string]func() (*asset, error){
|
|||||||
|
|
||||||
"0007_enable_waku_default.up.sql": _0007_enable_waku_defaultUpSql,
|
"0007_enable_waku_default.up.sql": _0007_enable_waku_defaultUpSql,
|
||||||
|
|
||||||
|
"0008_add_push_notifications.up.sql": _0008_add_push_notificationsUpSql,
|
||||||
|
|
||||||
"doc.go": docGo,
|
"doc.go": docGo,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,19 +523,20 @@ type bintree struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var _bintree = &bintree{nil, map[string]*bintree{
|
var _bintree = &bintree{nil, map[string]*bintree{
|
||||||
"0001_app.down.sql": &bintree{_0001_appDownSql, map[string]*bintree{}},
|
"0001_app.down.sql": &bintree{_0001_appDownSql, map[string]*bintree{}},
|
||||||
"0001_app.up.sql": &bintree{_0001_appUpSql, map[string]*bintree{}},
|
"0001_app.up.sql": &bintree{_0001_appUpSql, map[string]*bintree{}},
|
||||||
"0002_tokens.down.sql": &bintree{_0002_tokensDownSql, map[string]*bintree{}},
|
"0002_tokens.down.sql": &bintree{_0002_tokensDownSql, map[string]*bintree{}},
|
||||||
"0002_tokens.up.sql": &bintree{_0002_tokensUpSql, map[string]*bintree{}},
|
"0002_tokens.up.sql": &bintree{_0002_tokensUpSql, map[string]*bintree{}},
|
||||||
"0003_settings.down.sql": &bintree{_0003_settingsDownSql, map[string]*bintree{}},
|
"0003_settings.down.sql": &bintree{_0003_settingsDownSql, map[string]*bintree{}},
|
||||||
"0003_settings.up.sql": &bintree{_0003_settingsUpSql, map[string]*bintree{}},
|
"0003_settings.up.sql": &bintree{_0003_settingsUpSql, map[string]*bintree{}},
|
||||||
"0004_pending_stickers.down.sql": &bintree{_0004_pending_stickersDownSql, map[string]*bintree{}},
|
"0004_pending_stickers.down.sql": &bintree{_0004_pending_stickersDownSql, map[string]*bintree{}},
|
||||||
"0004_pending_stickers.up.sql": &bintree{_0004_pending_stickersUpSql, map[string]*bintree{}},
|
"0004_pending_stickers.up.sql": &bintree{_0004_pending_stickersUpSql, map[string]*bintree{}},
|
||||||
"0005_waku_mode.down.sql": &bintree{_0005_waku_modeDownSql, map[string]*bintree{}},
|
"0005_waku_mode.down.sql": &bintree{_0005_waku_modeDownSql, map[string]*bintree{}},
|
||||||
"0005_waku_mode.up.sql": &bintree{_0005_waku_modeUpSql, map[string]*bintree{}},
|
"0005_waku_mode.up.sql": &bintree{_0005_waku_modeUpSql, map[string]*bintree{}},
|
||||||
"0006_appearance.up.sql": &bintree{_0006_appearanceUpSql, map[string]*bintree{}},
|
"0006_appearance.up.sql": &bintree{_0006_appearanceUpSql, map[string]*bintree{}},
|
||||||
"0007_enable_waku_default.up.sql": &bintree{_0007_enable_waku_defaultUpSql, map[string]*bintree{}},
|
"0007_enable_waku_default.up.sql": &bintree{_0007_enable_waku_defaultUpSql, map[string]*bintree{}},
|
||||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
"0008_add_push_notifications.up.sql": &bintree{_0008_add_push_notificationsUpSql, map[string]*bintree{}},
|
||||||
|
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// RestoreAsset restores an asset under the given directory.
|
// RestoreAsset restores an asset under the given directory.
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE settings ADD COLUMN remote_push_notifications_enabled BOOLEAN DEFAULT FALSE;
|
||||||
|
ALTER TABLE settings ADD COLUMN send_push_notifications BOOLEAN DEFAULT TRUE;
|
||||||
|
ALTER TABLE settings ADD COLUMN push_notifications_server_enabled BOOLEAN DEFAULT FALSE;
|
||||||
|
ALTER TABLE settings ADD COLUMN push_notifications_from_contacts_only BOOLEAN DEFAULT FALSE;
|
@ -59,25 +59,34 @@ type Settings struct {
|
|||||||
Mnemonic *string `json:"mnemonic,omitempty"`
|
Mnemonic *string `json:"mnemonic,omitempty"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Networks *json.RawMessage `json:"networks/networks"`
|
Networks *json.RawMessage `json:"networks/networks"`
|
||||||
NotificationsEnabled bool `json:"notifications-enabled?,omitempty"`
|
// NotificationsEnabled indicates whether local notifications should be enabled (android only)
|
||||||
PhotoPath string `json:"photo-path"`
|
NotificationsEnabled bool `json:"notifications-enabled?,omitempty"`
|
||||||
PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"`
|
PhotoPath string `json:"photo-path"`
|
||||||
PreferredName *string `json:"preferred-name,omitempty"`
|
PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"`
|
||||||
PreviewPrivacy bool `json:"preview-privacy?"`
|
PreferredName *string `json:"preferred-name,omitempty"`
|
||||||
PublicKey string `json:"public-key"`
|
PreviewPrivacy bool `json:"preview-privacy?"`
|
||||||
RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"`
|
PublicKey string `json:"public-key"`
|
||||||
SigningPhrase string `json:"signing-phrase"`
|
// PushNotificationsServerEnabled indicates whether we should be running a push notification server
|
||||||
StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"`
|
PushNotificationsServerEnabled bool `json:"push-notifications-server-enabled?,omitempty"`
|
||||||
StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"`
|
// PushNotificationsFromContactsOnly indicates whether we should only receive push notifications from contacts
|
||||||
StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"`
|
PushNotificationsFromContactsOnly bool `json:"push-notifications-from-contacts-only?,omitempty"`
|
||||||
SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"`
|
RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"`
|
||||||
Appearance uint `json:"appearance"`
|
// RemotePushNotificationsEnabled indicates whether we should be using remote notifications (ios only for now)
|
||||||
Usernames *json.RawMessage `json:"usernames,omitempty"`
|
RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled?,omitempty"`
|
||||||
WalletRootAddress types.Address `json:"wallet-root-address,omitempty"`
|
SigningPhrase string `json:"signing-phrase"`
|
||||||
WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"`
|
StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"`
|
||||||
WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"`
|
StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"`
|
||||||
WakuEnabled bool `json:"waku-enabled,omitempty"`
|
StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"`
|
||||||
WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"`
|
SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"`
|
||||||
|
// SendPushNotifications indicates whether we should send push notifications for other clients
|
||||||
|
SendPushNotifications bool `json:"send-push-notifications?,omitempty"`
|
||||||
|
Appearance uint `json:"appearance"`
|
||||||
|
Usernames *json.RawMessage `json:"usernames,omitempty"`
|
||||||
|
WalletRootAddress types.Address `json:"wallet-root-address,omitempty"`
|
||||||
|
WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"`
|
||||||
|
WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"`
|
||||||
|
WakuEnabled bool `json:"waku-enabled,omitempty"`
|
||||||
|
WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDB(db *sql.DB) *Database {
|
func NewDB(db *sql.DB) *Database {
|
||||||
@ -246,6 +255,30 @@ func (db *Database) SaveSetting(setting string, value interface{}) error {
|
|||||||
return ErrInvalidConfig
|
return ErrInvalidConfig
|
||||||
}
|
}
|
||||||
update, err = db.db.Prepare("UPDATE settings SET remember_syncing_choice = ? WHERE synthetic_id = 'id'")
|
update, err = db.db.Prepare("UPDATE settings SET remember_syncing_choice = ? WHERE synthetic_id = 'id'")
|
||||||
|
case "remote-push-notifications-enabled?":
|
||||||
|
_, ok := value.(bool)
|
||||||
|
if !ok {
|
||||||
|
return ErrInvalidConfig
|
||||||
|
}
|
||||||
|
update, err = db.db.Prepare("UPDATE settings SET remote_push_notifications_enabled = ? WHERE synthetic_id = 'id'")
|
||||||
|
case "push-notifications-server-enabled?":
|
||||||
|
_, ok := value.(bool)
|
||||||
|
if !ok {
|
||||||
|
return ErrInvalidConfig
|
||||||
|
}
|
||||||
|
update, err = db.db.Prepare("UPDATE settings SET push_notifications_server_enabled = ? WHERE synthetic_id = 'id'")
|
||||||
|
case "push-notifications-from-contacts-only?":
|
||||||
|
_, ok := value.(bool)
|
||||||
|
if !ok {
|
||||||
|
return ErrInvalidConfig
|
||||||
|
}
|
||||||
|
update, err = db.db.Prepare("UPDATE settings SET push_notifications_from_contacts_only = ? WHERE synthetic_id = 'id'")
|
||||||
|
case "send-push-notifications?":
|
||||||
|
_, ok := value.(bool)
|
||||||
|
if !ok {
|
||||||
|
return ErrInvalidConfig
|
||||||
|
}
|
||||||
|
update, err = db.db.Prepare("UPDATE settings SET send_push_notifications = ? WHERE synthetic_id = 'id'")
|
||||||
case "stickers/packs-installed":
|
case "stickers/packs-installed":
|
||||||
value = &sqlite.JSONBlob{value}
|
value = &sqlite.JSONBlob{value}
|
||||||
update, err = db.db.Prepare("UPDATE settings SET stickers_packs_installed = ? WHERE synthetic_id = 'id'")
|
update, err = db.db.Prepare("UPDATE settings SET stickers_packs_installed = ? WHERE synthetic_id = 'id'")
|
||||||
@ -304,7 +337,7 @@ func (db *Database) GetNodeConfig(nodecfg interface{}) error {
|
|||||||
|
|
||||||
func (db *Database) GetSettings() (Settings, error) {
|
func (db *Database) GetSettings() (Settings, error) {
|
||||||
var s Settings
|
var s Settings
|
||||||
err := db.db.QueryRow("SELECT address, chaos_mode, currency, current_network, custom_bootnodes, custom_bootnodes_enabled, dapps_address, eip1581_address, fleet, hide_home_tooltip, installation_id, key_uid, keycard_instance_uid, keycard_paired_on, keycard_pairing, last_updated, latest_derived_path, log_level, mnemonic, name, networks, notifications_enabled, photo_path, pinned_mailservers, preferred_name, preview_privacy, public_key, remember_syncing_choice, signing_phrase, stickers_packs_installed, stickers_packs_pending, stickers_recent_stickers, syncing_on_mobile_network, usernames, appearance, wallet_root_address, wallet_set_up_passed, wallet_visible_tokens, waku_enabled, waku_bloom_filter_mode FROM settings WHERE synthetic_id = 'id'").Scan(
|
err := db.db.QueryRow("SELECT address, chaos_mode, currency, current_network, custom_bootnodes, custom_bootnodes_enabled, dapps_address, eip1581_address, fleet, hide_home_tooltip, installation_id, key_uid, keycard_instance_uid, keycard_paired_on, keycard_pairing, last_updated, latest_derived_path, log_level, mnemonic, name, networks, notifications_enabled, push_notifications_server_enabled, push_notifications_from_contacts_only, remote_push_notifications_enabled, send_push_notifications, photo_path, pinned_mailservers, preferred_name, preview_privacy, public_key, remember_syncing_choice, signing_phrase, stickers_packs_installed, stickers_packs_pending, stickers_recent_stickers, syncing_on_mobile_network, usernames, appearance, wallet_root_address, wallet_set_up_passed, wallet_visible_tokens, waku_enabled, waku_bloom_filter_mode FROM settings WHERE synthetic_id = 'id'").Scan(
|
||||||
&s.Address,
|
&s.Address,
|
||||||
&s.ChaosMode,
|
&s.ChaosMode,
|
||||||
&s.Currency,
|
&s.Currency,
|
||||||
@ -327,6 +360,10 @@ func (db *Database) GetSettings() (Settings, error) {
|
|||||||
&s.Name,
|
&s.Name,
|
||||||
&s.Networks,
|
&s.Networks,
|
||||||
&s.NotificationsEnabled,
|
&s.NotificationsEnabled,
|
||||||
|
&s.PushNotificationsServerEnabled,
|
||||||
|
&s.PushNotificationsFromContactsOnly,
|
||||||
|
&s.RemotePushNotificationsEnabled,
|
||||||
|
&s.SendPushNotifications,
|
||||||
&s.PhotoPath,
|
&s.PhotoPath,
|
||||||
&s.PinnedMailserver,
|
&s.PinnedMailserver,
|
||||||
&s.PreferredName,
|
&s.PreferredName,
|
||||||
|
@ -22,19 +22,20 @@ var (
|
|||||||
|
|
||||||
networks = json.RawMessage("{}")
|
networks = json.RawMessage("{}")
|
||||||
settings = Settings{
|
settings = Settings{
|
||||||
Address: types.HexToAddress("0xdC540f3745Ff2964AFC1171a5A0DD726d1F6B472"),
|
Address: types.HexToAddress("0xdC540f3745Ff2964AFC1171a5A0DD726d1F6B472"),
|
||||||
CurrentNetwork: "mainnet_rpc",
|
CurrentNetwork: "mainnet_rpc",
|
||||||
DappsAddress: types.HexToAddress("0xD1300f99fDF7346986CbC766903245087394ecd0"),
|
DappsAddress: types.HexToAddress("0xD1300f99fDF7346986CbC766903245087394ecd0"),
|
||||||
InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51",
|
InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51",
|
||||||
KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab",
|
KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab",
|
||||||
LatestDerivedPath: 0,
|
LatestDerivedPath: 0,
|
||||||
Name: "Jittery Cornflowerblue Kingbird",
|
Name: "Jittery Cornflowerblue Kingbird",
|
||||||
Networks: &networks,
|
Networks: &networks,
|
||||||
PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
|
PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
|
||||||
PreviewPrivacy: false,
|
PreviewPrivacy: false,
|
||||||
PublicKey: "0x04211fe0f69772ecf7eb0b5bfc7678672508a9fb01f2d699096f0d59ef7fe1a0cb1e648a80190db1c0f5f088872444d846f2956d0bd84069f3f9f69335af852ac0",
|
PublicKey: "0x04211fe0f69772ecf7eb0b5bfc7678672508a9fb01f2d699096f0d59ef7fe1a0cb1e648a80190db1c0f5f088872444d846f2956d0bd84069f3f9f69335af852ac0",
|
||||||
SigningPhrase: "yurt joey vibe",
|
SigningPhrase: "yurt joey vibe",
|
||||||
WalletRootAddress: types.HexToAddress("0x3B591fd819F86D0A6a2EF2Bcb94f77807a7De1a6")}
|
SendPushNotifications: true,
|
||||||
|
WalletRootAddress: types.HexToAddress("0x3B591fd819F86D0A6a2EF2Bcb94f77807a7De1a6")}
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupTestDB(t *testing.T) (*Database, func()) {
|
func setupTestDB(t *testing.T) (*Database, func()) {
|
||||||
|
@ -30,6 +30,21 @@ import (
|
|||||||
"github.com/status-im/status-go/whisper/v6"
|
"github.com/status-im/status-go/whisper/v6"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetFreePort asks the kernel for a free open port that is ready to use.
|
||||||
|
func getFreePort() (int, error) {
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := net.ListenTCP("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
return l.Addr().(*net.TCPAddr).Port, nil
|
||||||
|
}
|
||||||
|
|
||||||
type PeerPoolSimulationSuite struct {
|
type PeerPoolSimulationSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
|
|
||||||
@ -41,14 +56,20 @@ type PeerPoolSimulationSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPeerPoolSimulationSuite(t *testing.T) {
|
func TestPeerPoolSimulationSuite(t *testing.T) {
|
||||||
s := new(PeerPoolSimulationSuite)
|
s := &PeerPoolSimulationSuite{}
|
||||||
s.port = 33731
|
port, err := getFreePort()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.port = uint16(port)
|
||||||
|
|
||||||
suite.Run(t, s)
|
suite.Run(t, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PeerPoolSimulationSuite) nextPort() uint16 {
|
func (s *PeerPoolSimulationSuite) nextPort() uint16 {
|
||||||
s.port++
|
port, err := getFreePort()
|
||||||
return s.port
|
s.Require().NoError(err)
|
||||||
|
return uint16(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PeerPoolSimulationSuite) SetupTest() {
|
func (s *PeerPoolSimulationSuite) SetupTest() {
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// source: message.proto
|
|
||||||
|
|
||||||
package applicationmetadata
|
|
||||||
|
|
||||||
import (
|
|
||||||
fmt "fmt"
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
math "math"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
Signature []byte `protobuf:"bytes,4001,opt,name=signature,proto3" json:"signature,omitempty"`
|
|
||||||
Payload []byte `protobuf:"bytes,4002,opt,name=payload,proto3" json:"payload,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) Reset() { *m = Message{} }
|
|
||||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Message) ProtoMessage() {}
|
|
||||||
func (*Message) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_33c57e4bae7b9afd, []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_Message.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *Message) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_Message.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *Message) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_Message.Size(m)
|
|
||||||
}
|
|
||||||
func (m *Message) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_Message.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_Message proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *Message) GetSignature() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Signature
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetPayload() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Payload
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*Message)(nil), "applicationmetadata.Message")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { proto.RegisterFile("message.proto", fileDescriptor_33c57e4bae7b9afd) }
|
|
||||||
|
|
||||||
var fileDescriptor_33c57e4bae7b9afd = []byte{
|
|
||||||
// 112 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4d, 0x2d, 0x2e,
|
|
||||||
0x4e, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x4e, 0x2c, 0x28, 0xc8, 0xc9,
|
|
||||||
0x4c, 0x4e, 0x2c, 0xc9, 0xcc, 0xcf, 0xcb, 0x4d, 0x2d, 0x49, 0x4c, 0x49, 0x2c, 0x49, 0x54, 0x72,
|
|
||||||
0xe6, 0x62, 0xf7, 0x85, 0xa8, 0x12, 0x92, 0xe5, 0xe2, 0x2c, 0xce, 0x4c, 0xcf, 0x4b, 0x2c, 0x29,
|
|
||||||
0x2d, 0x4a, 0x95, 0x58, 0x28, 0xaf, 0xc0, 0xa8, 0xc1, 0x13, 0x84, 0x10, 0x11, 0x92, 0xe4, 0x62,
|
|
||||||
0x2f, 0x48, 0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x91, 0x58, 0x04, 0x91, 0x84, 0xf1, 0x93, 0xd8, 0xc0,
|
|
||||||
0x16, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xb0, 0x7f, 0x4a, 0x96, 0x71, 0x00, 0x00, 0x00,
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package applicationmetadata;
|
|
||||||
|
|
||||||
message Message {
|
|
||||||
bytes signature = 4001;
|
|
||||||
bytes payload = 4002;
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package applicationmetadata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m *Message) RecoverKey() (*ecdsa.PublicKey, error) {
|
|
||||||
if m.Signature == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
recoveredKey, err := crypto.SigToPub(
|
|
||||||
crypto.Keccak256(m.Payload),
|
|
||||||
m.Signature,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return recoveredKey, nil
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package applicationmetadata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate protoc --go_out=. ./message.proto
|
|
||||||
|
|
||||||
func Unmarshal(payload []byte) (*Message, error) {
|
|
||||||
var message Message
|
|
||||||
err := proto.Unmarshal(payload, &message)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &message, nil
|
|
||||||
}
|
|
@ -64,6 +64,10 @@ type Chat struct {
|
|||||||
Alias string `json:"alias,omitempty"`
|
Alias string `json:"alias,omitempty"`
|
||||||
// Identicon generated from public key
|
// Identicon generated from public key
|
||||||
Identicon string `json:"identicon"`
|
Identicon string `json:"identicon"`
|
||||||
|
|
||||||
|
// Muted is used to check whether we want to receive
|
||||||
|
// push notifications for this chat
|
||||||
|
Muted bool `json:"muted,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) {
|
func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) {
|
||||||
|
71
protocol/common/crypto.go
Normal file
71
protocol/common/crypto.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/sha3"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const nonceLength = 12
|
||||||
|
|
||||||
|
var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length")
|
||||||
|
|
||||||
|
func HashPublicKey(pk *ecdsa.PublicKey) []byte {
|
||||||
|
return Shake256(crypto.CompressPubkey(pk))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Decrypt(cyphertext []byte, key []byte) ([]byte, error) {
|
||||||
|
if len(cyphertext) < nonceLength {
|
||||||
|
return nil, ErrInvalidCiphertextLength
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gcm, err := cipher.NewGCM(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := cyphertext[:nonceLength]
|
||||||
|
return gcm.Open(nil, nonce, cyphertext[nonceLength:], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) {
|
||||||
|
c, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gcm, err := cipher.NewGCM(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
if _, err = io.ReadFull(reader, nonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gcm.Seal(nonce, nonce, plaintext, nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Shake256(buf []byte) []byte {
|
||||||
|
h := make([]byte, 64)
|
||||||
|
sha3.ShakeSum256(h, buf)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPubKeyEqual checks that two public keys are equal
|
||||||
|
func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
|
||||||
|
// the curve is always the same, just compare the points
|
||||||
|
return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
|
||||||
|
}
|
8
protocol/common/feature_flags.go
Normal file
8
protocol/common/feature_flags.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
type FeatureFlags struct {
|
||||||
|
// Datasync indicates whether direct messages should be sent exclusively
|
||||||
|
// using datasync, breaking change for non-v1 clients. Public messages
|
||||||
|
// are not impacted
|
||||||
|
Datasync bool
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package protocol
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
@ -34,24 +35,41 @@ const (
|
|||||||
whisperPoWTime = 5
|
whisperPoWTime = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
type messageProcessor struct {
|
// SentMessage reprent a message that has been passed to the transport layer
|
||||||
|
type SentMessage struct {
|
||||||
|
PublicKey *ecdsa.PublicKey
|
||||||
|
Spec *encryption.ProtocolMessageSpec
|
||||||
|
MessageIDs [][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageProcessor struct {
|
||||||
identity *ecdsa.PrivateKey
|
identity *ecdsa.PrivateKey
|
||||||
datasync *datasync.DataSync
|
datasync *datasync.DataSync
|
||||||
protocol *encryption.Protocol
|
protocol *encryption.Protocol
|
||||||
transport transport.Transport
|
transport transport.Transport
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
|
|
||||||
featureFlags featureFlags
|
// ephemeralKeys is a map that contains the ephemeral keys of the client, used
|
||||||
|
// to decrypt messages
|
||||||
|
ephemeralKeys map[string]*ecdsa.PrivateKey
|
||||||
|
ephemeralKeysMutex sync.Mutex
|
||||||
|
|
||||||
|
// sentMessagesSubscriptions contains all the subscriptions for sent messages
|
||||||
|
sentMessagesSubscriptions []chan<- *SentMessage
|
||||||
|
// sentMessagesSubscriptions contains all the subscriptions for scheduled messages
|
||||||
|
scheduledMessagesSubscriptions []chan<- *RawMessage
|
||||||
|
|
||||||
|
featureFlags FeatureFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMessageProcessor(
|
func NewMessageProcessor(
|
||||||
identity *ecdsa.PrivateKey,
|
identity *ecdsa.PrivateKey,
|
||||||
database *sql.DB,
|
database *sql.DB,
|
||||||
enc *encryption.Protocol,
|
enc *encryption.Protocol,
|
||||||
transport transport.Transport,
|
transport transport.Transport,
|
||||||
logger *zap.Logger,
|
logger *zap.Logger,
|
||||||
features featureFlags,
|
features FeatureFlags,
|
||||||
) (*messageProcessor, error) {
|
) (*MessageProcessor, error) {
|
||||||
dataSyncTransport := datasync.NewNodeTransport()
|
dataSyncTransport := datasync.NewNodeTransport()
|
||||||
dataSyncNode, err := datasyncnode.NewPersistentNode(
|
dataSyncNode, err := datasyncnode.NewPersistentNode(
|
||||||
database,
|
database,
|
||||||
@ -64,22 +82,23 @@ func newMessageProcessor(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ds := datasync.New(dataSyncNode, dataSyncTransport, features.datasync, logger)
|
ds := datasync.New(dataSyncNode, dataSyncTransport, features.Datasync, logger)
|
||||||
|
|
||||||
p := &messageProcessor{
|
p := &MessageProcessor{
|
||||||
identity: identity,
|
identity: identity,
|
||||||
datasync: ds,
|
datasync: ds,
|
||||||
protocol: enc,
|
protocol: enc,
|
||||||
transport: transport,
|
transport: transport,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
featureFlags: features,
|
ephemeralKeys: make(map[string]*ecdsa.PrivateKey),
|
||||||
|
featureFlags: features,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializing DataSync is required to encrypt and send messages.
|
// Initializing DataSync is required to encrypt and send messages.
|
||||||
// With DataSync enabled, messages are added to the DataSync
|
// With DataSync enabled, messages are added to the DataSync
|
||||||
// but actual encrypt and send calls are postponed.
|
// but actual encrypt and send calls are postponed.
|
||||||
// sendDataSync is responsible for encrypting and sending postponed messages.
|
// sendDataSync is responsible for encrypting and sending postponed messages.
|
||||||
if features.datasync {
|
if features.Datasync {
|
||||||
ds.Init(p.sendDataSync)
|
ds.Init(p.sendDataSync)
|
||||||
ds.Start(300 * time.Millisecond)
|
ds.Start(300 * time.Millisecond)
|
||||||
}
|
}
|
||||||
@ -87,27 +106,42 @@ func newMessageProcessor(
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *messageProcessor) Stop() {
|
func (p *MessageProcessor) Stop() {
|
||||||
|
for _, c := range p.sentMessagesSubscriptions {
|
||||||
|
close(c)
|
||||||
|
}
|
||||||
p.datasync.Stop() // idempotent op
|
p.datasync.Stop() // idempotent op
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendPrivate takes encoded data, encrypts it and sends through the wire.
|
// SendPrivate takes encoded data, encrypts it and sends through the wire.
|
||||||
func (p *messageProcessor) SendPrivate(
|
func (p *MessageProcessor) SendPrivate(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
recipient *ecdsa.PublicKey,
|
recipient *ecdsa.PublicKey,
|
||||||
rawMessage *RawMessage,
|
rawMessage *RawMessage,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
p.logger.Debug(
|
p.logger.Debug(
|
||||||
"sending a private message",
|
"sending a private message",
|
||||||
zap.Binary("public-key", crypto.FromECDSAPub(recipient)),
|
zap.String("public-key", types.EncodeHex(crypto.FromECDSAPub(recipient))),
|
||||||
zap.String("site", "SendPrivate"),
|
zap.String("site", "SendPrivate"),
|
||||||
)
|
)
|
||||||
|
// Currently we don't support sending through datasync and setting custom waku fields,
|
||||||
|
// as the datasync interface is not rich enough to propagate that information, so we
|
||||||
|
// would have to add some complexity to handle this.
|
||||||
|
if rawMessage.ResendAutomatically && (rawMessage.Sender != nil || rawMessage.SkipEncryption) {
|
||||||
|
return nil, errors.New("setting identity, skip-encryption and datasync not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sender identity if not specified
|
||||||
|
if rawMessage.Sender == nil {
|
||||||
|
rawMessage.Sender = p.identity
|
||||||
|
}
|
||||||
|
|
||||||
return p.sendPrivate(ctx, recipient, rawMessage)
|
return p.sendPrivate(ctx, recipient, rawMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendGroupRaw takes encoded data, encrypts it and sends through the wire,
|
// SendGroup takes encoded data, encrypts it and sends through the wire,
|
||||||
// always return the messageID
|
// always return the messageID
|
||||||
func (p *messageProcessor) SendGroup(
|
func (p *MessageProcessor) SendGroup(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
recipients []*ecdsa.PublicKey,
|
recipients []*ecdsa.PublicKey,
|
||||||
rawMessage *RawMessage,
|
rawMessage *RawMessage,
|
||||||
@ -116,12 +150,18 @@ func (p *messageProcessor) SendGroup(
|
|||||||
"sending a private group message",
|
"sending a private group message",
|
||||||
zap.String("site", "SendGroup"),
|
zap.String("site", "SendGroup"),
|
||||||
)
|
)
|
||||||
// Calculate messageID first
|
// Set sender if not specified
|
||||||
|
if rawMessage.Sender == nil {
|
||||||
|
rawMessage.Sender = p.identity
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate messageID first and set on raw message
|
||||||
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to wrap message")
|
return nil, errors.Wrap(err, "failed to wrap message")
|
||||||
}
|
}
|
||||||
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
||||||
|
rawMessage.ID = types.EncodeHex(messageID)
|
||||||
|
|
||||||
// Send to each recipients
|
// Send to each recipients
|
||||||
for _, recipient := range recipients {
|
for _, recipient := range recipients {
|
||||||
@ -134,51 +174,67 @@ func (p *messageProcessor) SendGroup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendPrivate sends data to the recipient identifying with a given public key.
|
// sendPrivate sends data to the recipient identifying with a given public key.
|
||||||
func (p *messageProcessor) sendPrivate(
|
func (p *MessageProcessor) sendPrivate(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
recipient *ecdsa.PublicKey,
|
recipient *ecdsa.PublicKey,
|
||||||
rawMessage *RawMessage,
|
rawMessage *RawMessage,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
p.logger.Debug("sending private message", zap.Binary("recipient", crypto.FromECDSAPub(recipient)))
|
p.logger.Debug("sending private message", zap.String("recipient", types.EncodeHex(crypto.FromECDSAPub(recipient))))
|
||||||
|
|
||||||
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to wrap message")
|
return nil, errors.Wrap(err, "failed to wrap message")
|
||||||
}
|
}
|
||||||
|
|
||||||
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
||||||
|
rawMessage.ID = types.EncodeHex(messageID)
|
||||||
|
|
||||||
if p.featureFlags.datasync {
|
// Notify before dispatching, otherwise the dispatch subscription might happen
|
||||||
|
// earlier than the scheduled
|
||||||
|
p.notifyOnScheduledMessage(rawMessage)
|
||||||
|
|
||||||
|
if p.featureFlags.Datasync && rawMessage.ResendAutomatically {
|
||||||
|
// No need to call transport tracking.
|
||||||
|
// It is done in a data sync dispatch step.
|
||||||
if err := p.addToDataSync(recipient, wrappedMessage); err != nil {
|
if err := p.addToDataSync(recipient, wrappedMessage); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to send message with datasync")
|
return nil, errors.Wrap(err, "failed to send message with datasync")
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to call transport tracking.
|
} else if rawMessage.SkipEncryption {
|
||||||
// It is done in a data sync dispatch step.
|
// When SkipEncryption is set we don't pass the message to the encryption layer
|
||||||
} else {
|
messageIDs := [][]byte{messageID}
|
||||||
messageSpec, err := p.protocol.BuildDirectMessage(p.identity, recipient, wrappedMessage)
|
hash, newMessage, err := p.sendRawMessage(ctx, recipient, wrappedMessage, messageIDs)
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to encrypt message")
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to send a message spec")
|
return nil, errors.Wrap(err, "failed to send a message spec")
|
||||||
}
|
}
|
||||||
|
|
||||||
p.transport.Track([][]byte{messageID}, hash, newMessage)
|
p.transport.Track(messageIDs, hash, newMessage)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
messageSpec, err := p.protocol.BuildDirectMessage(rawMessage.Sender, recipient, wrappedMessage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to encrypt message")
|
||||||
|
}
|
||||||
|
|
||||||
|
messageIDs := [][]byte{messageID}
|
||||||
|
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec, messageIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to send a message spec")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.transport.Track(messageIDs, hash, newMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
return messageID, nil
|
return messageID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendPairInstallation sends data to the recipients, using DH
|
// sendPairInstallation sends data to the recipients, using DH
|
||||||
func (p *messageProcessor) SendPairInstallation(
|
func (p *MessageProcessor) SendPairInstallation(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
recipient *ecdsa.PublicKey,
|
recipient *ecdsa.PublicKey,
|
||||||
rawMessage *RawMessage,
|
rawMessage *RawMessage,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
p.logger.Debug("sending private message", zap.Binary("recipient", crypto.FromECDSAPub(recipient)))
|
p.logger.Debug("sending private message", zap.String("recipient", types.EncodeHex(crypto.FromECDSAPub(recipient))))
|
||||||
|
|
||||||
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -190,20 +246,22 @@ func (p *messageProcessor) SendPairInstallation(
|
|||||||
return nil, errors.Wrap(err, "failed to encrypt message")
|
return nil, errors.Wrap(err, "failed to encrypt message")
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec)
|
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
||||||
|
messageIDs := [][]byte{messageID}
|
||||||
|
|
||||||
|
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec, messageIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to send a message spec")
|
return nil, errors.Wrap(err, "failed to send a message spec")
|
||||||
}
|
}
|
||||||
|
|
||||||
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
p.transport.Track(messageIDs, hash, newMessage)
|
||||||
p.transport.Track([][]byte{messageID}, hash, newMessage)
|
|
||||||
|
|
||||||
return messageID, nil
|
return messageID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeMembershipUpdate takes a group and an optional chat message and returns the protobuf representation to be sent on the wire.
|
// EncodeMembershipUpdate takes a group and an optional chat message and returns the protobuf representation to be sent on the wire.
|
||||||
// All the events in a group are encoded and added to the payload
|
// All the events in a group are encoded and added to the payload
|
||||||
func (p *messageProcessor) EncodeMembershipUpdate(
|
func (p *MessageProcessor) EncodeMembershipUpdate(
|
||||||
group *v1protocol.Group,
|
group *v1protocol.Group,
|
||||||
chatMessage *protobuf.ChatMessage,
|
chatMessage *protobuf.ChatMessage,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
@ -222,43 +280,50 @@ func (p *messageProcessor) EncodeMembershipUpdate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SendPublic takes encoded data, encrypts it and sends through the wire.
|
// SendPublic takes encoded data, encrypts it and sends through the wire.
|
||||||
func (p *messageProcessor) SendPublic(
|
func (p *MessageProcessor) SendPublic(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
chatName string,
|
chatName string,
|
||||||
rawMessage *RawMessage,
|
rawMessage *RawMessage,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
var newMessage *types.NewMessage
|
// Set sender
|
||||||
|
if rawMessage.Sender == nil {
|
||||||
|
rawMessage.Sender = p.identity
|
||||||
|
}
|
||||||
|
|
||||||
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to wrap message")
|
return nil, errors.Wrap(err, "failed to wrap message")
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessage = &types.NewMessage{
|
newMessage := &types.NewMessage{
|
||||||
TTL: whisperTTL,
|
TTL: whisperTTL,
|
||||||
Payload: wrappedMessage,
|
Payload: wrappedMessage,
|
||||||
PowTarget: calculatePoW(wrappedMessage),
|
PowTarget: calculatePoW(wrappedMessage),
|
||||||
PowTime: whisperPoWTime,
|
PowTime: whisperPoWTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
||||||
|
rawMessage.ID = types.EncodeHex(messageID)
|
||||||
|
|
||||||
|
// notify before dispatching
|
||||||
|
p.notifyOnScheduledMessage(rawMessage)
|
||||||
|
|
||||||
hash, err := p.transport.SendPublic(ctx, newMessage, chatName)
|
hash, err := p.transport.SendPublic(ctx, newMessage, chatName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
|
||||||
|
|
||||||
p.transport.Track([][]byte{messageID}, hash, newMessage)
|
p.transport.Track([][]byte{messageID}, hash, newMessage)
|
||||||
|
|
||||||
return messageID, nil
|
return messageID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleMessages expects a whisper message as input, and it will go through
|
// HandleMessages expects a whisper message as input, and it will go through
|
||||||
// a series of transformations until the message is parsed into an application
|
// a series of transformations until the message is parsed into an application
|
||||||
// layer message, or in case of Raw methods, the processing stops at the layer
|
// layer message, or in case of Raw methods, the processing stops at the layer
|
||||||
// before.
|
// before.
|
||||||
// It returns an error only if the processing of required steps failed.
|
// It returns an error only if the processing of required steps failed.
|
||||||
func (p *messageProcessor) handleMessages(shhMessage *types.Message, applicationLayer bool) ([]*v1protocol.StatusMessage, error) {
|
func (p *MessageProcessor) HandleMessages(shhMessage *types.Message, applicationLayer bool) ([]*v1protocol.StatusMessage, error) {
|
||||||
logger := p.logger.With(zap.String("site", "handleMessages"))
|
logger := p.logger.With(zap.String("site", "handleMessages"))
|
||||||
hlogger := logger.With(zap.ByteString("hash", shhMessage.Hash))
|
hlogger := logger.With(zap.ByteString("hash", shhMessage.Hash))
|
||||||
var statusMessage v1protocol.StatusMessage
|
var statusMessage v1protocol.StatusMessage
|
||||||
@ -296,12 +361,32 @@ func (p *messageProcessor) handleMessages(shhMessage *types.Message, application
|
|||||||
return statusMessages, nil
|
return statusMessages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *messageProcessor) handleEncryptionLayer(ctx context.Context, message *v1protocol.StatusMessage) error {
|
// fetchDecryptionKey returns the private key associated with this public key, and returns true if it's an ephemeral key
|
||||||
|
func (p *MessageProcessor) fetchDecryptionKey(destination *ecdsa.PublicKey) (*ecdsa.PrivateKey, bool) {
|
||||||
|
destinationID := types.EncodeHex(crypto.FromECDSAPub(destination))
|
||||||
|
|
||||||
|
p.ephemeralKeysMutex.Lock()
|
||||||
|
decryptionKey, ok := p.ephemeralKeys[destinationID]
|
||||||
|
p.ephemeralKeysMutex.Unlock()
|
||||||
|
|
||||||
|
// the key is not there, fallback on identity
|
||||||
|
if !ok {
|
||||||
|
return p.identity, false
|
||||||
|
}
|
||||||
|
return decryptionKey, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MessageProcessor) handleEncryptionLayer(ctx context.Context, message *v1protocol.StatusMessage) error {
|
||||||
logger := p.logger.With(zap.String("site", "handleEncryptionLayer"))
|
logger := p.logger.With(zap.String("site", "handleEncryptionLayer"))
|
||||||
publicKey := message.SigPubKey()
|
publicKey := message.SigPubKey()
|
||||||
|
|
||||||
err := message.HandleEncryption(p.identity, publicKey, p.protocol)
|
// if it's an ephemeral key, we don't negotiate a topic
|
||||||
if err == encryption.ErrDeviceNotFound {
|
decryptionKey, skipNegotiation := p.fetchDecryptionKey(message.Dst)
|
||||||
|
|
||||||
|
err := message.HandleEncryption(decryptionKey, publicKey, p.protocol, skipNegotiation)
|
||||||
|
|
||||||
|
// if it's an ephemeral key, we don't have to handle a device not found error
|
||||||
|
if err == encryption.ErrDeviceNotFound && !skipNegotiation {
|
||||||
if err := p.handleErrDeviceNotFound(ctx, publicKey); err != nil {
|
if err := p.handleErrDeviceNotFound(ctx, publicKey); err != nil {
|
||||||
logger.Error("failed to handle ErrDeviceNotFound", zap.Error(err))
|
logger.Error("failed to handle ErrDeviceNotFound", zap.Error(err))
|
||||||
}
|
}
|
||||||
@ -313,7 +398,7 @@ func (p *messageProcessor) handleEncryptionLayer(ctx context.Context, message *v
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKey *ecdsa.PublicKey) error {
|
func (p *MessageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKey *ecdsa.PublicKey) error {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
advertise, err := p.protocol.ShouldAdvertiseBundle(publicKey, now)
|
advertise, err := p.protocol.ShouldAdvertiseBundle(publicKey, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -330,7 +415,9 @@ func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKe
|
|||||||
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
_, _, err = p.sendMessageSpec(ctx, publicKey, messageSpec)
|
// We don't pass an array of messageIDs as no action needs to be taken
|
||||||
|
// when sending a bundle
|
||||||
|
_, _, err = p.sendMessageSpec(ctx, publicKey, messageSpec, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -340,15 +427,15 @@ func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKe
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *messageProcessor) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) {
|
func (p *MessageProcessor) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) {
|
||||||
wrappedMessage, err := v1protocol.WrapMessageV1(rawMessage.Payload, rawMessage.MessageType, p.identity)
|
wrappedMessage, err := v1protocol.WrapMessageV1(rawMessage.Payload, rawMessage.MessageType, rawMessage.Sender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to wrap message")
|
return nil, errors.Wrap(err, "failed to wrap message")
|
||||||
}
|
}
|
||||||
return wrappedMessage, nil
|
return wrappedMessage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *messageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []byte) error {
|
func (p *MessageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []byte) error {
|
||||||
groupID := datasync.ToOneToOneGroupID(&p.identity.PublicKey, publicKey)
|
groupID := datasync.ToOneToOneGroupID(&p.identity.PublicKey, publicKey)
|
||||||
peerID := datasyncpeer.PublicKeyToPeerID(*publicKey)
|
peerID := datasyncpeer.PublicKeyToPeerID(*publicKey)
|
||||||
exist, err := p.datasync.IsPeerInGroup(groupID, peerID)
|
exist, err := p.datasync.IsPeerInGroup(groupID, peerID)
|
||||||
@ -370,7 +457,8 @@ func (p *messageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []b
|
|||||||
|
|
||||||
// sendDataSync sends a message scheduled by the data sync layer.
|
// sendDataSync sends a message scheduled by the data sync layer.
|
||||||
// Data Sync layer calls this method "dispatch" function.
|
// Data Sync layer calls this method "dispatch" function.
|
||||||
func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.PublicKey, encodedMessage []byte, payload *datasyncproto.Payload) error {
|
func (p *MessageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.PublicKey, encodedMessage []byte, payload *datasyncproto.Payload) error {
|
||||||
|
// Calculate the messageIDs
|
||||||
messageIDs := make([][]byte, 0, len(payload.Messages))
|
messageIDs := make([][]byte, 0, len(payload.Messages))
|
||||||
for _, payload := range payload.Messages {
|
for _, payload := range payload.Messages {
|
||||||
messageIDs = append(messageIDs, v1protocol.MessageID(&p.identity.PublicKey, payload.Body))
|
messageIDs = append(messageIDs, v1protocol.MessageID(&p.identity.PublicKey, payload.Body))
|
||||||
@ -381,7 +469,7 @@ func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu
|
|||||||
return errors.Wrap(err, "failed to encrypt message")
|
return errors.Wrap(err, "failed to encrypt message")
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, newMessage, err := p.sendMessageSpec(ctx, publicKey, messageSpec)
|
hash, newMessage, err := p.sendMessageSpec(ctx, publicKey, messageSpec, messageIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -391,9 +479,26 @@ func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sendRawMessage sends a message not wrapped in an encryption layer
|
||||||
|
func (p *MessageProcessor) sendRawMessage(ctx context.Context, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
|
||||||
|
newMessage := &types.NewMessage{
|
||||||
|
TTL: whisperTTL,
|
||||||
|
Payload: payload,
|
||||||
|
PowTarget: calculatePoW(payload),
|
||||||
|
PowTime: whisperPoWTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := p.transport.SendPrivateWithPartitioned(ctx, newMessage, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash, newMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
// sendMessageSpec analyses the spec properties and selects a proper transport method.
|
// sendMessageSpec analyses the spec properties and selects a proper transport method.
|
||||||
func (p *messageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec) ([]byte, *types.NewMessage, error) {
|
func (p *MessageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
|
||||||
newMessage, err := messageSpecToWhisper(messageSpec)
|
newMessage, err := MessageSpecToWhisper(messageSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -414,10 +519,70 @@ func (p *messageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sentMessage := &SentMessage{
|
||||||
|
PublicKey: publicKey,
|
||||||
|
Spec: messageSpec,
|
||||||
|
MessageIDs: messageIDs,
|
||||||
|
}
|
||||||
|
|
||||||
|
p.notifyOnSentMessage(sentMessage)
|
||||||
|
|
||||||
return hash, newMessage, nil
|
return hash, newMessage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func messageSpecToWhisper(spec *encryption.ProtocolMessageSpec) (*types.NewMessage, error) {
|
// SubscribeToSentMessages returns a channel where we publish every time a message is sent
|
||||||
|
func (p *MessageProcessor) SubscribeToSentMessages() <-chan *SentMessage {
|
||||||
|
c := make(chan *SentMessage, 100)
|
||||||
|
p.sentMessagesSubscriptions = append(p.sentMessagesSubscriptions, c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MessageProcessor) notifyOnSentMessage(sentMessage *SentMessage) {
|
||||||
|
// Publish on channels, drop if buffer is full
|
||||||
|
for _, c := range p.sentMessagesSubscriptions {
|
||||||
|
select {
|
||||||
|
case c <- sentMessage:
|
||||||
|
default:
|
||||||
|
p.logger.Warn("sent messages subscription channel full, dropping message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeToScheduledMessages returns a channel where we publish every time a message is scheduled for sending
|
||||||
|
func (p *MessageProcessor) SubscribeToScheduledMessages() <-chan *RawMessage {
|
||||||
|
c := make(chan *RawMessage, 100)
|
||||||
|
p.scheduledMessagesSubscriptions = append(p.scheduledMessagesSubscriptions, c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MessageProcessor) notifyOnScheduledMessage(message *RawMessage) {
|
||||||
|
// Publish on channels, drop if buffer is full
|
||||||
|
for _, c := range p.scheduledMessagesSubscriptions {
|
||||||
|
select {
|
||||||
|
case c <- message:
|
||||||
|
default:
|
||||||
|
p.logger.Warn("scheduled messages subscription channel full, dropping message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MessageProcessor) JoinPublic(chatID string) error {
|
||||||
|
return p.transport.JoinPublic(chatID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEphemeralKey adds an ephemeral key that we will be listening to
|
||||||
|
// note that we never removed them from now, as waku/whisper does not
|
||||||
|
// recalculate topics on removal, so effectively there's no benefit.
|
||||||
|
// On restart they will be gone.
|
||||||
|
func (p *MessageProcessor) AddEphemeralKey(privateKey *ecdsa.PrivateKey) (*transport.Filter, error) {
|
||||||
|
p.ephemeralKeysMutex.Lock()
|
||||||
|
p.ephemeralKeys[types.EncodeHex(crypto.FromECDSAPub(&privateKey.PublicKey))] = privateKey
|
||||||
|
p.ephemeralKeysMutex.Unlock()
|
||||||
|
return p.transport.LoadKeyFilters(privateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MessageSpecToWhisper(spec *encryption.ProtocolMessageSpec) (*types.NewMessage, error) {
|
||||||
var newMessage *types.NewMessage
|
var newMessage *types.NewMessage
|
||||||
|
|
||||||
payload, err := proto.Marshal(spec.Message)
|
payload, err := proto.Marshal(spec.Message)
|
||||||
@ -444,9 +609,3 @@ func calculatePoW(payload []byte) float64 {
|
|||||||
}
|
}
|
||||||
return whisperDefaultPoW
|
return whisperDefaultPoW
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPubKeyEqual checks that two public keys are equal
|
|
||||||
func isPubKeyEqual(a, b *ecdsa.PublicKey) bool {
|
|
||||||
// the curve is always the same, just compare the points
|
|
||||||
return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package protocol
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -33,22 +33,20 @@ func TestMessageProcessorSuite(t *testing.T) {
|
|||||||
type MessageProcessorSuite struct {
|
type MessageProcessorSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
|
|
||||||
processor *messageProcessor
|
processor *MessageProcessor
|
||||||
tmpDir string
|
tmpDir string
|
||||||
testMessage Message
|
testMessage protobuf.ChatMessage
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageProcessorSuite) SetupTest() {
|
func (s *MessageProcessorSuite) SetupTest() {
|
||||||
s.testMessage = Message{
|
s.testMessage = protobuf.ChatMessage{
|
||||||
ChatMessage: protobuf.ChatMessage{
|
Text: "abc123",
|
||||||
Text: "abc123",
|
ChatId: "testing-adamb",
|
||||||
ChatId: "testing-adamb",
|
ContentType: protobuf.ChatMessage_TEXT_PLAIN,
|
||||||
ContentType: protobuf.ChatMessage_TEXT_PLAIN,
|
MessageType: protobuf.ChatMessage_PUBLIC_GROUP,
|
||||||
MessageType: protobuf.ChatMessage_PUBLIC_GROUP,
|
Clock: 154593077368201,
|
||||||
Clock: 154593077368201,
|
Timestamp: 1545930773682,
|
||||||
Timestamp: 1545930773682,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -81,8 +79,6 @@ func (s *MessageProcessorSuite) SetupTest() {
|
|||||||
whisperConfig.MinimumAcceptedPOW = 0
|
whisperConfig.MinimumAcceptedPOW = 0
|
||||||
shh := whisper.New(&whisperConfig)
|
shh := whisper.New(&whisperConfig)
|
||||||
s.Require().NoError(shh.Start(nil))
|
s.Require().NoError(shh.Start(nil))
|
||||||
config := &config{}
|
|
||||||
s.Require().NoError(WithDatasync()(config))
|
|
||||||
|
|
||||||
whisperTransport, err := transport.NewTransport(
|
whisperTransport, err := transport.NewTransport(
|
||||||
gethbridge.NewGethWhisperWrapper(shh),
|
gethbridge.NewGethWhisperWrapper(shh),
|
||||||
@ -94,13 +90,13 @@ func (s *MessageProcessorSuite) SetupTest() {
|
|||||||
)
|
)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
s.processor, err = newMessageProcessor(
|
s.processor, err = NewMessageProcessor(
|
||||||
identity,
|
identity,
|
||||||
database,
|
database,
|
||||||
encryptionProtocol,
|
encryptionProtocol,
|
||||||
whisperTransport,
|
whisperTransport,
|
||||||
s.logger,
|
s.logger,
|
||||||
featureFlags{},
|
FeatureFlags{},
|
||||||
)
|
)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
@ -127,7 +123,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesWrapped() {
|
|||||||
message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey)
|
message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey)
|
||||||
message.Payload = wrappedPayload
|
message.Payload = wrappedPayload
|
||||||
|
|
||||||
decodedMessages, err := s.processor.handleMessages(message, true)
|
decodedMessages, err := s.processor.HandleMessages(message, true)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
s.Require().Equal(1, len(decodedMessages))
|
s.Require().Equal(1, len(decodedMessages))
|
||||||
@ -135,7 +131,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesWrapped() {
|
|||||||
s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID)
|
s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID)
|
||||||
parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage)
|
parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage)
|
||||||
s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload)
|
s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload)
|
||||||
s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage))
|
s.Require().True(proto.Equal(&s.testMessage, &parsedMessage))
|
||||||
s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type)
|
s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +159,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasync() {
|
|||||||
message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey)
|
message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey)
|
||||||
message.Payload = marshalledDataSyncMessage
|
message.Payload = marshalledDataSyncMessage
|
||||||
|
|
||||||
decodedMessages, err := s.processor.handleMessages(message, true)
|
decodedMessages, err := s.processor.HandleMessages(message, true)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// We send two messages, the unwrapped one will be attributed to the relayer, while the wrapped one will be attributed to the author
|
// We send two messages, the unwrapped one will be attributed to the relayer, while the wrapped one will be attributed to the author
|
||||||
@ -172,7 +168,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasync() {
|
|||||||
s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID)
|
s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID)
|
||||||
s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload)
|
s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload)
|
||||||
parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage)
|
parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage)
|
||||||
s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage))
|
s.Require().True(proto.Equal(&s.testMessage, &parsedMessage))
|
||||||
s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type)
|
s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +226,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() {
|
|||||||
message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey)
|
message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey)
|
||||||
message.Payload = encryptedPayload
|
message.Payload = encryptedPayload
|
||||||
|
|
||||||
decodedMessages, err := s.processor.handleMessages(message, true)
|
decodedMessages, err := s.processor.HandleMessages(message, true)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// We send two messages, the unwrapped one will be attributed to the relayer,
|
// We send two messages, the unwrapped one will be attributed to the relayer,
|
||||||
@ -240,6 +236,6 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() {
|
|||||||
s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID)
|
s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID)
|
||||||
s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload)
|
s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload)
|
||||||
parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage)
|
parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage)
|
||||||
s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage))
|
s.Require().True(proto.Equal(&s.testMessage, &parsedMessage))
|
||||||
s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type)
|
s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type)
|
||||||
}
|
}
|
24
protocol/common/raw_message.go
Normal file
24
protocol/common/raw_message.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RawMessage represent a sent or received message, kept for being able
|
||||||
|
// to re-send/propagate
|
||||||
|
type RawMessage struct {
|
||||||
|
ID string
|
||||||
|
LocalChatID string
|
||||||
|
LastSent uint64
|
||||||
|
SendCount int
|
||||||
|
Sent bool
|
||||||
|
ResendAutomatically bool
|
||||||
|
SkipEncryption bool
|
||||||
|
SendPushNotification bool
|
||||||
|
MessageType protobuf.ApplicationMetadataMessage_Type
|
||||||
|
Payload []byte
|
||||||
|
Sender *ecdsa.PrivateKey
|
||||||
|
Recipients []*ecdsa.PublicKey
|
||||||
|
}
|
@ -109,20 +109,6 @@ type Message struct {
|
|||||||
SigPubKey *ecdsa.PublicKey `json:"-"`
|
SigPubKey *ecdsa.PublicKey `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawMessage represent a sent or received message, kept for being able
|
|
||||||
// to re-send/propagate
|
|
||||||
type RawMessage struct {
|
|
||||||
ID string
|
|
||||||
LocalChatID string
|
|
||||||
LastSent uint64
|
|
||||||
SendCount int
|
|
||||||
Sent bool
|
|
||||||
ResendAutomatically bool
|
|
||||||
MessageType protobuf.ApplicationMetadataMessage_Type
|
|
||||||
Payload []byte
|
|
||||||
Recipients []*ecdsa.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) MarshalJSON() ([]byte, error) {
|
func (m *Message) MarshalJSON() ([]byte, error) {
|
||||||
type StickerAlias struct {
|
type StickerAlias struct {
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
v1protocol "github.com/status-im/status-go/protocol/v1"
|
v1protocol "github.com/status-im/status-go/protocol/v1"
|
||||||
@ -146,7 +147,7 @@ func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, messa
|
|||||||
message.LocalChatID = chat.ID
|
message.LocalChatID = chat.ID
|
||||||
|
|
||||||
// Increase unviewed count
|
// Increase unviewed count
|
||||||
if !isPubKeyEqual(message.SigPubKey, &m.identity.PublicKey) {
|
if !common.IsPubKeyEqual(message.SigPubKey, &m.identity.PublicKey) {
|
||||||
chat.UnviewedMessagesCount++
|
chat.UnviewedMessagesCount++
|
||||||
message.OutgoingStatus = ""
|
message.OutgoingStatus = ""
|
||||||
} else {
|
} else {
|
||||||
@ -332,7 +333,7 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error {
|
|||||||
receivedMessage.LocalChatID = chat.ID
|
receivedMessage.LocalChatID = chat.ID
|
||||||
|
|
||||||
// Increase unviewed count
|
// Increase unviewed count
|
||||||
if !isPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) {
|
if !common.IsPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) {
|
||||||
chat.UnviewedMessagesCount++
|
chat.UnviewedMessagesCount++
|
||||||
} else {
|
} else {
|
||||||
// Our own message, mark as sent
|
// Our own message, mark as sent
|
||||||
@ -582,7 +583,7 @@ func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat,
|
|||||||
return nil, errors.New("received a public message from non-existing chat")
|
return nil, errors.New("received a public message from non-existing chat")
|
||||||
}
|
}
|
||||||
return chat, nil
|
return chat, nil
|
||||||
case message.MessageType == protobuf.ChatMessage_ONE_TO_ONE && isPubKeyEqual(message.SigPubKey, &m.identity.PublicKey):
|
case message.MessageType == protobuf.ChatMessage_ONE_TO_ONE && common.IsPubKeyEqual(message.SigPubKey, &m.identity.PublicKey):
|
||||||
// It's a private message coming from us so we rely on Message.ChatID
|
// It's a private message coming from us so we rely on Message.ChatID
|
||||||
// If chat does not exist, it should be created to support multidevice synchronization.
|
// If chat does not exist, it should be created to support multidevice synchronization.
|
||||||
chatID := message.ChatId
|
chatID := message.ChatId
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ import (
|
|||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
enstypes "github.com/status-im/status-go/eth-node/types/ens"
|
enstypes "github.com/status-im/status-go/eth-node/types/ens"
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
"github.com/status-im/status-go/protocol/encryption"
|
"github.com/status-im/status-go/protocol/encryption"
|
||||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||||
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
|
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
|
||||||
@ -25,6 +27,8 @@ import (
|
|||||||
"github.com/status-im/status-go/protocol/identity/identicon"
|
"github.com/status-im/status-go/protocol/identity/identicon"
|
||||||
"github.com/status-im/status-go/protocol/images"
|
"github.com/status-im/status-go/protocol/images"
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||||
|
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||||
"github.com/status-im/status-go/protocol/sqlite"
|
"github.com/status-im/status-go/protocol/sqlite"
|
||||||
"github.com/status-im/status-go/protocol/transport"
|
"github.com/status-im/status-go/protocol/transport"
|
||||||
wakutransp "github.com/status-im/status-go/protocol/transport/waku"
|
wakutransp "github.com/status-im/status-go/protocol/transport/waku"
|
||||||
@ -55,11 +59,13 @@ type Messenger struct {
|
|||||||
persistence *sqlitePersistence
|
persistence *sqlitePersistence
|
||||||
transport transport.Transport
|
transport transport.Transport
|
||||||
encryptor *encryption.Protocol
|
encryptor *encryption.Protocol
|
||||||
processor *messageProcessor
|
processor *common.MessageProcessor
|
||||||
handler *MessageHandler
|
handler *MessageHandler
|
||||||
|
pushNotificationClient *pushnotificationclient.Client
|
||||||
|
pushNotificationServer *pushnotificationserver.Server
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
verifyTransactionClient EthClient
|
verifyTransactionClient EthClient
|
||||||
featureFlags featureFlags
|
featureFlags common.FeatureFlags
|
||||||
messagesPersistenceEnabled bool
|
messagesPersistenceEnabled bool
|
||||||
shutdownTasks []func() error
|
shutdownTasks []func() error
|
||||||
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string
|
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string
|
||||||
@ -68,6 +74,8 @@ type Messenger struct {
|
|||||||
allInstallations map[string]*multidevice.Installation
|
allInstallations map[string]*multidevice.Installation
|
||||||
modifiedInstallations map[string]bool
|
modifiedInstallations map[string]bool
|
||||||
installationID string
|
installationID string
|
||||||
|
mailserver []byte
|
||||||
|
database *sql.DB
|
||||||
|
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
}
|
}
|
||||||
@ -88,111 +96,11 @@ func (m *MessengerResponse) IsEmpty() bool {
|
|||||||
return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.Installations) == 0
|
return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.Installations) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type featureFlags struct {
|
|
||||||
// datasync indicates whether direct messages should be sent exclusively
|
|
||||||
// using datasync, breaking change for non-v1 clients. Public messages
|
|
||||||
// are not impacted
|
|
||||||
datasync bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type dbConfig struct {
|
type dbConfig struct {
|
||||||
dbPath string
|
dbPath string
|
||||||
dbKey string
|
dbKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
type config struct {
|
|
||||||
// This needs to be exposed until we move here mailserver logic
|
|
||||||
// as otherwise the client is not notified of a new filter and
|
|
||||||
// won't be pulling messages from mailservers until it reloads the chats/filters
|
|
||||||
onNegotiatedFilters func([]*transport.Filter)
|
|
||||||
// DEPRECATED: no need to expose it
|
|
||||||
onSendContactCodeHandler func(*encryption.ProtocolMessageSpec)
|
|
||||||
|
|
||||||
// systemMessagesTranslations holds translations for system-messages
|
|
||||||
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string
|
|
||||||
// Config for the envelopes monitor
|
|
||||||
envelopesMonitorConfig *transport.EnvelopesMonitorConfig
|
|
||||||
|
|
||||||
messagesPersistenceEnabled bool
|
|
||||||
featureFlags featureFlags
|
|
||||||
|
|
||||||
// A path to a database or a database instance is required.
|
|
||||||
// The database instance has a higher priority.
|
|
||||||
dbConfig dbConfig
|
|
||||||
db *sql.DB
|
|
||||||
|
|
||||||
verifyTransactionClient EthClient
|
|
||||||
|
|
||||||
logger *zap.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
type Option func(*config) error
|
|
||||||
|
|
||||||
// WithSystemMessagesTranslations is required for Group Chats which are currently disabled.
|
|
||||||
// nolint: unused
|
|
||||||
func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option {
|
|
||||||
return func(c *config) error {
|
|
||||||
c.systemMessagesTranslations = t
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithOnNegotiatedFilters(h func([]*transport.Filter)) Option {
|
|
||||||
return func(c *config) error {
|
|
||||||
c.onNegotiatedFilters = h
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithCustomLogger(logger *zap.Logger) Option {
|
|
||||||
return func(c *config) error {
|
|
||||||
c.logger = logger
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithMessagesPersistenceEnabled() Option {
|
|
||||||
return func(c *config) error {
|
|
||||||
c.messagesPersistenceEnabled = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithDatabaseConfig(dbPath, dbKey string) Option {
|
|
||||||
return func(c *config) error {
|
|
||||||
c.dbConfig = dbConfig{dbPath: dbPath, dbKey: dbKey}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithVerifyTransactionClient(client EthClient) Option {
|
|
||||||
return func(c *config) error {
|
|
||||||
c.verifyTransactionClient = client
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithDatabase(db *sql.DB) Option {
|
|
||||||
return func(c *config) error {
|
|
||||||
c.db = db
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithDatasync() func(c *config) error {
|
|
||||||
return func(c *config) error {
|
|
||||||
c.featureFlags.datasync = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithEnvelopesMonitorConfig(emc *transport.EnvelopesMonitorConfig) Option {
|
|
||||||
return func(c *config) error {
|
|
||||||
c.envelopesMonitorConfig = emc
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMessenger(
|
func NewMessenger(
|
||||||
identity *ecdsa.PrivateKey,
|
identity *ecdsa.PrivateKey,
|
||||||
node types.Node,
|
node types.Node,
|
||||||
@ -245,7 +153,7 @@ func NewMessenger(
|
|||||||
slogger := logger.With(zap.String("site", "onSendContactCodeHandler"))
|
slogger := logger.With(zap.String("site", "onSendContactCodeHandler"))
|
||||||
slogger.Debug("received a SendContactCode request")
|
slogger.Debug("received a SendContactCode request")
|
||||||
|
|
||||||
newMessage, err := messageSpecToWhisper(messageSpec)
|
newMessage, err := common.MessageSpecToWhisper(messageSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slogger.Warn("failed to convert spec to Whisper message", zap.Error(err))
|
slogger.Warn("failed to convert spec to Whisper message", zap.Error(err))
|
||||||
return
|
return
|
||||||
@ -328,7 +236,7 @@ func NewMessenger(
|
|||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
|
|
||||||
processor, err := newMessageProcessor(
|
processor, err := common.NewMessageProcessor(
|
||||||
identity,
|
identity,
|
||||||
database,
|
database,
|
||||||
encryptionProtocol,
|
encryptionProtocol,
|
||||||
@ -340,6 +248,30 @@ func NewMessenger(
|
|||||||
return nil, errors.Wrap(err, "failed to create messageProcessor")
|
return nil, errors.Wrap(err, "failed to create messageProcessor")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize push notification server
|
||||||
|
var pushNotificationServer *pushnotificationserver.Server
|
||||||
|
if c.pushNotificationServerConfig != nil {
|
||||||
|
c.pushNotificationServerConfig.Identity = identity
|
||||||
|
pushNotificationServerPersistence := pushnotificationserver.NewSQLitePersistence(database)
|
||||||
|
pushNotificationServer = pushnotificationserver.New(c.pushNotificationServerConfig, pushNotificationServerPersistence, processor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize push notification client
|
||||||
|
pushNotificationClientPersistence := pushnotificationclient.NewPersistence(database)
|
||||||
|
pushNotificationClientConfig := c.pushNotificationClientConfig
|
||||||
|
if pushNotificationClientConfig == nil {
|
||||||
|
pushNotificationClientConfig = &pushnotificationclient.Config{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overriding until we handle different identities
|
||||||
|
pushNotificationClientConfig.Identity = identity
|
||||||
|
// Hardcoding this for now, as it's the only one we support
|
||||||
|
pushNotificationClientConfig.TokenType = protobuf.PushNotificationRegistration_APN_TOKEN
|
||||||
|
pushNotificationClientConfig.Logger = logger
|
||||||
|
pushNotificationClientConfig.InstallationID = installationID
|
||||||
|
|
||||||
|
pushNotificationClient := pushnotificationclient.New(pushNotificationClientPersistence, pushNotificationClientConfig, processor)
|
||||||
|
|
||||||
handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database})
|
handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database})
|
||||||
|
|
||||||
messenger = &Messenger{
|
messenger = &Messenger{
|
||||||
@ -350,6 +282,8 @@ func NewMessenger(
|
|||||||
encryptor: encryptionProtocol,
|
encryptor: encryptionProtocol,
|
||||||
processor: processor,
|
processor: processor,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
|
pushNotificationClient: pushNotificationClient,
|
||||||
|
pushNotificationServer: pushNotificationServer,
|
||||||
featureFlags: c.featureFlags,
|
featureFlags: c.featureFlags,
|
||||||
systemMessagesTranslations: c.systemMessagesTranslations,
|
systemMessagesTranslations: c.systemMessagesTranslations,
|
||||||
allChats: make(map[string]*Chat),
|
allChats: make(map[string]*Chat),
|
||||||
@ -359,8 +293,10 @@ func NewMessenger(
|
|||||||
modifiedInstallations: make(map[string]bool),
|
modifiedInstallations: make(map[string]bool),
|
||||||
messagesPersistenceEnabled: c.messagesPersistenceEnabled,
|
messagesPersistenceEnabled: c.messagesPersistenceEnabled,
|
||||||
verifyTransactionClient: c.verifyTransactionClient,
|
verifyTransactionClient: c.verifyTransactionClient,
|
||||||
|
database: database,
|
||||||
shutdownTasks: []func() error{
|
shutdownTasks: []func() error{
|
||||||
database.Close,
|
database.Close,
|
||||||
|
pushNotificationClient.Stop,
|
||||||
transp.ResetFilters,
|
transp.ResetFilters,
|
||||||
transp.Stop,
|
transp.Stop,
|
||||||
func() error { processor.Stop(); return nil },
|
func() error { processor.Stop(); return nil },
|
||||||
@ -377,6 +313,21 @@ func NewMessenger(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) Start() error {
|
func (m *Messenger) Start() error {
|
||||||
|
m.logger.Info("starting messenger", zap.String("identity", types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey))))
|
||||||
|
// Start push notification server
|
||||||
|
if m.pushNotificationServer != nil {
|
||||||
|
if err := m.pushNotificationServer.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start push notification client
|
||||||
|
if m.pushNotificationClient != nil {
|
||||||
|
if err := m.pushNotificationClient.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return m.encryptor.Start(m.identity)
|
return m.encryptor.Start(m.identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,7 +625,7 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string,
|
|||||||
}
|
}
|
||||||
m.allChats[chat.ID] = &chat
|
m.allChats[chat.ID] = &chat
|
||||||
|
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||||
@ -740,7 +691,7 @@ func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||||
@ -803,7 +754,7 @@ func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, me
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||||
@ -868,7 +819,7 @@ func (m *Messenger) ChangeGroupChatName(ctx context.Context, chatID string, name
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||||
@ -934,7 +885,7 @@ func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, mem
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||||
@ -1002,7 +953,7 @@ func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*Me
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||||
@ -1070,7 +1021,7 @@ func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string, remove bo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||||
@ -1174,6 +1125,14 @@ func (m *Messenger) isNewContact(contact *Contact) bool {
|
|||||||
return contact.IsAdded() && (!ok || !previousContact.IsAdded())
|
return contact.IsAdded() && (!ok || !previousContact.IsAdded())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) removedContact(contact *Contact) bool {
|
||||||
|
previousContact, ok := m.allContacts[contact.ID]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return previousContact.IsAdded() && !contact.IsAdded()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Messenger) saveContact(contact *Contact) error {
|
func (m *Messenger) saveContact(contact *Contact) error {
|
||||||
name, identicon, err := generateAliasAndIdenticon(contact.ID)
|
name, identicon, err := generateAliasAndIdenticon(contact.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1190,6 +1149,9 @@ func (m *Messenger) saveContact(contact *Contact) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We check if it should re-register with the push notification server
|
||||||
|
shouldReregisterForPushNotifications := m.pushNotificationClient != nil && (m.isNewContact(contact) || m.removedContact(contact))
|
||||||
|
|
||||||
err = m.persistence.SaveContact(contact, nil)
|
err = m.persistence.SaveContact(contact, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1197,6 +1159,16 @@ func (m *Messenger) saveContact(contact *Contact) error {
|
|||||||
|
|
||||||
m.allContacts[contact.ID] = contact
|
m.allContacts[contact.ID] = contact
|
||||||
|
|
||||||
|
// Reregister only when data has changed
|
||||||
|
if shouldReregisterForPushNotifications {
|
||||||
|
m.logger.Info("contact state changed, re-registering for push notification")
|
||||||
|
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||||
|
err := m.pushNotificationClient.Reregister(contactIDs, mutedChatIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1256,11 +1228,12 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) err
|
|||||||
return errors.New("chat not found")
|
return errors.New("chat not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: message.Payload,
|
Payload: message.Payload,
|
||||||
MessageType: message.MessageType,
|
MessageType: message.MessageType,
|
||||||
Recipients: message.Recipients,
|
Recipients: message.Recipients,
|
||||||
|
ResendAutomatically: message.ResendAutomatically,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1276,7 +1249,7 @@ func (m *Messenger) hasPairedDevices() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendToPairedDevices will check if we have any paired devices and send to them if necessary
|
// sendToPairedDevices will check if we have any paired devices and send to them if necessary
|
||||||
func (m *Messenger) sendToPairedDevices(ctx context.Context, spec *RawMessage) error {
|
func (m *Messenger) sendToPairedDevices(ctx context.Context, spec *common.RawMessage) error {
|
||||||
hasPairedDevices := m.hasPairedDevices()
|
hasPairedDevices := m.hasPairedDevices()
|
||||||
// We send a message to any paired device
|
// We send a message to any paired device
|
||||||
if hasPairedDevices {
|
if hasPairedDevices {
|
||||||
@ -1288,7 +1261,7 @@ func (m *Messenger) sendToPairedDevices(ctx context.Context, spec *RawMessage) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec *RawMessage) ([]byte, error) {
|
func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec *common.RawMessage) ([]byte, error) {
|
||||||
var err error
|
var err error
|
||||||
var id []byte
|
var id []byte
|
||||||
|
|
||||||
@ -1307,7 +1280,7 @@ func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec *R
|
|||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) dispatchMessage(ctx context.Context, spec *RawMessage) ([]byte, error) {
|
func (m *Messenger) dispatchMessage(ctx context.Context, spec *common.RawMessage) ([]byte, error) {
|
||||||
var err error
|
var err error
|
||||||
var id []byte
|
var id []byte
|
||||||
logger := m.logger.With(zap.String("site", "dispatchMessage"), zap.String("chatID", spec.LocalChatID))
|
logger := m.logger.With(zap.String("site", "dispatchMessage"), zap.String("chatID", spec.LocalChatID))
|
||||||
@ -1322,7 +1295,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec *RawMessage) ([]by
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !isPubKeyEqual(publicKey, &m.identity.PublicKey) {
|
if !common.IsPubKeyEqual(publicKey, &m.identity.PublicKey) {
|
||||||
id, err = m.processor.SendPrivate(ctx, publicKey, spec)
|
id, err = m.processor.SendPrivate(ctx, publicKey, spec)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1357,7 +1330,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec *RawMessage) ([]by
|
|||||||
// Filter out my key from the recipients
|
// Filter out my key from the recipients
|
||||||
n := 0
|
n := 0
|
||||||
for _, recipient := range spec.Recipients {
|
for _, recipient := range spec.Recipients {
|
||||||
if !isPubKeyEqual(recipient, &m.identity.PublicKey) {
|
if !common.IsPubKeyEqual(recipient, &m.identity.PublicKey) {
|
||||||
spec.Recipients[n] = recipient
|
spec.Recipients[n] = recipient
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
@ -1457,10 +1430,12 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes
|
|||||||
return nil, errors.New("chat type not supported")
|
return nil, errors.New("chat type not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := m.dispatchMessage(ctx, &RawMessage{
|
id, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
SendPushNotification: !chat.Public(),
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
|
Payload: encodedMessage,
|
||||||
|
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
|
||||||
|
ResendAutomatically: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1564,7 +1539,7 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chatID,
|
LocalChatID: chatID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE,
|
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE,
|
||||||
@ -1658,7 +1633,7 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.dispatchPairInstallationMessage(ctx, &RawMessage{
|
_, err = m.dispatchPairInstallationMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chatID,
|
LocalChatID: chatID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION,
|
MessageType: protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION,
|
||||||
@ -1705,7 +1680,7 @@ func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chatID,
|
LocalChatID: chatID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT,
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT,
|
||||||
@ -1748,7 +1723,7 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chatID,
|
LocalChatID: chatID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT,
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT,
|
||||||
@ -1828,7 +1803,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||||||
for _, messages := range chatWithMessages {
|
for _, messages := range chatWithMessages {
|
||||||
for _, shhMessage := range messages {
|
for _, shhMessage := range messages {
|
||||||
// TODO: fix this to use an exported method.
|
// TODO: fix this to use an exported method.
|
||||||
statusMessages, err := m.processor.handleMessages(shhMessage, true)
|
statusMessages, err := m.processor.HandleMessages(shhMessage, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Info("failed to decode messages", zap.Error(err))
|
logger.Info("failed to decode messages", zap.Error(err))
|
||||||
continue
|
continue
|
||||||
@ -1897,7 +1872,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case protobuf.PairInstallation:
|
case protobuf.PairInstallation:
|
||||||
if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
||||||
logger.Warn("not coming from us, ignoring")
|
logger.Warn("not coming from us, ignoring")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1910,7 +1885,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||||||
}
|
}
|
||||||
|
|
||||||
case protobuf.SyncInstallationContact:
|
case protobuf.SyncInstallationContact:
|
||||||
if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
||||||
logger.Warn("not coming from us, ignoring")
|
logger.Warn("not coming from us, ignoring")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1923,7 +1898,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case protobuf.SyncInstallationPublicChat:
|
case protobuf.SyncInstallationPublicChat:
|
||||||
if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
||||||
logger.Warn("not coming from us, ignoring")
|
logger.Warn("not coming from us, ignoring")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1996,8 +1971,80 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||||||
logger.Warn("failed to handle ContactUpdate", zap.Error(err))
|
logger.Warn("failed to handle ContactUpdate", zap.Error(err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
case protobuf.PushNotificationQuery:
|
||||||
|
logger.Debug("Received PushNotificationQuery")
|
||||||
|
if m.pushNotificationServer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Debug("Handling PushNotificationQuery")
|
||||||
|
if err := m.pushNotificationServer.HandlePushNotificationQuery(publicKey, msg.ID, msg.ParsedMessage.(protobuf.PushNotificationQuery)); err != nil {
|
||||||
|
logger.Warn("failed to handle PushNotificationQuery", zap.Error(err))
|
||||||
|
}
|
||||||
|
// We continue in any case, no changes to messenger
|
||||||
|
continue
|
||||||
|
case protobuf.PushNotificationRegistrationResponse:
|
||||||
|
logger.Debug("Received PushNotificationRegistrationResponse")
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Debug("Handling PushNotificationRegistrationResponse")
|
||||||
|
if err := m.pushNotificationClient.HandlePushNotificationRegistrationResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationRegistrationResponse)); err != nil {
|
||||||
|
logger.Warn("failed to handle PushNotificationRegistrationResponse", zap.Error(err))
|
||||||
|
}
|
||||||
|
// We continue in any case, no changes to messenger
|
||||||
|
continue
|
||||||
|
case protobuf.PushNotificationResponse:
|
||||||
|
logger.Debug("Received PushNotificationResponse")
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Debug("Handling PushNotificationResponse")
|
||||||
|
if err := m.pushNotificationClient.HandlePushNotificationResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationResponse)); err != nil {
|
||||||
|
logger.Warn("failed to handle PushNotificationResponse", zap.Error(err))
|
||||||
|
}
|
||||||
|
// We continue in any case, no changes to messenger
|
||||||
|
continue
|
||||||
|
|
||||||
|
case protobuf.PushNotificationQueryResponse:
|
||||||
|
logger.Debug("Received PushNotificationQueryResponse")
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Debug("Handling PushNotificationQueryResponse")
|
||||||
|
if err := m.pushNotificationClient.HandlePushNotificationQueryResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationQueryResponse)); err != nil {
|
||||||
|
logger.Warn("failed to handle PushNotificationQueryResponse", zap.Error(err))
|
||||||
|
}
|
||||||
|
// We continue in any case, no changes to messenger
|
||||||
|
continue
|
||||||
|
|
||||||
|
case protobuf.PushNotificationRequest:
|
||||||
|
logger.Debug("Received PushNotificationRequest")
|
||||||
|
if m.pushNotificationServer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Debug("Handling PushNotificationRequest")
|
||||||
|
if err := m.pushNotificationServer.HandlePushNotificationRequest(publicKey, msg.ParsedMessage.(protobuf.PushNotificationRequest)); err != nil {
|
||||||
|
logger.Warn("failed to handle PushNotificationRequest", zap.Error(err))
|
||||||
|
}
|
||||||
|
// We continue in any case, no changes to messenger
|
||||||
|
continue
|
||||||
|
|
||||||
default:
|
default:
|
||||||
logger.Debug("message not handled")
|
// Check if is an encrypted PushNotificationRegistration
|
||||||
|
if msg.Type == protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION {
|
||||||
|
logger.Debug("Received PushNotificationRegistration")
|
||||||
|
if m.pushNotificationServer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Debug("Handling PushNotificationRegistration")
|
||||||
|
if err := m.pushNotificationServer.HandlePushNotificationRegistration(publicKey, msg.ParsedMessage.([]byte)); err != nil {
|
||||||
|
logger.Warn("failed to handle PushNotificationRegistration", zap.Error(err))
|
||||||
|
}
|
||||||
|
// We continue in any case, no changes to messenger
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug("message not handled", zap.Any("messageType", reflect.TypeOf(msg.ParsedMessage)))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2076,13 +2123,20 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||||||
return messageState.Response, nil
|
return messageState.Response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMailserver sets the currently used mailserver
|
||||||
|
func (m *Messenger) SetMailserver(peer []byte) {
|
||||||
|
m.mailserver = peer
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Messenger) RequestHistoricMessages(
|
func (m *Messenger) RequestHistoricMessages(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
peer []byte, // should be removed after mailserver logic is ported
|
|
||||||
from, to uint32,
|
from, to uint32,
|
||||||
cursor []byte,
|
cursor []byte,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
return m.transport.SendMessagesRequest(ctx, peer, from, to, cursor)
|
if m.mailserver == nil {
|
||||||
|
return nil, errors.New("no mailserver selected")
|
||||||
|
}
|
||||||
|
return m.transport.SendMessagesRequest(ctx, m.mailserver, from, to, cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) LoadFilters(filters []*transport.Filter) ([]*transport.Filter, error) {
|
func (m *Messenger) LoadFilters(filters []*transport.Filter) ([]*transport.Filter, error) {
|
||||||
@ -2163,6 +2217,46 @@ func (m *Messenger) MarkAllRead(chatID string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MuteChat signals to the messenger that we don't want to be notified
|
||||||
|
// on new messages from this chat
|
||||||
|
func (m *Messenger) MuteChat(chatID string) error {
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
chat, ok := m.allChats[chatID]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("chat not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.persistence.MuteChat(chatID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
chat.Muted = true
|
||||||
|
m.allChats[chat.ID] = chat
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmuteChat signals to the messenger that we want to be notified
|
||||||
|
// on new messages from this chat
|
||||||
|
func (m *Messenger) UnmuteChat(chatID string) error {
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
chat, ok := m.allChats[chatID]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("chat not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.persistence.UnmuteChat(chatID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
chat.Muted = false
|
||||||
|
m.allChats[chat.ID] = chat
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Messenger) UpdateMessageOutgoingStatus(id, newOutgoingStatus string) error {
|
func (m *Messenger) UpdateMessageOutgoingStatus(id, newOutgoingStatus string) error {
|
||||||
return m.persistence.UpdateMessageOutgoingStatus(id, newOutgoingStatus)
|
return m.persistence.UpdateMessageOutgoingStatus(id, newOutgoingStatus)
|
||||||
}
|
}
|
||||||
@ -2274,7 +2368,7 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
id, err := m.dispatchMessage(ctx, &RawMessage{
|
id, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_REQUEST_TRANSACTION,
|
MessageType: protobuf.ApplicationMetadataMessage_REQUEST_TRANSACTION,
|
||||||
@ -2350,7 +2444,7 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
id, err := m.dispatchMessage(ctx, &RawMessage{
|
id, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION,
|
MessageType: protobuf.ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION,
|
||||||
@ -2452,7 +2546,7 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION,
|
MessageType: protobuf.ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION,
|
||||||
@ -2535,7 +2629,7 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION,
|
MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION,
|
||||||
@ -2617,7 +2711,7 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION,
|
MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION,
|
||||||
@ -2714,7 +2808,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION,
|
MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION,
|
||||||
@ -2791,7 +2885,7 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION,
|
MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION,
|
||||||
@ -2965,6 +3059,142 @@ func (m *Messenger) Timesource() TimeSource {
|
|||||||
return m.getTimesource()
|
return m.getTimesource()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddPushNotificationsServer adds a push notification server
|
||||||
|
func (m *Messenger) AddPushNotificationsServer(ctx context.Context, publicKey *ecdsa.PublicKey) error {
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
return errors.New("push notification client not enabled")
|
||||||
|
}
|
||||||
|
return m.pushNotificationClient.AddPushNotificationsServer(publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemovePushNotificationServer removes a push notification server
|
||||||
|
func (m *Messenger) RemovePushNotificationServer(ctx context.Context, publicKey *ecdsa.PublicKey) error {
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
return errors.New("push notification client not enabled")
|
||||||
|
}
|
||||||
|
return m.pushNotificationClient.RemovePushNotificationServer(publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnregisterFromPushNotifications unregister from any server
|
||||||
|
func (m *Messenger) UnregisterFromPushNotifications(ctx context.Context) error {
|
||||||
|
return m.pushNotificationClient.Unregister()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableSendingPushNotifications signals the client not to send any push notification
|
||||||
|
func (m *Messenger) DisableSendingPushNotifications() error {
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
return errors.New("push notification client not enabled")
|
||||||
|
}
|
||||||
|
m.pushNotificationClient.DisableSending()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableSendingPushNotifications signals the client to send push notifications
|
||||||
|
func (m *Messenger) EnableSendingPushNotifications() error {
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
return errors.New("push notification client not enabled")
|
||||||
|
}
|
||||||
|
m.pushNotificationClient.EnableSending()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string) {
|
||||||
|
var contactIDs []*ecdsa.PublicKey
|
||||||
|
var mutedChatIDs []string
|
||||||
|
|
||||||
|
for _, contact := range m.allContacts {
|
||||||
|
if contact.IsAdded() {
|
||||||
|
pk, err := contact.PublicKey()
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Warn("could not parse contact public key")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
contactIDs = append(contactIDs, pk)
|
||||||
|
} else if contact.IsBlocked() {
|
||||||
|
mutedChatIDs = append(mutedChatIDs, contact.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, chat := range m.allChats {
|
||||||
|
if chat.Muted {
|
||||||
|
mutedChatIDs = append(mutedChatIDs, chat.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return contactIDs, mutedChatIDs
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterForPushNotification register deviceToken with any push notification server enabled
|
||||||
|
func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) error {
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
return errors.New("push notification client not enabled")
|
||||||
|
}
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
|
||||||
|
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||||
|
return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisteredForPushNotifications returns whether we successfully registered with all the servers
|
||||||
|
func (m *Messenger) RegisteredForPushNotifications() (bool, error) {
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
return false, errors.New("no push notification client")
|
||||||
|
}
|
||||||
|
return m.pushNotificationClient.Registered()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications only from contacts
|
||||||
|
func (m *Messenger) EnablePushNotificationsFromContactsOnly() error {
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
return errors.New("no push notification client")
|
||||||
|
}
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
|
||||||
|
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||||
|
return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications from anyone
|
||||||
|
func (m *Messenger) DisablePushNotificationsFromContactsOnly() error {
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
return errors.New("no push notification client")
|
||||||
|
}
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
|
||||||
|
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||||
|
return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPushNotificationServers returns the servers used for push notifications
|
||||||
|
func (m *Messenger) GetPushNotificationServers() ([]*pushnotificationclient.PushNotificationServer, error) {
|
||||||
|
if m.pushNotificationClient == nil {
|
||||||
|
return nil, errors.New("no push notification client")
|
||||||
|
}
|
||||||
|
return m.pushNotificationClient.GetServers()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartPushNotificationsServer initialize and start a push notification server, using the current messenger identity key
|
||||||
|
func (m *Messenger) StartPushNotificationsServer() error {
|
||||||
|
if m.pushNotificationServer == nil {
|
||||||
|
pushNotificationServerPersistence := pushnotificationserver.NewSQLitePersistence(m.database)
|
||||||
|
config := &pushnotificationserver.Config{
|
||||||
|
Logger: m.logger,
|
||||||
|
Identity: m.identity,
|
||||||
|
}
|
||||||
|
m.pushNotificationServer = pushnotificationserver.New(config, pushNotificationServerPersistence, m.processor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.pushNotificationServer.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopPushNotificationServer stops the push notification server if running
|
||||||
|
func (m *Messenger) StopPushNotificationsServer() error {
|
||||||
|
m.pushNotificationServer = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func generateAliasAndIdenticon(pk string) (string, string, error) {
|
func generateAliasAndIdenticon(pk string) (string, string, error) {
|
||||||
identicon, err := identicon.GenerateBase64(pk)
|
identicon, err := identicon.GenerateBase64(pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
124
protocol/messenger_config.go
Normal file
124
protocol/messenger_config.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
"github.com/status-im/status-go/protocol/encryption"
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||||
|
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||||
|
"github.com/status-im/status-go/protocol/transport"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
// This needs to be exposed until we move here mailserver logic
|
||||||
|
// as otherwise the client is not notified of a new filter and
|
||||||
|
// won't be pulling messages from mailservers until it reloads the chats/filters
|
||||||
|
onNegotiatedFilters func([]*transport.Filter)
|
||||||
|
// DEPRECATED: no need to expose it
|
||||||
|
onSendContactCodeHandler func(*encryption.ProtocolMessageSpec)
|
||||||
|
|
||||||
|
// systemMessagesTranslations holds translations for system-messages
|
||||||
|
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string
|
||||||
|
// Config for the envelopes monitor
|
||||||
|
envelopesMonitorConfig *transport.EnvelopesMonitorConfig
|
||||||
|
|
||||||
|
messagesPersistenceEnabled bool
|
||||||
|
featureFlags common.FeatureFlags
|
||||||
|
|
||||||
|
// A path to a database or a database instance is required.
|
||||||
|
// The database instance has a higher priority.
|
||||||
|
dbConfig dbConfig
|
||||||
|
db *sql.DB
|
||||||
|
|
||||||
|
verifyTransactionClient EthClient
|
||||||
|
|
||||||
|
pushNotificationServerConfig *pushnotificationserver.Config
|
||||||
|
pushNotificationClientConfig *pushnotificationclient.Config
|
||||||
|
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*config) error
|
||||||
|
|
||||||
|
// WithSystemMessagesTranslations is required for Group Chats which are currently disabled.
|
||||||
|
// nolint: unused
|
||||||
|
func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.systemMessagesTranslations = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithOnNegotiatedFilters(h func([]*transport.Filter)) Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.onNegotiatedFilters = h
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithCustomLogger(logger *zap.Logger) Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.logger = logger
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithMessagesPersistenceEnabled() Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.messagesPersistenceEnabled = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDatabaseConfig(dbPath, dbKey string) Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.dbConfig = dbConfig{dbPath: dbPath, dbKey: dbKey}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithVerifyTransactionClient(client EthClient) Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.verifyTransactionClient = client
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDatabase(db *sql.DB) Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.db = db
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithPushNotificationServerConfig(pushNotificationServerConfig *pushnotificationserver.Config) Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.pushNotificationServerConfig = pushNotificationServerConfig
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithPushNotificationClientConfig(pushNotificationClientConfig *pushnotificationclient.Config) Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.pushNotificationClientConfig = pushNotificationClientConfig
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDatasync() func(c *config) error {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.featureFlags.Datasync = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithEnvelopesMonitorConfig(emc *transport.EnvelopesMonitorConfig) Option {
|
||||||
|
return func(c *config) error {
|
||||||
|
c.envelopesMonitorConfig = emc
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/protocol/tt"
|
"github.com/status-im/status-go/protocol/tt"
|
||||||
"github.com/status-im/status-go/whisper/v6"
|
"github.com/status-im/status-go/waku"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMessengerContactUpdateSuite(t *testing.T) {
|
func TestMessengerContactUpdateSuite(t *testing.T) {
|
||||||
@ -27,8 +27,8 @@ type MessengerContactUpdateSuite struct {
|
|||||||
m *Messenger // main instance of Messenger
|
m *Messenger // main instance of Messenger
|
||||||
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||||
// If one wants to send messages between different instances of Messenger,
|
// If one wants to send messages between different instances of Messenger,
|
||||||
// a single Whisper service should be shared.
|
// a single waku service should be shared.
|
||||||
shh types.Whisper
|
shh types.Waku
|
||||||
tmpFiles []*os.File // files to clean up
|
tmpFiles []*os.File // files to clean up
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
@ -36,17 +36,17 @@ type MessengerContactUpdateSuite struct {
|
|||||||
func (s *MessengerContactUpdateSuite) SetupTest() {
|
func (s *MessengerContactUpdateSuite) SetupTest() {
|
||||||
s.logger = tt.MustCreateTestLogger()
|
s.logger = tt.MustCreateTestLogger()
|
||||||
|
|
||||||
config := whisper.DefaultConfig
|
config := waku.DefaultConfig
|
||||||
config.MinimumAcceptedPOW = 0
|
config.MinimumAcceptedPoW = 0
|
||||||
shh := whisper.New(&config)
|
shh := waku.New(&config, s.logger)
|
||||||
s.shh = gethbridge.NewGethWhisperWrapper(shh)
|
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||||
s.Require().NoError(shh.Start(nil))
|
s.Require().NoError(shh.Start(nil))
|
||||||
|
|
||||||
s.m = s.newMessenger(s.shh)
|
s.m = s.newMessenger(s.shh)
|
||||||
s.privateKey = s.m.identity
|
s.privateKey = s.m.identity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger {
|
func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||||
tmpFile, err := ioutil.TempFile("", "")
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Whisper, pri
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerContactUpdateSuite) newMessenger(shh types.Whisper) *Messenger {
|
func (s *MessengerContactUpdateSuite) newMessenger(shh types.Waku) *Messenger {
|
||||||
privateKey, err := crypto.GenerateKey()
|
privateKey, err := crypto.GenerateKey()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
@ -17,9 +17,11 @@ import (
|
|||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||||
"github.com/status-im/status-go/protocol/tt"
|
"github.com/status-im/status-go/protocol/tt"
|
||||||
"github.com/status-im/status-go/whisper/v6"
|
"github.com/status-im/status-go/waku"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const statusChatID = "status"
|
||||||
|
|
||||||
func TestMessengerInstallationSuite(t *testing.T) {
|
func TestMessengerInstallationSuite(t *testing.T) {
|
||||||
suite.Run(t, new(MessengerInstallationSuite))
|
suite.Run(t, new(MessengerInstallationSuite))
|
||||||
}
|
}
|
||||||
@ -30,8 +32,8 @@ type MessengerInstallationSuite struct {
|
|||||||
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||||
|
|
||||||
// If one wants to send messages between different instances of Messenger,
|
// If one wants to send messages between different instances of Messenger,
|
||||||
// a single Whisper service should be shared.
|
// a single Waku service should be shared.
|
||||||
shh types.Whisper
|
shh types.Waku
|
||||||
|
|
||||||
tmpFiles []*os.File // files to clean up
|
tmpFiles []*os.File // files to clean up
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
@ -40,17 +42,17 @@ type MessengerInstallationSuite struct {
|
|||||||
func (s *MessengerInstallationSuite) SetupTest() {
|
func (s *MessengerInstallationSuite) SetupTest() {
|
||||||
s.logger = tt.MustCreateTestLogger()
|
s.logger = tt.MustCreateTestLogger()
|
||||||
|
|
||||||
config := whisper.DefaultConfig
|
config := waku.DefaultConfig
|
||||||
config.MinimumAcceptedPOW = 0
|
config.MinimumAcceptedPoW = 0
|
||||||
shh := whisper.New(&config)
|
shh := waku.New(&config, s.logger)
|
||||||
s.shh = gethbridge.NewGethWhisperWrapper(shh)
|
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||||
s.Require().NoError(shh.Start(nil))
|
s.Require().NoError(shh.Start(nil))
|
||||||
|
|
||||||
s.m = s.newMessenger(s.shh)
|
s.m = s.newMessenger(s.shh)
|
||||||
s.privateKey = s.m.identity
|
s.privateKey = s.m.identity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger {
|
func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||||
tmpFile, err := ioutil.TempFile("", "")
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
@ -77,7 +79,7 @@ func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Whisper, priv
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerInstallationSuite) newMessenger(shh types.Whisper) *Messenger {
|
func (s *MessengerInstallationSuite) newMessenger(shh types.Waku) *Messenger {
|
||||||
privateKey, err := crypto.GenerateKey()
|
privateKey, err := crypto.GenerateKey()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
@ -136,7 +138,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() {
|
|||||||
s.Require().Equal(contact.ID, actualContact.ID)
|
s.Require().Equal(contact.ID, actualContact.ID)
|
||||||
s.Require().True(actualContact.IsAdded())
|
s.Require().True(actualContact.IsAdded())
|
||||||
|
|
||||||
chat := CreatePublicChat("status", s.m.transport)
|
chat := CreatePublicChat(statusChatID, s.m.transport)
|
||||||
err = s.m.SaveChat(&chat)
|
err = s.m.SaveChat(&chat)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
@ -149,7 +151,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() {
|
|||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
actualChat := response.Chats[0]
|
actualChat := response.Chats[0]
|
||||||
s.Require().Equal("status", actualChat.ID)
|
s.Require().Equal(statusChatID, actualChat.ID)
|
||||||
s.Require().True(actualChat.Active)
|
s.Require().True(actualChat.Active)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +168,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
|
|||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// add chat
|
// add chat
|
||||||
chat := CreatePublicChat("status", s.m.transport)
|
chat := CreatePublicChat(statusChatID, s.m.transport)
|
||||||
err = s.m.SaveChat(&chat)
|
err = s.m.SaveChat(&chat)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
@ -230,7 +232,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
|
|||||||
|
|
||||||
var statusChat *Chat
|
var statusChat *Chat
|
||||||
for _, c := range allChats {
|
for _, c := range allChats {
|
||||||
if c.ID == "status" {
|
if c.ID == statusChatID {
|
||||||
statusChat = c
|
statusChat = c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
110
protocol/messenger_mute_test.go
Normal file
110
protocol/messenger_mute_test.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
"github.com/status-im/status-go/protocol/tt"
|
||||||
|
"github.com/status-im/status-go/waku"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMessengerMuteSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(MessengerMuteSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessengerMuteSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
m *Messenger // main instance of Messenger
|
||||||
|
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||||
|
|
||||||
|
// If one wants to send messages between different instances of Messenger,
|
||||||
|
// a single Waku service should be shared.
|
||||||
|
shh types.Waku
|
||||||
|
|
||||||
|
tmpFiles []*os.File // files to clean up
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerMuteSuite) SetupTest() {
|
||||||
|
s.logger = tt.MustCreateTestLogger()
|
||||||
|
|
||||||
|
config := waku.DefaultConfig
|
||||||
|
config.MinimumAcceptedPoW = 0
|
||||||
|
shh := waku.New(&config, s.logger)
|
||||||
|
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||||
|
s.Require().NoError(shh.Start(nil))
|
||||||
|
|
||||||
|
s.m = s.newMessenger(s.shh)
|
||||||
|
s.privateKey = s.m.identity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerMuteSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||||
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
options := []Option{
|
||||||
|
WithCustomLogger(s.logger),
|
||||||
|
WithMessagesPersistenceEnabled(),
|
||||||
|
WithDatabaseConfig(tmpFile.Name(), "some-key"),
|
||||||
|
WithDatasync(),
|
||||||
|
}
|
||||||
|
installationID := uuid.New().String()
|
||||||
|
m, err := NewMessenger(
|
||||||
|
privateKey,
|
||||||
|
&testNode{shh: shh},
|
||||||
|
installationID,
|
||||||
|
options...,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = m.Init()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.tmpFiles = append(s.tmpFiles, tmpFile)
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerMuteSuite) newMessenger(shh types.Waku) *Messenger {
|
||||||
|
privateKey, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
return s.newMessengerWithKey(s.shh, privateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerMuteSuite) TestSetMute() {
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
theirMessenger := s.newMessengerWithKey(s.shh, key)
|
||||||
|
|
||||||
|
chatID := "status"
|
||||||
|
|
||||||
|
chat := CreatePublicChat(chatID, s.m.transport)
|
||||||
|
|
||||||
|
err = s.m.SaveChat(&chat)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = s.m.Join(chat)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = theirMessenger.SaveChat(&chat)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().NoError(s.m.MuteChat(chatID))
|
||||||
|
|
||||||
|
s.Require().Len(s.m.Chats(), 1)
|
||||||
|
s.Require().True(s.m.Chats()[0].Muted)
|
||||||
|
|
||||||
|
s.Require().NoError(s.m.UnmuteChat(chatID))
|
||||||
|
s.Require().False(s.m.Chats()[0].Muted)
|
||||||
|
}
|
@ -27,7 +27,7 @@ import (
|
|||||||
"github.com/status-im/status-go/protocol/protobuf"
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
"github.com/status-im/status-go/protocol/tt"
|
"github.com/status-im/status-go/protocol/tt"
|
||||||
v1protocol "github.com/status-im/status-go/protocol/v1"
|
v1protocol "github.com/status-im/status-go/protocol/v1"
|
||||||
"github.com/status-im/status-go/whisper/v6"
|
"github.com/status-im/status-go/waku"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -62,13 +62,13 @@ type MessengerSuite struct {
|
|||||||
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||||
// If one wants to send messages between different instances of Messenger,
|
// If one wants to send messages between different instances of Messenger,
|
||||||
// a single Whisper service should be shared.
|
// a single Whisper service should be shared.
|
||||||
shh types.Whisper
|
shh types.Waku
|
||||||
tmpFiles []*os.File // files to clean up
|
tmpFiles []*os.File // files to clean up
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type testNode struct {
|
type testNode struct {
|
||||||
shh types.Whisper
|
shh types.Waku
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *testNode) NewENSVerifier(_ *zap.Logger) enstypes.ENSVerifier {
|
func (n *testNode) NewENSVerifier(_ *zap.Logger) enstypes.ENSVerifier {
|
||||||
@ -84,27 +84,27 @@ func (n *testNode) RemovePeer(_ string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *testNode) GetWaku(_ interface{}) (types.Waku, error) {
|
func (n *testNode) GetWaku(_ interface{}) (types.Waku, error) {
|
||||||
panic("not implemented")
|
return n.shh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *testNode) GetWhisper(_ interface{}) (types.Whisper, error) {
|
func (n *testNode) GetWhisper(_ interface{}) (types.Whisper, error) {
|
||||||
return n.shh, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerSuite) SetupTest() {
|
func (s *MessengerSuite) SetupTest() {
|
||||||
s.logger = tt.MustCreateTestLogger()
|
s.logger = tt.MustCreateTestLogger()
|
||||||
|
|
||||||
config := whisper.DefaultConfig
|
config := waku.DefaultConfig
|
||||||
config.MinimumAcceptedPOW = 0
|
config.MinimumAcceptedPoW = 0
|
||||||
shh := whisper.New(&config)
|
shh := waku.New(&config, s.logger)
|
||||||
s.shh = gethbridge.NewGethWhisperWrapper(shh)
|
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||||
s.Require().NoError(shh.Start(nil))
|
s.Require().NoError(shh.Start(nil))
|
||||||
|
|
||||||
s.m = s.newMessenger(s.shh)
|
s.m = s.newMessenger(s.shh)
|
||||||
s.privateKey = s.m.identity
|
s.privateKey = s.m.identity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger {
|
func (s *MessengerSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||||
tmpFile, err := ioutil.TempFile("", "")
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ func (s *MessengerSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecds
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerSuite) newMessenger(shh types.Whisper) *Messenger {
|
func (s *MessengerSuite) newMessenger(shh types.Waku) *Messenger {
|
||||||
privateKey, err := crypto.GenerateKey()
|
privateKey, err := crypto.GenerateKey()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
return s.newMessengerWithKey(shh, privateKey)
|
return s.newMessengerWithKey(shh, privateKey)
|
||||||
@ -2056,7 +2056,7 @@ type MockEthClient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockSendMessagesRequest struct {
|
type mockSendMessagesRequest struct {
|
||||||
types.Whisper
|
types.Waku
|
||||||
req types.MessagesRequest
|
req types.MessagesRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2101,12 +2101,13 @@ func (s *MessengerSuite) TestMessageJSON() {
|
|||||||
|
|
||||||
func (s *MessengerSuite) TestRequestHistoricMessagesRequest() {
|
func (s *MessengerSuite) TestRequestHistoricMessagesRequest() {
|
||||||
shh := &mockSendMessagesRequest{
|
shh := &mockSendMessagesRequest{
|
||||||
Whisper: s.shh,
|
Waku: s.shh,
|
||||||
}
|
}
|
||||||
m := s.newMessenger(shh)
|
m := s.newMessenger(shh)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
cursor, err := m.RequestHistoricMessages(ctx, nil, 10, 20, []byte{0x01})
|
m.mailserver = []byte("mailserver-id")
|
||||||
|
cursor, err := m.RequestHistoricMessages(ctx, 10, 20, []byte{0x01})
|
||||||
s.EqualError(err, ctx.Err().Error())
|
s.EqualError(err, ctx.Err().Error())
|
||||||
s.Empty(cursor)
|
s.Empty(cursor)
|
||||||
// verify request is correct
|
// verify request is correct
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
// 1589365189_add_pow_target.up.sql (66B)
|
// 1589365189_add_pow_target.up.sql (66B)
|
||||||
// 1591277220_add_index_messages.down.sql (237B)
|
// 1591277220_add_index_messages.down.sql (237B)
|
||||||
// 1591277220_add_index_messages.up.sql (240B)
|
// 1591277220_add_index_messages.up.sql (240B)
|
||||||
|
// 1593087212_add_mute_chat_and_raw_message_fields.down.sql (0)
|
||||||
|
// 1593087212_add_mute_chat_and_raw_message_fields.up.sql (215B)
|
||||||
// doc.go (850B)
|
// doc.go (850B)
|
||||||
|
|
||||||
package migrations
|
package migrations
|
||||||
@ -136,7 +138,7 @@ func _000002_add_last_ens_clock_valueDownSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "000002_add_last_ens_clock_value.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
info := bindataFileInfo{name: "000002_add_last_ens_clock_value.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -156,7 +158,7 @@ func _000002_add_last_ens_clock_valueUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "000002_add_last_ens_clock_value.up.sql", size: 77, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
info := bindataFileInfo{name: "000002_add_last_ens_clock_value.up.sql", size: 77, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4d, 0x3, 0x8f, 0xd5, 0x85, 0x83, 0x47, 0xbe, 0xf9, 0x82, 0x7e, 0x81, 0xa4, 0xbd, 0xaa, 0xd5, 0x98, 0x18, 0x5, 0x2d, 0x82, 0x42, 0x3b, 0x3, 0x50, 0xc3, 0x1e, 0x84, 0x35, 0xf, 0xb6, 0x2b}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4d, 0x3, 0x8f, 0xd5, 0x85, 0x83, 0x47, 0xbe, 0xf9, 0x82, 0x7e, 0x81, 0xa4, 0xbd, 0xaa, 0xd5, 0x98, 0x18, 0x5, 0x2d, 0x82, 0x42, 0x3b, 0x3, 0x50, 0xc3, 0x1e, 0x84, 0x35, 0xf, 0xb6, 0x2b}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -176,7 +178,7 @@ func _1586358095_add_replaceDownSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1586358095_add_replace.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1589265610, 0)}
|
info := bindataFileInfo{name: "1586358095_add_replace.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -196,7 +198,7 @@ func _1586358095_add_replaceUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1586358095_add_replace.up.sql", size: 224, mode: os.FileMode(0644), modTime: time.Unix(1589265610, 0)}
|
info := bindataFileInfo{name: "1586358095_add_replace.up.sql", size: 224, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd2, 0xb3, 0xa9, 0xc7, 0x7f, 0x9d, 0x8f, 0x43, 0x8c, 0x9e, 0x58, 0x8d, 0x44, 0xbc, 0xfa, 0x6b, 0x5f, 0x3f, 0x5a, 0xbe, 0xe8, 0xb1, 0x16, 0xf, 0x91, 0x2a, 0xa0, 0x71, 0xbb, 0x8d, 0x6b, 0xcb}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd2, 0xb3, 0xa9, 0xc7, 0x7f, 0x9d, 0x8f, 0x43, 0x8c, 0x9e, 0x58, 0x8d, 0x44, 0xbc, 0xfa, 0x6b, 0x5f, 0x3f, 0x5a, 0xbe, 0xe8, 0xb1, 0x16, 0xf, 0x91, 0x2a, 0xa0, 0x71, 0xbb, 0x8d, 0x6b, 0xcb}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -216,7 +218,7 @@ func _1588665364_add_image_dataDownSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1588665364_add_image_data.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)}
|
info := bindataFileInfo{name: "1588665364_add_image_data.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -236,7 +238,7 @@ func _1588665364_add_image_dataUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)}
|
info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd6, 0xc6, 0x35, 0xb4, 0x4c, 0x39, 0x96, 0x29, 0x30, 0xda, 0xf4, 0x8f, 0xcb, 0xf1, 0x9f, 0x84, 0xdc, 0x88, 0xd4, 0xd5, 0xbc, 0xb6, 0x5b, 0x46, 0x78, 0x67, 0x76, 0x1a, 0x5, 0x36, 0xdc, 0xe5}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd6, 0xc6, 0x35, 0xb4, 0x4c, 0x39, 0x96, 0x29, 0x30, 0xda, 0xf4, 0x8f, 0xcb, 0xf1, 0x9f, 0x84, 0xdc, 0x88, 0xd4, 0xd5, 0xbc, 0xb6, 0x5b, 0x46, 0x78, 0x67, 0x76, 0x1a, 0x5, 0x36, 0xdc, 0xe5}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -256,7 +258,7 @@ func _1589365189_add_pow_targetDownSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1589365189_add_pow_target.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)}
|
info := bindataFileInfo{name: "1589365189_add_pow_target.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -276,7 +278,7 @@ func _1589365189_add_pow_targetUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)}
|
info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x3a, 0xe2, 0x2e, 0x7d, 0xaf, 0xbb, 0xcc, 0x21, 0xa1, 0x7a, 0x41, 0x9a, 0xd0, 0xbb, 0xa9, 0xc8, 0x35, 0xf9, 0x32, 0x34, 0x46, 0x44, 0x9a, 0x86, 0x40, 0x7c, 0xb9, 0x23, 0xc7, 0x3, 0x3f}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x3a, 0xe2, 0x2e, 0x7d, 0xaf, 0xbb, 0xcc, 0x21, 0xa1, 0x7a, 0x41, 0x9a, 0xd0, 0xbb, 0xa9, 0xc8, 0x35, 0xf9, 0x32, 0x34, 0x46, 0x44, 0x9a, 0x86, 0x40, 0x7c, 0xb9, 0x23, 0xc7, 0x3, 0x3f}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -296,7 +298,7 @@ func _1591277220_add_index_messagesDownSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1591277220_add_index_messages.down.sql", size: 237, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)}
|
info := bindataFileInfo{name: "1591277220_add_index_messages.down.sql", size: 237, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x79, 0xe5, 0x42, 0x56, 0x64, 0x1d, 0xb7, 0x8a, 0x1b, 0x0, 0x99, 0xf0, 0x18, 0x8c, 0x69, 0xe3, 0x14, 0x3a, 0x7f, 0x78, 0xfe, 0xe3, 0x2e, 0xcb, 0x6e, 0x5c, 0x8c, 0x1f, 0x7b, 0xfc, 0x21, 0xc7}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x79, 0xe5, 0x42, 0x56, 0x64, 0x1d, 0xb7, 0x8a, 0x1b, 0x0, 0x99, 0xf0, 0x18, 0x8c, 0x69, 0xe3, 0x14, 0x3a, 0x7f, 0x78, 0xfe, 0xe3, 0x2e, 0xcb, 0x6e, 0x5c, 0x8c, 0x1f, 0x7b, 0xfc, 0x21, 0xc7}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -316,11 +318,51 @@ func _1591277220_add_index_messagesUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1591361844, 0)}
|
info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9c, 0xfe, 0xbe, 0xd5, 0xb8, 0x8f, 0xdd, 0xef, 0xbb, 0xa8, 0xad, 0x7f, 0xed, 0x5b, 0x5b, 0x2f, 0xe6, 0x82, 0x27, 0x78, 0x1f, 0xb9, 0x57, 0xdc, 0x8, 0xc2, 0xb2, 0xa9, 0x9a, 0x4, 0xe1, 0x7a}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9c, 0xfe, 0xbe, 0xd5, 0xb8, 0x8f, 0xdd, 0xef, 0xbb, 0xa8, 0xad, 0x7f, 0xed, 0x5b, 0x5b, 0x2f, 0xe6, 0x82, 0x27, 0x78, 0x1f, 0xb9, 0x57, 0xdc, 0x8, 0xc2, 0xb2, 0xa9, 0x9a, 0x4, 0xe1, 0x7a}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var __1593087212_add_mute_chat_and_raw_message_fieldsDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||||
|
|
||||||
|
func _1593087212_add_mute_chat_and_raw_message_fieldsDownSqlBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
__1593087212_add_mute_chat_and_raw_message_fieldsDownSql,
|
||||||
|
"1593087212_add_mute_chat_and_raw_message_fields.down.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _1593087212_add_mute_chat_and_raw_message_fieldsDownSql() (*asset, error) {
|
||||||
|
bytes, err := _1593087212_add_mute_chat_and_raw_message_fieldsDownSqlBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var __1593087212_add_mute_chat_and_raw_message_fieldsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\xcc\x31\x0e\xc2\x30\x0c\x00\xc0\x9d\x57\xf8\x1f\x4c\x2e\x71\x27\xd3\x48\x90\xce\x51\x94\x1a\x1a\xa1\xa6\x51\xed\x0a\xf1\x7b\x56\x58\x58\x78\xc0\x1d\x72\xa0\x0b\x04\xec\x98\x20\xcf\xc9\x14\xd0\x39\x38\x79\x1e\xcf\x03\x2c\xbb\xc9\x04\x9d\xf7\x4c\x38\x80\xa3\x1e\x47\x0e\xd0\x23\x5f\xe9\x78\xf8\x94\x5b\x7a\xc6\x45\x54\xd3\x5d\xbe\x02\x7d\x94\x16\xa5\xe6\xed\xd5\xac\xac\xf5\xaf\x4a\xea\x14\xdb\xae\x73\xac\xab\x95\x5b\xc9\xe9\xd7\xf8\x0e\x00\x00\xff\xff\xd9\x47\x38\x58\xd7\x00\x00\x00")
|
||||||
|
|
||||||
|
func _1593087212_add_mute_chat_and_raw_message_fieldsUpSqlBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
__1593087212_add_mute_chat_and_raw_message_fieldsUpSql,
|
||||||
|
"1593087212_add_mute_chat_and_raw_message_fields.up.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _1593087212_add_mute_chat_and_raw_message_fieldsUpSql() (*asset, error) {
|
||||||
|
bytes, err := _1593087212_add_mute_chat_and_raw_message_fieldsUpSqlBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.up.sql", size: 215, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x73, 0x99, 0x61, 0xd1, 0xaa, 0xb4, 0xbf, 0xaf, 0xd7, 0x20, 0x17, 0x40, 0xf9, 0x2, 0xfb, 0xcc, 0x40, 0x2a, 0xd, 0x86, 0x36, 0x30, 0x88, 0x89, 0x25, 0x80, 0x42, 0xb0, 0x5b, 0xe9, 0x73, 0x78}}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x52\x3f\x8f\xdb\x3e\x0c\xdd\xf3\x29\x1e\x6e\xb9\xe5\x22\x07\xf8\xfd\xa6\xdb\x3a\x74\xe8\xd2\x2e\xd9\x0b\x46\xa6\x6d\x22\x32\xe5\x8a\xf4\x39\xf9\xf6\x85\x74\x17\x9c\x51\x14\xe8\x4a\x89\x8f\xef\x5f\xd7\xe1\x3c\x89\x61\x90\xc4\x10\x83\x72\x64\x33\x2a\x77\x5c\x38\xd2\x6a\x8c\xa7\x51\x7c\x5a\x2f\x21\xe6\xb9\x33\x27\x5f\xed\x28\x73\x37\xcb\x58\xc8\xb9\x7b\xfb\xff\xe9\xd0\x75\x88\xa4\xcf\x8e\x89\xb4\x4f\xdc\xb0\x0c\xe6\x54\x5c\x74\xc4\x26\x3e\x81\xb0\x14\x1e\xe4\x16\xf0\xc5\x91\x98\xcc\xe1\x13\xf9\xb3\xc1\x27\x46\x24\xe3\x0a\x33\xe4\x82\x31\x1f\x2f\xa2\x3d\x39\x85\x3a\xfa\x36\xec\x26\x95\x61\xa4\x94\xb8\xc7\x50\xf2\xdc\x76\x8d\x66\x46\x2f\x85\xa3\xe7\x72\x7f\x01\x99\xb1\x43\x69\x66\xab\xfb\x13\xbd\x31\x34\x7f\x9c\x07\x69\xff\x6f\x45\xd8\x72\xb9\x1a\xc8\xc0\xb7\x85\xa3\x73\x1f\x0e\x15\xeb\xfb\x8f\xf3\xd7\x57\x9c\x27\xae\xf0\x55\x5a\x1e\x1a\x85\x66\x9e\x32\xf7\x06\xcf\x18\x72\x4a\x79\x6b\x0f\xab\xca\x0d\x2e\x33\x9b\xd3\xbc\x20\x66\x7d\x63\x75\xc9\x5a\xd1\x56\x4d\x72\xe5\xf6\xcf\xb7\x0c\x51\x71\xa1\xf4\xee\x5e\x93\x7e\x7e\x37\xe8\x11\x44\x5c\x4b\x61\xf5\x74\x6f\x2b\xac\xb1\xdc\x97\x8a\x85\x77\xe6\x92\xd5\x9a\xbc\xa5\x64\xcf\x31\xa7\xdd\xbc\xa2\xd9\x44\x85\x3f\x1d\x73\xba\x24\x7e\xc1\x36\x49\x9c\x30\x33\xa9\xb5\x40\xda\x87\x44\xce\xe6\x9f\xfb\x10\x85\x73\x99\xad\x0a\xae\xfc\xaa\xbb\x15\xb3\x16\xe7\x91\xc3\x8e\x50\x33\x7f\xa1\xf8\x51\x85\xc7\x95\xd5\xd8\x40\x7f\x98\xf2\x08\x79\x63\x50\xdf\xe3\x74\x3a\x9d\xfe\xfb\x19\x42\x68\x5d\xe0\x1b\xcd\x4b\xa5\xe9\xb5\xa3\x9b\xa4\x84\x0b\x43\x46\xcd\x85\xfb\xca\x8a\x6f\x62\xad\x64\x31\x09\xab\xd7\xcc\x2a\x5e\x4e\x3d\x97\xaa\x47\xf7\x7a\xfe\x66\x59\x38\x1c\x16\x8a\x57\x1a\x19\xf6\x2b\x89\x73\x0d\x7a\xcc\xaf\x23\x2b\xd7\x3a\xec\xcb\x77\x5c\xae\xe3\xde\xec\x63\x46\x08\xdd\xe7\x20\x8c\x19\xe1\xf0\x3b\x00\x00\xff\xff\x12\xcd\x7f\xc4\x52\x03\x00\x00")
|
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x52\x3f\x8f\xdb\x3e\x0c\xdd\xf3\x29\x1e\x6e\xb9\xe5\x22\x07\xf8\xfd\xa6\xdb\x3a\x74\xe8\xd2\x2e\xd9\x0b\x46\xa6\x6d\x22\x32\xe5\x8a\xf4\x39\xf9\xf6\x85\x74\x17\x9c\x51\x14\xe8\x4a\x89\x8f\xef\x5f\xd7\xe1\x3c\x89\x61\x90\xc4\x10\x83\x72\x64\x33\x2a\x77\x5c\x38\xd2\x6a\x8c\xa7\x51\x7c\x5a\x2f\x21\xe6\xb9\x33\x27\x5f\xed\x28\x73\x37\xcb\x58\xc8\xb9\x7b\xfb\xff\xe9\xd0\x75\x88\xa4\xcf\x8e\x89\xb4\x4f\xdc\xb0\x0c\xe6\x54\x5c\x74\xc4\x26\x3e\x81\xb0\x14\x1e\xe4\x16\xf0\xc5\x91\x98\xcc\xe1\x13\xf9\xb3\xc1\x27\x46\x24\xe3\x0a\x33\xe4\x82\x31\x1f\x2f\xa2\x3d\x39\x85\x3a\xfa\x36\xec\x26\x95\x61\xa4\x94\xb8\xc7\x50\xf2\xdc\x76\x8d\x66\x46\x2f\x85\xa3\xe7\x72\x7f\x01\x99\xb1\x43\x69\x66\xab\xfb\x13\xbd\x31\x34\x7f\x9c\x07\x69\xff\x6f\x45\xd8\x72\xb9\x1a\xc8\xc0\xb7\x85\xa3\x73\x1f\x0e\x15\xeb\xfb\x8f\xf3\xd7\x57\x9c\x27\xae\xf0\x55\x5a\x1e\x1a\x85\x66\x9e\x32\xf7\x06\xcf\x18\x72\x4a\x79\x6b\x0f\xab\xca\x0d\x2e\x33\x9b\xd3\xbc\x20\x66\x7d\x63\x75\xc9\x5a\xd1\x56\x4d\x72\xe5\xf6\xcf\xb7\x0c\x51\x71\xa1\xf4\xee\x5e\x93\x7e\x7e\x37\xe8\x11\x44\x5c\x4b\x61\xf5\x74\x6f\x2b\xac\xb1\xdc\x97\x8a\x85\x77\xe6\x92\xd5\x9a\xbc\xa5\x64\xcf\x31\xa7\xdd\xbc\xa2\xd9\x44\x85\x3f\x1d\x73\xba\x24\x7e\xc1\x36\x49\x9c\x30\x33\xa9\xb5\x40\xda\x87\x44\xce\xe6\x9f\xfb\x10\x85\x73\x99\xad\x0a\xae\xfc\xaa\xbb\x15\xb3\x16\xe7\x91\xc3\x8e\x50\x33\x7f\xa1\xf8\x51\x85\xc7\x95\xd5\xd8\x40\x7f\x98\xf2\x08\x79\x63\x50\xdf\xe3\x74\x3a\x9d\xfe\xfb\x19\x42\x68\x5d\xe0\x1b\xcd\x4b\xa5\xe9\xb5\xa3\x9b\xa4\x84\x0b\x43\x46\xcd\x85\xfb\xca\x8a\x6f\x62\xad\x64\x31\x09\xab\xd7\xcc\x2a\x5e\x4e\x3d\x97\xaa\x47\xf7\x7a\xfe\x66\x59\x38\x1c\x16\x8a\x57\x1a\x19\xf6\x2b\x89\x73\x0d\x7a\xcc\xaf\x23\x2b\xd7\x3a\xec\xcb\x77\x5c\xae\xe3\xde\xec\x63\x46\x08\xdd\xe7\x20\x8c\x19\xe1\xf0\x3b\x00\x00\xff\xff\x12\xcd\x7f\xc4\x52\x03\x00\x00")
|
||||||
|
|
||||||
func docGoBytes() ([]byte, error) {
|
func docGoBytes() ([]byte, error) {
|
||||||
@ -336,7 +378,7 @@ func docGo() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(0644), modTime: time.Unix(1589265610, 0)}
|
info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa0, 0xcc, 0x41, 0xe1, 0x61, 0x12, 0x97, 0xe, 0x36, 0x8c, 0xa7, 0x9e, 0xe0, 0x6e, 0x59, 0x9e, 0xee, 0xd5, 0x4a, 0xcf, 0x1e, 0x60, 0xd6, 0xc3, 0x3a, 0xc9, 0x6c, 0xf2, 0x86, 0x5a, 0xb4, 0x1e}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa0, 0xcc, 0x41, 0xe1, 0x61, 0x12, 0x97, 0xe, 0x36, 0x8c, 0xa7, 0x9e, 0xe0, 0x6e, 0x59, 0x9e, 0xee, 0xd5, 0x4a, 0xcf, 0x1e, 0x60, 0xd6, 0xc3, 0x3a, 0xc9, 0x6c, 0xf2, 0x86, 0x5a, 0xb4, 0x1e}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -456,6 +498,10 @@ var _bindata = map[string]func() (*asset, error){
|
|||||||
|
|
||||||
"1591277220_add_index_messages.up.sql": _1591277220_add_index_messagesUpSql,
|
"1591277220_add_index_messages.up.sql": _1591277220_add_index_messagesUpSql,
|
||||||
|
|
||||||
|
"1593087212_add_mute_chat_and_raw_message_fields.down.sql": _1593087212_add_mute_chat_and_raw_message_fieldsDownSql,
|
||||||
|
|
||||||
|
"1593087212_add_mute_chat_and_raw_message_fields.up.sql": _1593087212_add_mute_chat_and_raw_message_fieldsUpSql,
|
||||||
|
|
||||||
"doc.go": docGo,
|
"doc.go": docGo,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,19 +546,21 @@ type bintree struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var _bintree = &bintree{nil, map[string]*bintree{
|
var _bintree = &bintree{nil, map[string]*bintree{
|
||||||
"000001_init.down.db.sql": &bintree{_000001_initDownDbSql, map[string]*bintree{}},
|
"000001_init.down.db.sql": &bintree{_000001_initDownDbSql, map[string]*bintree{}},
|
||||||
"000001_init.up.db.sql": &bintree{_000001_initUpDbSql, map[string]*bintree{}},
|
"000001_init.up.db.sql": &bintree{_000001_initUpDbSql, map[string]*bintree{}},
|
||||||
"000002_add_last_ens_clock_value.down.sql": &bintree{_000002_add_last_ens_clock_valueDownSql, map[string]*bintree{}},
|
"000002_add_last_ens_clock_value.down.sql": &bintree{_000002_add_last_ens_clock_valueDownSql, map[string]*bintree{}},
|
||||||
"000002_add_last_ens_clock_value.up.sql": &bintree{_000002_add_last_ens_clock_valueUpSql, map[string]*bintree{}},
|
"000002_add_last_ens_clock_value.up.sql": &bintree{_000002_add_last_ens_clock_valueUpSql, map[string]*bintree{}},
|
||||||
"1586358095_add_replace.down.sql": &bintree{_1586358095_add_replaceDownSql, map[string]*bintree{}},
|
"1586358095_add_replace.down.sql": &bintree{_1586358095_add_replaceDownSql, map[string]*bintree{}},
|
||||||
"1586358095_add_replace.up.sql": &bintree{_1586358095_add_replaceUpSql, map[string]*bintree{}},
|
"1586358095_add_replace.up.sql": &bintree{_1586358095_add_replaceUpSql, map[string]*bintree{}},
|
||||||
"1588665364_add_image_data.down.sql": &bintree{_1588665364_add_image_dataDownSql, map[string]*bintree{}},
|
"1588665364_add_image_data.down.sql": &bintree{_1588665364_add_image_dataDownSql, map[string]*bintree{}},
|
||||||
"1588665364_add_image_data.up.sql": &bintree{_1588665364_add_image_dataUpSql, map[string]*bintree{}},
|
"1588665364_add_image_data.up.sql": &bintree{_1588665364_add_image_dataUpSql, map[string]*bintree{}},
|
||||||
"1589365189_add_pow_target.down.sql": &bintree{_1589365189_add_pow_targetDownSql, map[string]*bintree{}},
|
"1589365189_add_pow_target.down.sql": &bintree{_1589365189_add_pow_targetDownSql, map[string]*bintree{}},
|
||||||
"1589365189_add_pow_target.up.sql": &bintree{_1589365189_add_pow_targetUpSql, map[string]*bintree{}},
|
"1589365189_add_pow_target.up.sql": &bintree{_1589365189_add_pow_targetUpSql, map[string]*bintree{}},
|
||||||
"1591277220_add_index_messages.down.sql": &bintree{_1591277220_add_index_messagesDownSql, map[string]*bintree{}},
|
"1591277220_add_index_messages.down.sql": &bintree{_1591277220_add_index_messagesDownSql, map[string]*bintree{}},
|
||||||
"1591277220_add_index_messages.up.sql": &bintree{_1591277220_add_index_messagesUpSql, map[string]*bintree{}},
|
"1591277220_add_index_messages.up.sql": &bintree{_1591277220_add_index_messagesUpSql, map[string]*bintree{}},
|
||||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
"1593087212_add_mute_chat_and_raw_message_fields.down.sql": &bintree{_1593087212_add_mute_chat_and_raw_message_fieldsDownSql, map[string]*bintree{}},
|
||||||
|
"1593087212_add_mute_chat_and_raw_message_fields.up.sql": &bintree{_1593087212_add_mute_chat_and_raw_message_fieldsUpSql, map[string]*bintree{}},
|
||||||
|
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// RestoreAsset restores an asset under the given directory.
|
// RestoreAsset restores an asset under the given directory.
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE chats ADD COLUMN muted BOOLEAN DEFAULT FALSE;
|
||||||
|
ALTER TABLE raw_messages ADD COLUMN skip_encryption BOOLEAN DEFAULT FALSE;
|
||||||
|
ALTER TABLE raw_messages ADD COLUMN send_push_notification BOOLEAN DEFAULT FALSE;
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -103,8 +104,8 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert record
|
// Insert record
|
||||||
stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates)
|
stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates, muted)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -123,6 +124,7 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error {
|
|||||||
chat.LastMessage,
|
chat.LastMessage,
|
||||||
encodedMembers.Bytes(),
|
encodedMembers.Bytes(),
|
||||||
encodedMembershipUpdates.Bytes(),
|
encodedMembershipUpdates.Bytes(),
|
||||||
|
chat.Muted,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -136,6 +138,16 @@ func (db sqlitePersistence) DeleteChat(chatID string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db sqlitePersistence) MuteChat(chatID string) error {
|
||||||
|
_, err := db.db.Exec("UPDATE chats SET muted = 1 WHERE id = ?", chatID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db sqlitePersistence) UnmuteChat(chatID string) error {
|
||||||
|
_, err := db.db.Exec("UPDATE chats SET muted = 0 WHERE id = ?", chatID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (db sqlitePersistence) Chats() ([]*Chat, error) {
|
func (db sqlitePersistence) Chats() ([]*Chat, error) {
|
||||||
return db.chats(nil)
|
return db.chats(nil)
|
||||||
}
|
}
|
||||||
@ -170,6 +182,7 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) {
|
|||||||
chats.last_message,
|
chats.last_message,
|
||||||
chats.members,
|
chats.members,
|
||||||
chats.membership_updates,
|
chats.membership_updates,
|
||||||
|
chats.muted,
|
||||||
contacts.identicon,
|
contacts.identicon,
|
||||||
contacts.alias
|
contacts.alias
|
||||||
FROM chats LEFT JOIN contacts ON chats.id = contacts.id
|
FROM chats LEFT JOIN contacts ON chats.id = contacts.id
|
||||||
@ -201,6 +214,7 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) {
|
|||||||
&chat.LastMessage,
|
&chat.LastMessage,
|
||||||
&encodedMembers,
|
&encodedMembers,
|
||||||
&encodedMembershipUpdates,
|
&encodedMembershipUpdates,
|
||||||
|
&chat.Muted,
|
||||||
&identicon,
|
&identicon,
|
||||||
&alias,
|
&alias,
|
||||||
)
|
)
|
||||||
@ -251,7 +265,8 @@ func (db sqlitePersistence) Chat(chatID string) (*Chat, error) {
|
|||||||
last_clock_value,
|
last_clock_value,
|
||||||
last_message,
|
last_message,
|
||||||
members,
|
members,
|
||||||
membership_updates
|
membership_updates,
|
||||||
|
muted
|
||||||
FROM chats
|
FROM chats
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, chatID).Scan(&chat.ID,
|
`, chatID).Scan(&chat.ID,
|
||||||
@ -266,6 +281,7 @@ func (db sqlitePersistence) Chat(chatID string) (*Chat, error) {
|
|||||||
&chat.LastMessage,
|
&chat.LastMessage,
|
||||||
&encodedMembers,
|
&encodedMembers,
|
||||||
&encodedMembershipUpdates,
|
&encodedMembershipUpdates,
|
||||||
|
&chat.Muted,
|
||||||
)
|
)
|
||||||
switch err {
|
switch err {
|
||||||
case sql.ErrNoRows:
|
case sql.ErrNoRows:
|
||||||
@ -362,7 +378,7 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) {
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db sqlitePersistence) SaveRawMessage(message *RawMessage) error {
|
func (db sqlitePersistence) SaveRawMessage(message *common.RawMessage) error {
|
||||||
var pubKeys [][]byte
|
var pubKeys [][]byte
|
||||||
for _, pk := range message.Recipients {
|
for _, pk := range message.Recipients {
|
||||||
pubKeys = append(pubKeys, crypto.CompressPubkey(pk))
|
pubKeys = append(pubKeys, crypto.CompressPubkey(pk))
|
||||||
@ -387,9 +403,11 @@ func (db sqlitePersistence) SaveRawMessage(message *RawMessage) error {
|
|||||||
message_type,
|
message_type,
|
||||||
resend_automatically,
|
resend_automatically,
|
||||||
recipients,
|
recipients,
|
||||||
|
skip_encryption,
|
||||||
|
send_push_notification,
|
||||||
payload
|
payload
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
message.ID,
|
message.ID,
|
||||||
message.LocalChatID,
|
message.LocalChatID,
|
||||||
message.LastSent,
|
message.LastSent,
|
||||||
@ -398,14 +416,16 @@ func (db sqlitePersistence) SaveRawMessage(message *RawMessage) error {
|
|||||||
message.MessageType,
|
message.MessageType,
|
||||||
message.ResendAutomatically,
|
message.ResendAutomatically,
|
||||||
encodedRecipients.Bytes(),
|
encodedRecipients.Bytes(),
|
||||||
|
message.SkipEncryption,
|
||||||
|
message.SendPushNotification,
|
||||||
message.Payload)
|
message.Payload)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db sqlitePersistence) RawMessageByID(id string) (*RawMessage, error) {
|
func (db sqlitePersistence) RawMessageByID(id string) (*common.RawMessage, error) {
|
||||||
var rawPubKeys [][]byte
|
var rawPubKeys [][]byte
|
||||||
var encodedRecipients []byte
|
var encodedRecipients []byte
|
||||||
message := &RawMessage{}
|
message := &common.RawMessage{}
|
||||||
|
|
||||||
err := db.db.QueryRow(`
|
err := db.db.QueryRow(`
|
||||||
SELECT
|
SELECT
|
||||||
@ -417,6 +437,8 @@ func (db sqlitePersistence) RawMessageByID(id string) (*RawMessage, error) {
|
|||||||
message_type,
|
message_type,
|
||||||
resend_automatically,
|
resend_automatically,
|
||||||
recipients,
|
recipients,
|
||||||
|
skip_encryption,
|
||||||
|
send_push_notification,
|
||||||
payload
|
payload
|
||||||
FROM
|
FROM
|
||||||
raw_messages
|
raw_messages
|
||||||
@ -432,6 +454,8 @@ func (db sqlitePersistence) RawMessageByID(id string) (*RawMessage, error) {
|
|||||||
&message.MessageType,
|
&message.MessageType,
|
||||||
&message.ResendAutomatically,
|
&message.ResendAutomatically,
|
||||||
&encodedRecipients,
|
&encodedRecipients,
|
||||||
|
&message.SkipEncryption,
|
||||||
|
&message.SendPushNotification,
|
||||||
&message.Payload,
|
&message.Payload,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -38,6 +38,13 @@ const (
|
|||||||
ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT ApplicationMetadataMessage_Type = 12
|
ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT ApplicationMetadataMessage_Type = 12
|
||||||
ApplicationMetadataMessage_SYNC_INSTALLATION_ACCOUNT ApplicationMetadataMessage_Type = 13
|
ApplicationMetadataMessage_SYNC_INSTALLATION_ACCOUNT ApplicationMetadataMessage_Type = 13
|
||||||
ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT ApplicationMetadataMessage_Type = 14
|
ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT ApplicationMetadataMessage_Type = 14
|
||||||
|
ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT ApplicationMetadataMessage_Type = 15
|
||||||
|
ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION ApplicationMetadataMessage_Type = 16
|
||||||
|
ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE ApplicationMetadataMessage_Type = 17
|
||||||
|
ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY ApplicationMetadataMessage_Type = 18
|
||||||
|
ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE ApplicationMetadataMessage_Type = 19
|
||||||
|
ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST ApplicationMetadataMessage_Type = 20
|
||||||
|
ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE ApplicationMetadataMessage_Type = 21
|
||||||
)
|
)
|
||||||
|
|
||||||
var ApplicationMetadataMessage_Type_name = map[int32]string{
|
var ApplicationMetadataMessage_Type_name = map[int32]string{
|
||||||
@ -56,6 +63,13 @@ var ApplicationMetadataMessage_Type_name = map[int32]string{
|
|||||||
12: "SYNC_INSTALLATION_CONTACT",
|
12: "SYNC_INSTALLATION_CONTACT",
|
||||||
13: "SYNC_INSTALLATION_ACCOUNT",
|
13: "SYNC_INSTALLATION_ACCOUNT",
|
||||||
14: "SYNC_INSTALLATION_PUBLIC_CHAT",
|
14: "SYNC_INSTALLATION_PUBLIC_CHAT",
|
||||||
|
15: "CONTACT_CODE_ADVERTISEMENT",
|
||||||
|
16: "PUSH_NOTIFICATION_REGISTRATION",
|
||||||
|
17: "PUSH_NOTIFICATION_REGISTRATION_RESPONSE",
|
||||||
|
18: "PUSH_NOTIFICATION_QUERY",
|
||||||
|
19: "PUSH_NOTIFICATION_QUERY_RESPONSE",
|
||||||
|
20: "PUSH_NOTIFICATION_REQUEST",
|
||||||
|
21: "PUSH_NOTIFICATION_RESPONSE",
|
||||||
}
|
}
|
||||||
|
|
||||||
var ApplicationMetadataMessage_Type_value = map[string]int32{
|
var ApplicationMetadataMessage_Type_value = map[string]int32{
|
||||||
@ -74,6 +88,13 @@ var ApplicationMetadataMessage_Type_value = map[string]int32{
|
|||||||
"SYNC_INSTALLATION_CONTACT": 12,
|
"SYNC_INSTALLATION_CONTACT": 12,
|
||||||
"SYNC_INSTALLATION_ACCOUNT": 13,
|
"SYNC_INSTALLATION_ACCOUNT": 13,
|
||||||
"SYNC_INSTALLATION_PUBLIC_CHAT": 14,
|
"SYNC_INSTALLATION_PUBLIC_CHAT": 14,
|
||||||
|
"CONTACT_CODE_ADVERTISEMENT": 15,
|
||||||
|
"PUSH_NOTIFICATION_REGISTRATION": 16,
|
||||||
|
"PUSH_NOTIFICATION_REGISTRATION_RESPONSE": 17,
|
||||||
|
"PUSH_NOTIFICATION_QUERY": 18,
|
||||||
|
"PUSH_NOTIFICATION_QUERY_RESPONSE": 19,
|
||||||
|
"PUSH_NOTIFICATION_REQUEST": 20,
|
||||||
|
"PUSH_NOTIFICATION_RESPONSE": 21,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x ApplicationMetadataMessage_Type) String() string {
|
func (x ApplicationMetadataMessage_Type) String() string {
|
||||||
@ -150,29 +171,34 @@ func init() {
|
|||||||
func init() { proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) }
|
func init() { proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) }
|
||||||
|
|
||||||
var fileDescriptor_ad09a6406fcf24c7 = []byte{
|
var fileDescriptor_ad09a6406fcf24c7 = []byte{
|
||||||
// 377 bytes of a gzipped FileDescriptorProto
|
// 463 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0xcf, 0x8e, 0xd3, 0x30,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x51, 0x4f, 0x13, 0x41,
|
||||||
0x10, 0xc6, 0xc9, 0x36, 0x6c, 0x77, 0x67, 0x4b, 0x65, 0x06, 0x10, 0xe1, 0xcf, 0x6a, 0x97, 0x22,
|
0x10, 0xc7, 0x2d, 0x94, 0x16, 0x86, 0x5a, 0x97, 0x01, 0x42, 0x05, 0x81, 0x5a, 0x8d, 0xa2, 0x26,
|
||||||
0xc1, 0x02, 0x52, 0x0e, 0x70, 0xe6, 0xe0, 0x75, 0x0c, 0x1b, 0x91, 0x38, 0xc1, 0x76, 0x84, 0x38,
|
0x7d, 0xd0, 0x67, 0x1f, 0x96, 0xbd, 0x81, 0x6e, 0xec, 0xed, 0x1d, 0xbb, 0x7b, 0x1a, 0x9e, 0x36,
|
||||||
0x59, 0x2e, 0x0d, 0x55, 0xa5, 0xb6, 0x89, 0xda, 0xf4, 0xd0, 0x07, 0xe3, 0x29, 0x78, 0x29, 0x94,
|
0x87, 0x9c, 0xa4, 0x09, 0xd0, 0x0b, 0x3d, 0x1e, 0xfa, 0x8d, 0xfc, 0x14, 0x7e, 0x36, 0x73, 0xd7,
|
||||||
0xd0, 0xd2, 0x96, 0x82, 0x7a, 0xb2, 0xe6, 0xfb, 0x7e, 0x9f, 0x47, 0x33, 0x03, 0x3d, 0x5b, 0x96,
|
0xd6, 0x82, 0x2d, 0xf0, 0x74, 0xd9, 0xff, 0xff, 0x37, 0x33, 0x99, 0xff, 0x1c, 0xb4, 0xe2, 0x34,
|
||||||
0xe3, 0xd1, 0x37, 0x5b, 0x8d, 0x8a, 0xa9, 0x99, 0xe4, 0x95, 0x1d, 0xd8, 0xca, 0x9a, 0x49, 0x3e,
|
0xbd, 0xec, 0xfd, 0x8c, 0xb3, 0x5e, 0xff, 0xda, 0x5d, 0x25, 0x59, 0x7c, 0x1e, 0x67, 0xb1, 0xbb,
|
||||||
0x9f, 0xdb, 0x61, 0xee, 0x97, 0xb3, 0xa2, 0x2a, 0xf0, 0xa4, 0x79, 0xfa, 0x8b, 0xef, 0xbd, 0x9f,
|
0x4a, 0x06, 0x83, 0xf8, 0x22, 0x69, 0xa7, 0x37, 0xfd, 0xac, 0x8f, 0xcb, 0xc5, 0xe7, 0xec, 0xf6,
|
||||||
0x2e, 0x3c, 0xa6, 0x9b, 0x40, 0xbc, 0xe2, 0xe3, 0xdf, 0x38, 0x3e, 0x85, 0xd3, 0xf9, 0x68, 0x38,
|
0x57, 0xeb, 0x4f, 0x05, 0xb6, 0xf9, 0xb4, 0xc0, 0x1f, 0xf3, 0xfe, 0x08, 0xc7, 0x57, 0xb0, 0x32,
|
||||||
0xb5, 0xd5, 0x62, 0x96, 0x7b, 0xce, 0xa5, 0x73, 0xd5, 0x91, 0x1b, 0x01, 0x3d, 0x68, 0x97, 0x76,
|
0xe8, 0x5d, 0x5c, 0xc7, 0xd9, 0xed, 0x4d, 0xd2, 0x28, 0x35, 0x4b, 0x07, 0x35, 0x3d, 0x15, 0xb0,
|
||||||
0x39, 0x2e, 0xec, 0xc0, 0x3b, 0x6a, 0xbc, 0x75, 0x89, 0xef, 0xc1, 0xad, 0x96, 0x65, 0xee, 0xb5,
|
0x01, 0xd5, 0x34, 0x1e, 0x5e, 0xf6, 0xe3, 0xf3, 0xc6, 0x42, 0xe1, 0x4d, 0x9e, 0xf8, 0x15, 0xca,
|
||||||
0x2e, 0x9d, 0xab, 0xee, 0xdb, 0x57, 0xfe, 0xba, 0x9f, 0xff, 0xff, 0x5e, 0xbe, 0x5e, 0x96, 0xb9,
|
0xd9, 0x30, 0x4d, 0x1a, 0x8b, 0xcd, 0xd2, 0x41, 0xfd, 0xf3, 0x87, 0xf6, 0x64, 0x5e, 0xfb, 0xe1,
|
||||||
0x6c, 0x62, 0xbd, 0x1f, 0x2d, 0x70, 0xeb, 0x12, 0xcf, 0xa0, 0x9d, 0x89, 0x4f, 0x22, 0xf9, 0x22,
|
0x59, 0x6d, 0x3b, 0x4c, 0x13, 0x5d, 0x94, 0xb5, 0x7e, 0x2f, 0x41, 0x39, 0x7f, 0xe2, 0x2a, 0x54,
|
||||||
0xc8, 0x2d, 0x24, 0xd0, 0x61, 0x37, 0x54, 0x9b, 0x98, 0x2b, 0x45, 0x3f, 0x72, 0xe2, 0x20, 0x42,
|
0x23, 0xf5, 0x4d, 0x05, 0x3f, 0x14, 0x7b, 0x86, 0x0c, 0x6a, 0xa2, 0xc3, 0xad, 0xf3, 0xc9, 0x18,
|
||||||
0x97, 0x25, 0x42, 0x53, 0xa6, 0x4d, 0x96, 0x06, 0x54, 0x73, 0x72, 0x84, 0xe7, 0xf0, 0x28, 0xe6,
|
0x7e, 0x4c, 0xac, 0x84, 0x08, 0x75, 0x11, 0x28, 0xcb, 0x85, 0x75, 0x51, 0xe8, 0x71, 0x4b, 0x6c,
|
||||||
0xf1, 0x35, 0x97, 0xea, 0x26, 0x4c, 0x57, 0xf2, 0x9f, 0x48, 0x0b, 0x1f, 0xc0, 0xdd, 0x94, 0x86,
|
0x01, 0x77, 0xe1, 0xa5, 0x4f, 0xfe, 0x21, 0x69, 0xd3, 0x91, 0xe1, 0x58, 0xfe, 0x57, 0xb2, 0x88,
|
||||||
0xd2, 0x84, 0x42, 0x69, 0x1a, 0x45, 0x54, 0x87, 0x89, 0x20, 0x6e, 0x2d, 0xab, 0xaf, 0x82, 0xed,
|
0x9b, 0xb0, 0x16, 0x72, 0xa9, 0x9d, 0x54, 0xc6, 0xf2, 0x6e, 0x97, 0x5b, 0x19, 0x28, 0x56, 0xce,
|
||||||
0xca, 0xb7, 0xf1, 0x39, 0x5c, 0x48, 0xfe, 0x39, 0xe3, 0x4a, 0x1b, 0x1a, 0x04, 0x92, 0x2b, 0x65,
|
0x65, 0x73, 0xaa, 0xc4, 0x7d, 0x79, 0x09, 0xdf, 0xc0, 0xbe, 0xa6, 0x93, 0x88, 0x8c, 0x75, 0xdc,
|
||||||
0x3e, 0x24, 0xd2, 0x68, 0x49, 0x85, 0xa2, 0xac, 0x81, 0x8e, 0xf1, 0x35, 0xbc, 0xa0, 0x8c, 0xf1,
|
0xf3, 0x34, 0x19, 0xe3, 0x8e, 0x02, 0xed, 0xac, 0xe6, 0xca, 0x70, 0x51, 0x40, 0x15, 0xfc, 0x08,
|
||||||
0x54, 0x9b, 0x43, 0x6c, 0x1b, 0xdf, 0xc0, 0xcb, 0x80, 0xb3, 0x28, 0x14, 0xfc, 0x20, 0x7c, 0x82,
|
0xef, 0xb8, 0x10, 0x14, 0x5a, 0xf7, 0x14, 0x5b, 0xc5, 0x4f, 0xf0, 0xde, 0x23, 0xd1, 0x95, 0x8a,
|
||||||
0x0f, 0xe1, 0xde, 0x1a, 0xda, 0x36, 0x4e, 0xf1, 0x3e, 0x10, 0xc5, 0x45, 0xb0, 0xa3, 0x02, 0x5e,
|
0x9e, 0x84, 0x97, 0x71, 0x0b, 0xd6, 0x27, 0xd0, 0x5d, 0x63, 0x05, 0x37, 0x80, 0x19, 0x52, 0xde,
|
||||||
0xc0, 0x93, 0xbf, 0xff, 0xde, 0x06, 0xce, 0xea, 0xd5, 0xec, 0x0d, 0x69, 0x56, 0x0b, 0x24, 0x9d,
|
0x3d, 0x15, 0x70, 0x1f, 0x76, 0xfe, 0xef, 0x7d, 0x17, 0x58, 0xcd, 0xa3, 0x99, 0x59, 0xd2, 0x8d,
|
||||||
0x7f, 0xdb, 0x94, 0xb1, 0x24, 0x13, 0x9a, 0xdc, 0xc1, 0x67, 0x70, 0xbe, 0x6f, 0xa7, 0xd9, 0x75,
|
0x03, 0x64, 0xb5, 0xf9, 0x36, 0x17, 0x22, 0x88, 0x94, 0x65, 0xcf, 0xf1, 0x35, 0xec, 0xce, 0xda,
|
||||||
0x14, 0x32, 0x53, 0xdf, 0x85, 0x74, 0xfb, 0xc7, 0xcd, 0x9d, 0xdf, 0xfd, 0x0a, 0x00, 0x00, 0xff,
|
0x61, 0x74, 0xd8, 0x95, 0xc2, 0xe5, 0x77, 0x61, 0x75, 0xdc, 0x83, 0xed, 0xc9, 0x3d, 0x44, 0xe0,
|
||||||
0xff, 0xb7, 0x6c, 0xd6, 0xba, 0x84, 0x02, 0x00, 0x00,
|
0x91, 0xe3, 0xde, 0x77, 0xd2, 0x56, 0x1a, 0xf2, 0x49, 0x59, 0xf6, 0x02, 0x5b, 0xb0, 0x17, 0x46,
|
||||||
|
0xa6, 0xe3, 0x54, 0x60, 0xe5, 0x91, 0x14, 0xa3, 0x16, 0x9a, 0x8e, 0xa5, 0xb1, 0x7a, 0x14, 0x39,
|
||||||
|
0xcb, 0x13, 0x7a, 0x9c, 0x71, 0x9a, 0x4c, 0x18, 0x28, 0x43, 0x6c, 0x0d, 0x77, 0x60, 0x6b, 0x16,
|
||||||
|
0x3e, 0x89, 0x48, 0x9f, 0x32, 0xc4, 0xb7, 0xd0, 0x7c, 0xc0, 0x9c, 0xb6, 0x58, 0xcf, 0xb7, 0x9e,
|
||||||
|
0x37, 0xaf, 0xc8, 0x8f, 0x6d, 0xe4, 0x2b, 0xcd, 0xb3, 0xc7, 0xe5, 0x9b, 0x67, 0x95, 0xe2, 0xd7,
|
||||||
|
0xfe, 0xf2, 0x37, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xcd, 0xf5, 0xf1, 0x77, 0x03, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
@ -27,5 +27,12 @@ message ApplicationMetadataMessage {
|
|||||||
SYNC_INSTALLATION_CONTACT = 12;
|
SYNC_INSTALLATION_CONTACT = 12;
|
||||||
SYNC_INSTALLATION_ACCOUNT = 13;
|
SYNC_INSTALLATION_ACCOUNT = 13;
|
||||||
SYNC_INSTALLATION_PUBLIC_CHAT = 14;
|
SYNC_INSTALLATION_PUBLIC_CHAT = 14;
|
||||||
|
CONTACT_CODE_ADVERTISEMENT = 15;
|
||||||
|
PUSH_NOTIFICATION_REGISTRATION = 16;
|
||||||
|
PUSH_NOTIFICATION_REGISTRATION_RESPONSE = 17;
|
||||||
|
PUSH_NOTIFICATION_QUERY = 18;
|
||||||
|
PUSH_NOTIFICATION_QUERY_RESPONSE = 19;
|
||||||
|
PUSH_NOTIFICATION_REQUEST = 20;
|
||||||
|
PUSH_NOTIFICATION_RESPONSE = 21;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
813
protocol/protobuf/push_notifications.pb.go
Normal file
813
protocol/protobuf/push_notifications.pb.go
Normal file
@ -0,0 +1,813 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: push_notifications.proto
|
||||||
|
|
||||||
|
package protobuf
|
||||||
|
|
||||||
|
import (
|
||||||
|
fmt "fmt"
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
|
type PushNotificationRegistration_TokenType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PushNotificationRegistration_UNKNOWN_TOKEN_TYPE PushNotificationRegistration_TokenType = 0
|
||||||
|
PushNotificationRegistration_APN_TOKEN PushNotificationRegistration_TokenType = 1
|
||||||
|
PushNotificationRegistration_FIREBASE_TOKEN PushNotificationRegistration_TokenType = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var PushNotificationRegistration_TokenType_name = map[int32]string{
|
||||||
|
0: "UNKNOWN_TOKEN_TYPE",
|
||||||
|
1: "APN_TOKEN",
|
||||||
|
2: "FIREBASE_TOKEN",
|
||||||
|
}
|
||||||
|
|
||||||
|
var PushNotificationRegistration_TokenType_value = map[string]int32{
|
||||||
|
"UNKNOWN_TOKEN_TYPE": 0,
|
||||||
|
"APN_TOKEN": 1,
|
||||||
|
"FIREBASE_TOKEN": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x PushNotificationRegistration_TokenType) String() string {
|
||||||
|
return proto.EnumName(PushNotificationRegistration_TokenType_name, int32(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PushNotificationRegistration_TokenType) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationRegistrationResponse_ErrorType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PushNotificationRegistrationResponse_UNKNOWN_ERROR_TYPE PushNotificationRegistrationResponse_ErrorType = 0
|
||||||
|
PushNotificationRegistrationResponse_MALFORMED_MESSAGE PushNotificationRegistrationResponse_ErrorType = 1
|
||||||
|
PushNotificationRegistrationResponse_VERSION_MISMATCH PushNotificationRegistrationResponse_ErrorType = 2
|
||||||
|
PushNotificationRegistrationResponse_UNSUPPORTED_TOKEN_TYPE PushNotificationRegistrationResponse_ErrorType = 3
|
||||||
|
PushNotificationRegistrationResponse_INTERNAL_ERROR PushNotificationRegistrationResponse_ErrorType = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
var PushNotificationRegistrationResponse_ErrorType_name = map[int32]string{
|
||||||
|
0: "UNKNOWN_ERROR_TYPE",
|
||||||
|
1: "MALFORMED_MESSAGE",
|
||||||
|
2: "VERSION_MISMATCH",
|
||||||
|
3: "UNSUPPORTED_TOKEN_TYPE",
|
||||||
|
4: "INTERNAL_ERROR",
|
||||||
|
}
|
||||||
|
|
||||||
|
var PushNotificationRegistrationResponse_ErrorType_value = map[string]int32{
|
||||||
|
"UNKNOWN_ERROR_TYPE": 0,
|
||||||
|
"MALFORMED_MESSAGE": 1,
|
||||||
|
"VERSION_MISMATCH": 2,
|
||||||
|
"UNSUPPORTED_TOKEN_TYPE": 3,
|
||||||
|
"INTERNAL_ERROR": 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x PushNotificationRegistrationResponse_ErrorType) String() string {
|
||||||
|
return proto.EnumName(PushNotificationRegistrationResponse_ErrorType_name, int32(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PushNotificationRegistrationResponse_ErrorType) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationReport_ErrorType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PushNotificationReport_UNKNOWN_ERROR_TYPE PushNotificationReport_ErrorType = 0
|
||||||
|
PushNotificationReport_WRONG_TOKEN PushNotificationReport_ErrorType = 1
|
||||||
|
PushNotificationReport_INTERNAL_ERROR PushNotificationReport_ErrorType = 2
|
||||||
|
PushNotificationReport_NOT_REGISTERED PushNotificationReport_ErrorType = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
var PushNotificationReport_ErrorType_name = map[int32]string{
|
||||||
|
0: "UNKNOWN_ERROR_TYPE",
|
||||||
|
1: "WRONG_TOKEN",
|
||||||
|
2: "INTERNAL_ERROR",
|
||||||
|
3: "NOT_REGISTERED",
|
||||||
|
}
|
||||||
|
|
||||||
|
var PushNotificationReport_ErrorType_value = map[string]int32{
|
||||||
|
"UNKNOWN_ERROR_TYPE": 0,
|
||||||
|
"WRONG_TOKEN": 1,
|
||||||
|
"INTERNAL_ERROR": 2,
|
||||||
|
"NOT_REGISTERED": 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x PushNotificationReport_ErrorType) String() string {
|
||||||
|
return proto.EnumName(PushNotificationReport_ErrorType_name, int32(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PushNotificationReport_ErrorType) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{8, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationRegistration struct {
|
||||||
|
TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"`
|
||||||
|
DeviceToken string `protobuf:"bytes,2,opt,name=device_token,json=deviceToken,proto3" json:"device_token,omitempty"`
|
||||||
|
InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||||
|
AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||||
|
Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||||
|
Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"`
|
||||||
|
AllowedKeyList [][]byte `protobuf:"bytes,7,rep,name=allowed_key_list,json=allowedKeyList,proto3" json:"allowed_key_list,omitempty"`
|
||||||
|
BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"`
|
||||||
|
Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"`
|
||||||
|
Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,omitempty"`
|
||||||
|
AllowFromContactsOnly bool `protobuf:"varint,11,opt,name=allow_from_contacts_only,json=allowFromContactsOnly,proto3" json:"allow_from_contacts_only,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) Reset() { *m = PushNotificationRegistration{} }
|
||||||
|
func (m *PushNotificationRegistration) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*PushNotificationRegistration) ProtoMessage() {}
|
||||||
|
func (*PushNotificationRegistration) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_PushNotificationRegistration.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRegistration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_PushNotificationRegistration.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRegistration) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_PushNotificationRegistration.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRegistration) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_PushNotificationRegistration.Size(m)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRegistration) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_PushNotificationRegistration.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_PushNotificationRegistration proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetTokenType() PushNotificationRegistration_TokenType {
|
||||||
|
if m != nil {
|
||||||
|
return m.TokenType
|
||||||
|
}
|
||||||
|
return PushNotificationRegistration_UNKNOWN_TOKEN_TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetDeviceToken() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.DeviceToken
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetInstallationId() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.InstallationId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetAccessToken() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.AccessToken
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetEnabled() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.Enabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetVersion() uint64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Version
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetAllowedKeyList() [][]byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.AllowedKeyList
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetBlockedChatList() [][]byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.BlockedChatList
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetUnregister() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.Unregister
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetGrant() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Grant
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistration) GetAllowFromContactsOnly() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.AllowFromContactsOnly
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationRegistrationResponse struct {
|
||||||
|
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
|
||||||
|
Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"`
|
||||||
|
RequestId []byte `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistrationResponse) Reset() { *m = PushNotificationRegistrationResponse{} }
|
||||||
|
func (m *PushNotificationRegistrationResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*PushNotificationRegistrationResponse) ProtoMessage() {}
|
||||||
|
func (*PushNotificationRegistrationResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistrationResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_PushNotificationRegistrationResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRegistrationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_PushNotificationRegistrationResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRegistrationResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_PushNotificationRegistrationResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRegistrationResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_PushNotificationRegistrationResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRegistrationResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_PushNotificationRegistrationResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_PushNotificationRegistrationResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistrationResponse) GetSuccess() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.Success
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistrationResponse) GetError() PushNotificationRegistrationResponse_ErrorType {
|
||||||
|
if m != nil {
|
||||||
|
return m.Error
|
||||||
|
}
|
||||||
|
return PushNotificationRegistrationResponse_UNKNOWN_ERROR_TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRegistrationResponse) GetRequestId() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.RequestId
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactCodeAdvertisement struct {
|
||||||
|
PushNotificationInfo []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=push_notification_info,json=pushNotificationInfo,proto3" json:"push_notification_info,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ContactCodeAdvertisement) Reset() { *m = ContactCodeAdvertisement{} }
|
||||||
|
func (m *ContactCodeAdvertisement) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ContactCodeAdvertisement) ProtoMessage() {}
|
||||||
|
func (*ContactCodeAdvertisement) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ContactCodeAdvertisement) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ContactCodeAdvertisement.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ContactCodeAdvertisement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ContactCodeAdvertisement.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *ContactCodeAdvertisement) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ContactCodeAdvertisement.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *ContactCodeAdvertisement) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ContactCodeAdvertisement.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ContactCodeAdvertisement) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ContactCodeAdvertisement.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ContactCodeAdvertisement proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ContactCodeAdvertisement) GetPushNotificationInfo() []*PushNotificationQueryInfo {
|
||||||
|
if m != nil {
|
||||||
|
return m.PushNotificationInfo
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationQuery struct {
|
||||||
|
PublicKeys [][]byte `protobuf:"bytes,1,rep,name=public_keys,json=publicKeys,proto3" json:"public_keys,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQuery) Reset() { *m = PushNotificationQuery{} }
|
||||||
|
func (m *PushNotificationQuery) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*PushNotificationQuery) ProtoMessage() {}
|
||||||
|
func (*PushNotificationQuery) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQuery) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_PushNotificationQuery.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQuery) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_PushNotificationQuery.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQuery) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_PushNotificationQuery.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQuery) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_PushNotificationQuery.Size(m)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQuery) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_PushNotificationQuery.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_PushNotificationQuery proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PushNotificationQuery) GetPublicKeys() [][]byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.PublicKeys
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationQueryInfo struct {
|
||||||
|
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||||
|
InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||||
|
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
||||||
|
AllowedKeyList [][]byte `protobuf:"bytes,4,rep,name=allowed_key_list,json=allowedKeyList,proto3" json:"allowed_key_list,omitempty"`
|
||||||
|
Grant []byte `protobuf:"bytes,5,opt,name=grant,proto3" json:"grant,omitempty"`
|
||||||
|
Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"`
|
||||||
|
ServerPublicKey []byte `protobuf:"bytes,7,opt,name=server_public_key,json=serverPublicKey,proto3" json:"server_public_key,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryInfo) Reset() { *m = PushNotificationQueryInfo{} }
|
||||||
|
func (m *PushNotificationQueryInfo) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*PushNotificationQueryInfo) ProtoMessage() {}
|
||||||
|
func (*PushNotificationQueryInfo) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryInfo) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_PushNotificationQueryInfo.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQueryInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_PushNotificationQueryInfo.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQueryInfo) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_PushNotificationQueryInfo.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQueryInfo) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_PushNotificationQueryInfo.Size(m)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQueryInfo) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_PushNotificationQueryInfo.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_PushNotificationQueryInfo proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryInfo) GetAccessToken() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.AccessToken
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryInfo) GetInstallationId() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.InstallationId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryInfo) GetPublicKey() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.PublicKey
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryInfo) GetAllowedKeyList() [][]byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.AllowedKeyList
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryInfo) GetGrant() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Grant
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryInfo) GetVersion() uint64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Version
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryInfo) GetServerPublicKey() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.ServerPublicKey
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationQueryResponse struct {
|
||||||
|
Info []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=info,proto3" json:"info,omitempty"`
|
||||||
|
MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
|
||||||
|
Success bool `protobuf:"varint,3,opt,name=success,proto3" json:"success,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryResponse) Reset() { *m = PushNotificationQueryResponse{} }
|
||||||
|
func (m *PushNotificationQueryResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*PushNotificationQueryResponse) ProtoMessage() {}
|
||||||
|
func (*PushNotificationQueryResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_PushNotificationQueryResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQueryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_PushNotificationQueryResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQueryResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_PushNotificationQueryResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQueryResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_PushNotificationQueryResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationQueryResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_PushNotificationQueryResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_PushNotificationQueryResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryResponse) GetInfo() []*PushNotificationQueryInfo {
|
||||||
|
if m != nil {
|
||||||
|
return m.Info
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryResponse) GetMessageId() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.MessageId
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationQueryResponse) GetSuccess() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.Success
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotification struct {
|
||||||
|
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||||
|
ChatId string `protobuf:"bytes,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
|
||||||
|
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
||||||
|
InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||||
|
Message []byte `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotification) Reset() { *m = PushNotification{} }
|
||||||
|
func (m *PushNotification) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*PushNotification) ProtoMessage() {}
|
||||||
|
func (*PushNotification) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotification) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_PushNotification.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *PushNotification) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_PushNotification.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *PushNotification) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_PushNotification.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *PushNotification) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_PushNotification.Size(m)
|
||||||
|
}
|
||||||
|
func (m *PushNotification) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_PushNotification.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_PushNotification proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PushNotification) GetAccessToken() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.AccessToken
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotification) GetChatId() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ChatId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotification) GetPublicKey() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.PublicKey
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotification) GetInstallationId() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.InstallationId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotification) GetMessage() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Message
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationRequest struct {
|
||||||
|
Requests []*PushNotification `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty"`
|
||||||
|
MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRequest) Reset() { *m = PushNotificationRequest{} }
|
||||||
|
func (m *PushNotificationRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*PushNotificationRequest) ProtoMessage() {}
|
||||||
|
func (*PushNotificationRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{7}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRequest) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_PushNotificationRequest.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_PushNotificationRequest.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRequest) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_PushNotificationRequest.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRequest) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_PushNotificationRequest.Size(m)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationRequest) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_PushNotificationRequest.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_PushNotificationRequest proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PushNotificationRequest) GetRequests() []*PushNotification {
|
||||||
|
if m != nil {
|
||||||
|
return m.Requests
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationRequest) GetMessageId() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.MessageId
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationReport struct {
|
||||||
|
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
|
||||||
|
Error PushNotificationReport_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationReport_ErrorType" json:"error,omitempty"`
|
||||||
|
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
||||||
|
InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationReport) Reset() { *m = PushNotificationReport{} }
|
||||||
|
func (m *PushNotificationReport) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*PushNotificationReport) ProtoMessage() {}
|
||||||
|
func (*PushNotificationReport) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{8}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationReport) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_PushNotificationReport.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationReport) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_PushNotificationReport.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationReport) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_PushNotificationReport.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationReport) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_PushNotificationReport.Size(m)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationReport) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_PushNotificationReport.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_PushNotificationReport proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PushNotificationReport) GetSuccess() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.Success
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationReport) GetError() PushNotificationReport_ErrorType {
|
||||||
|
if m != nil {
|
||||||
|
return m.Error
|
||||||
|
}
|
||||||
|
return PushNotificationReport_UNKNOWN_ERROR_TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationReport) GetPublicKey() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.PublicKey
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationReport) GetInstallationId() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.InstallationId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushNotificationResponse struct {
|
||||||
|
MessageId []byte `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
|
||||||
|
Reports []*PushNotificationReport `protobuf:"bytes,2,rep,name=reports,proto3" json:"reports,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationResponse) Reset() { *m = PushNotificationResponse{} }
|
||||||
|
func (m *PushNotificationResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*PushNotificationResponse) ProtoMessage() {}
|
||||||
|
func (*PushNotificationResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_200acd86044eaa5d, []int{9}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_PushNotificationResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_PushNotificationResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_PushNotificationResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_PushNotificationResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *PushNotificationResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_PushNotificationResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_PushNotificationResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PushNotificationResponse) GetMessageId() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.MessageId
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PushNotificationResponse) GetReports() []*PushNotificationReport {
|
||||||
|
if m != nil {
|
||||||
|
return m.Reports
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterEnum("protobuf.PushNotificationRegistration_TokenType", PushNotificationRegistration_TokenType_name, PushNotificationRegistration_TokenType_value)
|
||||||
|
proto.RegisterEnum("protobuf.PushNotificationRegistrationResponse_ErrorType", PushNotificationRegistrationResponse_ErrorType_name, PushNotificationRegistrationResponse_ErrorType_value)
|
||||||
|
proto.RegisterEnum("protobuf.PushNotificationReport_ErrorType", PushNotificationReport_ErrorType_name, PushNotificationReport_ErrorType_value)
|
||||||
|
proto.RegisterType((*PushNotificationRegistration)(nil), "protobuf.PushNotificationRegistration")
|
||||||
|
proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse")
|
||||||
|
proto.RegisterType((*ContactCodeAdvertisement)(nil), "protobuf.ContactCodeAdvertisement")
|
||||||
|
proto.RegisterType((*PushNotificationQuery)(nil), "protobuf.PushNotificationQuery")
|
||||||
|
proto.RegisterType((*PushNotificationQueryInfo)(nil), "protobuf.PushNotificationQueryInfo")
|
||||||
|
proto.RegisterType((*PushNotificationQueryResponse)(nil), "protobuf.PushNotificationQueryResponse")
|
||||||
|
proto.RegisterType((*PushNotification)(nil), "protobuf.PushNotification")
|
||||||
|
proto.RegisterType((*PushNotificationRequest)(nil), "protobuf.PushNotificationRequest")
|
||||||
|
proto.RegisterType((*PushNotificationReport)(nil), "protobuf.PushNotificationReport")
|
||||||
|
proto.RegisterType((*PushNotificationResponse)(nil), "protobuf.PushNotificationResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) }
|
||||||
|
|
||||||
|
var fileDescriptor_200acd86044eaa5d = []byte{
|
||||||
|
// 878 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x41, 0x6f, 0xeb, 0x44,
|
||||||
|
0x17, 0xfd, 0x9c, 0xa4, 0x4d, 0x72, 0x93, 0x2f, 0x4d, 0x47, 0x6d, 0x9f, 0x79, 0xa2, 0x10, 0x0c,
|
||||||
|
0x12, 0x51, 0x17, 0x11, 0x2a, 0x12, 0xef, 0x89, 0x15, 0xa1, 0x75, 0x8b, 0xd5, 0xc6, 0x0e, 0x13,
|
||||||
|
0x97, 0xa7, 0x27, 0x21, 0x59, 0x8e, 0x3d, 0x69, 0xad, 0xba, 0x1e, 0x33, 0x33, 0x2e, 0xca, 0x8e,
|
||||||
|
0x1f, 0xc0, 0x86, 0x2d, 0x1b, 0xfe, 0x02, 0xe2, 0x17, 0x22, 0x8f, 0xed, 0xe0, 0x36, 0x6e, 0x5a,
|
||||||
|
0x24, 0x56, 0xf6, 0x9c, 0xb9, 0xf7, 0xce, 0xcc, 0x39, 0xf7, 0x5c, 0x50, 0xe3, 0x84, 0xdf, 0x38,
|
||||||
|
0x11, 0x15, 0xc1, 0x22, 0xf0, 0x5c, 0x11, 0xd0, 0x88, 0x8f, 0x62, 0x46, 0x05, 0x45, 0x2d, 0xf9,
|
||||||
|
0x99, 0x27, 0x0b, 0xed, 0x8f, 0x06, 0x7c, 0x38, 0x4d, 0xf8, 0x8d, 0x59, 0x8a, 0xc2, 0xe4, 0x3a,
|
||||||
|
0xe0, 0x82, 0xc9, 0x7f, 0x64, 0x01, 0x08, 0x7a, 0x4b, 0x22, 0x47, 0x2c, 0x63, 0xa2, 0x2a, 0x03,
|
||||||
|
0x65, 0xd8, 0x3b, 0xfe, 0x62, 0x54, 0xe4, 0x8f, 0x36, 0xe5, 0x8e, 0xec, 0x34, 0xd1, 0x5e, 0xc6,
|
||||||
|
0x04, 0xb7, 0x45, 0xf1, 0x8b, 0x3e, 0x81, 0xae, 0x4f, 0xee, 0x03, 0x8f, 0x38, 0x12, 0x53, 0x6b,
|
||||||
|
0x03, 0x65, 0xd8, 0xc6, 0x9d, 0x0c, 0x93, 0x19, 0xe8, 0x73, 0xd8, 0x09, 0x22, 0x2e, 0xdc, 0x30,
|
||||||
|
0x94, 0x75, 0x9c, 0xc0, 0x57, 0xeb, 0x32, 0xaa, 0x57, 0x86, 0x0d, 0x3f, 0xad, 0xe5, 0x7a, 0x1e,
|
||||||
|
0xe1, 0x3c, 0xaf, 0xd5, 0xc8, 0x6a, 0x65, 0x58, 0x56, 0x4b, 0x85, 0x26, 0x89, 0xdc, 0x79, 0x48,
|
||||||
|
0x7c, 0x75, 0x6b, 0xa0, 0x0c, 0x5b, 0xb8, 0x58, 0xa6, 0x3b, 0xf7, 0x84, 0xf1, 0x80, 0x46, 0xea,
|
||||||
|
0xf6, 0x40, 0x19, 0x36, 0x70, 0xb1, 0x44, 0x43, 0xe8, 0xbb, 0x61, 0x48, 0x7f, 0x26, 0xbe, 0x73,
|
||||||
|
0x4b, 0x96, 0x4e, 0x18, 0x70, 0xa1, 0x36, 0x07, 0xf5, 0x61, 0x17, 0xf7, 0x72, 0xfc, 0x82, 0x2c,
|
||||||
|
0x2f, 0x03, 0x2e, 0xd0, 0x11, 0xec, 0xce, 0x43, 0xea, 0xdd, 0x12, 0xdf, 0xf1, 0x6e, 0x5c, 0x91,
|
||||||
|
0x85, 0xb6, 0x64, 0xe8, 0x4e, 0xbe, 0x71, 0x72, 0xe3, 0x0a, 0x19, 0xfb, 0x11, 0x40, 0x12, 0x31,
|
||||||
|
0xc9, 0x0f, 0x61, 0x6a, 0x5b, 0x5e, 0xa6, 0x84, 0xa0, 0x3d, 0xd8, 0xba, 0x66, 0x6e, 0x24, 0x54,
|
||||||
|
0x18, 0x28, 0xc3, 0x2e, 0xce, 0x16, 0xe8, 0x0d, 0xa8, 0xf2, 0x4c, 0x67, 0xc1, 0xe8, 0x9d, 0xe3,
|
||||||
|
0xd1, 0x48, 0xb8, 0x9e, 0xe0, 0x0e, 0x8d, 0xc2, 0xa5, 0xda, 0x91, 0x35, 0xf6, 0xe5, 0xfe, 0x19,
|
||||||
|
0xa3, 0x77, 0x27, 0xf9, 0xae, 0x15, 0x85, 0x4b, 0xed, 0x0c, 0xda, 0x2b, 0xfe, 0xd1, 0x01, 0xa0,
|
||||||
|
0x2b, 0xf3, 0xc2, 0xb4, 0xde, 0x99, 0x8e, 0x6d, 0x5d, 0xe8, 0xa6, 0x63, 0xbf, 0x9f, 0xea, 0xfd,
|
||||||
|
0xff, 0xa1, 0xff, 0x43, 0x7b, 0x3c, 0xcd, 0xb1, 0xbe, 0x82, 0x10, 0xf4, 0xce, 0x0c, 0xac, 0x7f,
|
||||||
|
0x3b, 0x9e, 0xe9, 0x39, 0x56, 0xd3, 0xfe, 0xaa, 0xc1, 0x67, 0x9b, 0x54, 0xc6, 0x84, 0xc7, 0x34,
|
||||||
|
0xe2, 0x24, 0xe5, 0x93, 0x27, 0x92, 0x79, 0xd9, 0x26, 0x2d, 0x5c, 0x2c, 0x91, 0x09, 0x5b, 0x84,
|
||||||
|
0x31, 0xca, 0xa4, 0xd6, 0xbd, 0xe3, 0xb7, 0x2f, 0x6b, 0x9f, 0xa2, 0xf0, 0x48, 0x4f, 0x73, 0x65,
|
||||||
|
0x1b, 0x65, 0x65, 0xd0, 0x21, 0x00, 0x23, 0x3f, 0x25, 0x84, 0x8b, 0xa2, 0x35, 0xba, 0xb8, 0x9d,
|
||||||
|
0x23, 0x86, 0xaf, 0xfd, 0xa2, 0x40, 0x7b, 0x95, 0x53, 0x7e, 0xba, 0x8e, 0xb1, 0x85, 0x8b, 0xa7,
|
||||||
|
0xef, 0xc3, 0xee, 0x64, 0x7c, 0x79, 0x66, 0xe1, 0x89, 0x7e, 0xea, 0x4c, 0xf4, 0xd9, 0x6c, 0x7c,
|
||||||
|
0xae, 0xf7, 0x15, 0xb4, 0x07, 0xfd, 0x1f, 0x74, 0x3c, 0x33, 0x2c, 0xd3, 0x99, 0x18, 0xb3, 0xc9,
|
||||||
|
0xd8, 0x3e, 0xf9, 0xae, 0x5f, 0x43, 0xaf, 0xe1, 0xe0, 0xca, 0x9c, 0x5d, 0x4d, 0xa7, 0x16, 0xb6,
|
||||||
|
0xf5, 0xd3, 0x32, 0x87, 0xf5, 0x94, 0x34, 0xc3, 0xb4, 0x75, 0x6c, 0x8e, 0x2f, 0xb3, 0x13, 0xfa,
|
||||||
|
0x0d, 0x2d, 0x01, 0x35, 0x17, 0xe3, 0x84, 0xfa, 0x64, 0xec, 0xdf, 0x13, 0x26, 0x02, 0x4e, 0xee,
|
||||||
|
0x48, 0x24, 0xd0, 0x7b, 0x38, 0x58, 0x33, 0xa6, 0x13, 0x44, 0x0b, 0xaa, 0x2a, 0x83, 0xfa, 0xb0,
|
||||||
|
0x73, 0xfc, 0xe9, 0xd3, 0xf4, 0x7c, 0x9f, 0x10, 0xb6, 0x34, 0xa2, 0x05, 0xc5, 0x7b, 0xf1, 0xa3,
|
||||||
|
0xad, 0x14, 0xd5, 0xde, 0xc2, 0x7e, 0x65, 0x0a, 0xfa, 0x18, 0x3a, 0x71, 0x32, 0x0f, 0x03, 0x2f,
|
||||||
|
0x6d, 0x68, 0x2e, 0x0f, 0xea, 0x62, 0xc8, 0xa0, 0x0b, 0xb2, 0xe4, 0xda, 0xaf, 0x35, 0xf8, 0xe0,
|
||||||
|
0xc9, 0xd3, 0xd6, 0x7c, 0xa6, 0xac, 0xfb, 0xac, 0xc2, 0xb3, 0xb5, 0x4a, 0xcf, 0x1e, 0x02, 0xfc,
|
||||||
|
0x73, 0x95, 0x42, 0xbc, 0xd5, 0x4d, 0x2a, 0xbd, 0xd7, 0xa8, 0xf4, 0xde, 0xca, 0x2f, 0x5b, 0x65,
|
||||||
|
0xbf, 0x3c, 0xed, 0xea, 0x23, 0xd8, 0xe5, 0x84, 0xdd, 0x13, 0xe6, 0x94, 0xce, 0x6f, 0xca, 0xdc,
|
||||||
|
0x9d, 0x6c, 0x63, 0x5a, 0xdc, 0x42, 0xfb, 0x4d, 0x81, 0xc3, 0x4a, 0x3a, 0x56, 0xdd, 0xfe, 0x06,
|
||||||
|
0x1a, 0xff, 0x56, 0x33, 0x99, 0x90, 0xbe, 0xff, 0x8e, 0x70, 0xee, 0x5e, 0x93, 0x82, 0xa3, 0x2e,
|
||||||
|
0x6e, 0xe7, 0x88, 0xe1, 0x97, 0x5d, 0x54, 0x7f, 0xe0, 0x22, 0xed, 0x4f, 0x05, 0xfa, 0x8f, 0x8b,
|
||||||
|
0xbf, 0x44, 0x99, 0x57, 0xd0, 0x94, 0xb3, 0x69, 0xa5, 0xc8, 0x76, 0xba, 0x7c, 0x5e, 0x89, 0x0a,
|
||||||
|
0x45, 0x1b, 0x95, 0x8a, 0xaa, 0xd0, 0xcc, 0xef, 0x9f, 0x4b, 0x51, 0x2c, 0xb5, 0x18, 0x5e, 0xad,
|
||||||
|
0x3b, 0x5c, 0xda, 0x14, 0x7d, 0x05, 0xad, 0xdc, 0xb1, 0x3c, 0xe7, 0xf0, 0xf5, 0x86, 0xb1, 0xb0,
|
||||||
|
0x8a, 0x7d, 0x86, 0x3e, 0xed, 0xf7, 0x1a, 0x1c, 0xac, 0x1f, 0x19, 0x53, 0x26, 0x36, 0xcc, 0xa7,
|
||||||
|
0x6f, 0x1e, 0xce, 0xa7, 0xa3, 0x4d, 0xf3, 0x29, 0x2d, 0x55, 0x39, 0x91, 0xfe, 0x0b, 0x2a, 0xb5,
|
||||||
|
0x1f, 0x5f, 0x32, 0xb9, 0x76, 0xa0, 0xf3, 0x0e, 0x5b, 0xe6, 0x79, 0x79, 0x6c, 0x3f, 0x9a, 0x40,
|
||||||
|
0xb5, 0x14, 0x33, 0x2d, 0xdb, 0xc1, 0xfa, 0xb9, 0x31, 0xb3, 0x75, 0xac, 0x9f, 0xf6, 0xeb, 0xe9,
|
||||||
|
0x54, 0x5a, 0x7f, 0x50, 0xde, 0xcf, 0x0f, 0x79, 0x55, 0x1e, 0xb7, 0xe5, 0xd7, 0xd0, 0x64, 0xf2,
|
||||||
|
0xed, 0x5c, 0xad, 0x49, 0xb5, 0x06, 0xcf, 0x91, 0x84, 0x8b, 0x84, 0xf9, 0xb6, 0x8c, 0xfc, 0xf2,
|
||||||
|
0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6f, 0xe8, 0xb9, 0x16, 0x90, 0x08, 0x00, 0x00,
|
||||||
|
}
|
91
protocol/protobuf/push_notifications.proto
Normal file
91
protocol/protobuf/push_notifications.proto
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package protobuf;
|
||||||
|
|
||||||
|
message PushNotificationRegistration {
|
||||||
|
enum TokenType {
|
||||||
|
UNKNOWN_TOKEN_TYPE = 0;
|
||||||
|
APN_TOKEN = 1;
|
||||||
|
FIREBASE_TOKEN = 2;
|
||||||
|
}
|
||||||
|
TokenType token_type = 1;
|
||||||
|
string device_token = 2;
|
||||||
|
string installation_id = 3;
|
||||||
|
string access_token = 4;
|
||||||
|
bool enabled = 5;
|
||||||
|
uint64 version = 6;
|
||||||
|
repeated bytes allowed_key_list = 7;
|
||||||
|
repeated bytes blocked_chat_list = 8;
|
||||||
|
bool unregister = 9;
|
||||||
|
bytes grant = 10;
|
||||||
|
bool allow_from_contacts_only = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PushNotificationRegistrationResponse {
|
||||||
|
bool success = 1;
|
||||||
|
ErrorType error = 2;
|
||||||
|
bytes request_id = 3;
|
||||||
|
|
||||||
|
enum ErrorType {
|
||||||
|
UNKNOWN_ERROR_TYPE = 0;
|
||||||
|
MALFORMED_MESSAGE = 1;
|
||||||
|
VERSION_MISMATCH = 2;
|
||||||
|
UNSUPPORTED_TOKEN_TYPE = 3;
|
||||||
|
INTERNAL_ERROR = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContactCodeAdvertisement {
|
||||||
|
repeated PushNotificationQueryInfo push_notification_info = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PushNotificationQuery {
|
||||||
|
repeated bytes public_keys = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PushNotificationQueryInfo {
|
||||||
|
string access_token = 1;
|
||||||
|
string installation_id = 2;
|
||||||
|
bytes public_key = 3;
|
||||||
|
repeated bytes allowed_key_list = 4;
|
||||||
|
bytes grant = 5;
|
||||||
|
uint64 version = 6;
|
||||||
|
bytes server_public_key = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PushNotificationQueryResponse {
|
||||||
|
repeated PushNotificationQueryInfo info = 1;
|
||||||
|
bytes message_id = 2;
|
||||||
|
bool success = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PushNotification {
|
||||||
|
string access_token = 1;
|
||||||
|
string chat_id = 2;
|
||||||
|
bytes public_key = 3;
|
||||||
|
string installation_id = 4;
|
||||||
|
bytes message = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PushNotificationRequest {
|
||||||
|
repeated PushNotification requests = 1;
|
||||||
|
bytes message_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PushNotificationReport {
|
||||||
|
bool success = 1;
|
||||||
|
ErrorType error = 2;
|
||||||
|
enum ErrorType {
|
||||||
|
UNKNOWN_ERROR_TYPE = 0;
|
||||||
|
WRONG_TOKEN = 1;
|
||||||
|
INTERNAL_ERROR = 2;
|
||||||
|
NOT_REGISTERED = 3;
|
||||||
|
}
|
||||||
|
bytes public_key = 3;
|
||||||
|
string installation_id = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PushNotificationResponse {
|
||||||
|
bytes message_id = 1;
|
||||||
|
repeated PushNotificationReport reports = 2;
|
||||||
|
}
|
@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate protoc --go_out=. ./chat_message.proto ./application_metadata_message.proto ./membership_update_message.proto ./command.proto ./contact.proto ./pairing.proto
|
//go:generate protoc --go_out=. ./chat_message.proto ./application_metadata_message.proto ./membership_update_message.proto ./command.proto ./contact.proto ./pairing.proto ./push_notifications.proto
|
||||||
|
|
||||||
func Unmarshal(payload []byte) (*ApplicationMetadataMessage, error) {
|
func Unmarshal(payload []byte) (*ApplicationMetadataMessage, error) {
|
||||||
var message ApplicationMetadataMessage
|
var message ApplicationMetadataMessage
|
||||||
|
797
protocol/push_notification_test.go
Normal file
797
protocol/push_notification_test.go
Normal file
@ -0,0 +1,797 @@
|
|||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||||
|
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||||
|
"github.com/status-im/status-go/protocol/tt"
|
||||||
|
"github.com/status-im/status-go/waku"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bob1DeviceToken = "token-1"
|
||||||
|
bob2DeviceToken = "token-2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMessengerPushNotificationSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(MessengerPushNotificationSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessengerPushNotificationSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
m *Messenger // main instance of Messenger
|
||||||
|
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||||
|
// If one wants to send messages between different instances of Messenger,
|
||||||
|
// a single Waku service should be shared.
|
||||||
|
shh types.Waku
|
||||||
|
tmpFiles []*os.File // files to clean up
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerPushNotificationSuite) SetupTest() {
|
||||||
|
s.logger = tt.MustCreateTestLogger()
|
||||||
|
|
||||||
|
config := waku.DefaultConfig
|
||||||
|
config.MinimumAcceptedPoW = 0
|
||||||
|
shh := waku.New(&config, s.logger)
|
||||||
|
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||||
|
s.Require().NoError(shh.Start(nil))
|
||||||
|
|
||||||
|
s.m = s.newMessenger(s.shh)
|
||||||
|
s.privateKey = s.m.identity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerPushNotificationSuite) TearDownTest() {
|
||||||
|
s.Require().NoError(s.m.Shutdown())
|
||||||
|
for _, f := range s.tmpFiles {
|
||||||
|
_ = os.Remove(f.Name())
|
||||||
|
}
|
||||||
|
_ = s.logger.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerPushNotificationSuite) newMessengerWithOptions(shh types.Waku, privateKey *ecdsa.PrivateKey, options []Option) *Messenger {
|
||||||
|
m, err := NewMessenger(
|
||||||
|
privateKey,
|
||||||
|
&testNode{shh: shh},
|
||||||
|
uuid.New().String(),
|
||||||
|
options...,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = m.Init()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||||
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
options := []Option{
|
||||||
|
WithCustomLogger(s.logger),
|
||||||
|
WithMessagesPersistenceEnabled(),
|
||||||
|
WithDatabaseConfig(tmpFile.Name(), ""),
|
||||||
|
WithDatasync(),
|
||||||
|
}
|
||||||
|
return s.newMessengerWithOptions(shh, privateKey, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerPushNotificationSuite) newMessenger(shh types.Waku) *Messenger {
|
||||||
|
privateKey, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
return s.newMessengerWithKey(s.shh, privateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||||
|
|
||||||
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
serverConfig := &pushnotificationserver.Config{
|
||||||
|
Logger: s.logger,
|
||||||
|
Identity: privateKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
options := []Option{
|
||||||
|
WithCustomLogger(s.logger),
|
||||||
|
WithMessagesPersistenceEnabled(),
|
||||||
|
WithDatabaseConfig(tmpFile.Name(), "some-key"),
|
||||||
|
WithPushNotificationServerConfig(serverConfig),
|
||||||
|
WithDatasync(),
|
||||||
|
}
|
||||||
|
return s.newMessengerWithOptions(shh, privateKey, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
|
||||||
|
|
||||||
|
bob1 := s.m
|
||||||
|
bob2 := s.newMessengerWithKey(s.shh, s.m.identity)
|
||||||
|
|
||||||
|
serverKey, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
server := s.newPushNotificationServer(s.shh, serverKey)
|
||||||
|
|
||||||
|
alice := s.newMessenger(s.shh)
|
||||||
|
// start alice and enable sending push notifications
|
||||||
|
s.Require().NoError(alice.Start())
|
||||||
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||||
|
bobInstallationIDs := []string{bob1.installationID, bob2.installationID}
|
||||||
|
|
||||||
|
// Register bob1
|
||||||
|
err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||||
|
|
||||||
|
// Pull servers and check we registered
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = bob1.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registered, err := bob1.RegisteredForPushNotifications()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !registered {
|
||||||
|
return errors.New("not registered")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
// Make sure we receive it
|
||||||
|
s.Require().NoError(err)
|
||||||
|
bob1Servers, err := bob1.GetPushNotificationServers()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Register bob2
|
||||||
|
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = bob2.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
registered, err := bob2.RegisteredForPushNotifications()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !registered {
|
||||||
|
return errors.New("not registered")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
// Make sure we receive it
|
||||||
|
s.Require().NoError(err)
|
||||||
|
bob2Servers, err := bob2.GetPushNotificationServers()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Create one to one chat & send message
|
||||||
|
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
|
||||||
|
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
|
||||||
|
s.Require().NoError(alice.SaveChat(&chat))
|
||||||
|
inputMessage := buildTestMessage(chat)
|
||||||
|
response, err := alice.SendChatMessage(context.Background(), inputMessage)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
messageIDString := response.Messages[0].ID
|
||||||
|
messageID, err := hex.DecodeString(messageIDString[2:])
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
var info []*pushnotificationclient.PushNotificationInfo
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Check we have replies for both bob1 and bob2
|
||||||
|
if len(info) != 2 {
|
||||||
|
return errors.New("info not fetched")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Check we have replies for both bob1 and bob2
|
||||||
|
var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo
|
||||||
|
|
||||||
|
if info[0].AccessToken == bob1Servers[0].AccessToken {
|
||||||
|
bob1Info = info[0]
|
||||||
|
bob2Info = info[1]
|
||||||
|
} else {
|
||||||
|
bob2Info = info[0]
|
||||||
|
bob1Info = info[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NotNil(bob1Info)
|
||||||
|
s.Require().Equal(bob1.installationID, bob1Info.InstallationID)
|
||||||
|
s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken)
|
||||||
|
s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey)
|
||||||
|
|
||||||
|
s.Require().NotNil(bob2Info)
|
||||||
|
s.Require().Equal(bob2.installationID, bob2Info.InstallationID)
|
||||||
|
s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken)
|
||||||
|
s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey)
|
||||||
|
|
||||||
|
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(retrievedNotificationInfo)
|
||||||
|
s.Require().Len(retrievedNotificationInfo, 2)
|
||||||
|
|
||||||
|
var sentNotification *pushnotificationclient.SentNotification
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sentNotification == nil {
|
||||||
|
return errors.New("sent notification not found")
|
||||||
|
}
|
||||||
|
if !sentNotification.Success {
|
||||||
|
return errors.New("sent notification not successul")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NoError(bob2.Shutdown())
|
||||||
|
s.Require().NoError(alice.Shutdown())
|
||||||
|
s.Require().NoError(server.Shutdown())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactOnly() {
|
||||||
|
|
||||||
|
bob := s.m
|
||||||
|
|
||||||
|
serverKey, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
server := s.newPushNotificationServer(s.shh, serverKey)
|
||||||
|
|
||||||
|
alice := s.newMessenger(s.shh)
|
||||||
|
// start alice and enable push notifications
|
||||||
|
s.Require().NoError(alice.Start())
|
||||||
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||||
|
bobInstallationIDs := []string{bob.installationID}
|
||||||
|
|
||||||
|
// Register bob
|
||||||
|
err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Add alice has a contact
|
||||||
|
aliceContact := &Contact{
|
||||||
|
ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)),
|
||||||
|
Name: "Some Contact",
|
||||||
|
SystemTags: []string{contactAdded},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bob.SaveContact(aliceContact)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Enable from contacts only
|
||||||
|
err = bob.EnablePushNotificationsFromContactsOnly()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Pull servers and check we registered
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = bob.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registered, err := bob.RegisteredForPushNotifications()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !registered {
|
||||||
|
return errors.New("not registered")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
// Make sure we receive it
|
||||||
|
s.Require().NoError(err)
|
||||||
|
bobServers, err := bob.GetPushNotificationServers()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Create one to one chat & send message
|
||||||
|
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
|
||||||
|
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
|
||||||
|
s.Require().NoError(alice.SaveChat(&chat))
|
||||||
|
inputMessage := buildTestMessage(chat)
|
||||||
|
response, err := alice.SendChatMessage(context.Background(), inputMessage)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
messageIDString := response.Messages[0].ID
|
||||||
|
messageID, err := hex.DecodeString(messageIDString[2:])
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
var info []*pushnotificationclient.PushNotificationInfo
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Check we have replies for bob
|
||||||
|
if len(info) != 1 {
|
||||||
|
return errors.New("info not fetched")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().NotNil(info)
|
||||||
|
s.Require().Equal(bob.installationID, info[0].InstallationID)
|
||||||
|
s.Require().Equal(bobServers[0].AccessToken, info[0].AccessToken)
|
||||||
|
s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey)
|
||||||
|
|
||||||
|
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(retrievedNotificationInfo)
|
||||||
|
s.Require().Len(retrievedNotificationInfo, 1)
|
||||||
|
|
||||||
|
var sentNotification *pushnotificationclient.SentNotification
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sentNotification == nil {
|
||||||
|
return errors.New("sent notification not found")
|
||||||
|
}
|
||||||
|
if !sentNotification.Success {
|
||||||
|
return errors.New("sent notification not successul")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NoError(alice.Shutdown())
|
||||||
|
s.Require().NoError(server.Shutdown())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
|
||||||
|
|
||||||
|
bob := s.m
|
||||||
|
|
||||||
|
serverKey, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
server := s.newPushNotificationServer(s.shh, serverKey)
|
||||||
|
|
||||||
|
alice := s.newMessenger(s.shh)
|
||||||
|
// another contact to invalidate the token
|
||||||
|
frank := s.newMessenger(s.shh)
|
||||||
|
// start alice and enable push notifications
|
||||||
|
s.Require().NoError(alice.Start())
|
||||||
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||||
|
bobInstallationIDs := []string{bob.installationID}
|
||||||
|
|
||||||
|
// Register bob
|
||||||
|
err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Add alice has a contact
|
||||||
|
aliceContact := &Contact{
|
||||||
|
ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)),
|
||||||
|
Name: "Some Contact",
|
||||||
|
SystemTags: []string{contactAdded},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bob.SaveContact(aliceContact)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Add frank has a contact
|
||||||
|
frankContact := &Contact{
|
||||||
|
ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)),
|
||||||
|
Name: "Some Contact",
|
||||||
|
SystemTags: []string{contactAdded},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bob.SaveContact(frankContact)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Enable from contacts only
|
||||||
|
err = bob.EnablePushNotificationsFromContactsOnly()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Pull servers and check we registered
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = bob.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registered, err := bob.RegisteredForPushNotifications()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !registered {
|
||||||
|
return errors.New("not registered")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
// Make sure we receive it
|
||||||
|
s.Require().NoError(err)
|
||||||
|
bobServers, err := bob.GetPushNotificationServers()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Create one to one chat & send message
|
||||||
|
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
|
||||||
|
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
|
||||||
|
s.Require().NoError(alice.SaveChat(&chat))
|
||||||
|
inputMessage := buildTestMessage(chat)
|
||||||
|
_, err = alice.SendChatMessage(context.Background(), inputMessage)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// We check that alice retrieves the info from the server
|
||||||
|
var info []*pushnotificationclient.PushNotificationInfo
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Check we have replies for bob
|
||||||
|
if len(info) != 1 {
|
||||||
|
return errors.New("info not fetched")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().NotNil(info)
|
||||||
|
s.Require().Equal(bob.installationID, info[0].InstallationID)
|
||||||
|
s.Require().Equal(bobServers[0].AccessToken, info[0].AccessToken)
|
||||||
|
s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey)
|
||||||
|
|
||||||
|
// The message has been sent, but not received, now we remove a contact so that the token is invalidated
|
||||||
|
frankContact = &Contact{
|
||||||
|
ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)),
|
||||||
|
Name: "Some Contact",
|
||||||
|
SystemTags: []string{},
|
||||||
|
}
|
||||||
|
err = bob.SaveContact(frankContact)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Re-registration should be triggered, pull from server and bob to check we are correctly registered
|
||||||
|
// Pull servers and check we registered
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = bob.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registered, err := bob.RegisteredForPushNotifications()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !registered {
|
||||||
|
return errors.New("not registered")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
newBobServers, err := bob.GetPushNotificationServers()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
// Make sure access token is not the same
|
||||||
|
s.Require().NotEqual(newBobServers[0].AccessToken, bobServers[0].AccessToken)
|
||||||
|
|
||||||
|
// Send another message, here the token will not be valid
|
||||||
|
inputMessage = buildTestMessage(chat)
|
||||||
|
response, err := alice.SendChatMessage(context.Background(), inputMessage)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
messageIDString := response.Messages[0].ID
|
||||||
|
messageID, err := hex.DecodeString(messageIDString[2:])
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Check we have replies for bob
|
||||||
|
if len(info) != 1 {
|
||||||
|
return errors.New("info not fetched")
|
||||||
|
}
|
||||||
|
if newBobServers[0].AccessToken != info[0].AccessToken {
|
||||||
|
return errors.New("still using the old access token")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().NotNil(info)
|
||||||
|
s.Require().Equal(bob.installationID, info[0].InstallationID)
|
||||||
|
s.Require().Equal(newBobServers[0].AccessToken, info[0].AccessToken)
|
||||||
|
s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey)
|
||||||
|
|
||||||
|
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(retrievedNotificationInfo)
|
||||||
|
s.Require().Len(retrievedNotificationInfo, 1)
|
||||||
|
|
||||||
|
var sentNotification *pushnotificationclient.SentNotification
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sentNotification == nil {
|
||||||
|
return errors.New("sent notification not found")
|
||||||
|
}
|
||||||
|
if !sentNotification.Success {
|
||||||
|
return errors.New("sent notification not successul")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NoError(alice.Shutdown())
|
||||||
|
s.Require().NoError(server.Shutdown())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here bob acts as his own server
|
||||||
|
func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer() {
|
||||||
|
bob1 := s.m
|
||||||
|
server := s.newPushNotificationServer(s.shh, s.m.identity)
|
||||||
|
bob2 := server
|
||||||
|
alice := s.newMessenger(s.shh)
|
||||||
|
// start alice and enable sending push notifications
|
||||||
|
s.Require().NoError(alice.Start())
|
||||||
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||||
|
bobInstallationIDs := []string{bob1.installationID, bob2.installationID}
|
||||||
|
|
||||||
|
// Register bob1
|
||||||
|
err := bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||||
|
|
||||||
|
// Pull servers and check we registered
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = bob1.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registered, err := bob1.RegisteredForPushNotifications()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !registered {
|
||||||
|
return errors.New("not registered")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
// Make sure we receive it
|
||||||
|
s.Require().NoError(err)
|
||||||
|
bob1Servers, err := bob1.GetPushNotificationServers()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Register bob2
|
||||||
|
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = bob2.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
registered, err := bob2.RegisteredForPushNotifications()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !registered {
|
||||||
|
return errors.New("not registered")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
// Make sure we receive it
|
||||||
|
s.Require().NoError(err)
|
||||||
|
bob2Servers, err := bob2.GetPushNotificationServers()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Create one to one chat & send message
|
||||||
|
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
|
||||||
|
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
|
||||||
|
s.Require().NoError(alice.SaveChat(&chat))
|
||||||
|
inputMessage := buildTestMessage(chat)
|
||||||
|
response, err := alice.SendChatMessage(context.Background(), inputMessage)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
messageIDString := response.Messages[0].ID
|
||||||
|
messageID, err := hex.DecodeString(messageIDString[2:])
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
var info []*pushnotificationclient.PushNotificationInfo
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Check we have replies for both bob1 and bob2
|
||||||
|
if len(info) != 2 {
|
||||||
|
return errors.New("info not fetched")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Check we have replies for both bob1 and bob2
|
||||||
|
var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo
|
||||||
|
|
||||||
|
if info[0].AccessToken == bob1Servers[0].AccessToken {
|
||||||
|
bob1Info = info[0]
|
||||||
|
bob2Info = info[1]
|
||||||
|
} else {
|
||||||
|
bob2Info = info[0]
|
||||||
|
bob1Info = info[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NotNil(bob1Info)
|
||||||
|
s.Require().Equal(bob1.installationID, bob1Info.InstallationID)
|
||||||
|
s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken)
|
||||||
|
s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey)
|
||||||
|
|
||||||
|
s.Require().NotNil(bob2Info)
|
||||||
|
s.Require().Equal(bob2.installationID, bob2Info.InstallationID)
|
||||||
|
s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken)
|
||||||
|
s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey)
|
||||||
|
|
||||||
|
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(retrievedNotificationInfo)
|
||||||
|
s.Require().Len(retrievedNotificationInfo, 2)
|
||||||
|
|
||||||
|
var sentNotification *pushnotificationclient.SentNotification
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = server.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sentNotification == nil {
|
||||||
|
return errors.New("sent notification not found")
|
||||||
|
}
|
||||||
|
if !sentNotification.Success {
|
||||||
|
return errors.New("sent notification not successul")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NoError(bob2.Shutdown())
|
||||||
|
s.Require().NoError(alice.Shutdown())
|
||||||
|
}
|
1287
protocol/pushnotificationclient/client.go
Normal file
1287
protocol/pushnotificationclient/client.go
Normal file
File diff suppressed because it is too large
Load Diff
230
protocol/pushnotificationclient/client_test.go
Normal file
230
protocol/pushnotificationclient/client_test.go
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
package pushnotificationclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto/ecies"
|
||||||
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
"github.com/status-im/status-go/protocol/sqlite"
|
||||||
|
"github.com/status-im/status-go/protocol/tt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testDeviceToken = "test-token"
|
||||||
|
|
||||||
|
type ClientSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
tmpFile *os.File
|
||||||
|
persistence *Persistence
|
||||||
|
identity *ecdsa.PrivateKey
|
||||||
|
installationID string
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientSuite(t *testing.T) {
|
||||||
|
s := new(ClientSuite)
|
||||||
|
s.installationID = "c6ae4fde-bb65-11ea-b3de-0242ac130004"
|
||||||
|
|
||||||
|
suite.Run(t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClientSuite) SetupTest() {
|
||||||
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.tmpFile = tmpFile
|
||||||
|
|
||||||
|
database, err := sqlite.Open(s.tmpFile.Name(), "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.persistence = NewPersistence(database)
|
||||||
|
|
||||||
|
identity, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.identity = identity
|
||||||
|
|
||||||
|
config := &Config{
|
||||||
|
Identity: identity,
|
||||||
|
Logger: tt.MustCreateTestLogger(),
|
||||||
|
RemoteNotificationsEnabled: true,
|
||||||
|
InstallationID: s.installationID,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.client = New(s.persistence, config, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() {
|
||||||
|
mutedChatList := []string{"a", "b"}
|
||||||
|
|
||||||
|
// build chat lish hashes
|
||||||
|
var mutedChatListHashes [][]byte
|
||||||
|
for _, chatID := range mutedChatList {
|
||||||
|
mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID)))
|
||||||
|
}
|
||||||
|
|
||||||
|
contactKey, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
|
||||||
|
|
||||||
|
// Set random generator for uuid
|
||||||
|
var seed int64 = 1
|
||||||
|
uuid.SetRand(rand.New(rand.NewSource(seed)))
|
||||||
|
|
||||||
|
// Get token
|
||||||
|
expectedUUID := uuid.New().String()
|
||||||
|
|
||||||
|
// Reset random generator
|
||||||
|
uuid.SetRand(rand.New(rand.NewSource(seed)))
|
||||||
|
|
||||||
|
s.client.deviceToken = testDeviceToken
|
||||||
|
// Set reader
|
||||||
|
s.client.reader = bytes.NewReader([]byte(expectedUUID))
|
||||||
|
|
||||||
|
options := &protobuf.PushNotificationRegistration{
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: expectedUUID,
|
||||||
|
DeviceToken: testDeviceToken,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Enabled: true,
|
||||||
|
BlockedChatList: mutedChatListHashes,
|
||||||
|
}
|
||||||
|
|
||||||
|
actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Equal(options, actualMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsOnly() {
|
||||||
|
mutedChatList := []string{"a", "b"}
|
||||||
|
|
||||||
|
// build chat lish hashes
|
||||||
|
var mutedChatListHashes [][]byte
|
||||||
|
for _, chatID := range mutedChatList {
|
||||||
|
mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID)))
|
||||||
|
}
|
||||||
|
|
||||||
|
contactKey, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
|
||||||
|
|
||||||
|
// Set random generator for uuid
|
||||||
|
var seed int64 = 1
|
||||||
|
uuid.SetRand(rand.New(rand.NewSource(seed)))
|
||||||
|
|
||||||
|
// Get token
|
||||||
|
expectedUUID := uuid.New().String()
|
||||||
|
|
||||||
|
// set up reader
|
||||||
|
reader := bytes.NewReader([]byte(expectedUUID))
|
||||||
|
|
||||||
|
sharedKey, err := ecies.ImportECDSA(s.identity).GenerateShared(
|
||||||
|
ecies.ImportECDSAPublic(&contactKey.PublicKey),
|
||||||
|
accessTokenKeyLength,
|
||||||
|
accessTokenKeyLength,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
// build encrypted token
|
||||||
|
encryptedToken, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Reset random generator
|
||||||
|
uuid.SetRand(rand.New(rand.NewSource(seed)))
|
||||||
|
|
||||||
|
s.client.config.AllowFromContactsOnly = true
|
||||||
|
s.client.deviceToken = testDeviceToken
|
||||||
|
// Set reader
|
||||||
|
s.client.reader = bytes.NewReader([]byte(expectedUUID))
|
||||||
|
|
||||||
|
options := &protobuf.PushNotificationRegistration{
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: expectedUUID,
|
||||||
|
DeviceToken: testDeviceToken,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
AllowFromContactsOnly: true,
|
||||||
|
Enabled: true,
|
||||||
|
BlockedChatList: mutedChatListHashes,
|
||||||
|
AllowedKeyList: [][]byte{encryptedToken},
|
||||||
|
}
|
||||||
|
|
||||||
|
actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Equal(options, actualMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClientSuite) TestHandleMessageScheduled() {
|
||||||
|
messageID := []byte("message-id")
|
||||||
|
chatID := "chat-id"
|
||||||
|
installationID1 := "1"
|
||||||
|
installationID2 := "2"
|
||||||
|
rawMessage := &common.RawMessage{
|
||||||
|
ID: types.EncodeHex(messageID),
|
||||||
|
SendPushNotification: true,
|
||||||
|
LocalChatID: chatID,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NoError(s.client.handleMessageScheduled(rawMessage))
|
||||||
|
|
||||||
|
key1, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// First time, should notify
|
||||||
|
response, err := s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().True(response)
|
||||||
|
|
||||||
|
// Save notification
|
||||||
|
s.Require().NoError(s.client.notifiedOn(&key1.PublicKey, installationID1, messageID))
|
||||||
|
|
||||||
|
// Second time, should not notify
|
||||||
|
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().False(response)
|
||||||
|
|
||||||
|
// Different installationID
|
||||||
|
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID2, messageID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().True(response)
|
||||||
|
|
||||||
|
key2, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
// different key, should notify
|
||||||
|
response, err = s.client.shouldNotifyOn(&key2.PublicKey, installationID1, messageID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().True(response)
|
||||||
|
|
||||||
|
// non tracked message id
|
||||||
|
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, []byte("not-existing"))
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().False(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClientSuite) TestShouldRefreshToken() {
|
||||||
|
key1, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
key2, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
key3, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
key4, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Contacts are added
|
||||||
|
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey, &key3.PublicKey, &key4.PublicKey}))
|
||||||
|
|
||||||
|
// everything the same
|
||||||
|
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}))
|
||||||
|
|
||||||
|
// A contact is removed
|
||||||
|
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey}))
|
||||||
|
|
||||||
|
}
|
319
protocol/pushnotificationclient/migrations/migrations.go
Normal file
319
protocol/pushnotificationclient/migrations/migrations.go
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
// Code generated by go-bindata. DO NOT EDIT.
|
||||||
|
// sources:
|
||||||
|
// 1593601729_initial_schema.down.sql (144B)
|
||||||
|
// 1593601729_initial_schema.up.sql (1.773kB)
|
||||||
|
// doc.go (382B)
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, err = io.Copy(&buf, gz)
|
||||||
|
clErr := gz.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
if clErr != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type asset struct {
|
||||||
|
bytes []byte
|
||||||
|
info os.FileInfo
|
||||||
|
digest [sha256.Size]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type bindataFileInfo struct {
|
||||||
|
name string
|
||||||
|
size int64
|
||||||
|
mode os.FileMode
|
||||||
|
modTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi bindataFileInfo) Name() string {
|
||||||
|
return fi.name
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Size() int64 {
|
||||||
|
return fi.size
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||||
|
return fi.mode
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) ModTime() time.Time {
|
||||||
|
return fi.modTime
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Sys() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var __1593601729_initial_schemaDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x4f\xce\xc9\x4c\xcd\x2b\x89\x2f\x4e\x2d\x2a\x4b\x2d\x2a\xb6\xe6\x22\x46\x71\x66\x5e\x5a\x3e\x54\xa5\xa7\x9f\x8b\x6b\x84\x42\x66\x4a\x45\x3c\x5e\xd5\xf1\x05\xa5\x49\x39\x99\xc9\xf1\xd9\xa9\x95\xd6\x5c\x80\x00\x00\x00\xff\xff\x6d\xb4\xf8\x65\x90\x00\x00\x00")
|
||||||
|
|
||||||
|
func _1593601729_initial_schemaDownSqlBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
__1593601729_initial_schemaDownSql,
|
||||||
|
"1593601729_initial_schema.down.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _1593601729_initial_schemaDownSql() (*asset, error) {
|
||||||
|
bytes, err := _1593601729_initial_schemaDownSqlBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "1593601729_initial_schema.down.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa, 0x95, 0x55, 0x64, 0x38, 0x40, 0x16, 0xbf, 0x8b, 0x1c, 0x18, 0xb4, 0xc5, 0x7f, 0xd0, 0xb8, 0xf0, 0x3c, 0xa2, 0x82, 0xf8, 0x8d, 0x5a, 0xd3, 0xb6, 0x6e, 0xa3, 0xb4, 0xc, 0x9, 0x33, 0x0}}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x54\xc1\x8e\xdb\x20\x10\xbd\xfb\x2b\xe6\xb8\x91\x72\xe8\x7d\x4f\x4e\x96\x54\x96\x10\x6e\x13\x22\xe5\x86\x28\x9e\x5d\xa3\xb8\x78\x0b\x78\xd5\xfc\x7d\x85\x9d\x78\x93\xc5\xc5\x55\xba\x17\x4b\x1e\x1e\xa3\x79\x6f\x1e\x6f\xbd\x25\x39\x27\xc0\xf3\x15\x25\x50\x6c\x80\x95\x1c\xc8\xa1\xd8\xf1\x1d\xbc\x76\xae\x16\xa6\xf5\xfa\x59\x2b\xe9\x75\x6b\x84\x6a\x34\x1a\x2f\x1c\xda\x37\xb4\x0e\x1e\x32\x80\xd7\xee\x47\xa3\x95\x38\xe2\x09\x56\xb4\x5c\xf5\xf7\xd9\x9e\xd2\x65\x06\x60\xf1\x45\x3b\x8f\x16\x2b\x58\x95\x25\x25\x39\x83\x27\xb2\xc9\xf7\x94\xc3\x26\xa7\x3b\x72\x8b\x11\xd2\x43\xc1\xf8\xd8\x61\xc4\x7e\x09\xb8\x46\x3a\x2f\x2c\x7a\xab\xe7\x90\x01\x74\x12\xaa\xed\x4c\x0a\x25\x95\x42\xe7\x84\x6f\x8f\x68\x80\x93\x03\x0f\xc5\x3d\x2b\xbe\xef\xc9\xc3\x3b\xa7\x05\x94\x0c\xd6\x25\xdb\xd0\x62\xcd\x61\x4b\xbe\xd1\x7c\x4d\xb2\xc5\x63\x96\xdd\xa3\xdb\xaf\x0e\xad\xc6\x79\xdd\x06\x5c\x44\xf3\x72\x74\x12\xba\x8a\x2f\x45\xb3\x2f\x2f\xd8\xcf\x25\xa1\xcd\x73\x3b\xcb\x60\x70\x88\x48\x41\xb4\x71\x5e\x36\xcd\xd0\x5b\x57\xfd\x0e\x6e\x00\xd1\x86\x3e\x78\x2b\x58\xe1\x6d\x5a\xa5\xe0\x4e\xdd\x9a\xa8\x1e\x6b\xf4\x71\x8c\x65\x3c\xfa\xe7\xca\xe7\xad\x54\x47\xac\xc4\x4f\x74\x4e\xbe\x9c\xcd\x70\xfe\x99\xdc\xab\xaa\xa5\x9f\xd4\xe7\xd2\x69\x82\xff\x99\xe7\x7b\xdb\x5b\x0e\xc5\x57\x56\x6e\x49\x06\x70\x2f\x09\x17\x3e\xd7\x07\xf3\x34\x52\x56\xa8\xa5\xab\xb1\xfa\x3f\xb7\xf4\xf9\x30\x99\x0e\xff\x9e\x09\xae\xeb\x2d\x37\x86\x55\x84\x1a\x53\x0b\xad\x6d\x6d\xa2\x53\xb4\x80\x25\x24\x4c\x37\xed\xb0\xfb\xd7\x33\x64\xaa\xbd\xda\xcc\x25\x67\x87\x5a\x2c\x2f\x80\x6a\x8d\x97\x2a\x38\xcd\xf5\xc7\x43\xd5\x9d\x8c\xaf\xd1\x6b\x15\x34\xff\x3b\xdd\x91\xf0\x35\x7e\xf6\xdd\x14\xec\x89\x1c\x40\x57\xbf\x45\x32\x6c\xae\x7d\x51\xb2\x74\x30\xa5\x9e\xf6\xe2\x31\xfb\x13\x00\x00\xff\xff\xfb\x06\xc2\x3d\xed\x06\x00\x00")
|
||||||
|
|
||||||
|
func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
__1593601729_initial_schemaUpSql,
|
||||||
|
"1593601729_initial_schema.up.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _1593601729_initial_schemaUpSql() (*asset, error) {
|
||||||
|
bytes, err := _1593601729_initial_schemaUpSqlBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1773, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x1e, 0x5, 0x35, 0x9, 0xb2, 0x2d, 0x6f, 0x33, 0x63, 0xa2, 0x7a, 0x5b, 0xd2, 0x2d, 0xcb, 0x79, 0x7e, 0x6, 0xb4, 0x9d, 0x35, 0xd8, 0x9b, 0x55, 0xe5, 0xf8, 0x44, 0xca, 0xa6, 0xf3, 0xd3}}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3d\x6e\xec\x30\x0c\x84\x7b\x9d\x62\xb0\xcd\x36\xcf\x52\xf3\xaa\x74\x29\xd3\xe7\x02\x5c\x89\x96\x88\xb5\x24\x43\xa4\xf7\xe7\xf6\x81\x37\x01\xe2\x2e\xed\x87\xf9\x86\xc3\x10\xf0\x59\x44\x31\xcb\xc2\x10\x45\xe3\xc8\xaa\x34\x9e\xb8\x70\xa4\x4d\x19\xa7\x2c\x56\xb6\x8b\x8f\xbd\x06\x35\xb2\x4d\x27\xa9\xa1\x4a\x1e\x64\x1c\x6e\xff\x4f\x2e\x04\x44\x6a\x67\x43\xa1\x96\x16\x7e\x75\x29\xd4\x68\x98\xb4\x8c\xbb\x58\x01\x61\x1d\x3c\xcb\xc3\xe3\xdd\xb0\x30\xa9\xc1\x0a\xd9\x59\x61\x85\x11\x49\x79\xaf\x99\xfb\x40\xee\xd3\x45\x5a\x22\x23\xbf\xa3\x8f\xf9\x40\xf6\x85\x91\x96\x85\x13\xe6\xd1\xeb\xcb\x55\xaa\x8c\x24\x83\xa3\xf5\xf1\xfc\x07\x52\x65\x43\xa3\xca\xba\xfb\x85\x6e\x8c\xd6\x7f\xce\x83\x5a\xfa\xfb\x23\xdc\xfb\xb8\x2a\x48\xc1\x8f\x95\xa3\x71\xf2\xce\xad\x14\xaf\x94\x19\xdf\x39\xe9\x4d\x9d\x0b\x21\xf7\xb7\xcc\x8d\x77\xf3\xb8\x73\x5a\xaf\xf9\x90\xc4\xd4\xe1\x7d\xf8\x05\x3e\x77\xf8\xe0\xbe\x02\x00\x00\xff\xff\x4d\x1d\x5d\x50\x7e\x01\x00\x00")
|
||||||
|
|
||||||
|
func docGoBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
_docGo,
|
||||||
|
"doc.go",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func docGo() (*asset, error) {
|
||||||
|
bytes, err := docGoBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asset loads and returns the asset for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func Asset(name string) ([]byte, error) {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[canonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.bytes, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetString returns the asset contents as a string (instead of a []byte).
|
||||||
|
func AssetString(name string) (string, error) {
|
||||||
|
data, err := Asset(name)
|
||||||
|
return string(data), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustAsset is like Asset but panics when Asset would return an error.
|
||||||
|
// It simplifies safe initialization of global variables.
|
||||||
|
func MustAsset(name string) []byte {
|
||||||
|
a, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
panic("asset: Asset(" + name + "): " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustAssetString is like AssetString but panics when Asset would return an
|
||||||
|
// error. It simplifies safe initialization of global variables.
|
||||||
|
func MustAssetString(name string) string {
|
||||||
|
return string(MustAsset(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetInfo loads and returns the asset info for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func AssetInfo(name string) (os.FileInfo, error) {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[canonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.info, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetDigest returns the digest of the file with the given name. It returns an
|
||||||
|
// error if the asset could not be found or the digest could not be loaded.
|
||||||
|
func AssetDigest(name string) ([sha256.Size]byte, error) {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[canonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.digest, nil
|
||||||
|
}
|
||||||
|
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Digests returns a map of all known files and their checksums.
|
||||||
|
func Digests() (map[string][sha256.Size]byte, error) {
|
||||||
|
mp := make(map[string][sha256.Size]byte, len(_bindata))
|
||||||
|
for name := range _bindata {
|
||||||
|
a, err := _bindata[name]()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mp[name] = a.digest
|
||||||
|
}
|
||||||
|
return mp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetNames returns the names of the assets.
|
||||||
|
func AssetNames() []string {
|
||||||
|
names := make([]string, 0, len(_bindata))
|
||||||
|
for name := range _bindata {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
|
var _bindata = map[string]func() (*asset, error){
|
||||||
|
"1593601729_initial_schema.down.sql": _1593601729_initial_schemaDownSql,
|
||||||
|
|
||||||
|
"1593601729_initial_schema.up.sql": _1593601729_initial_schemaUpSql,
|
||||||
|
|
||||||
|
"doc.go": docGo,
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetDir returns the file names below a certain
|
||||||
|
// directory embedded in the file by go-bindata.
|
||||||
|
// For example if you run go-bindata on data/... and data contains the
|
||||||
|
// following hierarchy:
|
||||||
|
// data/
|
||||||
|
// foo.txt
|
||||||
|
// img/
|
||||||
|
// a.png
|
||||||
|
// b.png
|
||||||
|
// then AssetDir("data") would return []string{"foo.txt", "img"},
|
||||||
|
// AssetDir("data/img") would return []string{"a.png", "b.png"},
|
||||||
|
// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
|
||||||
|
// AssetDir("") will return []string{"data"}.
|
||||||
|
func AssetDir(name string) ([]string, error) {
|
||||||
|
node := _bintree
|
||||||
|
if len(name) != 0 {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
pathList := strings.Split(canonicalName, "/")
|
||||||
|
for _, p := range pathList {
|
||||||
|
node = node.Children[p]
|
||||||
|
if node == nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Func != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
rv := make([]string, 0, len(node.Children))
|
||||||
|
for childName := range node.Children {
|
||||||
|
rv = append(rv, childName)
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type bintree struct {
|
||||||
|
Func func() (*asset, error)
|
||||||
|
Children map[string]*bintree
|
||||||
|
}
|
||||||
|
|
||||||
|
var _bintree = &bintree{nil, map[string]*bintree{
|
||||||
|
"1593601729_initial_schema.down.sql": &bintree{_1593601729_initial_schemaDownSql, map[string]*bintree{}},
|
||||||
|
"1593601729_initial_schema.up.sql": &bintree{_1593601729_initial_schemaUpSql, map[string]*bintree{}},
|
||||||
|
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
// RestoreAsset restores an asset under the given directory.
|
||||||
|
func RestoreAsset(dir, name string) error {
|
||||||
|
data, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := AssetInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreAssets restores an asset under the given directory recursively.
|
||||||
|
func RestoreAssets(dir, name string) error {
|
||||||
|
children, err := AssetDir(name)
|
||||||
|
// File
|
||||||
|
if err != nil {
|
||||||
|
return RestoreAsset(dir, name)
|
||||||
|
}
|
||||||
|
// Dir
|
||||||
|
for _, child := range children {
|
||||||
|
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _filePath(dir, name string) string {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
DROP TABLE push_notification_client_servers;
|
||||||
|
DROP TABLE push_notification_client_info;
|
||||||
|
DROP INDEX idx_push_notification_client_info_public_key;
|
@ -0,0 +1,54 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS push_notification_client_servers (
|
||||||
|
public_key BLOB NOT NULL,
|
||||||
|
registered BOOLEAN DEFAULT FALSE,
|
||||||
|
registered_at INT NOT NULL DEFAULT 0,
|
||||||
|
last_retried_at INT NOT NULL DEFAULT 0,
|
||||||
|
retry_count INT NOT NULL DEFAULT 0,
|
||||||
|
access_token TEXT,
|
||||||
|
UNIQUE(public_key) ON CONFLICT REPLACE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS push_notification_client_queries (
|
||||||
|
public_key BLOB NOT NULL,
|
||||||
|
queried_at INT NOT NULL,
|
||||||
|
query_id BLOB NOT NULL,
|
||||||
|
UNIQUE(public_key,query_id) ON CONFLICT REPLACE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS push_notification_client_info (
|
||||||
|
public_key BLOB NOT NULL,
|
||||||
|
server_public_key BLOB NOT NULL,
|
||||||
|
installation_id TEXT NOT NULL,
|
||||||
|
access_token TEXT NOT NULL,
|
||||||
|
retrieved_at INT NOT NULL,
|
||||||
|
version INT NOT NULL,
|
||||||
|
UNIQUE(public_key, installation_id, server_public_key) ON CONFLICT REPLACE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS push_notification_client_tracked_messages (
|
||||||
|
message_id BLOB NOT NULL,
|
||||||
|
chat_id TEXT NOT NULL,
|
||||||
|
tracked_at INT NOT NULL,
|
||||||
|
UNIQUE(message_id) ON CONFLICT IGNORE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS push_notification_client_sent_notifications (
|
||||||
|
message_id BLOB NOT NULL,
|
||||||
|
public_key BLOB NOT NULL,
|
||||||
|
hashed_public_key BLOB NOT NULL,
|
||||||
|
installation_id TEXT NOT NULL,
|
||||||
|
last_tried_at INT NOT NULL,
|
||||||
|
retry_count INT NOT NULL DEFAULT 0,
|
||||||
|
success BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
error INT NOT NULL DEFAULT 0,
|
||||||
|
UNIQUE(message_id, public_key, installation_id) ON CONFLICT REPLACE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS push_notification_client_registrations (
|
||||||
|
registration BLOB NOT NULL,
|
||||||
|
contact_ids BLOB,
|
||||||
|
synthetic_id INT NOT NULL DEFAULT 0,
|
||||||
|
UNIQUE(synthetic_id) ON CONFLICT REPLACE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_push_notification_client_info_public_key ON push_notification_client_info(public_key, installation_id);
|
9
protocol/pushnotificationclient/migrations/sql/doc.go
Normal file
9
protocol/pushnotificationclient/migrations/sql/doc.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// This file is necessary because "github.com/status-im/migrate/v4"
|
||||||
|
// can't handle files starting with a prefix. At least that's the case
|
||||||
|
// for go-bindata.
|
||||||
|
// If go-bindata is called from the same directory, asset names
|
||||||
|
// have no prefix and "github.com/status-im/migrate/v4" works as expected.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
//go:generate go-bindata -pkg migrations -o ../migrations.go ./
|
396
protocol/pushnotificationclient/persistence.go
Normal file
396
protocol/pushnotificationclient/persistence.go
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
package pushnotificationclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/gob"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Persistence struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPersistence(db *sql.DB) *Persistence {
|
||||||
|
return &Persistence{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) GetLastPushNotificationRegistration() (*protobuf.PushNotificationRegistration, []*ecdsa.PublicKey, error) {
|
||||||
|
var registrationBytes []byte
|
||||||
|
var contactIDsBytes []byte
|
||||||
|
err := p.db.QueryRow(`SELECT registration,contact_ids FROM push_notification_client_registrations LIMIT 1`).Scan(®istrationBytes, &contactIDsBytes)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var publicKeyBytes [][]byte
|
||||||
|
var contactIDs []*ecdsa.PublicKey
|
||||||
|
// Restore contactIDs
|
||||||
|
contactIDsDecoder := gob.NewDecoder(bytes.NewBuffer(contactIDsBytes))
|
||||||
|
err = contactIDsDecoder.Decode(&publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
for _, pkBytes := range publicKeyBytes {
|
||||||
|
pk, err := crypto.DecompressPubkey(pkBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
contactIDs = append(contactIDs, pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
registration := &protobuf.PushNotificationRegistration{}
|
||||||
|
|
||||||
|
err = proto.Unmarshal(registrationBytes, registration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return registration, contactIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) SaveLastPushNotificationRegistration(registration *protobuf.PushNotificationRegistration, contactIDs []*ecdsa.PublicKey) error {
|
||||||
|
var encodedContactIDs bytes.Buffer
|
||||||
|
var contactIDsBytes [][]byte
|
||||||
|
for _, pk := range contactIDs {
|
||||||
|
contactIDsBytes = append(contactIDsBytes, crypto.CompressPubkey(pk))
|
||||||
|
}
|
||||||
|
pkEncoder := gob.NewEncoder(&encodedContactIDs)
|
||||||
|
if err := pkEncoder.Encode(contactIDsBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
marshaledRegistration, err := proto.Marshal(registration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = p.db.Exec(`INSERT INTO push_notification_client_registrations (registration,contact_ids) VALUES (?, ?)`, marshaledRegistration, encodedContactIDs.Bytes())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) TrackPushNotification(chatID string, messageID []byte) error {
|
||||||
|
trackedAt := time.Now().Unix()
|
||||||
|
_, err := p.db.Exec(`INSERT INTO push_notification_client_tracked_messages (chat_id, message_id, tracked_at) VALUES (?,?,?)`, chatID, messageID, trackedAt)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) TrackedMessage(messageID []byte) (bool, error) {
|
||||||
|
var count uint64
|
||||||
|
err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) SavePushNotificationQuery(publicKey *ecdsa.PublicKey, queryID []byte) error {
|
||||||
|
queriedAt := time.Now().Unix()
|
||||||
|
_, err := p.db.Exec(`INSERT INTO push_notification_client_queries (public_key, query_id, queried_at) VALUES (?,?,?)`, crypto.CompressPubkey(publicKey), queryID, queriedAt)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) GetQueriedAt(publicKey *ecdsa.PublicKey) (int64, error) {
|
||||||
|
var queriedAt int64
|
||||||
|
err := p.db.QueryRow(`SELECT queried_at FROM push_notification_client_queries WHERE public_key = ? ORDER BY queried_at DESC LIMIT 1`, crypto.CompressPubkey(publicKey)).Scan(&queriedAt)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return queriedAt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) GetQueryPublicKey(queryID []byte) (*ecdsa.PublicKey, error) {
|
||||||
|
var publicKeyBytes []byte
|
||||||
|
err := p.db.QueryRow(`SELECT public_key FROM push_notification_client_queries WHERE query_id = ?`, queryID).Scan(&publicKeyBytes)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey, err := crypto.DecompressPubkey(publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return publicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) SavePushNotificationInfo(infos []*PushNotificationInfo) error {
|
||||||
|
tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
|
||||||
|
defer func() {
|
||||||
|
if err == nil {
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// don't shadow original error
|
||||||
|
_ = tx.Rollback()
|
||||||
|
}()
|
||||||
|
for _, info := range infos {
|
||||||
|
var latestVersion uint64
|
||||||
|
clientCompressedKey := crypto.CompressPubkey(info.PublicKey)
|
||||||
|
err := tx.QueryRow(`SELECT IFNULL(MAX(version),0) FROM push_notification_client_info WHERE public_key = ? AND installation_id = ? LIMIT 1`, clientCompressedKey, info.InstallationID).Scan(&latestVersion)
|
||||||
|
if err != sql.ErrNoRows && err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if latestVersion > info.Version {
|
||||||
|
// Nothing to do
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove anything that as a lower version
|
||||||
|
_, err = tx.Exec(`DELETE FROM push_notification_client_info WHERE public_key = ? AND installation_id = ? AND version < ?`, clientCompressedKey, info.InstallationID, info.Version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Insert
|
||||||
|
_, err = tx.Exec(`INSERT INTO push_notification_client_info (public_key, server_public_key, installation_id, access_token, retrieved_at, version) VALUES (?, ?, ?, ?, ?,?)`, clientCompressedKey, crypto.CompressPubkey(info.ServerPublicKey), info.InstallationID, info.AccessToken, info.RetrievedAt, info.Version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installationIDs []string) ([]*PushNotificationInfo, error) {
|
||||||
|
queryArgs := make([]interface{}, 0, len(installationIDs)+1)
|
||||||
|
queryArgs = append(queryArgs, crypto.CompressPubkey(publicKey))
|
||||||
|
for _, installationID := range installationIDs {
|
||||||
|
queryArgs = append(queryArgs, installationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
inVector := strings.Repeat("?, ", len(installationIDs)-1) + "?"
|
||||||
|
|
||||||
|
rows, err := p.db.Query(`SELECT server_public_key, installation_id, version, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ? AND installation_id IN (`+inVector+`)`, queryArgs...) //nolint: gosec
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var infos []*PushNotificationInfo
|
||||||
|
for rows.Next() {
|
||||||
|
var serverPublicKeyBytes []byte
|
||||||
|
info := &PushNotificationInfo{PublicKey: publicKey}
|
||||||
|
err := rows.Scan(&serverPublicKeyBytes, &info.InstallationID, &info.Version, &info.AccessToken, &info.RetrievedAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serverPublicKey, err := crypto.DecompressPubkey(serverPublicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info.ServerPublicKey = serverPublicKey
|
||||||
|
infos = append(infos, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return infos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) GetPushNotificationInfoByPublicKey(publicKey *ecdsa.PublicKey) ([]*PushNotificationInfo, error) {
|
||||||
|
rows, err := p.db.Query(`SELECT server_public_key, installation_id, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ?`, crypto.CompressPubkey(publicKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var infos []*PushNotificationInfo
|
||||||
|
for rows.Next() {
|
||||||
|
var serverPublicKeyBytes []byte
|
||||||
|
info := &PushNotificationInfo{PublicKey: publicKey}
|
||||||
|
err := rows.Scan(&serverPublicKeyBytes, &info.InstallationID, &info.AccessToken, &info.RetrievedAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serverPublicKey, err := crypto.DecompressPubkey(serverPublicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info.ServerPublicKey = serverPublicKey
|
||||||
|
infos = append(infos, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return infos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) ShouldSendNotificationFor(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) (bool, error) {
|
||||||
|
// First we check that we are tracking this message, next we check that we haven't already sent this
|
||||||
|
var count uint64
|
||||||
|
err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_sent_notifications WHERE message_id = ? AND public_key = ? AND installation_id = ? `, messageID, crypto.CompressPubkey(publicKey), installationID).Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) ShouldSendNotificationToAllInstallationIDs(publicKey *ecdsa.PublicKey, messageID []byte) (bool, error) {
|
||||||
|
// First we check that we are tracking this message, next we check that we haven't already sent this
|
||||||
|
var count uint64
|
||||||
|
err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_sent_notifications WHERE message_id = ? AND public_key = ? `, messageID, crypto.CompressPubkey(publicKey)).Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) UpsertSentNotification(n *SentNotification) error {
|
||||||
|
_, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, last_tried_at, retry_count, success, error, hashed_public_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, crypto.CompressPubkey(n.PublicKey), n.InstallationID, n.MessageID, n.LastTriedAt, n.RetryCount, n.Success, n.Error, n.HashedPublicKey())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) GetSentNotification(hashedPublicKey []byte, installationID string, messageID []byte) (*SentNotification, error) {
|
||||||
|
var publicKeyBytes []byte
|
||||||
|
sentNotification := &SentNotification{
|
||||||
|
InstallationID: installationID,
|
||||||
|
MessageID: messageID,
|
||||||
|
}
|
||||||
|
err := p.db.QueryRow(`SELECT retry_count, last_tried_at, error, success, public_key FROM push_notification_client_sent_notifications WHERE hashed_public_key = ?`, hashedPublicKey).Scan(&sentNotification.RetryCount, &sentNotification.LastTriedAt, &sentNotification.Error, &sentNotification.Success, &publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey, err := crypto.DecompressPubkey(publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sentNotification.PublicKey = publicKey
|
||||||
|
|
||||||
|
return sentNotification, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) UpdateNotificationResponse(messageID []byte, response *protobuf.PushNotificationReport) error {
|
||||||
|
_, err := p.db.Exec(`UPDATE push_notification_client_sent_notifications SET success = ?, error = ? WHERE hashed_public_key = ? AND installation_id = ? AND message_id = ? AND NOT success`, response.Success, response.Error, response.PublicKey, response.InstallationId, messageID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) GetRetriablePushNotifications() ([]*SentNotification, error) {
|
||||||
|
var notifications []*SentNotification
|
||||||
|
rows, err := p.db.Query(`SELECT retry_count, last_tried_at, error, success, public_key, installation_id, message_id FROM push_notification_client_sent_notifications WHERE NOT success AND error = ?`, protobuf.PushNotificationReport_WRONG_TOKEN)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var publicKeyBytes []byte
|
||||||
|
notification := &SentNotification{}
|
||||||
|
err = rows.Scan(¬ification.RetryCount, ¬ification.LastTriedAt, ¬ification.Error, ¬ification.Success, &publicKeyBytes, ¬ification.InstallationID, ¬ification.MessageID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
publicKey, err := crypto.DecompressPubkey(publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
notification.PublicKey = publicKey
|
||||||
|
notifications = append(notifications, notification)
|
||||||
|
}
|
||||||
|
return notifications, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) UpsertServer(server *PushNotificationServer) error {
|
||||||
|
_, err := p.db.Exec(`INSERT INTO push_notification_client_servers (public_key, registered, registered_at, access_token, last_retried_at, retry_count) VALUES (?,?,?,?,?,?)`, crypto.CompressPubkey(server.PublicKey), server.Registered, server.RegisteredAt, server.AccessToken, server.LastRetriedAt, server.RetryCount)
|
||||||
|
return err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) GetServers() ([]*PushNotificationServer, error) {
|
||||||
|
rows, err := p.db.Query(`SELECT public_key, registered, registered_at,access_token,last_retried_at, retry_count FROM push_notification_client_servers`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var servers []*PushNotificationServer
|
||||||
|
for rows.Next() {
|
||||||
|
server := &PushNotificationServer{}
|
||||||
|
var key []byte
|
||||||
|
err := rows.Scan(&key, &server.Registered, &server.RegisteredAt, &server.AccessToken, &server.LastRetriedAt, &server.RetryCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parsedKey, err := crypto.DecompressPubkey(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
server.PublicKey = parsedKey
|
||||||
|
servers = append(servers, server)
|
||||||
|
}
|
||||||
|
return servers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Persistence) GetServersByPublicKey(keys []*ecdsa.PublicKey) ([]*PushNotificationServer, error) {
|
||||||
|
|
||||||
|
keyArgs := make([]interface{}, 0, len(keys))
|
||||||
|
for _, key := range keys {
|
||||||
|
keyArgs = append(keyArgs, crypto.CompressPubkey(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
inVector := strings.Repeat("?, ", len(keys)-1) + "?"
|
||||||
|
rows, err := p.db.Query(`SELECT public_key, registered, registered_at,access_token FROM push_notification_client_servers WHERE public_key IN (`+inVector+")", keyArgs...) //nolint: gosec
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var servers []*PushNotificationServer
|
||||||
|
for rows.Next() {
|
||||||
|
server := &PushNotificationServer{}
|
||||||
|
var key []byte
|
||||||
|
err := rows.Scan(&key, &server.Registered, &server.RegisteredAt, &server.AccessToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parsedKey, err := crypto.DecompressPubkey(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
server.PublicKey = parsedKey
|
||||||
|
servers = append(servers, server)
|
||||||
|
}
|
||||||
|
return servers, nil
|
||||||
|
}
|
334
protocol/pushnotificationclient/persistence_test.go
Normal file
334
protocol/pushnotificationclient/persistence_test.go
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
package pushnotificationclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
"github.com/status-im/status-go/protocol/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testAccessToken = "token"
|
||||||
|
installationID1 = "installation-id-1"
|
||||||
|
installationID2 = "installation-id-2"
|
||||||
|
installationID3 = "installation-id-3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSQLitePersistenceSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(SQLitePersistenceSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type SQLitePersistenceSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
tmpFile *os.File
|
||||||
|
persistence *Persistence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) SetupTest() {
|
||||||
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.tmpFile = tmpFile
|
||||||
|
|
||||||
|
database, err := sqlite.Open(s.tmpFile.Name(), "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.persistence = NewPersistence(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TearDownTest() {
|
||||||
|
_ = os.Remove(s.tmpFile.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() {
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
server := &PushNotificationServer{
|
||||||
|
PublicKey: &key.PublicKey,
|
||||||
|
Registered: true,
|
||||||
|
RegisteredAt: 1,
|
||||||
|
AccessToken: testAccessToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.UpsertServer(server))
|
||||||
|
|
||||||
|
retrievedServers, err := s.persistence.GetServers()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Len(retrievedServers, 1)
|
||||||
|
s.Require().True(retrievedServers[0].Registered)
|
||||||
|
s.Require().Equal(int64(1), retrievedServers[0].RegisteredAt)
|
||||||
|
s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey))
|
||||||
|
s.Require().Equal(testAccessToken, retrievedServers[0].AccessToken)
|
||||||
|
|
||||||
|
server.Registered = false
|
||||||
|
server.RegisteredAt = 2
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.UpsertServer(server))
|
||||||
|
|
||||||
|
retrievedServers, err = s.persistence.GetServers()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Len(retrievedServers, 1)
|
||||||
|
s.Require().False(retrievedServers[0].Registered)
|
||||||
|
s.Require().Equal(int64(2), retrievedServers[0].RegisteredAt)
|
||||||
|
s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() {
|
||||||
|
key1, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
key2, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
serverKey, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
infos := []*PushNotificationInfo{
|
||||||
|
{
|
||||||
|
PublicKey: &key1.PublicKey,
|
||||||
|
ServerPublicKey: &serverKey.PublicKey,
|
||||||
|
RetrievedAt: 1,
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: testAccessToken,
|
||||||
|
InstallationID: installationID1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PublicKey: &key1.PublicKey,
|
||||||
|
ServerPublicKey: &serverKey.PublicKey,
|
||||||
|
RetrievedAt: 1,
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: testAccessToken,
|
||||||
|
InstallationID: installationID2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PublicKey: &key1.PublicKey,
|
||||||
|
ServerPublicKey: &serverKey.PublicKey,
|
||||||
|
RetrievedAt: 1,
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: testAccessToken,
|
||||||
|
InstallationID: installationID3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PublicKey: &key2.PublicKey,
|
||||||
|
ServerPublicKey: &serverKey.PublicKey,
|
||||||
|
RetrievedAt: 1,
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: testAccessToken,
|
||||||
|
InstallationID: installationID1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PublicKey: &key2.PublicKey,
|
||||||
|
ServerPublicKey: &serverKey.PublicKey,
|
||||||
|
RetrievedAt: 1,
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: testAccessToken,
|
||||||
|
InstallationID: installationID2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PublicKey: &key2.PublicKey,
|
||||||
|
ServerPublicKey: &serverKey.PublicKey,
|
||||||
|
RetrievedAt: 1,
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: testAccessToken,
|
||||||
|
InstallationID: installationID3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
|
||||||
|
|
||||||
|
retrievedInfos, err := s.persistence.GetPushNotificationInfo(&key1.PublicKey, []string{installationID1, installationID2})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Len(retrievedInfos, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfoWithVersion() {
|
||||||
|
installationID := "installation-id-1"
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
serverKey1, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
serverKey2, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
infos := []*PushNotificationInfo{
|
||||||
|
{
|
||||||
|
PublicKey: &key.PublicKey,
|
||||||
|
ServerPublicKey: &serverKey1.PublicKey,
|
||||||
|
RetrievedAt: 1,
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: testAccessToken,
|
||||||
|
InstallationID: installationID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PublicKey: &key.PublicKey,
|
||||||
|
ServerPublicKey: &serverKey2.PublicKey,
|
||||||
|
RetrievedAt: 1,
|
||||||
|
Version: 1,
|
||||||
|
AccessToken: testAccessToken,
|
||||||
|
InstallationID: installationID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
|
||||||
|
|
||||||
|
retrievedInfos, err := s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// We should retrieve both
|
||||||
|
s.Require().Len(retrievedInfos, 2)
|
||||||
|
s.Require().Equal(uint64(1), retrievedInfos[0].Version)
|
||||||
|
|
||||||
|
// Bump version
|
||||||
|
infos[0].Version = 2
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
|
||||||
|
|
||||||
|
retrievedInfos, err = s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Only one should be retrieved now
|
||||||
|
s.Require().Len(retrievedInfos, 1)
|
||||||
|
s.Require().Equal(uint64(2), retrievedInfos[0].Version)
|
||||||
|
|
||||||
|
// Lower version
|
||||||
|
infos[0].Version = 1
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
|
||||||
|
|
||||||
|
retrievedInfos, err = s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Len(retrievedInfos, 1)
|
||||||
|
s.Require().Equal(uint64(2), retrievedInfos[0].Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TestNotifiedOnAndUpdateNotificationResponse() {
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
installationID := "installation-id"
|
||||||
|
messageID := []byte("message-id")
|
||||||
|
|
||||||
|
sentNotification := &SentNotification{
|
||||||
|
PublicKey: &key.PublicKey,
|
||||||
|
InstallationID: installationID,
|
||||||
|
MessageID: messageID,
|
||||||
|
LastTriedAt: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.UpsertSentNotification(sentNotification))
|
||||||
|
|
||||||
|
retrievedNotification, err := s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Equal(sentNotification, retrievedNotification)
|
||||||
|
|
||||||
|
retriableNotifications, err := s.persistence.GetRetriablePushNotifications()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(retriableNotifications, 0)
|
||||||
|
|
||||||
|
response := &protobuf.PushNotificationReport{
|
||||||
|
Success: false,
|
||||||
|
Error: protobuf.PushNotificationReport_WRONG_TOKEN,
|
||||||
|
PublicKey: sentNotification.HashedPublicKey(),
|
||||||
|
InstallationId: installationID,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response))
|
||||||
|
// This notification should be retriable
|
||||||
|
retriableNotifications, err = s.persistence.GetRetriablePushNotifications()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(retriableNotifications, 1)
|
||||||
|
|
||||||
|
sentNotification.Error = protobuf.PushNotificationReport_WRONG_TOKEN
|
||||||
|
|
||||||
|
retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Equal(sentNotification, retrievedNotification)
|
||||||
|
|
||||||
|
// Update with a successful notification
|
||||||
|
response = &protobuf.PushNotificationReport{
|
||||||
|
Success: true,
|
||||||
|
PublicKey: sentNotification.HashedPublicKey(),
|
||||||
|
InstallationId: installationID,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response))
|
||||||
|
|
||||||
|
sentNotification.Success = true
|
||||||
|
sentNotification.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
|
||||||
|
|
||||||
|
retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Equal(sentNotification, retrievedNotification)
|
||||||
|
|
||||||
|
// This notification should not be retriable
|
||||||
|
retriableNotifications, err = s.persistence.GetRetriablePushNotifications()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(retriableNotifications, 0)
|
||||||
|
|
||||||
|
// Update with a unsuccessful notification, it should be ignored
|
||||||
|
response = &protobuf.PushNotificationReport{
|
||||||
|
Success: false,
|
||||||
|
Error: protobuf.PushNotificationReport_WRONG_TOKEN,
|
||||||
|
PublicKey: sentNotification.HashedPublicKey(),
|
||||||
|
InstallationId: installationID,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response))
|
||||||
|
|
||||||
|
sentNotification.Success = true
|
||||||
|
sentNotification.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
|
||||||
|
|
||||||
|
retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Equal(sentNotification, retrievedNotification)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveRegistration() {
|
||||||
|
// Try with nil first
|
||||||
|
retrievedRegistration, retrievedContactIDs, err := s.persistence.GetLastPushNotificationRegistration()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Nil(retrievedRegistration)
|
||||||
|
s.Require().Nil(retrievedContactIDs)
|
||||||
|
|
||||||
|
// Save & retrieve registration
|
||||||
|
registration := &protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: "test",
|
||||||
|
Version: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
key1, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
key2, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
key3, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
publicKeys := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration, publicKeys))
|
||||||
|
retrievedRegistration, retrievedContactIDs, err = s.persistence.GetLastPushNotificationRegistration()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().True(proto.Equal(registration, retrievedRegistration))
|
||||||
|
s.Require().Equal(publicKeys, retrievedContactIDs)
|
||||||
|
|
||||||
|
// Override and retrieve
|
||||||
|
|
||||||
|
registration.Version = 5
|
||||||
|
publicKeys = append(publicKeys, &key3.PublicKey)
|
||||||
|
s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration, publicKeys))
|
||||||
|
retrievedRegistration, retrievedContactIDs, err = s.persistence.GetLastPushNotificationRegistration()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().True(proto.Equal(registration, retrievedRegistration))
|
||||||
|
s.Require().Equal(publicKeys, retrievedContactIDs)
|
||||||
|
}
|
13
protocol/pushnotificationserver/errors.go
Normal file
13
protocol/pushnotificationserver/errors.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package pushnotificationserver
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var ErrInvalidPushNotificationRegistrationVersion = errors.New("invalid version")
|
||||||
|
var ErrEmptyPushNotificationRegistrationPayload = errors.New("empty payload")
|
||||||
|
var ErrMalformedPushNotificationRegistrationInstallationID = errors.New("invalid installationID")
|
||||||
|
var ErrEmptyPushNotificationRegistrationPublicKey = errors.New("no public key")
|
||||||
|
var ErrCouldNotUnmarshalPushNotificationRegistration = errors.New("could not unmarshal preferences")
|
||||||
|
var ErrMalformedPushNotificationRegistrationDeviceToken = errors.New("invalid device token")
|
||||||
|
var ErrMalformedPushNotificationRegistrationGrant = errors.New("invalid grant")
|
||||||
|
var ErrMalformedPushNotificationRegistrationAccessToken = errors.New("invalid access token")
|
||||||
|
var ErrUnknownPushNotificationRegistrationTokenType = errors.New("invalid token type")
|
77
protocol/pushnotificationserver/gorush.go
Normal file
77
protocol/pushnotificationserver/gorush.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package pushnotificationserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultNotificationMessage = "You have a new message"
|
||||||
|
|
||||||
|
type GoRushRequestData struct {
|
||||||
|
EncryptedMessage string `json:"encryptedMessage"`
|
||||||
|
ChatID string `json:"chatId"`
|
||||||
|
PublicKey string `json:"publicKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoRushRequestNotification struct {
|
||||||
|
Tokens []string `json:"tokens"`
|
||||||
|
Platform uint `json:"platform"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data *GoRushRequestData `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoRushRequest struct {
|
||||||
|
Notifications []*GoRushRequestNotification `json:"notifications"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestAndRegistration struct {
|
||||||
|
Request *protobuf.PushNotification
|
||||||
|
Registration *protobuf.PushNotificationRegistration
|
||||||
|
}
|
||||||
|
|
||||||
|
func tokenTypeToGoRushPlatform(tokenType protobuf.PushNotificationRegistration_TokenType) uint {
|
||||||
|
switch tokenType {
|
||||||
|
case protobuf.PushNotificationRegistration_APN_TOKEN:
|
||||||
|
return 1
|
||||||
|
case protobuf.PushNotificationRegistration_FIREBASE_TOKEN:
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*RequestAndRegistration) *GoRushRequest {
|
||||||
|
goRushRequests := &GoRushRequest{}
|
||||||
|
for _, requestAndRegistration := range requestAndRegistrations {
|
||||||
|
request := requestAndRegistration.Request
|
||||||
|
registration := requestAndRegistration.Registration
|
||||||
|
goRushRequests.Notifications = append(goRushRequests.Notifications,
|
||||||
|
&GoRushRequestNotification{
|
||||||
|
Tokens: []string{registration.DeviceToken},
|
||||||
|
Platform: tokenTypeToGoRushPlatform(registration.TokenType),
|
||||||
|
Message: defaultNotificationMessage,
|
||||||
|
Data: &GoRushRequestData{
|
||||||
|
EncryptedMessage: hex.EncodeToString(request.Message),
|
||||||
|
ChatID: request.ChatId,
|
||||||
|
PublicKey: hex.EncodeToString(request.PublicKey),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return goRushRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendGoRushNotification(request *GoRushRequest, url string) error {
|
||||||
|
payload, err := json.Marshal(request)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = http.Post(url+"/api/push", "application/json", bytes.NewReader(payload))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
107
protocol/pushnotificationserver/gorush_test.go
Normal file
107
protocol/pushnotificationserver/gorush_test.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package pushnotificationserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
|
||||||
|
message1 := []byte("message-1")
|
||||||
|
message2 := []byte("message-2")
|
||||||
|
message3 := []byte("message-3")
|
||||||
|
hexMessage1 := hex.EncodeToString(message1)
|
||||||
|
hexMessage2 := hex.EncodeToString(message2)
|
||||||
|
hexMessage3 := hex.EncodeToString(message3)
|
||||||
|
chatID := "chat-id"
|
||||||
|
publicKey1 := []byte("public-key-1")
|
||||||
|
publicKey2 := []byte("public-key-2")
|
||||||
|
installationID1 := "installation-id-1"
|
||||||
|
installationID2 := "installation-id-2"
|
||||||
|
installationID3 := "installation-id-3"
|
||||||
|
var platform1 uint = 1
|
||||||
|
var platform2 uint = 2
|
||||||
|
var platform3 uint = 2
|
||||||
|
token1 := "token-1"
|
||||||
|
token2 := "token-2"
|
||||||
|
token3 := "token-3"
|
||||||
|
|
||||||
|
requestAndRegistrations := []*RequestAndRegistration{
|
||||||
|
{
|
||||||
|
Request: &protobuf.PushNotification{
|
||||||
|
ChatId: chatID,
|
||||||
|
PublicKey: publicKey1,
|
||||||
|
InstallationId: installationID1,
|
||||||
|
Message: message1,
|
||||||
|
},
|
||||||
|
Registration: &protobuf.PushNotificationRegistration{
|
||||||
|
DeviceToken: token1,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Request: &protobuf.PushNotification{
|
||||||
|
ChatId: chatID,
|
||||||
|
PublicKey: publicKey1,
|
||||||
|
InstallationId: installationID2,
|
||||||
|
Message: message2,
|
||||||
|
},
|
||||||
|
Registration: &protobuf.PushNotificationRegistration{
|
||||||
|
DeviceToken: token2,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Request: &protobuf.PushNotification{
|
||||||
|
ChatId: chatID,
|
||||||
|
PublicKey: publicKey2,
|
||||||
|
InstallationId: installationID3,
|
||||||
|
Message: message3,
|
||||||
|
},
|
||||||
|
Registration: &protobuf.PushNotificationRegistration{
|
||||||
|
DeviceToken: token3,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedRequests := &GoRushRequest{
|
||||||
|
Notifications: []*GoRushRequestNotification{
|
||||||
|
{
|
||||||
|
Tokens: []string{token1},
|
||||||
|
Platform: platform1,
|
||||||
|
Message: defaultNotificationMessage,
|
||||||
|
Data: &GoRushRequestData{
|
||||||
|
EncryptedMessage: hexMessage1,
|
||||||
|
ChatID: chatID,
|
||||||
|
PublicKey: hex.EncodeToString(publicKey1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Tokens: []string{token2},
|
||||||
|
Platform: platform2,
|
||||||
|
Message: defaultNotificationMessage,
|
||||||
|
Data: &GoRushRequestData{
|
||||||
|
EncryptedMessage: hexMessage2,
|
||||||
|
ChatID: chatID,
|
||||||
|
PublicKey: hex.EncodeToString(publicKey1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Tokens: []string{token3},
|
||||||
|
Platform: platform3,
|
||||||
|
Message: defaultNotificationMessage,
|
||||||
|
Data: &GoRushRequestData{
|
||||||
|
EncryptedMessage: hexMessage3,
|
||||||
|
ChatID: chatID,
|
||||||
|
PublicKey: hex.EncodeToString(publicKey2),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
actualRequests := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations)
|
||||||
|
require.Equal(t, expectedRequests, actualRequests)
|
||||||
|
}
|
319
protocol/pushnotificationserver/migrations/migrations.go
Normal file
319
protocol/pushnotificationserver/migrations/migrations.go
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
// Code generated by go-bindata. DO NOT EDIT.
|
||||||
|
// sources:
|
||||||
|
// 1593601728_initial_schema.down.sql (200B)
|
||||||
|
// 1593601728_initial_schema.up.sql (675B)
|
||||||
|
// doc.go (382B)
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, err = io.Copy(&buf, gz)
|
||||||
|
clErr := gz.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
if clErr != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type asset struct {
|
||||||
|
bytes []byte
|
||||||
|
info os.FileInfo
|
||||||
|
digest [sha256.Size]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type bindataFileInfo struct {
|
||||||
|
name string
|
||||||
|
size int64
|
||||||
|
mode os.FileMode
|
||||||
|
modTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi bindataFileInfo) Name() string {
|
||||||
|
return fi.name
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Size() int64 {
|
||||||
|
return fi.size
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||||
|
return fi.mode
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) ModTime() time.Time {
|
||||||
|
return fi.modTime
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Sys() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var __1593601728_initial_schemaDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x2f\x4e\x2d\x2a\x4b\x2d\x8a\x2f\x4a\x4d\xcf\x2c\x2e\x29\x02\x8b\x15\x5b\x73\x81\xb5\x78\xfa\xb9\xb8\x46\x28\x64\xa6\x54\xc4\x13\xa7\x2d\xbe\xa0\x34\x29\x27\x33\x39\x3e\x3b\xb5\x92\x72\x13\xe2\x33\xf3\x8a\x4b\x12\x73\x72\x20\x8a\x33\x53\xac\xb9\xb8\x00\x01\x00\x00\xff\xff\x90\x39\xe0\x1c\xc8\x00\x00\x00")
|
||||||
|
|
||||||
|
func _1593601728_initial_schemaDownSqlBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
__1593601728_initial_schemaDownSql,
|
||||||
|
"1593601728_initial_schema.down.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _1593601728_initial_schemaDownSql() (*asset, error) {
|
||||||
|
bytes, err := _1593601728_initial_schemaDownSqlBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x88, 0x8a, 0x61, 0x81, 0x57, 0x45, 0x9b, 0x97, 0x9b, 0x1f, 0xf6, 0x94, 0x8a, 0x20, 0xb3, 0x2b, 0xff, 0x69, 0x49, 0xf4, 0x58, 0xcc, 0xd0, 0x55, 0xcc, 0x9a, 0x8b, 0xb6, 0x7f, 0x29, 0x53, 0xc1}}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var __1593601728_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x91\x31\x6b\xc3\x30\x14\x84\x77\xfd\x8a\x37\xc6\x90\xa1\xbb\x27\xd9\x91\xa9\x40\x48\xad\x23\x97\x6c\xc2\xb5\xd5\xe6\x51\x23\x07\x49\x31\xf5\xbf\x2f\x71\x86\x2a\x69\x87\x10\xb2\x1e\x8f\xbb\xf7\xdd\x95\x35\xa3\x9a\x81\xa6\x85\x60\xc0\x2b\x90\x4a\x03\xdb\xf1\xad\xde\xc2\xe1\x18\xf6\xc6\x8d\x11\x3f\xb0\x6b\x23\x8e\xce\x04\xeb\x27\xeb\x8d\xb7\x9f\x18\xa2\x5f\xb4\x00\x2b\x02\x70\x38\xbe\x0f\xd8\x99\x2f\x3b\x43\x21\x54\xb1\xb8\xc8\x46\x88\x35\x01\x40\x17\x62\x3b\x0c\x67\x07\xec\xe1\x8d\xd6\xe5\x33\xad\x2f\x6e\x26\xeb\x03\x8e\x0e\xb8\xd4\x17\x7a\x9a\xb4\x38\x9f\xc4\x46\xf2\xd7\x86\xad\x7e\x33\xd7\xd7\x19\x19\x28\x09\xa5\x92\x95\xe0\xa5\x86\x9a\xbd\x08\x5a\x32\x92\xe5\x84\xdc\x83\x8b\xbd\x75\x11\xe3\x7c\x26\xf5\x38\xb5\xd1\xfe\x8f\x1a\x66\x17\xf7\x36\x62\x77\xe2\x4c\x59\x60\xc3\x2a\xda\x08\x0d\x4f\x09\x40\x7a\x9d\xa5\xdf\x71\xb9\x61\x3b\xc0\xfe\xdb\xdc\x36\x81\x49\xea\x57\xf2\xc6\xdd\x92\xfe\xb2\xfc\x01\xc9\xe6\x7a\xe7\x7b\x3e\xf9\xbb\x64\x4e\xc8\x4f\x00\x00\x00\xff\xff\xcc\xa0\x4d\x54\xa3\x02\x00\x00")
|
||||||
|
|
||||||
|
func _1593601728_initial_schemaUpSqlBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
__1593601728_initial_schemaUpSql,
|
||||||
|
"1593601728_initial_schema.up.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _1593601728_initial_schemaUpSql() (*asset, error) {
|
||||||
|
bytes, err := _1593601728_initial_schemaUpSqlBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x61, 0x90, 0x79, 0xd9, 0x14, 0x65, 0xe9, 0x96, 0x53, 0x17, 0x33, 0x54, 0xeb, 0x8b, 0x5d, 0x95, 0x99, 0x10, 0x36, 0x58, 0xdd, 0xb2, 0xbf, 0x45, 0xd9, 0xbb, 0xc4, 0x92, 0xe, 0xce, 0x2}}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3d\x6e\xec\x30\x0c\x84\x7b\x9d\x62\xb0\xcd\x36\xcf\x52\xf3\xaa\x74\x29\xd3\xe7\x02\x5c\x89\x96\x88\xb5\x24\x43\xa4\xf7\xe7\xf6\x81\x37\x01\xe2\x2e\xed\x87\xf9\x86\xc3\x10\xf0\x59\x44\x31\xcb\xc2\x10\x45\xe3\xc8\xaa\x34\x9e\xb8\x70\xa4\x4d\x19\xa7\x2c\x56\xb6\x8b\x8f\xbd\x06\x35\xb2\x4d\x27\xa9\xa1\x4a\x1e\x64\x1c\x6e\xff\x4f\x2e\x04\x44\x6a\x67\x43\xa1\x96\x16\x7e\x75\x29\xd4\x68\x98\xb4\x8c\xbb\x58\x01\x61\x1d\x3c\xcb\xc3\xe3\xdd\xb0\x30\xa9\xc1\x0a\xd9\x59\x61\x85\x11\x49\x79\xaf\x99\xfb\x40\xee\xd3\x45\x5a\x22\x23\xbf\xa3\x8f\xf9\x40\xf6\x85\x91\x96\x85\x13\xe6\xd1\xeb\xcb\x55\xaa\x8c\x24\x83\xa3\xf5\xf1\xfc\x07\x52\x65\x43\xa3\xca\xba\xfb\x85\x6e\x8c\xd6\x7f\xce\x83\x5a\xfa\xfb\x23\xdc\xfb\xb8\x2a\x48\xc1\x8f\x95\xa3\x71\xf2\xce\xad\x14\xaf\x94\x19\xdf\x39\xe9\x4d\x9d\x0b\x21\xf7\xb7\xcc\x8d\x77\xf3\xb8\x73\x5a\xaf\xf9\x90\xc4\xd4\xe1\x7d\xf8\x05\x3e\x77\xf8\xe0\xbe\x02\x00\x00\xff\xff\x4d\x1d\x5d\x50\x7e\x01\x00\x00")
|
||||||
|
|
||||||
|
func docGoBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
_docGo,
|
||||||
|
"doc.go",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func docGo() (*asset, error) {
|
||||||
|
bytes, err := docGoBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asset loads and returns the asset for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func Asset(name string) ([]byte, error) {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[canonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.bytes, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetString returns the asset contents as a string (instead of a []byte).
|
||||||
|
func AssetString(name string) (string, error) {
|
||||||
|
data, err := Asset(name)
|
||||||
|
return string(data), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustAsset is like Asset but panics when Asset would return an error.
|
||||||
|
// It simplifies safe initialization of global variables.
|
||||||
|
func MustAsset(name string) []byte {
|
||||||
|
a, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
panic("asset: Asset(" + name + "): " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustAssetString is like AssetString but panics when Asset would return an
|
||||||
|
// error. It simplifies safe initialization of global variables.
|
||||||
|
func MustAssetString(name string) string {
|
||||||
|
return string(MustAsset(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetInfo loads and returns the asset info for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func AssetInfo(name string) (os.FileInfo, error) {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[canonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.info, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetDigest returns the digest of the file with the given name. It returns an
|
||||||
|
// error if the asset could not be found or the digest could not be loaded.
|
||||||
|
func AssetDigest(name string) ([sha256.Size]byte, error) {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[canonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.digest, nil
|
||||||
|
}
|
||||||
|
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Digests returns a map of all known files and their checksums.
|
||||||
|
func Digests() (map[string][sha256.Size]byte, error) {
|
||||||
|
mp := make(map[string][sha256.Size]byte, len(_bindata))
|
||||||
|
for name := range _bindata {
|
||||||
|
a, err := _bindata[name]()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mp[name] = a.digest
|
||||||
|
}
|
||||||
|
return mp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetNames returns the names of the assets.
|
||||||
|
func AssetNames() []string {
|
||||||
|
names := make([]string, 0, len(_bindata))
|
||||||
|
for name := range _bindata {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
|
var _bindata = map[string]func() (*asset, error){
|
||||||
|
"1593601728_initial_schema.down.sql": _1593601728_initial_schemaDownSql,
|
||||||
|
|
||||||
|
"1593601728_initial_schema.up.sql": _1593601728_initial_schemaUpSql,
|
||||||
|
|
||||||
|
"doc.go": docGo,
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetDir returns the file names below a certain
|
||||||
|
// directory embedded in the file by go-bindata.
|
||||||
|
// For example if you run go-bindata on data/... and data contains the
|
||||||
|
// following hierarchy:
|
||||||
|
// data/
|
||||||
|
// foo.txt
|
||||||
|
// img/
|
||||||
|
// a.png
|
||||||
|
// b.png
|
||||||
|
// then AssetDir("data") would return []string{"foo.txt", "img"},
|
||||||
|
// AssetDir("data/img") would return []string{"a.png", "b.png"},
|
||||||
|
// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
|
||||||
|
// AssetDir("") will return []string{"data"}.
|
||||||
|
func AssetDir(name string) ([]string, error) {
|
||||||
|
node := _bintree
|
||||||
|
if len(name) != 0 {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
pathList := strings.Split(canonicalName, "/")
|
||||||
|
for _, p := range pathList {
|
||||||
|
node = node.Children[p]
|
||||||
|
if node == nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Func != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
rv := make([]string, 0, len(node.Children))
|
||||||
|
for childName := range node.Children {
|
||||||
|
rv = append(rv, childName)
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type bintree struct {
|
||||||
|
Func func() (*asset, error)
|
||||||
|
Children map[string]*bintree
|
||||||
|
}
|
||||||
|
|
||||||
|
var _bintree = &bintree{nil, map[string]*bintree{
|
||||||
|
"1593601728_initial_schema.down.sql": &bintree{_1593601728_initial_schemaDownSql, map[string]*bintree{}},
|
||||||
|
"1593601728_initial_schema.up.sql": &bintree{_1593601728_initial_schemaUpSql, map[string]*bintree{}},
|
||||||
|
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
// RestoreAsset restores an asset under the given directory.
|
||||||
|
func RestoreAsset(dir, name string) error {
|
||||||
|
data, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := AssetInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreAssets restores an asset under the given directory recursively.
|
||||||
|
func RestoreAssets(dir, name string) error {
|
||||||
|
children, err := AssetDir(name)
|
||||||
|
// File
|
||||||
|
if err != nil {
|
||||||
|
return RestoreAsset(dir, name)
|
||||||
|
}
|
||||||
|
// Dir
|
||||||
|
for _, child := range children {
|
||||||
|
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _filePath(dir, name string) string {
|
||||||
|
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
DROP TABLE push_notification_server_registrations;
|
||||||
|
DROP INDEX idx_push_notification_server_registrations_public_key;
|
||||||
|
DROP INDEX idx_push_notification_server_registrations_public_key_installation_id;
|
||||||
|
|
@ -0,0 +1,17 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS push_notification_server_registrations (
|
||||||
|
public_key BLOB NOT NULL,
|
||||||
|
installation_id VARCHAR NOT NULL,
|
||||||
|
version INT NOT NULL,
|
||||||
|
registration BLOB,
|
||||||
|
UNIQUE(public_key, installation_id) ON CONFLICT REPLACE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS push_notification_server_identity (
|
||||||
|
private_key BLOB NOT NULL,
|
||||||
|
synthetic_id INT NOT NULL DEFAULT 0,
|
||||||
|
UNIQUE(synthetic_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_push_notification_server_registrations_public_key ON push_notification_server_registrations(public_key);
|
||||||
|
CREATE INDEX idx_push_notification_server_registrations_public_key_installation_id ON push_notification_server_registrations(public_key, installation_id);
|
||||||
|
|
9
protocol/pushnotificationserver/migrations/sql/doc.go
Normal file
9
protocol/pushnotificationserver/migrations/sql/doc.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// This file is necessary because "github.com/status-im/migrate/v4"
|
||||||
|
// can't handle files starting with a prefix. At least that's the case
|
||||||
|
// for go-bindata.
|
||||||
|
// If go-bindata is called from the same directory, asset names
|
||||||
|
// have no prefix and "github.com/status-im/migrate/v4" works as expected.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
//go:generate go-bindata -pkg migrations -o ../migrations.go ./
|
154
protocol/pushnotificationserver/persistence.go
Normal file
154
protocol/pushnotificationserver/persistence.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package pushnotificationserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"database/sql"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/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)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = ?`, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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{}
|
||||||
|
|
||||||
|
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`)
|
||||||
|
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) 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
|
||||||
|
}
|
83
protocol/pushnotificationserver/persistence_test.go
Normal file
83
protocol/pushnotificationserver/persistence_test.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package pushnotificationserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
"github.com/status-im/status-go/protocol/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSQLitePersistenceSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(SQLitePersistenceSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type SQLitePersistenceSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
tmpFile *os.File
|
||||||
|
persistence Persistence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) SetupTest() {
|
||||||
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.tmpFile = tmpFile
|
||||||
|
|
||||||
|
database, err := sqlite.Open(s.tmpFile.Name(), "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.persistence = NewSQLitePersistence(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TearDownTest() {
|
||||||
|
_ = os.Remove(s.tmpFile.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TestSaveAndRetrieve() {
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
installationID := "54242d02-bb92-11ea-b3de-0242ac130004"
|
||||||
|
|
||||||
|
registration := &protobuf.PushNotificationRegistration{
|
||||||
|
InstallationId: installationID,
|
||||||
|
Version: 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&key.PublicKey), registration))
|
||||||
|
|
||||||
|
retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&key.PublicKey), installationID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().True(proto.Equal(registration, retrievedRegistration))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveIdentity() {
|
||||||
|
retrievedKey, err := s.persistence.GetIdentity()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Nil(retrievedKey)
|
||||||
|
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NoError(s.persistence.SaveIdentity(key))
|
||||||
|
|
||||||
|
retrievedKey, err = s.persistence.GetIdentity()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Equal(key, retrievedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLitePersistenceSuite) TestSaveDifferentIdenities() {
|
||||||
|
key1, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
key2, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// First one should be successul, second should fail
|
||||||
|
s.Require().NoError(s.persistence.SaveIdentity(key1))
|
||||||
|
s.Require().Error(s.persistence.SaveIdentity(key2))
|
||||||
|
}
|
434
protocol/pushnotificationserver/server.go
Normal file
434
protocol/pushnotificationserver/server.go
Normal file
@ -0,0 +1,434 @@
|
|||||||
|
package pushnotificationserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto/ecies"
|
||||||
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
const encryptedPayloadKeyLength = 16
|
||||||
|
const defaultGorushURL = "https://gorush.status.im"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
// Identity is our identity key
|
||||||
|
Identity *ecdsa.PrivateKey
|
||||||
|
// GorushUrl is the url for the gorush service
|
||||||
|
GorushURL string
|
||||||
|
|
||||||
|
Logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
persistence Persistence
|
||||||
|
config *Config
|
||||||
|
messageProcessor *common.MessageProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(config *Config, persistence Persistence, messageProcessor *common.MessageProcessor) *Server {
|
||||||
|
if len(config.GorushURL) == 0 {
|
||||||
|
config.GorushURL = defaultGorushURL
|
||||||
|
|
||||||
|
}
|
||||||
|
return &Server{persistence: persistence, config: config, messageProcessor: messageProcessor}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() error {
|
||||||
|
s.config.Logger.Info("starting push notification server")
|
||||||
|
if s.config.Identity == nil {
|
||||||
|
s.config.Logger.Info("Identity nil")
|
||||||
|
// Pull identity from database
|
||||||
|
identity, err := s.persistence.GetIdentity()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if identity == nil {
|
||||||
|
identity, err = crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.persistence.SaveIdentity(identity); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.config.Identity = identity
|
||||||
|
}
|
||||||
|
|
||||||
|
pks, err := s.persistence.GetPushNotificationRegistrationPublicKeys()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// listen to all topics for users registered
|
||||||
|
for _, pk := range pks {
|
||||||
|
if err := s.listenToPublicKeyQueryTopic(pk); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.config.Logger.Info("started push notification server", zap.String("identity", types.EncodeHex(crypto.FromECDSAPub(&s.config.Identity.PublicKey))))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlePushNotificationRegistration builds a response for the registration and sends it back to the user
|
||||||
|
func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) error {
|
||||||
|
response := s.buildPushNotificationRegistrationResponse(publicKey, payload)
|
||||||
|
if response == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
encodedMessage, err := proto.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawMessage := &common.RawMessage{
|
||||||
|
Payload: encodedMessage,
|
||||||
|
MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE,
|
||||||
|
// we skip encryption as might be sent from an ephemeral key
|
||||||
|
SkipEncryption: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlePushNotificationQuery builds a response for the query and sends it back to the user
|
||||||
|
func (s *Server) HandlePushNotificationQuery(publicKey *ecdsa.PublicKey, messageID []byte, query protobuf.PushNotificationQuery) error {
|
||||||
|
response := s.buildPushNotificationQueryResponse(&query)
|
||||||
|
if response == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
response.MessageId = messageID
|
||||||
|
encodedMessage, err := proto.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawMessage := &common.RawMessage{
|
||||||
|
Payload: encodedMessage,
|
||||||
|
MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE,
|
||||||
|
// we skip encryption as sent from an ephemeral key
|
||||||
|
SkipEncryption: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlePushNotificationRequest will send a gorush notification and send a response back to the user
|
||||||
|
func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey,
|
||||||
|
request protobuf.PushNotificationRequest) error {
|
||||||
|
s.config.Logger.Debug("handling pn request")
|
||||||
|
response := s.buildPushNotificationRequestResponseAndSendNotification(&request)
|
||||||
|
if response == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
encodedMessage, err := proto.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawMessage := &common.RawMessage{
|
||||||
|
Payload: encodedMessage,
|
||||||
|
MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE,
|
||||||
|
// We skip encryption here as the message has been sent from an ephemeral key
|
||||||
|
SkipEncryption: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildGrantSignatureMaterial builds a grant for a specific server.
|
||||||
|
// We use 3 components:
|
||||||
|
// 1) The client public key. Not sure this applies to our signature scheme, but best to be conservative. https://crypto.stackexchange.com/questions/15538/given-a-message-and-signature-find-a-public-key-that-makes-the-signature-valid
|
||||||
|
// 2) The server public key
|
||||||
|
// 3) The access token
|
||||||
|
// By verifying this signature, a client can trust the server was instructed to store this access token.
|
||||||
|
func (s *Server) buildGrantSignatureMaterial(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, accessToken string) []byte {
|
||||||
|
var signatureMaterial []byte
|
||||||
|
signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(clientPublicKey)...)
|
||||||
|
signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(serverPublicKey)...)
|
||||||
|
signatureMaterial = append(signatureMaterial, []byte(accessToken)...)
|
||||||
|
a := crypto.Keccak256(signatureMaterial)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) verifyGrantSignature(clientPublicKey *ecdsa.PublicKey, accessToken string, grant []byte) error {
|
||||||
|
signatureMaterial := s.buildGrantSignatureMaterial(clientPublicKey, &s.config.Identity.PublicKey, accessToken)
|
||||||
|
recoveredPublicKey, err := crypto.SigToPub(signatureMaterial, grant)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !common.IsPubKeyEqual(recoveredPublicKey, clientPublicKey) {
|
||||||
|
return errors.New("pubkey mismatch")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) {
|
||||||
|
return ecies.ImportECDSA(s.config.Identity).GenerateShared(
|
||||||
|
ecies.ImportECDSAPublic(publicKey),
|
||||||
|
encryptedPayloadKeyLength,
|
||||||
|
encryptedPayloadKeyLength,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) validateUUID(u string) error {
|
||||||
|
if len(u) == 0 {
|
||||||
|
return errors.New("empty uuid")
|
||||||
|
}
|
||||||
|
_, err := uuid.Parse(u)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) decryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) {
|
||||||
|
sharedKey, err := s.generateSharedKey(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return common.Decrypt(payload, sharedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateRegistration validates a new message against the last one received for a given installationID and and public key
|
||||||
|
// and return the decrypted message
|
||||||
|
func (s *Server) validateRegistration(publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationRegistration, error) {
|
||||||
|
if payload == nil {
|
||||||
|
return nil, ErrEmptyPushNotificationRegistrationPayload
|
||||||
|
}
|
||||||
|
|
||||||
|
if publicKey == nil {
|
||||||
|
return nil, ErrEmptyPushNotificationRegistrationPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptedPayload, err := s.decryptRegistration(publicKey, payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
registration := &protobuf.PushNotificationRegistration{}
|
||||||
|
|
||||||
|
if err := proto.Unmarshal(decryptedPayload, registration); err != nil {
|
||||||
|
return nil, ErrCouldNotUnmarshalPushNotificationRegistration
|
||||||
|
}
|
||||||
|
|
||||||
|
if registration.Version < 1 {
|
||||||
|
return nil, ErrInvalidPushNotificationRegistrationVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.validateUUID(registration.InstallationId); err != nil {
|
||||||
|
return nil, ErrMalformedPushNotificationRegistrationInstallationID
|
||||||
|
}
|
||||||
|
|
||||||
|
previousRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(publicKey), registration.InstallationId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if previousRegistration != nil && registration.Version <= previousRegistration.Version {
|
||||||
|
return nil, ErrInvalidPushNotificationRegistrationVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// unregistering message
|
||||||
|
if registration.Unregister {
|
||||||
|
return registration, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.validateUUID(registration.AccessToken); err != nil {
|
||||||
|
return nil, ErrMalformedPushNotificationRegistrationAccessToken
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(registration.Grant) == 0 {
|
||||||
|
return nil, ErrMalformedPushNotificationRegistrationGrant
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.verifyGrantSignature(publicKey, registration.AccessToken, registration.Grant); err != nil {
|
||||||
|
|
||||||
|
s.config.Logger.Error("failed to verify grant", zap.Error(err))
|
||||||
|
return nil, ErrMalformedPushNotificationRegistrationGrant
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(registration.DeviceToken) == 0 {
|
||||||
|
return nil, ErrMalformedPushNotificationRegistrationDeviceToken
|
||||||
|
}
|
||||||
|
|
||||||
|
if registration.TokenType == protobuf.PushNotificationRegistration_UNKNOWN_TOKEN_TYPE {
|
||||||
|
return nil, ErrUnknownPushNotificationRegistrationTokenType
|
||||||
|
}
|
||||||
|
|
||||||
|
return registration, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildPushNotificationQueryResponse check if we have the client information and send them back
|
||||||
|
func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse {
|
||||||
|
|
||||||
|
s.config.Logger.Debug("handling push notification query")
|
||||||
|
response := &protobuf.PushNotificationQueryResponse{}
|
||||||
|
if query == nil || len(query.PublicKeys) == 0 {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
registrations, err := s.persistence.GetPushNotificationRegistrationByPublicKeys(query.PublicKeys)
|
||||||
|
if err != nil {
|
||||||
|
s.config.Logger.Error("failed to retrieve registration", zap.Error(err))
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, idAndResponse := range registrations {
|
||||||
|
|
||||||
|
registration := idAndResponse.Registration
|
||||||
|
info := &protobuf.PushNotificationQueryInfo{
|
||||||
|
PublicKey: idAndResponse.ID,
|
||||||
|
Grant: registration.Grant,
|
||||||
|
Version: registration.Version,
|
||||||
|
InstallationId: registration.InstallationId,
|
||||||
|
}
|
||||||
|
|
||||||
|
// if instructed to only allow from contacts, send back a list
|
||||||
|
if registration.AllowFromContactsOnly {
|
||||||
|
info.AllowedKeyList = registration.AllowedKeyList
|
||||||
|
} else {
|
||||||
|
info.AccessToken = registration.AccessToken
|
||||||
|
}
|
||||||
|
response.Info = append(response.Info, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Success = true
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildPushNotificationRequestResponseAndSendNotification will build a response
|
||||||
|
// and fire-and-forget send a query to the gorush instance
|
||||||
|
func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse {
|
||||||
|
response := &protobuf.PushNotificationResponse{}
|
||||||
|
// We don't even send a response in this case
|
||||||
|
if request == nil || len(request.MessageId) == 0 {
|
||||||
|
s.config.Logger.Warn("empty message id")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
response.MessageId = request.MessageId
|
||||||
|
|
||||||
|
// TODO: filter by chat id
|
||||||
|
// collect successful requests & registrations
|
||||||
|
var requestAndRegistrations []*RequestAndRegistration
|
||||||
|
|
||||||
|
for _, pn := range request.Requests {
|
||||||
|
registration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId)
|
||||||
|
report := &protobuf.PushNotificationReport{
|
||||||
|
PublicKey: pn.PublicKey,
|
||||||
|
InstallationId: pn.InstallationId,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.config.Logger.Error("failed to retrieve registration", zap.Error(err))
|
||||||
|
report.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
|
||||||
|
} else if registration == nil {
|
||||||
|
s.config.Logger.Warn("empty registration")
|
||||||
|
report.Error = protobuf.PushNotificationReport_NOT_REGISTERED
|
||||||
|
} else if registration.AccessToken != pn.AccessToken {
|
||||||
|
s.config.Logger.Warn("invalid access token")
|
||||||
|
report.Error = protobuf.PushNotificationReport_WRONG_TOKEN
|
||||||
|
} else {
|
||||||
|
// For now we just assume that the notification will be successful
|
||||||
|
requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{
|
||||||
|
Request: pn,
|
||||||
|
Registration: registration,
|
||||||
|
})
|
||||||
|
report.Success = true
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Reports = append(response.Reports, report)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.config.Logger.Debug("built pn request")
|
||||||
|
if len(requestAndRegistrations) == 0 {
|
||||||
|
s.config.Logger.Warn("no request and registration")
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can be done asynchronously
|
||||||
|
goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations)
|
||||||
|
err := sendGoRushNotification(goRushRequest, s.config.GorushURL)
|
||||||
|
if err != nil {
|
||||||
|
s.config.Logger.Error("failed to send go rush notification", zap.Error(err))
|
||||||
|
// TODO: handle this error?
|
||||||
|
// GoRush will not let us know that the sending of the push notification has failed,
|
||||||
|
// so this likely mean that the actual HTTP request has failed, or there was some unexpected error
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
// listenToPublicKeyQueryTopic listen to a topic derived from the hashed public key
|
||||||
|
func (s *Server) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error {
|
||||||
|
if s.messageProcessor == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
encodedPublicKey := hex.EncodeToString(hashedPublicKey)
|
||||||
|
return s.messageProcessor.JoinPublic(encodedPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildPushNotificationRegistrationResponse will check the registration is valid, save it, and listen to the topic for the queries
|
||||||
|
func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse {
|
||||||
|
|
||||||
|
s.config.Logger.Info("handling push notification registration")
|
||||||
|
response := &protobuf.PushNotificationRegistrationResponse{
|
||||||
|
RequestId: common.Shake256(payload),
|
||||||
|
}
|
||||||
|
|
||||||
|
registration, err := s.validateRegistration(publicKey, payload)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrInvalidPushNotificationRegistrationVersion {
|
||||||
|
response.Error = protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH
|
||||||
|
} else {
|
||||||
|
response.Error = protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE
|
||||||
|
}
|
||||||
|
s.config.Logger.Warn("registration did not validate", zap.Error(err))
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
if registration.Unregister {
|
||||||
|
// We save an empty registration, only keeping version and installation-id
|
||||||
|
emptyRegistration := &protobuf.PushNotificationRegistration{
|
||||||
|
Version: registration.Version,
|
||||||
|
InstallationId: registration.InstallationId,
|
||||||
|
}
|
||||||
|
if err := s.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), emptyRegistration); err != nil {
|
||||||
|
response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR
|
||||||
|
s.config.Logger.Error("failed to unregister ", zap.Error(err))
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if err := s.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), registration); err != nil {
|
||||||
|
response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR
|
||||||
|
s.config.Logger.Error("failed to save registration", zap.Error(err))
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.listenToPublicKeyQueryTopic(common.HashPublicKey(publicKey)); err != nil {
|
||||||
|
response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR
|
||||||
|
s.config.Logger.Error("failed to listen to topic", zap.Error(err))
|
||||||
|
return response
|
||||||
|
|
||||||
|
}
|
||||||
|
response.Success = true
|
||||||
|
|
||||||
|
s.config.Logger.Info("handled push notification registration successfully")
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
584
protocol/pushnotificationserver/server_test.go
Normal file
584
protocol/pushnotificationserver/server_test.go
Normal file
@ -0,0 +1,584 @@
|
|||||||
|
package pushnotificationserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
"github.com/status-im/status-go/protocol/sqlite"
|
||||||
|
"github.com/status-im/status-go/protocol/tt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServerSuite(t *testing.T) {
|
||||||
|
s := new(ServerSuite)
|
||||||
|
s.accessToken = "b6ae4fde-bb65-11ea-b3de-0242ac130004"
|
||||||
|
s.installationID = "c6ae4fde-bb65-11ea-b3de-0242ac130004"
|
||||||
|
|
||||||
|
suite.Run(t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
tmpFile *os.File
|
||||||
|
persistence Persistence
|
||||||
|
accessToken string
|
||||||
|
installationID string
|
||||||
|
identity *ecdsa.PrivateKey
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
sharedKey []byte
|
||||||
|
grant []byte
|
||||||
|
server *Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerSuite) SetupTest() {
|
||||||
|
tmpFile, err := ioutil.TempFile("", "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.tmpFile = tmpFile
|
||||||
|
|
||||||
|
database, err := sqlite.Open(s.tmpFile.Name(), "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.persistence = NewSQLitePersistence(database)
|
||||||
|
|
||||||
|
identity, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.identity = identity
|
||||||
|
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.key = key
|
||||||
|
|
||||||
|
config := &Config{
|
||||||
|
Identity: identity,
|
||||||
|
Logger: tt.MustCreateTestLogger(),
|
||||||
|
}
|
||||||
|
|
||||||
|
s.server = New(config, s.persistence, nil)
|
||||||
|
|
||||||
|
sharedKey, err := s.server.generateSharedKey(&s.key.PublicKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.sharedKey = sharedKey
|
||||||
|
signatureMaterial := s.server.buildGrantSignatureMaterial(&s.key.PublicKey, &identity.PublicKey, s.accessToken)
|
||||||
|
grant, err := crypto.Sign(signatureMaterial, s.key)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.grant = grant
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerSuite) TestPushNotificationServerValidateRegistration() {
|
||||||
|
|
||||||
|
// Empty payload
|
||||||
|
_, err := s.server.validateRegistration(&s.key.PublicKey, nil)
|
||||||
|
s.Require().Equal(ErrEmptyPushNotificationRegistrationPayload, err)
|
||||||
|
|
||||||
|
// Empty key
|
||||||
|
_, err = s.server.validateRegistration(nil, []byte("payload"))
|
||||||
|
s.Require().Equal(ErrEmptyPushNotificationRegistrationPublicKey, err)
|
||||||
|
|
||||||
|
// Invalid cyphertext length
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, []byte("too short"))
|
||||||
|
s.Require().Equal(common.ErrInvalidCiphertextLength, err)
|
||||||
|
|
||||||
|
// Invalid cyphertext length
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, []byte("too short"))
|
||||||
|
s.Require().Equal(common.ErrInvalidCiphertextLength, err)
|
||||||
|
|
||||||
|
// Invalid ciphertext
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, []byte("not too short but invalid"))
|
||||||
|
s.Require().Error(common.ErrInvalidCiphertextLength, err)
|
||||||
|
|
||||||
|
// Different key ciphertext
|
||||||
|
cyphertext, err := common.Encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Error(err)
|
||||||
|
|
||||||
|
// Right cyphertext but non unmarshable payload
|
||||||
|
cyphertext, err = common.Encrypt([]byte("plaintext"), s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrCouldNotUnmarshalPushNotificationRegistration, err)
|
||||||
|
|
||||||
|
// Missing installationID
|
||||||
|
payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err)
|
||||||
|
|
||||||
|
// Malformed installationID
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: "abc",
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err)
|
||||||
|
|
||||||
|
// Version set to 0
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err)
|
||||||
|
|
||||||
|
// Version lower than previous one
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Setup persistence
|
||||||
|
s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 2}))
|
||||||
|
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err)
|
||||||
|
|
||||||
|
// Cleanup persistence
|
||||||
|
s.Require().NoError(s.persistence.DeletePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), s.installationID))
|
||||||
|
|
||||||
|
// Unregistering message
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Unregister: true,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Nil(err)
|
||||||
|
|
||||||
|
// Missing access token
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Grant: s.grant,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err)
|
||||||
|
|
||||||
|
// Invalid access token
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: "bc",
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err)
|
||||||
|
|
||||||
|
// Missing device token
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrMalformedPushNotificationRegistrationDeviceToken, err)
|
||||||
|
|
||||||
|
// Missing grant
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
DeviceToken: "device-token",
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrMalformedPushNotificationRegistrationGrant, err)
|
||||||
|
|
||||||
|
// Invalid grant
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
DeviceToken: "device-token",
|
||||||
|
Grant: crypto.Keccak256([]byte("invalid")),
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrMalformedPushNotificationRegistrationGrant, err)
|
||||||
|
|
||||||
|
// Missing token type
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
DeviceToken: "device-token",
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().Equal(ErrUnknownPushNotificationRegistrationTokenType, err)
|
||||||
|
|
||||||
|
// Successful
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
DeviceToken: "abc",
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerSuite) TestPushNotificationHandleRegistration() {
|
||||||
|
// Empty payload
|
||||||
|
response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, nil)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Empty key
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(nil, []byte("payload"))
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Invalid cyphertext length
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, []byte("too short"))
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Invalid cyphertext length
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, []byte("too short"))
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Invalid ciphertext
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, []byte("not too short but invalid"))
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Different key ciphertext
|
||||||
|
cyphertext, err := common.Encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Right cyphertext but non unmarshable payload
|
||||||
|
cyphertext, err = common.Encrypt([]byte("plaintext"), s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Missing installationID
|
||||||
|
payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Malformed installationID
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
InstallationId: "abc",
|
||||||
|
Grant: s.grant,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Version set to 0
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH)
|
||||||
|
|
||||||
|
// Version lower than previous one
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Setup persistence
|
||||||
|
s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 2}))
|
||||||
|
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH)
|
||||||
|
|
||||||
|
// Cleanup persistence
|
||||||
|
s.Require().NoError(s.persistence.DeletePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), s.installationID))
|
||||||
|
|
||||||
|
// Missing access token
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Grant: s.grant,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Invalid access token
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: "bc",
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Missing device token
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().False(response.Success)
|
||||||
|
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||||
|
|
||||||
|
// Successful
|
||||||
|
registration := &protobuf.PushNotificationRegistration{
|
||||||
|
DeviceToken: "abc",
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
}
|
||||||
|
payload, err = proto.Marshal(registration)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().True(response.Success)
|
||||||
|
|
||||||
|
// Pull from the db
|
||||||
|
retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&s.key.PublicKey), s.installationID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(retrievedRegistration)
|
||||||
|
s.Require().True(proto.Equal(retrievedRegistration, registration))
|
||||||
|
|
||||||
|
// Unregistering message
|
||||||
|
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||||
|
DeviceToken: "token",
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Unregister: true,
|
||||||
|
Version: 2,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().True(response.Success)
|
||||||
|
|
||||||
|
// Check is gone from the db
|
||||||
|
retrievedRegistration, err = s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&s.key.PublicKey), s.installationID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(retrievedRegistration)
|
||||||
|
s.Require().Empty(retrievedRegistration.AccessToken)
|
||||||
|
s.Require().Empty(retrievedRegistration.DeviceToken)
|
||||||
|
s.Require().Equal(uint64(2), retrievedRegistration.Version)
|
||||||
|
s.Require().Equal(s.installationID, retrievedRegistration.InstallationId)
|
||||||
|
s.Require().Equal(common.Shake256(cyphertext), response.RequestId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerSuite) TestbuildPushNotificationQueryResponseNoFiltering() {
|
||||||
|
hashedPublicKey := common.HashPublicKey(&s.key.PublicKey)
|
||||||
|
// Successful
|
||||||
|
registration := &protobuf.PushNotificationRegistration{
|
||||||
|
DeviceToken: "abc",
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
Version: 1,
|
||||||
|
}
|
||||||
|
payload, err := proto.Marshal(registration)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().True(response.Success)
|
||||||
|
|
||||||
|
query := &protobuf.PushNotificationQuery{
|
||||||
|
PublicKeys: [][]byte{[]byte("non-existing"), hashedPublicKey},
|
||||||
|
}
|
||||||
|
|
||||||
|
queryResponse := s.server.buildPushNotificationQueryResponse(query)
|
||||||
|
s.Require().NotNil(queryResponse)
|
||||||
|
s.Require().True(queryResponse.Success)
|
||||||
|
s.Require().Len(queryResponse.Info, 1)
|
||||||
|
s.Require().Equal(s.accessToken, queryResponse.Info[0].AccessToken)
|
||||||
|
s.Require().Equal(hashedPublicKey, queryResponse.Info[0].PublicKey)
|
||||||
|
s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId)
|
||||||
|
s.Require().Nil(queryResponse.Info[0].AllowedKeyList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerSuite) TestbuildPushNotificationQueryResponseWithFiltering() {
|
||||||
|
hashedPublicKey := common.HashPublicKey(&s.key.PublicKey)
|
||||||
|
allowedKeyList := [][]byte{[]byte("a")}
|
||||||
|
// Successful
|
||||||
|
|
||||||
|
registration := &protobuf.PushNotificationRegistration{
|
||||||
|
DeviceToken: "abc",
|
||||||
|
AccessToken: s.accessToken,
|
||||||
|
Grant: s.grant,
|
||||||
|
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||||
|
InstallationId: s.installationID,
|
||||||
|
AllowFromContactsOnly: true,
|
||||||
|
AllowedKeyList: allowedKeyList,
|
||||||
|
Version: 1,
|
||||||
|
}
|
||||||
|
payload, err := proto.Marshal(registration)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().True(response.Success)
|
||||||
|
|
||||||
|
query := &protobuf.PushNotificationQuery{
|
||||||
|
PublicKeys: [][]byte{[]byte("non-existing"), hashedPublicKey},
|
||||||
|
}
|
||||||
|
|
||||||
|
queryResponse := s.server.buildPushNotificationQueryResponse(query)
|
||||||
|
s.Require().NotNil(queryResponse)
|
||||||
|
s.Require().True(queryResponse.Success)
|
||||||
|
s.Require().Len(queryResponse.Info, 1)
|
||||||
|
s.Require().Equal(hashedPublicKey, queryResponse.Info[0].PublicKey)
|
||||||
|
s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId)
|
||||||
|
s.Require().Equal(allowedKeyList, queryResponse.Info[0].AllowedKeyList)
|
||||||
|
}
|
@ -7,6 +7,8 @@ import (
|
|||||||
|
|
||||||
encryptmigrations "github.com/status-im/status-go/protocol/encryption/migrations"
|
encryptmigrations "github.com/status-im/status-go/protocol/encryption/migrations"
|
||||||
appmigrations "github.com/status-im/status-go/protocol/migrations"
|
appmigrations "github.com/status-im/status-go/protocol/migrations"
|
||||||
|
push_notification_client_migrations "github.com/status-im/status-go/protocol/pushnotificationclient/migrations"
|
||||||
|
push_notification_server_migrations "github.com/status-im/status-go/protocol/pushnotificationserver/migrations"
|
||||||
wakumigrations "github.com/status-im/status-go/protocol/transport/waku/migrations"
|
wakumigrations "github.com/status-im/status-go/protocol/transport/waku/migrations"
|
||||||
whispermigrations "github.com/status-im/status-go/protocol/transport/whisper/migrations"
|
whispermigrations "github.com/status-im/status-go/protocol/transport/whisper/migrations"
|
||||||
)
|
)
|
||||||
@ -35,6 +37,14 @@ var defaultMigrations = []migrationsWithGetter{
|
|||||||
Names: appmigrations.AssetNames(),
|
Names: appmigrations.AssetNames(),
|
||||||
Getter: appmigrations.Asset,
|
Getter: appmigrations.Asset,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Names: push_notification_server_migrations.AssetNames(),
|
||||||
|
Getter: push_notification_server_migrations.Asset,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: push_notification_client_migrations.AssetNames(),
|
||||||
|
Getter: push_notification_client_migrations.Asset,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareMigrations(migrations []migrationsWithGetter) ([]string, getter, error) {
|
func prepareMigrations(migrations []migrationsWithGetter) ([]string, getter, error) {
|
||||||
|
@ -218,15 +218,15 @@ func (s *FiltersManager) Remove(filters ...*Filter) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadPartitioned creates a filter for a partitioned topic.
|
// LoadPartitioned creates a filter for a partitioned topic.
|
||||||
func (s *FiltersManager) LoadPartitioned(publicKey *ecdsa.PublicKey) (*Filter, error) {
|
func (s *FiltersManager) LoadPartitioned(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen bool) (*Filter, error) {
|
||||||
return s.loadPartitioned(publicKey, false)
|
return s.loadPartitioned(publicKey, identity, listen)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FiltersManager) loadMyPartitioned() (*Filter, error) {
|
func (s *FiltersManager) loadMyPartitioned() (*Filter, error) {
|
||||||
return s.loadPartitioned(&s.privateKey.PublicKey, true)
|
return s.loadPartitioned(&s.privateKey.PublicKey, s.privateKey, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, listen bool) (*Filter, error) {
|
func (s *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen bool) (*Filter, error) {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ func (s *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, listen bool
|
|||||||
|
|
||||||
// We set up a filter so we can publish,
|
// We set up a filter so we can publish,
|
||||||
// but we discard envelopes if listen is false.
|
// but we discard envelopes if listen is false.
|
||||||
filter, err := s.addAsymmetric(chatID, listen)
|
filter, err := s.addAsymmetric(chatID, identity, listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -321,7 +321,7 @@ func (s *FiltersManager) LoadDiscovery() ([]*Filter, error) {
|
|||||||
OneToOne: true,
|
OneToOne: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
discoveryResponse, err := s.addAsymmetric(personalDiscoveryChat.ChatID, true)
|
discoveryResponse, err := s.addAsymmetric(personalDiscoveryChat.ChatID, s.privateKey, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -439,7 +439,7 @@ func (s *FiltersManager) addSymmetric(chatID string) (*RawFilter, error) {
|
|||||||
|
|
||||||
// addAsymmetricFilter adds a filter with our private key
|
// addAsymmetricFilter adds a filter with our private key
|
||||||
// and set minPow according to the listen parameter.
|
// and set minPow according to the listen parameter.
|
||||||
func (s *FiltersManager) addAsymmetric(chatID string, listen bool) (*RawFilter, error) {
|
func (s *FiltersManager) addAsymmetric(chatID string, identity *ecdsa.PrivateKey, listen bool) (*RawFilter, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
pow = 1.0 // use PoW high enough to discard all messages for the filter
|
pow = 1.0 // use PoW high enough to discard all messages for the filter
|
||||||
@ -452,7 +452,7 @@ func (s *FiltersManager) addAsymmetric(chatID string, listen bool) (*RawFilter,
|
|||||||
topic := ToTopic(chatID)
|
topic := ToTopic(chatID)
|
||||||
topics := [][]byte{topic}
|
topics := [][]byte{topic}
|
||||||
|
|
||||||
privateKeyID, err := s.service.AddKeyPair(s.privateKey)
|
privateKeyID, err := s.service.AddKeyPair(identity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ type Transport interface {
|
|||||||
RemoveFilters(filters []*Filter) error
|
RemoveFilters(filters []*Filter) error
|
||||||
ResetFilters() error
|
ResetFilters() error
|
||||||
Filters() []*Filter
|
Filters() []*Filter
|
||||||
|
LoadKeyFilters(*ecdsa.PrivateKey) (*Filter, error)
|
||||||
ProcessNegotiatedSecret(secret types.NegotiatedSecret) (*Filter, error)
|
ProcessNegotiatedSecret(secret types.NegotiatedSecret) (*Filter, error)
|
||||||
RetrieveRawAll() (map[Filter][]*types.Message, error)
|
RetrieveRawAll() (map[Filter][]*types.Message, error)
|
||||||
}
|
}
|
||||||
|
@ -204,61 +204,6 @@ func (a *Transport) LeaveGroup(publicKeys []*ecdsa.PublicKey) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
Message *types.Message
|
|
||||||
Public bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Transport) RetrieveAllMessages() ([]Message, error) {
|
|
||||||
var messages []Message
|
|
||||||
|
|
||||||
for _, filter := range a.filters.Filters() {
|
|
||||||
filterMsgs, err := a.api.GetFilterMessages(filter.FilterID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, m := range filterMsgs {
|
|
||||||
messages = append(messages, Message{
|
|
||||||
Message: m,
|
|
||||||
Public: filter.IsPublic(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return messages, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Transport) RetrievePublicMessages(chatID string) ([]*types.Message, error) {
|
|
||||||
filter, err := a.filters.LoadPublic(chatID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.api.GetFilterMessages(filter.FilterID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Transport) RetrievePrivateMessages(publicKey *ecdsa.PublicKey) ([]*types.Message, error) {
|
|
||||||
chats := a.filters.FiltersByPublicKey(publicKey)
|
|
||||||
discoveryChats, err := a.filters.Init(nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var result []*types.Message
|
|
||||||
|
|
||||||
for _, chat := range append(chats, discoveryChats...) {
|
|
||||||
filterMsgs, err := a.api.GetFilterMessages(chat.FilterID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result = append(result, filterMsgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Transport) RetrieveRawAll() (map[transport.Filter][]*types.Message, error) {
|
func (a *Transport) RetrieveRawAll() (map[transport.Filter][]*types.Message, error) {
|
||||||
result := make(map[transport.Filter][]*types.Message)
|
result := make(map[transport.Filter][]*types.Message)
|
||||||
|
|
||||||
@ -318,7 +263,7 @@ func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage *
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
filter, err := a.filters.LoadPartitioned(publicKey)
|
filter, err := a.filters.LoadPartitioned(publicKey, a.keysManager.privateKey, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -329,6 +274,10 @@ func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage *
|
|||||||
return a.api.Post(ctx, *newMessage)
|
return a.api.Post(ctx, *newMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Transport) LoadKeyFilters(key *ecdsa.PrivateKey) (*transport.Filter, error) {
|
||||||
|
return a.filters.LoadPartitioned(&key.PublicKey, key, true)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Transport) SendPrivateOnDiscovery(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) {
|
func (a *Transport) SendPrivateOnDiscovery(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) {
|
||||||
if err := a.addSig(newMessage); err != nil {
|
if err := a.addSig(newMessage); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -318,7 +318,7 @@ func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage *
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
filter, err := a.filters.LoadPartitioned(publicKey)
|
filter, err := a.filters.LoadPartitioned(publicKey, a.keysManager.privateKey, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -406,6 +406,10 @@ func (a *Transport) SendMessagesRequest(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Transport) LoadKeyFilters(key *ecdsa.PrivateKey) (*transport.Filter, error) {
|
||||||
|
return a.filters.LoadPartitioned(&key.PublicKey, key, true)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Transport) waitForRequestCompleted(ctx context.Context, requestID []byte, events chan types.EnvelopeEvent) (*types.MailServerResponse, error) {
|
func (a *Transport) waitForRequestCompleted(ctx context.Context, requestID []byte, events chan types.EnvelopeEvent) (*types.MailServerResponse, error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -37,6 +37,9 @@ type StatusMessage struct {
|
|||||||
// Hash is the transport layer hash
|
// Hash is the transport layer hash
|
||||||
Hash []byte `json:"-"`
|
Hash []byte `json:"-"`
|
||||||
|
|
||||||
|
// Dst is the targeted public key
|
||||||
|
Dst *ecdsa.PublicKey
|
||||||
|
|
||||||
// TransportLayerSigPubKey contains the public key provided by the transport layer
|
// TransportLayerSigPubKey contains the public key provided by the transport layer
|
||||||
TransportLayerSigPubKey *ecdsa.PublicKey `json:"-"`
|
TransportLayerSigPubKey *ecdsa.PublicKey `json:"-"`
|
||||||
// ApplicationMetadataLayerPubKey contains the public key provided by the application metadata layer
|
// ApplicationMetadataLayerPubKey contains the public key provided by the application metadata layer
|
||||||
@ -87,13 +90,25 @@ func (m *StatusMessage) HandleTransport(shhMessage *types.Message) error {
|
|||||||
m.TransportLayerSigPubKey = publicKey
|
m.TransportLayerSigPubKey = publicKey
|
||||||
m.TransportPayload = shhMessage.Payload
|
m.TransportPayload = shhMessage.Payload
|
||||||
|
|
||||||
|
if shhMessage.Dst != nil {
|
||||||
|
publicKey, err := crypto.UnmarshalPubkey(shhMessage.Dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.Dst = publicKey
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecdsa.PublicKey, enc *encryption.Protocol) error {
|
func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecdsa.PublicKey, enc *encryption.Protocol, skipNegotiation bool) error {
|
||||||
// As we handle non-encrypted messages, we make sure that DecryptPayload
|
// As we handle non-encrypted messages, we make sure that DecryptPayload
|
||||||
// is set regardless of whether this step is successful
|
// is set regardless of whether this step is successful
|
||||||
m.DecryptedPayload = m.TransportPayload
|
m.DecryptedPayload = m.TransportPayload
|
||||||
|
// Nothing to do
|
||||||
|
if skipNegotiation {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var protocolMessage encryption.ProtocolMessage
|
var protocolMessage encryption.ProtocolMessage
|
||||||
err := proto.Unmarshal(m.TransportPayload, &protocolMessage)
|
err := proto.Unmarshal(m.TransportPayload, &protocolMessage)
|
||||||
@ -319,6 +334,82 @@ func (m *StatusMessage) HandleApplication() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION:
|
||||||
|
// This message is a bit different as it's encrypted, so we pass it straight through
|
||||||
|
|
||||||
|
m.ParsedMessage = m.DecryptedPayload
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT:
|
||||||
|
var message protobuf.ContactCodeAdvertisement
|
||||||
|
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||||
|
if err != nil {
|
||||||
|
m.ParsedMessage = nil
|
||||||
|
log.Printf("[message::DecodeMessage] could not decode ContactCodeAdvertisement: %#x, err: %v", m.Hash, err.Error())
|
||||||
|
} else {
|
||||||
|
m.ParsedMessage = message
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST:
|
||||||
|
var message protobuf.PushNotificationRequest
|
||||||
|
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||||
|
if err != nil {
|
||||||
|
m.ParsedMessage = nil
|
||||||
|
log.Printf("[message::DecodeMessage] could not decode PushNotificationRequest: %#x, err: %v", m.Hash, err.Error())
|
||||||
|
} else {
|
||||||
|
m.ParsedMessage = message
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE:
|
||||||
|
var message protobuf.PushNotificationRegistrationResponse
|
||||||
|
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||||
|
if err != nil {
|
||||||
|
m.ParsedMessage = nil
|
||||||
|
log.Printf("[message::DecodeMessage] could not decode PushNotificationRegistrationResponse: %#x, err: %v", m.Hash, err.Error())
|
||||||
|
} else {
|
||||||
|
m.ParsedMessage = message
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY:
|
||||||
|
var message protobuf.PushNotificationQuery
|
||||||
|
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||||
|
if err != nil {
|
||||||
|
m.ParsedMessage = nil
|
||||||
|
log.Printf("[message::DecodeMessage] could not decode PushNotificationQuery: %#x, err: %v", m.Hash, err.Error())
|
||||||
|
} else {
|
||||||
|
m.ParsedMessage = message
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE:
|
||||||
|
var message protobuf.PushNotificationQueryResponse
|
||||||
|
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||||
|
if err != nil {
|
||||||
|
m.ParsedMessage = nil
|
||||||
|
log.Printf("[message::DecodeMessage] could not decode PushNotificationQueryResponse: %#x, err: %v", m.Hash, err.Error())
|
||||||
|
} else {
|
||||||
|
m.ParsedMessage = message
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE:
|
||||||
|
var message protobuf.PushNotificationResponse
|
||||||
|
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||||
|
if err != nil {
|
||||||
|
m.ParsedMessage = nil
|
||||||
|
log.Printf("[message::DecodeMessage] could not decode PushNotificationResponse: %#x, err: %v", m.Hash, err.Error())
|
||||||
|
} else {
|
||||||
|
m.ParsedMessage = message
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,12 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/mailserver"
|
"github.com/status-im/status-go/mailserver"
|
||||||
"github.com/status-im/status-go/protocol"
|
"github.com/status-im/status-go/protocol"
|
||||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||||
|
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||||
"github.com/status-im/status-go/protocol/transport"
|
"github.com/status-im/status-go/protocol/transport"
|
||||||
"github.com/status-im/status-go/services/ext/mailservers"
|
"github.com/status-im/status-go/services/ext/mailservers"
|
||||||
)
|
)
|
||||||
@ -247,6 +249,14 @@ func (api *PublicAPI) DeleteChat(parent context.Context, chatID string) error {
|
|||||||
return api.service.messenger.DeleteChat(chatID)
|
return api.service.messenger.DeleteChat(chatID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) MuteChat(parent context.Context, chatID string) error {
|
||||||
|
return api.service.messenger.MuteChat(chatID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) UnmuteChat(parent context.Context, chatID string) error {
|
||||||
|
return api.service.messenger.UnmuteChat(chatID)
|
||||||
|
}
|
||||||
|
|
||||||
func (api *PublicAPI) SaveContact(parent context.Context, contact *protocol.Contact) error {
|
func (api *PublicAPI) SaveContact(parent context.Context, contact *protocol.Contact) error {
|
||||||
return api.service.messenger.SaveContact(contact)
|
return api.service.messenger.SaveContact(contact)
|
||||||
}
|
}
|
||||||
@ -393,6 +403,114 @@ func (api *PublicAPI) UpdateMailservers(enodes []string) error {
|
|||||||
return api.service.UpdateMailservers(nodes)
|
return api.service.UpdateMailservers(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushNotifications server endpoints
|
||||||
|
|
||||||
|
func (api *PublicAPI) StartPushNotificationsServer() error {
|
||||||
|
err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled?", true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.service.messenger.StartPushNotificationsServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) StopPushNotificationsServer() error {
|
||||||
|
err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled?", false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.service.messenger.StopPushNotificationsServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushNotification client endpoints
|
||||||
|
|
||||||
|
func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string) error {
|
||||||
|
// We set both for now as they are equivalent
|
||||||
|
err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = api.service.accountsDB.SaveSetting("notifications-enabled?", true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) UnregisterForPushNotifications(ctx context.Context) error {
|
||||||
|
err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = api.service.accountsDB.SaveSetting("notifications-enabled?", false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.service.messenger.UnregisterFromPushNotifications(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) DisableSendingNotifications(ctx context.Context) error {
|
||||||
|
err := api.service.accountsDB.SaveSetting("send-push-notifications?", false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.service.messenger.DisableSendingPushNotifications()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) EnableSendingNotifications(ctx context.Context) error {
|
||||||
|
err := api.service.accountsDB.SaveSetting("send-push-notifications?", true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return api.service.messenger.EnableSendingPushNotifications()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) EnablePushNotificationsFromContactsOnly(ctx context.Context) error {
|
||||||
|
err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only?", true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return api.service.messenger.EnablePushNotificationsFromContactsOnly()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) DisablePushNotificationsFromContactsOnly(ctx context.Context) error {
|
||||||
|
err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only?", false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return api.service.messenger.DisablePushNotificationsFromContactsOnly()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) AddPushNotificationsServer(ctx context.Context, publicKeyBytes types.HexBytes) error {
|
||||||
|
publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.service.messenger.AddPushNotificationsServer(ctx, publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) RemovePushNotificationServer(ctx context.Context, publicKeyBytes types.HexBytes) error {
|
||||||
|
publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.service.messenger.RemovePushNotificationServer(ctx, publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) GetPushNotificationServers() ([]*pushnotificationclient.PushNotificationServer, error) {
|
||||||
|
return api.service.messenger.GetPushNotificationServers()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) RegisteredForPushNotifications() (bool, error) {
|
||||||
|
return api.service.messenger.RegisteredForPushNotifications()
|
||||||
|
}
|
||||||
|
|
||||||
// Echo is a method for testing purposes.
|
// Echo is a method for testing purposes.
|
||||||
func (api *PublicAPI) Echo(ctx context.Context, message string) (string, error) {
|
func (api *PublicAPI) Echo(ctx context.Context, message string) (string, error) {
|
||||||
return message, nil
|
return message, nil
|
||||||
|
@ -33,6 +33,8 @@ import (
|
|||||||
coretypes "github.com/status-im/status-go/eth-node/core/types"
|
coretypes "github.com/status-im/status-go/eth-node/core/types"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/protocol"
|
"github.com/status-im/status-go/protocol"
|
||||||
|
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||||
|
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||||
"github.com/status-im/status-go/protocol/transport"
|
"github.com/status-im/status-go/protocol/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -144,7 +146,12 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, logger *z
|
|||||||
EnvelopeEventsHandler: EnvelopeSignalHandler{},
|
EnvelopeEventsHandler: EnvelopeSignalHandler{},
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
}
|
}
|
||||||
options := buildMessengerOptions(s.config, db, envelopesMonitorConfig, logger)
|
s.accountsDB = accounts.NewDB(db)
|
||||||
|
|
||||||
|
options, err := buildMessengerOptions(s.config, identity, db, envelopesMonitorConfig, s.accountsDB, logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
messenger, err := protocol.NewMessenger(
|
messenger, err := protocol.NewMessenger(
|
||||||
identity,
|
identity,
|
||||||
@ -155,7 +162,6 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, logger *z
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.accountsDB = accounts.NewDB(db)
|
|
||||||
s.messenger = messenger
|
s.messenger = messenger
|
||||||
return messenger.Init()
|
return messenger.Init()
|
||||||
}
|
}
|
||||||
@ -338,6 +344,9 @@ func (s *Service) DisableInstallation(installationID string) error {
|
|||||||
|
|
||||||
// UpdateMailservers updates information about selected mail servers.
|
// UpdateMailservers updates information about selected mail servers.
|
||||||
func (s *Service) UpdateMailservers(nodes []*enode.Node) error {
|
func (s *Service) UpdateMailservers(nodes []*enode.Node) error {
|
||||||
|
if len(nodes) > 0 && s.messenger != nil {
|
||||||
|
s.messenger.SetMailserver(nodes[0].ID().Bytes())
|
||||||
|
}
|
||||||
if err := s.peerStore.Update(nodes); err != nil {
|
if err := s.peerStore.Update(nodes); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -439,10 +448,12 @@ func onNegotiatedFilters(filters []*transport.Filter) {
|
|||||||
|
|
||||||
func buildMessengerOptions(
|
func buildMessengerOptions(
|
||||||
config params.ShhextConfig,
|
config params.ShhextConfig,
|
||||||
|
identity *ecdsa.PrivateKey,
|
||||||
db *sql.DB,
|
db *sql.DB,
|
||||||
envelopesMonitorConfig *transport.EnvelopesMonitorConfig,
|
envelopesMonitorConfig *transport.EnvelopesMonitorConfig,
|
||||||
|
accountsDB *accounts.Database,
|
||||||
logger *zap.Logger,
|
logger *zap.Logger,
|
||||||
) []protocol.Option {
|
) ([]protocol.Option, error) {
|
||||||
options := []protocol.Option{
|
options := []protocol.Option{
|
||||||
protocol.WithCustomLogger(logger),
|
protocol.WithCustomLogger(logger),
|
||||||
protocol.WithDatabase(db),
|
protocol.WithDatabase(db),
|
||||||
@ -453,6 +464,23 @@ func buildMessengerOptions(
|
|||||||
if config.DataSyncEnabled {
|
if config.DataSyncEnabled {
|
||||||
options = append(options, protocol.WithDatasync())
|
options = append(options, protocol.WithDatasync())
|
||||||
}
|
}
|
||||||
|
settings, err := accountsDB.GetSettings()
|
||||||
|
if err != sql.ErrNoRows && err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.PushNotificationsServerEnabled {
|
||||||
|
config := &pushnotificationserver.Config{
|
||||||
|
Logger: logger,
|
||||||
|
}
|
||||||
|
options = append(options, protocol.WithPushNotificationServerConfig(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, protocol.WithPushNotificationClientConfig(&pushnotificationclient.Config{
|
||||||
|
SendEnabled: settings.SendPushNotifications,
|
||||||
|
AllowFromContactsOnly: settings.PushNotificationsFromContactsOnly,
|
||||||
|
RemoteNotificationsEnabled: settings.RemotePushNotificationsEnabled,
|
||||||
|
}))
|
||||||
|
|
||||||
if config.VerifyTransactionURL != "" {
|
if config.VerifyTransactionURL != "" {
|
||||||
client := &verifyTransactionClient{
|
client := &verifyTransactionClient{
|
||||||
@ -462,5 +490,5 @@ func buildMessengerOptions(
|
|||||||
options = append(options, protocol.WithVerifyTransactionClient(client))
|
options = append(options, protocol.WithVerifyTransactionClient(client))
|
||||||
}
|
}
|
||||||
|
|
||||||
return options
|
return options, nil
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,12 @@ import (
|
|||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
"github.com/syndtr/goleveldb/leveldb/storage"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/appdatabase"
|
||||||
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/params"
|
"github.com/status-im/status-go/params"
|
||||||
"github.com/status-im/status-go/services/ext"
|
"github.com/status-im/status-go/services/ext"
|
||||||
"github.com/status-im/status-go/sqlite"
|
|
||||||
"github.com/status-im/status-go/t/helpers"
|
"github.com/status-im/status-go/t/helpers"
|
||||||
"github.com/status-im/status-go/whisper/v6"
|
"github.com/status-im/status-go/whisper/v6"
|
||||||
)
|
)
|
||||||
@ -209,7 +209,7 @@ func TestInitProtocol(t *testing.T) {
|
|||||||
tmpdir, err := ioutil.TempDir("", "test-shhext-service-init-protocol")
|
tmpdir, err := ioutil.TempDir("", "test-shhext-service-init-protocol")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/db.sql", tmpdir), "password")
|
sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/db.sql", tmpdir), "password")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = service.InitProtocol(privateKey, sqlDB, zap.NewNop())
|
err = service.InitProtocol(privateKey, sqlDB, zap.NewNop())
|
||||||
@ -262,7 +262,7 @@ func (s *ShhExtSuite) createAndAddNode() {
|
|||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
nodeWrapper := ext.NewTestNodeWrapper(gethbridge.NewGethWhisperWrapper(whisper), nil)
|
nodeWrapper := ext.NewTestNodeWrapper(gethbridge.NewGethWhisperWrapper(whisper), nil)
|
||||||
service := New(config, nodeWrapper, nil, nil, db)
|
service := New(config, nodeWrapper, nil, nil, db)
|
||||||
sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/%d", s.dir, idx), "password")
|
sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/%d", s.dir, idx), "password")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
privateKey, err := crypto.GenerateKey()
|
privateKey, err := crypto.GenerateKey()
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
|
@ -23,12 +23,12 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
|
"github.com/status-im/status-go/appdatabase"
|
||||||
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/params"
|
"github.com/status-im/status-go/params"
|
||||||
"github.com/status-im/status-go/services/ext"
|
"github.com/status-im/status-go/services/ext"
|
||||||
"github.com/status-im/status-go/sqlite"
|
|
||||||
"github.com/status-im/status-go/t/helpers"
|
"github.com/status-im/status-go/t/helpers"
|
||||||
"github.com/status-im/status-go/waku"
|
"github.com/status-im/status-go/waku"
|
||||||
wakucommon "github.com/status-im/status-go/waku/common"
|
wakucommon "github.com/status-im/status-go/waku/common"
|
||||||
@ -126,7 +126,7 @@ func TestInitProtocol(t *testing.T) {
|
|||||||
tmpdir, err := ioutil.TempDir("", "test-shhext-service-init-protocol")
|
tmpdir, err := ioutil.TempDir("", "test-shhext-service-init-protocol")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/db.sql", tmpdir), "password")
|
sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/db.sql", tmpdir), "password")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = service.InitProtocol(privateKey, sqlDB, zap.NewNop())
|
err = service.InitProtocol(privateKey, sqlDB, zap.NewNop())
|
||||||
@ -179,7 +179,7 @@ func (s *ShhExtSuite) createAndAddNode() {
|
|||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
nodeWrapper := ext.NewTestNodeWrapper(nil, gethbridge.NewGethWakuWrapper(w))
|
nodeWrapper := ext.NewTestNodeWrapper(nil, gethbridge.NewGethWakuWrapper(w))
|
||||||
service := New(config, nodeWrapper, nil, nil, db)
|
service := New(config, nodeWrapper, nil, nil, db)
|
||||||
sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/%d", s.dir, idx), "password")
|
sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/%d", s.dir, idx), "password")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
privateKey, err := crypto.GenerateKey()
|
privateKey, err := crypto.GenerateKey()
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
|
@ -97,7 +97,7 @@ func ConfigReadmeMd() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "../config/README.md", size: 3132, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
info := bindataFileInfo{name: "../config/README.md", size: 3132, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd0, 0x71, 0x14, 0x3e, 0x9d, 0x9c, 0x11, 0x40, 0xe1, 0xe9, 0x8b, 0xcc, 0x38, 0x17, 0x69, 0xb, 0xa7, 0xf2, 0x91, 0xa6, 0x4a, 0x9f, 0xd2, 0xc6, 0xf4, 0xff, 0x37, 0x5d, 0xd3, 0xed, 0x7c, 0xbd}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd0, 0x71, 0x14, 0x3e, 0x9d, 0x9c, 0x11, 0x40, 0xe1, 0xe9, 0x8b, 0xcc, 0x38, 0x17, 0x69, 0xb, 0xa7, 0xf2, 0x91, 0xa6, 0x4a, 0x9f, 0xd2, 0xc6, 0xf4, 0xff, 0x37, 0x5d, 0xd3, 0xed, 0x7c, 0xbd}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ func ConfigCliFleetEthProdJson() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.prod.json", size: 3625, mode: os.FileMode(0644), modTime: time.Unix(1585559578, 0)}
|
info := bindataFileInfo{name: "../config/cli/fleet-eth.prod.json", size: 3625, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1c, 0x86, 0x93, 0x3b, 0x8c, 0xb4, 0x1b, 0x8b, 0x70, 0xe6, 0x90, 0x3, 0xc9, 0x93, 0x99, 0x97, 0x18, 0xb7, 0x49, 0xbf, 0x2d, 0x35, 0x98, 0xb8, 0x49, 0xa8, 0x73, 0x30, 0xf5, 0x2d, 0x7f, 0x5c}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1c, 0x86, 0x93, 0x3b, 0x8c, 0xb4, 0x1b, 0x8b, 0x70, 0xe6, 0x90, 0x3, 0xc9, 0x93, 0x99, 0x97, 0x18, 0xb7, 0x49, 0xbf, 0x2d, 0x35, 0x98, 0xb8, 0x49, 0xa8, 0x73, 0x30, 0xf5, 0x2d, 0x7f, 0x5c}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ func ConfigCliFleetEthStagingJson() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1846, mode: os.FileMode(0644), modTime: time.Unix(1585559578, 0)}
|
info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1846, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0xef, 0x3d, 0x5c, 0x41, 0xb1, 0xc8, 0x54, 0xd3, 0x82, 0x60, 0xcf, 0x5f, 0x8b, 0x92, 0xd0, 0x41, 0xa7, 0xff, 0xfd, 0x2c, 0x95, 0x44, 0x68, 0xd3, 0xad, 0x16, 0x8c, 0x49, 0x10, 0x4e, 0xb7}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0xef, 0x3d, 0x5c, 0x41, 0xb1, 0xc8, 0x54, 0xd3, 0x82, 0x60, 0xcf, 0x5f, 0x8b, 0x92, 0xd0, 0x41, 0xa7, 0xff, 0xfd, 0x2c, 0x95, 0x44, 0x68, 0xd3, 0xad, 0x16, 0x8c, 0x49, 0x10, 0x4e, 0xb7}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ func ConfigCliFleetEthTestJson() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1855, mode: os.FileMode(0644), modTime: time.Unix(1585559578, 0)}
|
info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1855, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe4, 0x90, 0x4, 0x6, 0x2b, 0xe4, 0x67, 0x7a, 0xfc, 0x70, 0xa9, 0xb, 0x85, 0x8c, 0xb3, 0x8, 0x8e, 0xee, 0xec, 0x64, 0xb7, 0xac, 0x4c, 0x6, 0x4d, 0xcc, 0x4f, 0xda, 0xbd, 0xed, 0x17, 0x53}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe4, 0x90, 0x4, 0x6, 0x2b, 0xe4, 0x67, 0x7a, 0xfc, 0x70, 0xa9, 0xb, 0x85, 0x8c, 0xb3, 0x8, 0x8e, 0xee, 0xec, 0x64, 0xb7, 0xac, 0x4c, 0x6, 0x4d, 0xcc, 0x4f, 0xda, 0xbd, 0xed, 0x17, 0x53}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user