Add chats API calls

This commits adds two API calls:

SaveChat
GetChats

The first one behaves like an upsert.
The second takes two parameters, from, to and returns chats in reverse
order by timestamp.

Members and MembershipUpdates have been stored as blob of data to
simplify the querying (we never query them separately).
This commit is contained in:
Andrea Maria Piana 2019-07-30 18:02:45 +02:00
parent f102941b62
commit b742356b68
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
9 changed files with 560 additions and 83 deletions

80
chat.go
View File

@ -2,8 +2,80 @@ package statusproto
import "crypto/ecdsa"
type Chat interface {
ID() string
PublicName() string
PublicKey() *ecdsa.PublicKey
type ChatPagination struct {
From uint
To uint
}
type ChatType int
const (
ChatTypeOneToOne = iota + 1
ChatTypePublic
ChatTypePrivateGroupChat
)
type Chat struct {
// ID is the id of the chat, for public chats it is the name e.g. status, for one-to-one
// is the hex encoded public key and for group chats is a random uuid appended with
// the hex encoded pk of the creator of the chat
ID string `json:"id"`
Name string `json:"name"`
Color string `json:"color"`
// Active indicates whether the chat has been soft deleted
Active bool `json:"active"`
ChatType ChatType `json:"chatType"`
// Only filled for one to one chats
PublicKey *ecdsa.PublicKey `json:"-"`
// Timestamp indicates the last time this chat has received/sent a message
Timestamp int64 `json:"timestamp"`
// LastClockValue indicates the last clock value to be used when sending messages
LastClockValue uint64 `json:"lastClockValue"`
// DeletedAtClockValue indicates the clock value at time of deletion, messages
// with lower clock value of this should be discarded
DeletedAtClockValue uint64 `json:"deletedAtClockValue"`
// Denormalized fields
UnviewedMessagesCount uint `json:"unviewedMessagesCount"`
LastMessageContentType string `json:"lastMessageContentType"`
LastMessageContent string `json:"lastMessageContent"`
// Group chat fields
// Members are the members who have been invited to the group chat
Members []ChatMember `json:"members"`
// MembershipUpdates is all the membership events in the chat
MembershipUpdates []ChatMembershipUpdate `json:"membershipUpdates"`
}
// ChatMembershipUpdate represent an event on membership of the chat
type ChatMembershipUpdate struct {
// Unique identifier for the event
ID string `json:"id"`
// Type indicates the kind of event (i.e changed-name, added-member, etc)
Type string `json:"type"`
// Name represents the name in the event of changing name events
Name string `json:"name"`
// Clock value of the event
ClockValue uint64 `json:"clockValue"`
// Signature of the event
Signature string `json:"signature"`
// Hex encoded public key of the creator of the event
From string `json:"from"`
// Target of the event for single-target events
Member string `json:"member"`
// Target of the event for multi-target events
Members []string `json:"members"`
}
// ChatMember represents a member who participates in a group chat
type ChatMember struct {
// ID is the hex encoded public key of the member
ID string `json:"id"`
// Admin indicates if the member is an admin of the group chat
Admin bool `json:"admin"`
// Joined indicates if the member has joined the group chat
Joined bool `json:"joined"`
}

View File

@ -100,7 +100,7 @@ func _1536754952_initial_schemaDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x44, 0xcf, 0x76, 0x71, 0x1f, 0x5e, 0x9a, 0x43, 0xd8, 0xcd, 0xb8, 0xc3, 0x70, 0xc3, 0x7f, 0xfc, 0x90, 0xb4, 0x25, 0x1e, 0xf4, 0x66, 0x20, 0xb8, 0x33, 0x7e, 0xb0, 0x76, 0x1f, 0xc, 0xc0, 0x75}}
return a, nil
}
@ -120,7 +120,7 @@ func _1536754952_initial_schemaUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xea, 0x90, 0x5a, 0x59, 0x3e, 0x3, 0xe2, 0x3c, 0x81, 0x42, 0xcd, 0x4c, 0x9a, 0xe8, 0xda, 0x93, 0x2b, 0x70, 0xa4, 0xd5, 0x29, 0x3e, 0xd5, 0xc9, 0x27, 0xb6, 0xb7, 0x65, 0xff, 0x0, 0xcb, 0xde}}
return a, nil
}
@ -140,7 +140,7 @@ func _1539249977_update_ratchet_infoDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1, 0xa4, 0xeb, 0xa0, 0xe6, 0xa0, 0xd4, 0x48, 0xbb, 0xad, 0x6f, 0x7d, 0x67, 0x8c, 0xbd, 0x25, 0xde, 0x1f, 0x73, 0x9a, 0xbb, 0xa8, 0xc9, 0x30, 0xb7, 0xa9, 0x7c, 0xaf, 0xb5, 0x1, 0x61, 0xdd}}
return a, nil
}
@ -160,7 +160,7 @@ func _1539249977_update_ratchet_infoUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0x8e, 0xbf, 0x6f, 0xa, 0xc0, 0xe1, 0x3c, 0x42, 0x28, 0x88, 0x1d, 0xdb, 0xba, 0x1c, 0x83, 0xec, 0xba, 0xd3, 0x5f, 0x5c, 0x77, 0x5e, 0xa7, 0x46, 0x36, 0xec, 0x69, 0xa, 0x4b, 0x17, 0x79}}
return a, nil
}
@ -180,7 +180,7 @@ func _1540715431_add_versionDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x9, 0x4, 0xe3, 0x76, 0x2e, 0xb8, 0x9, 0x23, 0xf0, 0x70, 0x93, 0xc4, 0x50, 0xe, 0x9d, 0x84, 0x22, 0x8c, 0x94, 0xd3, 0x24, 0x9, 0x9a, 0xc1, 0xa1, 0x48, 0x45, 0xfd, 0x40, 0x6e, 0xe6}}
return a, nil
}
@ -200,7 +200,7 @@ func _1540715431_add_versionUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc7, 0x4c, 0x36, 0x96, 0xdf, 0x16, 0x10, 0xa6, 0x27, 0x1a, 0x79, 0x8b, 0x42, 0x83, 0x23, 0xc, 0x7e, 0xb6, 0x3d, 0x2, 0xda, 0xa4, 0xb4, 0xd, 0x27, 0x55, 0xba, 0xdc, 0xb2, 0x88, 0x8f, 0xa6}}
return a, nil
}
@ -220,7 +220,7 @@ func _1541164797_add_installationsDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1541164797_add_installations.down.sql", size: 26, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1541164797_add_installations.down.sql", size: 26, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0xfd, 0xe6, 0xd8, 0xca, 0x3b, 0x38, 0x18, 0xee, 0x0, 0x5f, 0x36, 0x9e, 0x1e, 0xd, 0x19, 0x3e, 0xb4, 0x73, 0x53, 0xe9, 0xa5, 0xac, 0xdd, 0xa1, 0x2f, 0xc7, 0x6c, 0xa8, 0xd9, 0xa, 0x88}}
return a, nil
}
@ -240,7 +240,7 @@ func _1541164797_add_installationsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1541164797_add_installations.up.sql", size: 216, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1541164797_add_installations.up.sql", size: 216, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2d, 0x18, 0x26, 0xb8, 0x88, 0x47, 0xdb, 0x83, 0xcc, 0xb6, 0x9d, 0x1c, 0x1, 0xae, 0x2f, 0xde, 0x97, 0x82, 0x3, 0x30, 0xa8, 0x63, 0xa1, 0x78, 0x4b, 0xa5, 0x9, 0x8, 0x75, 0xa2, 0x57, 0x81}}
return a, nil
}
@ -260,7 +260,7 @@ func _1558084410_add_secretDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1558084410_add_secret.down.sql", size: 56, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1558084410_add_secret.down.sql", size: 56, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x49, 0xb, 0x65, 0xdf, 0x59, 0xbf, 0xe9, 0x5, 0x5b, 0x6f, 0xd5, 0x3a, 0xb7, 0x57, 0xe8, 0x78, 0x38, 0x73, 0x53, 0x57, 0xf7, 0x24, 0x4, 0xe4, 0xa2, 0x49, 0x22, 0xa2, 0xc6, 0xfd, 0x80, 0xa4}}
return a, nil
}
@ -280,7 +280,7 @@ func _1558084410_add_secretUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1558084410_add_secret.up.sql", size: 301, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1558084410_add_secret.up.sql", size: 301, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x32, 0x36, 0x8e, 0x47, 0xb0, 0x8f, 0xc1, 0xc6, 0xf7, 0xc6, 0x9f, 0x2d, 0x44, 0x75, 0x2b, 0x26, 0xec, 0x6, 0xa0, 0x7b, 0xa5, 0xbd, 0xc8, 0x76, 0x8a, 0x82, 0x68, 0x2, 0x42, 0xb5, 0xf4}}
return a, nil
}
@ -300,7 +300,7 @@ func _1558588866_add_versionDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1558588866_add_version.down.sql", size: 47, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1558588866_add_version.down.sql", size: 47, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x52, 0x34, 0x3c, 0x46, 0x4a, 0xf0, 0x72, 0x47, 0x6f, 0x49, 0x5c, 0xc7, 0xf9, 0x32, 0xce, 0xc4, 0x3d, 0xfd, 0x61, 0xa1, 0x8b, 0x8f, 0xf2, 0x31, 0x34, 0xde, 0x15, 0x49, 0xa6, 0xde, 0xb9}}
return a, nil
}
@ -320,7 +320,7 @@ func _1558588866_add_versionUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1558588866_add_version.up.sql", size: 57, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1558588866_add_version.up.sql", size: 57, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2a, 0xea, 0x64, 0x39, 0x61, 0x20, 0x83, 0x83, 0xb, 0x2e, 0x79, 0x64, 0xb, 0x53, 0xfa, 0xfe, 0xc6, 0xf7, 0x67, 0x42, 0xd3, 0x4f, 0xdc, 0x7e, 0x30, 0x32, 0xe8, 0x14, 0x41, 0xe9, 0xe7, 0x3b}}
return a, nil
}
@ -340,7 +340,7 @@ func _1559627659_add_contact_codeDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5d, 0x64, 0x6d, 0xce, 0x24, 0x42, 0x20, 0x8d, 0x4f, 0x37, 0xaa, 0x9d, 0xc, 0x57, 0x98, 0xc1, 0xd1, 0x1a, 0x34, 0xcd, 0x9f, 0x8f, 0x34, 0x86, 0xb3, 0xd3, 0xdc, 0xf1, 0x7d, 0xe5, 0x1b, 0x6e}}
return a, nil
}
@ -360,7 +360,7 @@ func _1559627659_add_contact_codeUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x16, 0xf6, 0xc2, 0x62, 0x9c, 0xd2, 0xc9, 0x1e, 0xd8, 0xea, 0xaa, 0xea, 0x95, 0x8f, 0x89, 0x6a, 0x85, 0x5d, 0x9d, 0x99, 0x78, 0x3c, 0x90, 0x66, 0x99, 0x3e, 0x4b, 0x19, 0x62, 0xfb, 0x31, 0x4d}}
return a, nil
}
@ -380,7 +380,7 @@ func _1561368210_add_installation_metadataDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1561368210_add_installation_metadata.down.sql", size: 35, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1561368210_add_installation_metadata.down.sql", size: 35, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa8, 0xde, 0x3f, 0xd2, 0x4a, 0x50, 0x98, 0x56, 0xe3, 0xc0, 0xcd, 0x9d, 0xb0, 0x34, 0x3b, 0xe5, 0x62, 0x18, 0xb5, 0x20, 0xc9, 0x3e, 0xdc, 0x6a, 0x40, 0x36, 0x66, 0xea, 0x51, 0x8c, 0x71, 0xf5}}
return a, nil
}
@ -400,7 +400,7 @@ func _1561368210_add_installation_metadataUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1561368210_add_installation_metadata.up.sql", size: 267, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1561368210_add_installation_metadata.up.sql", size: 267, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb4, 0x71, 0x8f, 0x29, 0xb1, 0xaa, 0xd6, 0xd1, 0x8c, 0x17, 0xef, 0x6c, 0xd5, 0x80, 0xb8, 0x2c, 0xc3, 0xfe, 0xec, 0x24, 0x4d, 0xc8, 0x25, 0xd3, 0xb4, 0xcd, 0xa9, 0xac, 0x63, 0x61, 0xb2, 0x9c}}
return a, nil
}
@ -420,7 +420,7 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xef, 0xaf, 0xdf, 0xcf, 0x65, 0xae, 0x19, 0xfc, 0x9d, 0x29, 0xc1, 0x91, 0xaf, 0xb5, 0xd5, 0xb1, 0x56, 0xf3, 0xee, 0xa8, 0xba, 0x13, 0x65, 0xdb, 0xab, 0xcf, 0x4e, 0xac, 0x92, 0xe9, 0x60, 0xf1}}
return a, nil
}

View File

@ -366,36 +366,44 @@ func (m *Messenger) Mailservers() ([]string, error) {
}
func (m *Messenger) Join(chat Chat) error {
if chat.PublicKey() != nil {
return m.adapter.JoinPrivate(chat.PublicKey())
} else if chat.PublicName() != "" {
return m.adapter.JoinPublic(chat.PublicName())
if chat.PublicKey != nil {
return m.adapter.JoinPrivate(chat.PublicKey)
} else if chat.Name != "" {
return m.adapter.JoinPublic(chat.Name)
}
return errors.New("chat is neither public nor private")
}
func (m *Messenger) Leave(chat Chat) error {
if chat.PublicKey() != nil {
return m.adapter.LeavePrivate(chat.PublicKey())
} else if chat.PublicName() != "" {
return m.adapter.LeavePublic(chat.PublicName())
if chat.PublicKey != nil {
return m.adapter.LeavePrivate(chat.PublicKey)
} else if chat.Name != "" {
return m.adapter.LeavePublic(chat.Name)
}
return errors.New("chat is neither public nor private")
}
func (m *Messenger) SaveChat(chat Chat) error {
return m.persistence.SaveChat(chat)
}
func (m *Messenger) Chats(from, to int) ([]*Chat, error) {
return m.persistence.Chats(from, to)
}
func (m *Messenger) Send(ctx context.Context, chat Chat, data []byte) ([]byte, error) {
chatID := chat.ID()
chatID := chat.ID
if chatID == "" {
return nil, ErrChatIDEmpty
}
clock, err := m.persistence.LastMessageClock(chat.ID())
clock, err := m.persistence.LastMessageClock(chat.ID)
if err != nil {
return nil, err
}
if chat.PublicKey() != nil {
hash, message, err := m.adapter.SendPrivate(ctx, chat.PublicKey(), chat.ID(), data, clock)
if chat.PublicKey != nil {
hash, message, err := m.adapter.SendPrivate(ctx, chat.PublicKey, chat.ID, data, clock)
if err != nil {
return nil, err
}
@ -405,7 +413,7 @@ func (m *Messenger) Send(ctx context.Context, chat Chat, data []byte) ([]byte, e
message.SigPubKey = &m.identity.PublicKey
if m.messagesPersistenceEnabled {
_, err = m.persistence.SaveMessages(chat.ID(), []*protocol.Message{message})
_, err = m.persistence.SaveMessages(chat.ID, []*protocol.Message{message})
if err != nil {
return nil, err
}
@ -415,8 +423,8 @@ func (m *Messenger) Send(ctx context.Context, chat Chat, data []byte) ([]byte, e
m.ownMessages[chatID] = append(m.ownMessages[chatID], message)
return hash, nil
} else if chat.PublicName() != "" {
return m.adapter.SendPublic(ctx, chat.PublicName(), chat.ID(), data, clock)
} else if chat.Name != "" {
return m.adapter.SendPublic(ctx, chat.Name, chat.ID, data, clock)
}
return nil, errors.New("chat is neither public nor private")
}
@ -424,10 +432,10 @@ func (m *Messenger) Send(ctx context.Context, chat Chat, data []byte) ([]byte, e
// SendRaw takes encoded data, encrypts it and sends through the wire.
// DEPRECATED
func (m *Messenger) SendRaw(ctx context.Context, chat Chat, data []byte) ([]byte, whisper.NewMessage, error) {
if chat.PublicKey() != nil {
return m.adapter.SendPrivateRaw(ctx, chat.PublicKey(), data)
} else if chat.PublicName() != "" {
return m.adapter.SendPublicRaw(ctx, chat.PublicName(), data)
if chat.PublicKey != nil {
return m.adapter.SendPrivateRaw(ctx, chat.PublicKey, data)
} else if chat.Name != "" {
return m.adapter.SendPublicRaw(ctx, chat.Name, data)
}
return nil, whisper.NewMessage{}, errors.New("chat is neither public nor private")
}
@ -491,14 +499,14 @@ func (m *Messenger) Retrieve(ctx context.Context, chat Chat, c RetrieveConfig) (
ownLatest []*protocol.Message
)
if chat.PublicKey() != nil {
latest, err = m.adapter.RetrievePrivateMessages(chat.PublicKey())
if chat.PublicKey != nil {
latest, err = m.adapter.RetrievePrivateMessages(chat.PublicKey)
// Return any own messages for this chat as well.
if ownMessages, ok := m.ownMessages[chat.ID()]; ok {
if ownMessages, ok := m.ownMessages[chat.ID]; ok {
ownLatest = ownMessages
}
} else if chat.PublicName() != "" {
latest, err = m.adapter.RetrievePublicMessages(chat.PublicName())
} else if chat.Name != "" {
latest, err = m.adapter.RetrievePublicMessages(chat.Name)
} else {
return nil, errors.New("chat is neither public nor private")
}
@ -509,14 +517,14 @@ func (m *Messenger) Retrieve(ctx context.Context, chat Chat, c RetrieveConfig) (
}
if m.messagesPersistenceEnabled {
_, err = m.persistence.SaveMessages(chat.ID(), latest)
_, err = m.persistence.SaveMessages(chat.ID, latest)
if err != nil {
return nil, errors.Wrap(err, "failed to save latest messages")
}
}
// Confirm received and decrypted messages.
if m.messagesPersistenceEnabled && chat.PublicKey() != nil {
if m.messagesPersistenceEnabled && chat.PublicKey != nil {
for _, message := range latest {
// Confirm received and decrypted messages.
if err := m.encryptor.ConfirmMessageProcessed(message.ID); err != nil {
@ -526,13 +534,13 @@ func (m *Messenger) Retrieve(ctx context.Context, chat Chat, c RetrieveConfig) (
}
// We may need to add more messages from the past.
result, err := m.retrieveSaved(ctx, chat.ID(), c, append(latest, ownLatest...))
result, err := m.retrieveSaved(ctx, chat.ID, c, append(latest, ownLatest...))
if err != nil {
return nil, err
}
// When our messages are returned, we can delete them.
delete(m.ownMessages, chat.ID())
delete(m.ownMessages, chat.ID)
return result, nil
}

View File

@ -4,6 +4,7 @@ import (
"context"
"crypto/ecdsa"
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"testing"
@ -18,27 +19,6 @@ import (
whisper "github.com/status-im/whisper/whisperv6"
)
type testChat struct {
publicName string
publicKey *ecdsa.PublicKey
}
func (c testChat) ID() string {
if c.publicKey != nil {
return hex.EncodeToString(crypto.FromECDSAPub(c.publicKey))
}
// Deliberately use a different ID than public name.
return hex.EncodeToString([]byte(c.publicName))
}
func (c testChat) PublicKey() *ecdsa.PublicKey {
return c.publicKey
}
func (c testChat) PublicName() string {
return c.publicName
}
func TestMessengerSuite(t *testing.T) {
suite.Run(t, new(MessengerSuite))
}
@ -87,19 +67,19 @@ func (s *MessengerSuite) TearDownTest() {
}
func (s *MessengerSuite) TestSendPublic() {
_, err := s.m.Send(context.Background(), testChat{publicName: "status"}, []byte("test"))
_, err := s.m.Send(context.Background(), Chat{Name: "status", ID: "status"}, []byte("test"))
s.NoError(err)
}
func (s *MessengerSuite) TestSendPrivate() {
recipientKey, err := crypto.GenerateKey()
s.NoError(err)
_, err = s.m.Send(context.Background(), testChat{publicKey: &recipientKey.PublicKey}, []byte("test"))
_, err = s.m.Send(context.Background(), Chat{ID: "x", PublicKey: &recipientKey.PublicKey}, []byte("test"))
s.NoError(err)
}
func (s *MessengerSuite) TestRetrievePublic() {
chat := testChat{publicName: "status"}
chat := Chat{ID: "status", Name: "status"}
_, err := s.m.Send(context.Background(), chat, []byte("test"))
s.NoError(err)
@ -126,7 +106,7 @@ func (s *MessengerSuite) TestRetrievePublic() {
func (s *MessengerSuite) TestRetrievePrivate() {
publicContact, err := crypto.GenerateKey()
s.NoError(err)
chat := testChat{publicKey: &publicContact.PublicKey}
chat := Chat{ID: "x", PublicKey: &publicContact.PublicKey}
_, err = s.m.Send(context.Background(), chat, []byte("test"))
s.NoError(err)
@ -150,6 +130,197 @@ func (s *MessengerSuite) TestRetrievePrivate() {
s.Equal(&s.privateKey.PublicKey, message.SigPubKey) // this is OUR message
}
func (s *MessengerSuite) TestChatPersistencePublic() {
chat := Chat{
ID: "chat-name",
Name: "chat-name",
Color: "#fffff",
Active: true,
ChatType: ChatTypePublic,
Timestamp: 10,
LastClockValue: 20,
DeletedAtClockValue: 30,
UnviewedMessagesCount: 40,
LastMessageContentType: "something",
LastMessageContent: "something-else",
}
s.Require().NoError(s.m.SaveChat(chat))
savedChats, err := s.m.Chats(0, 10)
s.Require().NoError(err)
s.Require().Equal(1, len(savedChats))
actualChat := savedChats[0]
expectedChat := &chat
s.Require().Equal(actualChat, expectedChat)
}
func (s *MessengerSuite) TestChatPersistenceUpdate() {
chat := Chat{
ID: "chat-name",
Name: "chat-name",
Color: "#fffff",
Active: true,
ChatType: ChatTypePublic,
Timestamp: 10,
LastClockValue: 20,
DeletedAtClockValue: 30,
UnviewedMessagesCount: 40,
LastMessageContentType: "something",
LastMessageContent: "something-else",
}
s.Require().NoError(s.m.SaveChat(chat))
savedChats, err := s.m.Chats(0, 10)
s.Require().NoError(err)
s.Require().Equal(1, len(savedChats))
actualChat := savedChats[0]
expectedChat := &chat
s.Require().Equal(expectedChat, actualChat)
chat.Name = "updated-name"
s.Require().NoError(s.m.SaveChat(chat))
updatedChats, err := s.m.Chats(0, 10)
s.Require().NoError(err)
s.Require().Equal(1, len(updatedChats))
actualUpdatedChat := updatedChats[0]
expectedUpdatedChat := &chat
s.Require().Equal(expectedUpdatedChat, actualUpdatedChat)
}
func (s *MessengerSuite) TestChatPagination() {
for i := 0; i <= 20; i++ {
chat := Chat{
ID: fmt.Sprintf("chat-name-%d", i),
Name: "chat-name",
Color: "#fffff",
Active: true,
ChatType: ChatTypePublic,
Timestamp: int64(i),
LastClockValue: 20,
DeletedAtClockValue: 30,
UnviewedMessagesCount: 40,
LastMessageContentType: "something",
LastMessageContent: "something-else",
}
s.Require().NoError(s.m.SaveChat(chat))
}
firstPageChats, err := s.m.Chats(0, 10)
s.Require().NoError(err)
s.Require().Equal(10, len(firstPageChats))
s.Require().Equal(int64(20), firstPageChats[0].Timestamp)
s.Require().Equal(int64(11), firstPageChats[9].Timestamp)
secondPageChats, err := s.m.Chats(10, -1)
s.Require().NoError(err)
s.Require().Equal(11, len(secondPageChats))
s.Require().Equal(int64(10), secondPageChats[0].Timestamp)
s.Require().Equal(int64(0), secondPageChats[10].Timestamp)
}
func (s *MessengerSuite) TestChatPersistenceOneToOne() {
pkStr := "0x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1"
chat := Chat{
ID: pkStr,
Name: pkStr,
Color: "#fffff",
Active: true,
ChatType: ChatTypeOneToOne,
Timestamp: 10,
LastClockValue: 20,
DeletedAtClockValue: 30,
UnviewedMessagesCount: 40,
LastMessageContentType: "something",
LastMessageContent: "something-else",
}
publicKeyBytes, err := hex.DecodeString(pkStr[2:])
s.Require().NoError(err)
pk, err := crypto.UnmarshalPubkey(publicKeyBytes)
s.Require().NoError(err)
s.Require().NoError(s.m.SaveChat(chat))
savedChats, err := s.m.Chats(0, 10)
s.Require().NoError(err)
s.Require().Equal(1, len(savedChats))
actualChat := savedChats[0]
expectedChat := &chat
expectedChat.PublicKey = pk
s.Require().Equal(expectedChat, actualChat)
}
func (s *MessengerSuite) TestChatPersistencePrivateGroupChat() {
chat := Chat{
ID: "chat-id",
Name: "chat-id",
Color: "#fffff",
Active: true,
ChatType: ChatTypePrivateGroupChat,
Timestamp: 10,
Members: []ChatMember{
ChatMember{
ID: "1",
Admin: false,
Joined: true,
},
ChatMember{
ID: "2",
Admin: true,
Joined: false,
},
ChatMember{
ID: "3",
Admin: true,
Joined: true,
},
},
MembershipUpdates: []ChatMembershipUpdate{
ChatMembershipUpdate{
ID: "1",
Type: "type-1",
Name: "name-1",
ClockValue: 1,
Signature: "signature-1",
From: "from-1",
Member: "member-1",
Members: []string{"member-1", "member-2"},
},
ChatMembershipUpdate{
ID: "2",
Type: "type-2",
Name: "name-2",
ClockValue: 2,
Signature: "signature-2",
From: "from-2",
Member: "member-2",
Members: []string{"member-2", "member-3"},
},
},
LastClockValue: 20,
DeletedAtClockValue: 30,
UnviewedMessagesCount: 40,
LastMessageContentType: "something",
LastMessageContent: "something-else",
}
s.Require().NoError(s.m.SaveChat(chat))
savedChats, err := s.m.Chats(0, 10)
s.Require().NoError(err)
s.Require().Equal(1, len(savedChats))
actualChat := savedChats[0]
expectedChat := &chat
s.Require().Equal(expectedChat, actualChat)
}
func (s *MessengerSuite) TestSharedSecretHandler() {
err := s.m.handleSharedSecrets(nil)
s.NoError(err)

View File

@ -2,6 +2,8 @@
// sources:
// 000001_init.down.db.sql (82B)
// 000001_init.up.db.sql (840B)
// 000002_add_chats.down.db.sql (74B)
// 000002_add_chats.up.db.sql (541B)
// doc.go (377B)
package migrations
@ -86,7 +88,7 @@ func _000001_initDownDbSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000001_init.down.db.sql", size: 82, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "000001_init.down.db.sql", size: 82, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe8, 0x5f, 0xe0, 0x6, 0xfc, 0xed, 0xb7, 0xff, 0xb5, 0xf3, 0x33, 0x45, 0x1, 0x5b, 0x84, 0x80, 0x74, 0x60, 0x81, 0xa6, 0x8b, 0xb4, 0xd4, 0xad, 0x10, 0xa8, 0xb3, 0x61, 0x6f, 0xc5, 0x2f, 0xaa}}
return a, nil
}
@ -106,11 +108,51 @@ func _000001_initUpDbSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000001_init.up.db.sql", size: 840, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "000001_init.up.db.sql", size: 840, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe7, 0x27, 0x96, 0x3b, 0x72, 0x81, 0x7d, 0xba, 0xa4, 0xfb, 0xf7, 0x4, 0xd, 0x6f, 0xc8, 0x30, 0xfe, 0x47, 0xe0, 0x9, 0xf, 0x43, 0x13, 0x6, 0x55, 0xfc, 0xee, 0x15, 0x69, 0x99, 0x53, 0x3f}}
return a, nil
}
var __000002_add_chatsDownDbSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\xc8\x4d\xcd\x4d\x4a\x2d\x2a\xce\xc8\x2c\x88\x2f\x2d\x48\x49\x2c\x49\x2d\xb6\xe6\x42\x92\x4e\xce\x48\x2c\x89\x87\xaa\xc1\x90\x28\xb6\xe6\x02\x04\x00\x00\xff\xff\xde\x59\xf6\x29\x4a\x00\x00\x00")
func _000002_add_chatsDownDbSqlBytes() ([]byte, error) {
return bindataRead(
__000002_add_chatsDownDbSql,
"000002_add_chats.down.db.sql",
)
}
func _000002_add_chatsDownDbSql() (*asset, error) {
bytes, err := _000002_add_chatsDownDbSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "000002_add_chats.down.db.sql", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1564502835, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd3, 0xa7, 0xf0, 0x94, 0x7a, 0x9, 0xdc, 0x6c, 0x7b, 0xdc, 0x12, 0x30, 0x55, 0x31, 0x17, 0xf2, 0xcc, 0x6e, 0xfd, 0xbb, 0x70, 0xb9, 0xd8, 0x9f, 0x81, 0x83, 0xdc, 0x1d, 0x1c, 0x3a, 0x8d, 0xce}}
return a, nil
}
var __000002_add_chatsUpDbSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x91\x4f\x4b\x03\x31\x10\xc5\xef\xf3\x29\x06\x3c\x54\x61\x0f\x7a\x10\x85\x9e\xb2\xdb\x14\x17\xe3\xa6\xa4\xa9\xd8\x53\x48\xb3\x83\x5d\xba\xff\x68\xb2\x95\x7e\x7b\x69\x5d\xda\xea\x2a\x78\x4c\xde\xef\xbd\x49\xe6\x25\x8a\x33\xcd\x51\xb3\x58\x70\x4c\xa7\x98\x49\x8d\xfc\x2d\x9d\xeb\x39\xba\xb5\x0d\x1e\xaf\xa1\xc8\xf1\x95\xa9\xe4\x89\x29\x9c\xa9\xf4\x85\xa9\x25\x3e\xf3\x25\xca\x0c\x13\x99\x4d\x45\x9a\x68\x54\x7c\x26\x58\xc2\x23\xa8\x6d\x45\x27\xfa\x90\x95\x2d\x84\x88\xc0\x35\x65\xb3\x1d\xdc\xe3\x84\x4f\xd9\x42\x68\x1c\x5d\xd9\xbb\xc7\x87\xfc\x7e\x14\x41\xd8\xb7\x84\x69\xa6\x2f\xcc\xd6\x85\x62\x47\x18\x4b\x29\x38\xcb\x86\x6e\xad\x16\x3c\x82\x50\x54\xe4\x83\xad\xda\x1f\xee\x9c\x4a\x0a\x94\x1b\x1b\x8c\x2b\x1b\xb7\x31\x3b\x5b\x76\xdf\x47\x9c\x92\x6e\x23\x68\xbb\x55\x59\x38\xb3\xa1\x3d\xc6\x42\xc6\x11\x74\xf5\xae\xa0\x0f\xca\x4d\x45\xde\xdb\x77\x32\xae\xe9\xea\xf0\xa7\xbf\xb4\xfe\x7f\x83\x8e\xe0\x39\xb3\x0e\x54\x07\x73\xfc\x7d\xbf\xa6\xdf\x91\xb3\x5a\x51\xb5\xa2\xad\xef\x9f\xd9\x9f\xd6\x45\x6b\xba\x36\xb7\x81\xbe\x04\xb8\x19\x03\xc0\x44\xc9\x59\x5f\xf1\x90\x1b\x5f\xca\x87\xce\x4d\xcf\x8c\xe1\x33\x00\x00\xff\xff\xf3\xb4\xa4\xad\x1d\x02\x00\x00")
func _000002_add_chatsUpDbSqlBytes() ([]byte, error) {
return bindataRead(
__000002_add_chatsUpDbSql,
"000002_add_chats.up.db.sql",
)
}
func _000002_add_chatsUpDbSql() (*asset, error) {
bytes, err := _000002_add_chatsUpDbSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "000002_add_chats.up.db.sql", size: 541, mode: os.FileMode(0644), modTime: time.Unix(1564587096, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd, 0x7f, 0x3a, 0xd7, 0xf6, 0x8b, 0x6e, 0x4d, 0xce, 0x7d, 0x63, 0x1d, 0x30, 0xa2, 0xc1, 0xb, 0xa0, 0x35, 0x2e, 0xfa, 0xef, 0xf0, 0x39, 0xf7, 0x22, 0xdd, 0x31, 0x11, 0xb1, 0xff, 0xbf, 0xb3}}
return a, nil
}
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\xbb\x6e\xc3\x30\x0c\x45\x77\x7f\xc5\x45\x96\x2c\xb5\xb4\x74\xea\xd6\xb1\x7b\x7f\x80\x91\x68\x89\x88\x1e\xae\x48\xe7\xf1\xf7\x85\xd3\x02\xcd\xd6\xf5\x00\xe7\xf0\xd2\x7b\x7c\x66\x51\x2c\x52\x18\xa2\x68\x1c\x58\x95\xc6\x1d\x27\x0e\xb4\x29\xe3\x90\xc4\xf2\x76\x72\xa1\x57\xaf\x46\xb6\xe9\x2c\xd5\x57\x49\x83\x8c\xfd\xe5\xf5\x30\x79\x8f\x40\xed\x68\xc8\xd4\x62\xe1\x47\x4b\xa1\x46\xc3\xa4\x25\x5c\xc5\x32\x08\xeb\xe0\x45\x6e\x0e\xef\x86\xc2\xa4\x06\xcb\x64\x47\x85\x65\x46\x20\xe5\x3d\xb3\xf4\x81\xd4\xe7\x93\xb4\x48\x46\x6e\x47\x1f\xcb\x13\xd9\x17\x06\x2a\x85\x23\x96\xd1\xeb\xc3\x55\xaa\x8c\x28\x83\x83\xf5\x71\x7f\x01\xa9\xb2\xa1\x51\x65\xdd\xfd\x4c\x17\x46\xeb\xbf\xe7\x41\x2d\xfe\xff\x11\xae\x7d\x9c\x15\xa4\xe0\xdb\xca\xc1\x38\xba\x69\x5a\x29\x9c\x29\x31\xf4\xab\x88\xf1\x34\x79\x9f\xfa\x5b\xe2\xc6\xbb\xf5\xbc\x71\x5e\xcf\x09\x3f\x35\xe9\x4d\x31\x77\x38\xe7\xff\x80\x4b\x1d\x6e\xfa\x0e\x00\x00\xff\xff\x9d\x60\x3d\x88\x79\x01\x00\x00")
func docGoBytes() ([]byte, error) {
@ -126,7 +168,7 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xef, 0xaf, 0xdf, 0xcf, 0x65, 0xae, 0x19, 0xfc, 0x9d, 0x29, 0xc1, 0x91, 0xaf, 0xb5, 0xd5, 0xb1, 0x56, 0xf3, 0xee, 0xa8, 0xba, 0x13, 0x65, 0xdb, 0xab, 0xcf, 0x4e, 0xac, 0x92, 0xe9, 0x60, 0xf1}}
return a, nil
}
@ -226,6 +268,10 @@ var _bindata = map[string]func() (*asset, error){
"000001_init.up.db.sql": _000001_initUpDbSql,
"000002_add_chats.down.db.sql": _000002_add_chatsDownDbSql,
"000002_add_chats.up.db.sql": _000002_add_chatsUpDbSql,
"doc.go": docGo,
}
@ -270,9 +316,11 @@ type bintree struct {
}
var _bintree = &bintree{nil, 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{}},
"doc.go": &bintree{docGo, 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{}},
"000002_add_chats.down.db.sql": &bintree{_000002_add_chatsDownDbSql, map[string]*bintree{}},
"000002_add_chats.up.db.sql": &bintree{_000002_add_chatsUpDbSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory.

View File

@ -0,0 +1,3 @@
DROP TABLE membership_updates;
DROP TABLE chat_members;
DROP TABLE chats;

View File

@ -0,0 +1,20 @@
CREATE TABLE IF NOT EXISTS chats (
id VARCHAR PRIMARY KEY ON CONFLICT REPLACE,
name VARCHAR NOT NULL,
color VARCHAR NOT NULL DEFAULT '#a187d5',
type INT NOT NULL,
active BOOLEAN NOT NULL DEFAULT TRUE,
timestamp INT NOT NULL,
deleted_at_clock_value INT NOT NULL DEFAULT 0,
public_key BLOB,
unviewed_message_count INT NOT NULL DEFAULT 0,
last_clock_value INT NOT NULL DEFAULT 0,
last_message_content_type VARCHAR,
last_message_content VARCHAR,
members BLOB,
membership_updates BLOB
);
DROP TABLE membership_updates;
DROP TABLE chat_members;

View File

@ -1,11 +1,16 @@
package statusproto
import (
"bytes"
"context"
"crypto/ecdsa"
"database/sql"
"encoding/gob"
"encoding/hex"
"fmt"
"time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/pkg/errors"
@ -92,6 +97,151 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
return
}
func (db sqlitePersistence) SaveChat(chat Chat) error {
var err error
// We build the db chatID using the type, so that we have no clashes
chatID := fmt.Sprintf("%s-%d", chat.ID, chat.ChatType)
pkey := []byte{}
// For one to one chatID is an encoded public key
if chat.ChatType == ChatTypeOneToOne {
pkey, err = hex.DecodeString(chat.ID[2:])
if err != nil {
return err
}
// Safety check, make sure is well formed
_, err := crypto.UnmarshalPubkey(pkey)
if err != nil {
return err
}
}
// Encode members
var encodedMembers bytes.Buffer
memberEncoder := gob.NewEncoder(&encodedMembers)
if err := memberEncoder.Encode(chat.Members); err != nil {
return err
}
// Encode membership updates
var encodedMembershipUpdates bytes.Buffer
membershipUpdatesEncoder := gob.NewEncoder(&encodedMembershipUpdates)
if err := membershipUpdatesEncoder.Encode(chat.MembershipUpdates); err != nil {
return err
}
// Insert record
stmt, err := db.db.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, public_key, unviewed_message_count, last_clock_value, last_message_content_type, last_message_content, members, membership_updates)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(
chatID,
chat.Name,
chat.Color,
chat.Active,
chat.ChatType,
chat.Timestamp,
chat.DeletedAtClockValue,
pkey,
chat.UnviewedMessagesCount,
chat.LastClockValue,
chat.LastMessageContentType,
chat.LastMessageContent,
encodedMembers.Bytes(),
encodedMembershipUpdates.Bytes(),
)
if err != nil {
return err
}
return err
}
func (db sqlitePersistence) Chats(from, to int) ([]*Chat, error) {
rows, err := db.db.Query(`SELECT
id,
name,
color,
active,
type,
timestamp,
deleted_at_clock_value,
public_key,
unviewed_message_count,
last_clock_value,
last_message_content_type,
last_message_content,
members,
membership_updates
FROM chats
ORDER BY chats.timestamp DESC LIMIT ? OFFSET ?`, to, from)
if err != nil {
return nil, err
}
defer rows.Close()
var response []*Chat
for rows.Next() {
chat := &Chat{}
encodedMembers := []byte{}
encodedMembershipUpdates := []byte{}
pkey := []byte{}
err := rows.Scan(
&chat.ID,
&chat.Name,
&chat.Color,
&chat.Active,
&chat.ChatType,
&chat.Timestamp,
&chat.DeletedAtClockValue,
&pkey,
&chat.UnviewedMessagesCount,
&chat.LastClockValue,
&chat.LastMessageContentType,
&chat.LastMessageContent,
&encodedMembers,
&encodedMembershipUpdates,
)
if err != nil {
return nil, err
}
// Restore the backward compatible ID
chat.ID = chat.ID[:len(chat.ID)-2]
// Restore members
membersDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembers))
if err := membersDecoder.Decode(&chat.Members); err != nil {
return nil, err
}
// Restore membership updates
membershipUpdatesDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembershipUpdates))
if err := membershipUpdatesDecoder.Decode(&chat.MembershipUpdates); err != nil {
return nil, err
}
if len(pkey) != 0 {
chat.PublicKey, err = crypto.UnmarshalPubkey(pkey)
if err != nil {
return nil, err
}
}
response = append(response, chat)
}
return response, nil
}
// Messages returns messages for a given contact, in a given period. Ordered by a timestamp.
func (db sqlitePersistence) Messages(chatID string, from, to time.Time) (result []*protocol.Message, err error) {
rows, err := db.db.Query(`SELECT
@ -101,6 +251,8 @@ FROM user_messages WHERE chat_id = ? AND timestamp >= ? AND timestamp <= ? ORDER
if err != nil {
return nil, err
}
defer rows.Close()
var (
rst = []*protocol.Message{}
)
@ -134,6 +286,8 @@ FROM user_messages WHERE chat_id = ? AND rowid >= ? ORDER BY clock`,
if err != nil {
return nil, err
}
defer rows.Close()
var (
rst = []*protocol.Message{}
)
@ -185,6 +339,7 @@ func (db sqlitePersistence) UnreadMessages(chatID string) ([]*protocol.Message,
if err != nil {
return nil, err
}
defer rows.Close()
var result []*protocol.Message

View File

@ -86,7 +86,7 @@ func _1561059285_add_whisper_keysDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1561059285_add_whisper_keys.down.sql", size: 25, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1561059285_add_whisper_keys.down.sql", size: 25, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb9, 0x31, 0x3f, 0xce, 0xfa, 0x44, 0x36, 0x1b, 0xb0, 0xec, 0x5d, 0xb, 0x90, 0xb, 0x21, 0x4f, 0xd5, 0xe5, 0x50, 0xed, 0xc7, 0x43, 0xdf, 0x83, 0xb4, 0x3a, 0xc1, 0x55, 0x2e, 0x53, 0x7c, 0x67}}
return a, nil
}
@ -106,7 +106,7 @@ func _1561059285_add_whisper_keysUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1561059285_add_whisper_keys.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "1561059285_add_whisper_keys.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x25, 0x41, 0xc, 0x92, 0xdd, 0x9e, 0xff, 0x5d, 0xd0, 0x93, 0xe4, 0x24, 0x50, 0x29, 0xcf, 0xc6, 0xf7, 0x49, 0x3c, 0x73, 0xd9, 0x8c, 0xfa, 0xf2, 0xcf, 0xf6, 0x6f, 0xbc, 0x31, 0xe6, 0xf7, 0xe2}}
return a, nil
}
@ -126,7 +126,7 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 373, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
info := bindataFileInfo{name: "doc.go", size: 373, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x23, 0x6a, 0xc1, 0xce, 0x94, 0xf6, 0xef, 0xf1, 0x97, 0x95, 0xb, 0x35, 0xaf, 0x5f, 0xe7, 0x5f, 0xac, 0x6e, 0xb8, 0xab, 0xba, 0xb5, 0x35, 0x97, 0x22, 0x36, 0x11, 0xce, 0x44, 0xfc, 0xfa, 0xac}}
return a, nil
}