diff --git a/model/message.go b/model/message.go index 4ce71f0..ded861d 100644 --- a/model/message.go +++ b/model/message.go @@ -55,11 +55,11 @@ func (t *Timeline) Swap(i, j int) { t.Messages[i], t.Messages[j] = t.Messages[j], t.Messages[i] } -// Less checks 2 messages (i and j) in the timeline and returns true if i occcured before j, else false +// Less checks 2 messages (i and j) in the timeline and returns true if i occurred before j, else false func (t *Timeline) Less(i, j int) bool { - if t.Messages[i].Timestamp.Before(t.Messages[j].Timestamp) { - return true + if t.Messages[j].Timestamp.Before(t.Messages[i].Timestamp) { + return false } if compareSignatures(t.Messages[i].PreviousMessageSig, t.SignedGroupID) { diff --git a/model/profile.go b/model/profile.go index 8bb48fe..5fceef9 100644 --- a/model/profile.go +++ b/model/profile.go @@ -22,6 +22,7 @@ type PublicProfile struct { Blocked bool Onion string Attributes map[string]string + Timeline Timeline lock sync.Mutex } @@ -102,6 +103,25 @@ func (p *Profile) RejectInvite(groupID string) { p.lock.Unlock() } +// AddMessageToContactTimeline allows the saving of a message sent via a direct connection chat to the profile. +func (p *Profile) AddMessageToContactTimeline(onion string, fromMe bool, message string, sent time.Time) { + p.lock.Lock() + defer p.lock.Unlock() + contact, ok := p.Contacts[onion] + + // We don't really need a Signature here, but we use it to maintain order + now := time.Now() + sig := p.SignMessage(onion + message + sent.String() + now.String()) + if ok { + if fromMe { + contact.Timeline.Insert(&Message{PeerID: p.Onion, Message: message, Timestamp: sent, Received: now, Signature: sig}) + } else { + contact.Timeline.Insert(&Message{PeerID: onion, Message: message, Timestamp: sent, Received: now, Signature: sig}) + } + } +} + + // AcceptInvite accepts a group invite func (p *Profile) AcceptInvite(groupID string) (err error) { p.lock.Lock() diff --git a/model/profile_test.go b/model/profile_test.go index e8493bc..53e845c 100644 --- a/model/profile_test.go +++ b/model/profile_test.go @@ -4,8 +4,33 @@ import ( "cwtch.im/cwtch/protocol" "github.com/golang/protobuf/proto" "testing" + "time" ) + +func TestP2P(t *testing.T) { + sarah := GenerateNewProfile("Sarah") + alice := GenerateNewProfile("Alice") + + sarah.AddContact(alice.Onion, &alice.PublicProfile) + + sarah.AddMessageToContactTimeline(alice.Onion, false, "hello",time.Now()) + sarah.AddMessageToContactTimeline(alice.Onion, true, "world",time.Now()) + + contact,_ := sarah.GetContact(alice.Onion) + for i,m := range contact.Timeline.GetMessages() { + if i == 0 && (m.Message != "hello" || m.PeerID != alice.Onion) { + t.Fatalf("Timeline is invalid: %v", m) + } + + if i == 1 && (m.Message != "world" || m.PeerID != sarah.Onion) { + t.Fatalf("Timeline is invalid: %v", m) + } + t.Logf("Message: %v", m) + } +} + + func TestProfileIdentity(t *testing.T) { sarah := GenerateNewProfile("Sarah") alice := GenerateNewProfile("Alice") @@ -66,6 +91,8 @@ func TestBlockPeer(t *testing.T) { } } + + func TestAcceptNonExistentGroup(t *testing.T) { sarah := GenerateNewProfile("Sarah") sarah.AcceptInvite("doesnotexist")