libcwtch-go first cut integration / message timelines etc
continuous-integration/drone/push Build is pending
Details
continuous-integration/drone/push Build is pending
Details
This commit is contained in:
parent
5c47dd789a
commit
e296c30818
|
@ -126,8 +126,7 @@ const (
|
||||||
// a peer contact has been added
|
// a peer contact has been added
|
||||||
// attributes:
|
// attributes:
|
||||||
// RemotePeer [eg ""]
|
// RemotePeer [eg ""]
|
||||||
// Authorization
|
ContactCreated = Type("ContactCreated")
|
||||||
PeerCreated = Type("PeerCreated")
|
|
||||||
|
|
||||||
// Password, NewPassword
|
// Password, NewPassword
|
||||||
ChangePassword = Type("ChangePassword")
|
ChangePassword = Type("ChangePassword")
|
||||||
|
@ -273,12 +272,12 @@ const (
|
||||||
|
|
||||||
Identity = Field("Identity")
|
Identity = Field("Identity")
|
||||||
|
|
||||||
GroupConversationID = Field("GroupConversationID")
|
ConversationID = Field("ConversationID")
|
||||||
GroupID = Field("GroupID")
|
GroupID = Field("GroupID")
|
||||||
GroupServer = Field("GroupServer")
|
GroupServer = Field("GroupServer")
|
||||||
ServerTokenY = Field("ServerTokenY")
|
ServerTokenY = Field("ServerTokenY")
|
||||||
ServerTokenOnion = Field("ServerTokenOnion")
|
ServerTokenOnion = Field("ServerTokenOnion")
|
||||||
GroupInvite = Field("GroupInvite")
|
GroupInvite = Field("GroupInvite")
|
||||||
|
|
||||||
ProfileName = Field("ProfileName")
|
ProfileName = Field("ProfileName")
|
||||||
Password = Field("Password")
|
Password = Field("Password")
|
||||||
|
|
|
@ -40,7 +40,7 @@ type OverlayMessage struct {
|
||||||
|
|
||||||
// DownloadFile given a profile, a conversation handle and a file sharing key, start off a download process
|
// DownloadFile given a profile, a conversation handle and a file sharing key, start off a download process
|
||||||
// to downloadFilePath
|
// to downloadFilePath
|
||||||
func (f *Functionality) DownloadFile(profile peer.CwtchPeer, handle string, downloadFilePath string, manifestFilePath string, key string) {
|
func (f *Functionality) DownloadFile(profile peer.CwtchPeer, conversationID int, downloadFilePath string, manifestFilePath string, key string) {
|
||||||
|
|
||||||
// Store local.filesharing.filekey.manifest as the location of the manifest
|
// Store local.filesharing.filekey.manifest as the location of the manifest
|
||||||
profile.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.manifest", key), manifestFilePath)
|
profile.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.manifest", key), manifestFilePath)
|
||||||
|
@ -49,7 +49,7 @@ func (f *Functionality) DownloadFile(profile peer.CwtchPeer, handle string, down
|
||||||
profile.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, key, downloadFilePath)
|
profile.SetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, key, downloadFilePath)
|
||||||
|
|
||||||
// Get the value of conversation.filesharing.filekey.manifest.size from `handle`
|
// Get the value of conversation.filesharing.filekey.manifest.size from `handle`
|
||||||
profile.SendScopedZonedGetValToContact(handle, attr.ConversationScope, attr.FilesharingZone, fmt.Sprintf("%s.manifest.size", key))
|
profile.SendScopedZonedGetValToContact(conversationID, attr.ConversationScope, attr.FilesharingZone, fmt.Sprintf("%s.manifest.size", key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShareFile given a profile and a conversation handle, sets up a file sharing process to share the file
|
// ShareFile given a profile and a conversation handle, sets up a file sharing process to share the file
|
||||||
|
|
|
@ -35,3 +35,6 @@ const AttrAck = "ack"
|
||||||
|
|
||||||
// AttrErr - conversation attribute for errored status
|
// AttrErr - conversation attribute for errored status
|
||||||
const AttrErr = "error"
|
const AttrErr = "error"
|
||||||
|
|
||||||
|
// AttrSentTimestamp - conversation attribute for the time the message was (nominally) sent
|
||||||
|
const AttrSentTimestamp = "sent"
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import "encoding/json"
|
import (
|
||||||
|
"cwtch.im/cwtch/model/attr"
|
||||||
|
"cwtch.im/cwtch/model/constants"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
// AccessControl is a type determining client assigned authorization to a peer
|
// AccessControl is a type determining client assigned authorization to a peer
|
||||||
type AccessControl struct {
|
type AccessControl struct {
|
||||||
|
@ -57,3 +61,32 @@ type Conversation struct {
|
||||||
ACL AccessControlList
|
ACL AccessControlList
|
||||||
Accepted bool
|
Accepted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ci *Conversation) GetAttribute(scope attr.Scope, zone attr.Zone, key string) (string, bool) {
|
||||||
|
if value, exists := ci.Attributes[scope.ConstructScopedZonedPath(zone.ConstructZonedPath(key)).ToString()]; exists {
|
||||||
|
return value, true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci *Conversation) IsGroup() bool {
|
||||||
|
if _, exists := ci.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupID)).ToString()]; exists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci *Conversation) IsServer() bool {
|
||||||
|
if _, exists := ci.Attributes[attr.PublicScope.ConstructScopedZonedPath(attr.ServerKeyZone.ConstructZonedPath(string(BundleType))).ToString()]; exists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConversationMessage struct {
|
||||||
|
ID int
|
||||||
|
Body string
|
||||||
|
Attr Attributes
|
||||||
|
Signature string
|
||||||
|
ContentHash string
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ const GroupInvitePrefix = "torv3"
|
||||||
type Group struct {
|
type Group struct {
|
||||||
// GroupID is now derived from the GroupKey and the GroupServer
|
// GroupID is now derived from the GroupKey and the GroupServer
|
||||||
GroupID string
|
GroupID string
|
||||||
|
GroupName string
|
||||||
GroupKey [32]byte
|
GroupKey [32]byte
|
||||||
GroupServer string
|
GroupServer string
|
||||||
Version int
|
Version int
|
||||||
|
@ -73,11 +74,11 @@ func deriveGroupID(groupKey []byte, serverHostname string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invite generates a invitation that can be sent to a cwtch peer
|
// Invite generates a invitation that can be sent to a cwtch peer
|
||||||
func (g *Group) Invite(name string) (string, error) {
|
func (g *Group) Invite() (string, error) {
|
||||||
|
|
||||||
gci := &groups.GroupInvite{
|
gci := &groups.GroupInvite{
|
||||||
GroupID: g.GroupID,
|
GroupID: g.GroupID,
|
||||||
GroupName: name,
|
GroupName: g.GroupName,
|
||||||
SharedKey: g.GroupKey[:],
|
SharedKey: g.GroupKey[:],
|
||||||
ServerHost: g.GroupServer,
|
ServerHost: g.GroupServer,
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ func TestGroup(t *testing.T) {
|
||||||
Padding: []byte{},
|
Padding: []byte{},
|
||||||
}
|
}
|
||||||
|
|
||||||
invite, err := g.Invite("name")
|
invite, err := g.Invite()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating group invite: %v", err)
|
t.Fatalf("error creating group invite: %v", err)
|
||||||
|
@ -64,7 +64,7 @@ func TestGroupValidation(t *testing.T) {
|
||||||
Version: 0,
|
Version: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
invite, _ := group.Invite("name")
|
invite, _ := group.Invite()
|
||||||
_, err := ValidateInvite(invite)
|
_, err := ValidateInvite(invite)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -75,7 +75,7 @@ func TestGroupValidation(t *testing.T) {
|
||||||
// Generate a valid group but replace the group server...
|
// Generate a valid group but replace the group server...
|
||||||
group, _ = NewGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
|
group, _ = NewGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
|
||||||
group.GroupServer = "tcnkoch4nyr3cldkemejtkpqok342rbql6iclnjjs3ndgnjgufzyxvqd"
|
group.GroupServer = "tcnkoch4nyr3cldkemejtkpqok342rbql6iclnjjs3ndgnjgufzyxvqd"
|
||||||
invite, _ = group.Invite("name")
|
invite, _ = group.Invite()
|
||||||
_, err = ValidateInvite(invite)
|
_, err = ValidateInvite(invite)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -86,7 +86,7 @@ func TestGroupValidation(t *testing.T) {
|
||||||
// Generate a valid group but replace the group key...
|
// Generate a valid group but replace the group key...
|
||||||
group, _ = NewGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
|
group, _ = NewGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
|
||||||
group.GroupKey = sha256.Sum256([]byte{})
|
group.GroupKey = sha256.Sum256([]byte{})
|
||||||
invite, _ = group.Invite("name")
|
invite, _ = group.Invite()
|
||||||
_, err = ValidateInvite(invite)
|
_, err = ValidateInvite(invite)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"git.openprivacy.ca/cwtch.im/tapir/primitives"
|
"git.openprivacy.ca/cwtch.im/tapir/primitives"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -63,6 +64,13 @@ func getRandomness(arr *[]byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateRandomID generates a random 16 byte hex id code
|
||||||
|
func GenerateRandomID() string {
|
||||||
|
randBytes := make([]byte, 16)
|
||||||
|
rand.Read(randBytes)
|
||||||
|
return filepath.Join(hex.EncodeToString(randBytes))
|
||||||
|
}
|
||||||
|
|
||||||
// EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and
|
// EncryptMessageToGroup when given a message and a group, encrypts and signs the message under the group and
|
||||||
// profile
|
// profile
|
||||||
func EncryptMessageToGroup(message string, author primitives.Identity, group *Group) ([]byte, []byte, *groups.DecryptedGroupMessage, error) {
|
func EncryptMessageToGroup(message string, author primitives.Identity, group *Group) ([]byte, []byte, *groups.DecryptedGroupMessage, error) {
|
||||||
|
|
|
@ -98,9 +98,14 @@ func (cp *cwtchPeer) GenerateProtocolEngine(acn connectivity.ACN, bus event.Mana
|
||||||
|
|
||||||
// SendScopedZonedGetValToContact
|
// SendScopedZonedGetValToContact
|
||||||
// Status: No change in 1.5
|
// Status: No change in 1.5
|
||||||
func (cp *cwtchPeer) SendScopedZonedGetValToContact(handle string, scope attr.Scope, zone attr.Zone, path string) {
|
func (cp *cwtchPeer) SendScopedZonedGetValToContact(conversationID int, scope attr.Scope, zone attr.Zone, path string) {
|
||||||
ev := event.NewEventList(event.SendGetValMessageToPeer, event.RemotePeer, handle, event.Scope, string(scope), event.Path, string(zone.ConstructZonedPath(path)))
|
ci, err := cp.GetConversationInfo(conversationID)
|
||||||
cp.eventBus.Publish(ev)
|
if err == nil {
|
||||||
|
ev := event.NewEventList(event.SendGetValMessageToPeer, event.RemotePeer, ci.Handle, event.Scope, string(scope), event.Path, string(zone.ConstructZonedPath(path)))
|
||||||
|
cp.eventBus.Publish(ev)
|
||||||
|
} else {
|
||||||
|
log.Errorf("Error sending scoped zone to contact %v %v", conversationID, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetScopedZonedAttribute
|
// GetScopedZonedAttribute
|
||||||
|
@ -151,11 +156,11 @@ func (cp *cwtchPeer) SendMessage(conversation int, message string) error {
|
||||||
if conversationInfo != nil && err == nil {
|
if conversationInfo != nil && err == nil {
|
||||||
|
|
||||||
if tor.IsValidHostname(conversationInfo.Handle) {
|
if tor.IsValidHostname(conversationInfo.Handle) {
|
||||||
ev := event.NewEvent(event.SendMessageToPeer, map[event.Field]string{event.RemotePeer: conversationInfo.Handle, event.Data: message})
|
ev := event.NewEvent(event.SendMessageToPeer, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationInfo.ID), event.RemotePeer: conversationInfo.Handle, event.Data: message})
|
||||||
onion, _ := cp.storage.LoadProfileKeyValue(TypeAttribute, attr.PublicScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Onion)).ToString())
|
onion, _ := cp.storage.LoadProfileKeyValue(TypeAttribute, attr.PublicScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Onion)).ToString())
|
||||||
|
|
||||||
// For p2p messages we store the event id of the message as the "signature" we can then look this up in the database later for acks
|
// For p2p messages we store the event id of the message as the "signature" we can then look this up in the database later for acks
|
||||||
err := cp.storage.InsertMessage(conversationInfo.ID, 0, message, model.Attributes{"ack": event.False, "sent": time.Now().String()}, ev.EventID, model.CalculateContentHash(string(onion), message))
|
err := cp.storage.InsertMessage(conversationInfo.ID, 0, message, model.Attributes{constants.AttrAck: event.False, constants.AttrSentTimestamp: time.Now().Format(time.RFC3339Nano)}, ev.EventID, model.CalculateContentHash(string(onion), message))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -187,10 +192,13 @@ func (cp *cwtchPeer) SendMessage(conversation int, message string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the Group Message
|
// Insert the Group Message
|
||||||
cp.storage.InsertMessage(conversationInfo.ID, 0, dm.Text, model.Attributes{constants.AttrAck: constants.False, "PreviousSignature": base64.StdEncoding.EncodeToString(dm.PreviousMessageSig), "Author": dm.Onion, "Sent": strconv.Itoa(int(dm.Timestamp))}, base64.StdEncoding.EncodeToString(sig), model.CalculateContentHash(dm.Onion, dm.Text))
|
err = cp.storage.InsertMessage(conversationInfo.ID, 0, dm.Text, model.Attributes{constants.AttrAck: constants.False, "PreviousSignature": base64.StdEncoding.EncodeToString(dm.PreviousMessageSig), "Author": dm.Onion, constants.AttrSentTimestamp: strconv.Itoa(int(dm.Timestamp))}, base64.StdEncoding.EncodeToString(sig), model.CalculateContentHash(dm.Onion, dm.Text))
|
||||||
|
if err == nil {
|
||||||
ev := event.NewEvent(event.SendMessageToGroup, map[event.Field]string{event.GroupID: conversationInfo.Handle, event.GroupServer: group.GroupServer, event.Ciphertext: base64.StdEncoding.EncodeToString(ct), event.Signature: base64.StdEncoding.EncodeToString(sig)})
|
ev := event.NewEvent(event.SendMessageToGroup, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationInfo.ID), event.GroupID: conversationInfo.Handle, event.GroupServer: group.GroupServer, event.Ciphertext: base64.StdEncoding.EncodeToString(ct), event.Signature: base64.StdEncoding.EncodeToString(sig)})
|
||||||
cp.eventBus.Publish(ev)
|
cp.eventBus.Publish(ev)
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -355,7 +363,7 @@ func (cp *cwtchPeer) ImportGroup(exportedInvite string) (int, error) {
|
||||||
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupServer)), gci.ServerHost)
|
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupServer)), gci.ServerHost)
|
||||||
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupKey)), base64.StdEncoding.EncodeToString(gci.SharedKey))
|
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupKey)), base64.StdEncoding.EncodeToString(gci.SharedKey))
|
||||||
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.Name)), gci.GroupName)
|
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.Name)), gci.GroupName)
|
||||||
cp.eventBus.Publish(event.NewEvent(event.NewGroup, map[event.Field]string{event.GroupConversationID: strconv.Itoa(groupConversationID), event.GroupServer: gci.ServerHost, event.GroupInvite: exportedInvite}))
|
cp.eventBus.Publish(event.NewEvent(event.NewGroup, map[event.Field]string{event.ConversationID: strconv.Itoa(groupConversationID), event.GroupServer: gci.ServerHost, event.GroupInvite: exportedInvite}))
|
||||||
}
|
}
|
||||||
return groupConversationID, err
|
return groupConversationID, err
|
||||||
}
|
}
|
||||||
|
@ -364,7 +372,9 @@ func (cp *cwtchPeer) ImportGroup(exportedInvite string) (int, error) {
|
||||||
func (cp *cwtchPeer) NewContactConversation(handle string, acl model.AccessControl, accepted bool) (int, error) {
|
func (cp *cwtchPeer) NewContactConversation(handle string, acl model.AccessControl, accepted bool) (int, error) {
|
||||||
cp.mutex.Lock()
|
cp.mutex.Lock()
|
||||||
defer cp.mutex.Unlock()
|
defer cp.mutex.Unlock()
|
||||||
return cp.storage.NewConversation(handle, model.Attributes{event.SaveHistoryKey: event.DeleteHistoryDefault}, model.AccessControlList{handle: acl}, accepted)
|
conversationID, err := cp.storage.NewConversation(handle, model.Attributes{event.SaveHistoryKey: event.DeleteHistoryDefault}, model.AccessControlList{handle: acl}, accepted)
|
||||||
|
cp.eventBus.Publish(event.NewEvent(event.ContactCreated, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationID), event.RemotePeer: handle}))
|
||||||
|
return conversationID, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptConversation looks up a conversation by `handle` and sets the Accepted status to `true`
|
// AcceptConversation looks up a conversation by `handle` and sets the Accepted status to `true`
|
||||||
|
@ -375,6 +385,23 @@ func (cp *cwtchPeer) AcceptConversation(id int) error {
|
||||||
return cp.storage.AcceptConversation(id)
|
return cp.storage.AcceptConversation(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockConversation looks up a conversation by `handle` and sets the Accepted status to `true`
|
||||||
|
// This will cause Cwtch to auto connect to this conversation on start up
|
||||||
|
func (cp *cwtchPeer) BlockConversation(id int) error {
|
||||||
|
cp.mutex.Lock()
|
||||||
|
defer cp.mutex.Unlock()
|
||||||
|
ci, err := cp.storage.GetConversation(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// p2p conversations have a single ACL referencing the remote peer. Set this to blocked...
|
||||||
|
ci.ACL[ci.Handle] = model.AccessControl{Blocked: true, Read: false, Append: false}
|
||||||
|
// Send an event in any case to block the protocol engine...
|
||||||
|
// TODO at some point in the future engine needs to understand ACLs not just legacy auth status
|
||||||
|
cp.eventBus.Publish(event.NewEvent(event.SetPeerAuthorization, map[event.Field]string{event.RemotePeer: ci.Handle, event.Authorization: string(model.AuthBlocked)}))
|
||||||
|
return cp.storage.SetConversationACL(id, ci.ACL)
|
||||||
|
}
|
||||||
|
|
||||||
func (cp *cwtchPeer) FetchConversations() ([]*model.Conversation, error) {
|
func (cp *cwtchPeer) FetchConversations() ([]*model.Conversation, error) {
|
||||||
cp.mutex.Lock()
|
cp.mutex.Lock()
|
||||||
defer cp.mutex.Unlock()
|
defer cp.mutex.Unlock()
|
||||||
|
@ -424,12 +451,29 @@ func (cp *cwtchPeer) GetConversationAttribute(id int, path attr.ScopedZonedPath)
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChannelMessage returns a message from a conversation channel referenced by the absolute ID.
|
||||||
|
// Note: This should note be used to index a list as the ID is not expected to be tied to absolute position
|
||||||
|
// in the table (e.g. deleted messages, expired messages, etc.)
|
||||||
func (cp *cwtchPeer) GetChannelMessage(conversation int, channel int, id int) (string, model.Attributes, error) {
|
func (cp *cwtchPeer) GetChannelMessage(conversation int, channel int, id int) (string, model.Attributes, error) {
|
||||||
cp.mutex.Lock()
|
cp.mutex.Lock()
|
||||||
defer cp.mutex.Unlock()
|
defer cp.mutex.Unlock()
|
||||||
return cp.storage.GetChannelMessage(conversation, channel, id)
|
return cp.storage.GetChannelMessage(conversation, channel, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChannelMessageCount returns the absolute number of messages in a given conversation channel
|
||||||
|
func (cp *cwtchPeer) GetChannelMessageCount(conversation int, channel int) (int, error) {
|
||||||
|
cp.mutex.Lock()
|
||||||
|
defer cp.mutex.Unlock()
|
||||||
|
return cp.storage.GetChannelMessageCount(conversation, channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMostRecentMessages returns a selection of messages, ordered by most recently inserted
|
||||||
|
func (cp *cwtchPeer) GetMostRecentMessages(conversation int, channel int, offset int, limit int) ([]model.ConversationMessage, error) {
|
||||||
|
cp.mutex.Lock()
|
||||||
|
defer cp.mutex.Unlock()
|
||||||
|
return cp.storage.GetMostRecentMessages(conversation, channel, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
// StartGroup create a new group linked to the given server and returns the group ID, an invite or an error.
|
// StartGroup create a new group linked to the given server and returns the group ID, an invite or an error.
|
||||||
// Status: TODO change server handle to conversation id...?
|
// Status: TODO change server handle to conversation id...?
|
||||||
func (cp *cwtchPeer) StartGroup(name string, server string) (int, error) {
|
func (cp *cwtchPeer) StartGroup(name string, server string) (int, error) {
|
||||||
|
@ -445,8 +489,9 @@ func (cp *cwtchPeer) StartGroup(name string, server string) (int, error) {
|
||||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.Name)), name)
|
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.Name)), name)
|
||||||
|
|
||||||
cp.eventBus.Publish(event.NewEvent(event.GroupCreated, map[event.Field]string{
|
cp.eventBus.Publish(event.NewEvent(event.GroupCreated, map[event.Field]string{
|
||||||
event.GroupID: group.GroupID,
|
event.ConversationID: strconv.Itoa(conversationID),
|
||||||
event.GroupServer: group.GroupServer,
|
event.GroupID: group.GroupID,
|
||||||
|
event.GroupServer: group.GroupServer,
|
||||||
}))
|
}))
|
||||||
return conversationID, nil
|
return conversationID, nil
|
||||||
}
|
}
|
||||||
|
@ -480,7 +525,10 @@ func (cp *cwtchPeer) AddServer(serverSpecification string) error {
|
||||||
// Add the contact if we don't already have it
|
// Add the contact if we don't already have it
|
||||||
conversationInfo, _ := cp.FetchConversationInfo(onion)
|
conversationInfo, _ := cp.FetchConversationInfo(onion)
|
||||||
if conversationInfo == nil {
|
if conversationInfo == nil {
|
||||||
cp.NewContactConversation(onion, model.DefaultP2PAccessControl(), true)
|
_, err := cp.NewContactConversation(onion, model.DefaultP2PAccessControl(), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conversationInfo, err = cp.FetchConversationInfo(onion)
|
conversationInfo, err = cp.FetchConversationInfo(onion)
|
||||||
|
@ -530,13 +578,13 @@ func (cp *cwtchPeer) GetOnion() string {
|
||||||
|
|
||||||
// GetPeerState
|
// GetPeerState
|
||||||
// Status: Ready for 1.5
|
// Status: Ready for 1.5
|
||||||
func (cp *cwtchPeer) GetPeerState(handle string) (connections.ConnectionState, bool) {
|
func (cp *cwtchPeer) GetPeerState(handle string) connections.ConnectionState {
|
||||||
cp.mutex.Lock()
|
cp.mutex.Lock()
|
||||||
defer cp.mutex.Unlock()
|
defer cp.mutex.Unlock()
|
||||||
if state, ok := cp.state[handle]; ok {
|
if state, ok := cp.state[handle]; ok {
|
||||||
return state, ok
|
return state
|
||||||
}
|
}
|
||||||
return connections.DISCONNECTED, false
|
return connections.DISCONNECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerWithOnion initiates a request to the Protocol Engine to set up Cwtch Session with a given tor v3 onion
|
// PeerWithOnion initiates a request to the Protocol Engine to set up Cwtch Session with a given tor v3 onion
|
||||||
|
@ -593,11 +641,12 @@ func (cp *cwtchPeer) SendInviteToConversation(conversationID int, inviteConversa
|
||||||
|
|
||||||
group := model.Group{
|
group := model.Group{
|
||||||
GroupID: groupID,
|
GroupID: groupID,
|
||||||
|
GroupName: groupName,
|
||||||
GroupKey: groupKeyFixed,
|
GroupKey: groupKeyFixed,
|
||||||
GroupServer: groupServer,
|
GroupServer: groupServer,
|
||||||
}
|
}
|
||||||
|
|
||||||
groupInvite, err := group.Invite(groupName)
|
groupInvite, err := group.Invite()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("group invite is malformed")
|
return errors.New("group invite is malformed")
|
||||||
}
|
}
|
||||||
|
@ -773,7 +822,7 @@ func (cp *cwtchPeer) storeMessage(handle string, message string, sent time.Time)
|
||||||
|
|
||||||
// Generate a random number and use it as the signature
|
// Generate a random number and use it as the signature
|
||||||
signature := event.GetRandNumber().String()
|
signature := event.GetRandNumber().String()
|
||||||
return cp.storage.InsertMessage(ci.ID, 0, message, model.Attributes{constants.AttrAck: event.True, "sent": sent.String()}, signature, model.CalculateContentHash(handle, message))
|
return cp.storage.InsertMessage(ci.ID, 0, message, model.Attributes{constants.AttrAck: event.True, constants.AttrSentTimestamp: sent.Format(time.RFC3339Nano)}, signature, model.CalculateContentHash(handle, message))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShareFile begins hosting the given serialized manifest
|
// ShareFile begins hosting the given serialized manifest
|
||||||
|
@ -870,7 +919,7 @@ func (cp *cwtchPeer) eventHandler() {
|
||||||
val, exists = cp.GetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
val, exists = cp.GetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := event.NewEvent(event.SendRetValMessageToPeer, map[event.Field]string{event.RemotePeer: onion, event.Exists: strconv.FormatBool(exists)})
|
resp := event.NewEvent(event.SendRetValMessageToPeer, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationInfo.ID), event.RemotePeer: onion, event.Exists: strconv.FormatBool(exists)})
|
||||||
resp.EventID = ev.EventID
|
resp.EventID = ev.EventID
|
||||||
if exists {
|
if exists {
|
||||||
resp.Data[event.Data] = val
|
resp.Data[event.Data] = val
|
||||||
|
@ -998,14 +1047,14 @@ func (cp *cwtchPeer) attemptInsertOrAcknowledgeLegacyGroupConversation(conversat
|
||||||
attr[constants.AttrAck] = constants.True
|
attr[constants.AttrAck] = constants.True
|
||||||
cp.storage.UpdateMessageAttributes(conversationID, 0, messageID, attr)
|
cp.storage.UpdateMessageAttributes(conversationID, 0, messageID, attr)
|
||||||
cp.mutex.Unlock()
|
cp.mutex.Unlock()
|
||||||
cp.eventBus.Publish(event.NewEvent(event.IndexedAcknowledgement, map[event.Field]string{event.GroupConversationID: strconv.Itoa(conversationID), event.Index: strconv.Itoa(messageID)}))
|
cp.eventBus.Publish(event.NewEvent(event.IndexedAcknowledgement, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationID), event.Index: strconv.Itoa(messageID)}))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cp.mutex.Lock()
|
cp.mutex.Lock()
|
||||||
cp.storage.InsertMessage(conversationID, 0, dm.Text, model.Attributes{constants.AttrAck: constants.True, "PreviousSignature": base64.StdEncoding.EncodeToString(dm.PreviousMessageSig), "Author": dm.Onion, "Sent": strconv.Itoa(int(dm.Timestamp))}, signature, model.CalculateContentHash(dm.Onion, dm.Text))
|
cp.storage.InsertMessage(conversationID, 0, dm.Text, model.Attributes{constants.AttrAck: constants.True, "PreviousSignature": base64.StdEncoding.EncodeToString(dm.PreviousMessageSig), "Author": dm.Onion, constants.AttrSentTimestamp: strconv.Itoa(int(dm.Timestamp))}, signature, model.CalculateContentHash(dm.Onion, dm.Text))
|
||||||
cp.mutex.Unlock()
|
cp.mutex.Unlock()
|
||||||
cp.eventBus.Publish(event.NewEvent(event.NewMessageFromGroup, map[event.Field]string{event.GroupConversationID: strconv.Itoa(conversationID), event.Index: strconv.Itoa(messageID)}))
|
cp.eventBus.Publish(event.NewEvent(event.NewMessageFromGroup, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationID), event.Index: strconv.Itoa(messageID)}))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -1027,7 +1076,7 @@ func (cp *cwtchPeer) attemptAcknowledgeP2PConversation(handle string, signature
|
||||||
attr[constants.AttrAck] = constants.True
|
attr[constants.AttrAck] = constants.True
|
||||||
cp.storage.UpdateMessageAttributes(ci.ID, 0, id, attr)
|
cp.storage.UpdateMessageAttributes(ci.ID, 0, id, attr)
|
||||||
cp.mutex.Unlock()
|
cp.mutex.Unlock()
|
||||||
cp.eventBus.Publish(event.NewEvent(event.IndexedAcknowledgement, map[event.Field]string{event.RemotePeer: handle, event.Index: strconv.Itoa(id)}))
|
cp.eventBus.Publish(event.NewEvent(event.IndexedAcknowledgement, map[event.Field]string{event.ConversationID: strconv.Itoa(ci.ID), event.RemotePeer: handle, event.Index: strconv.Itoa(id)}))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -1053,7 +1102,7 @@ func (cp *cwtchPeer) attemptErrorConversationMessage(handle string, signature st
|
||||||
attr[constants.AttrErr] = constants.True
|
attr[constants.AttrErr] = constants.True
|
||||||
cp.storage.UpdateMessageAttributes(ci.ID, 0, id, attr)
|
cp.storage.UpdateMessageAttributes(ci.ID, 0, id, attr)
|
||||||
cp.mutex.Unlock()
|
cp.mutex.Unlock()
|
||||||
cp.eventBus.Publish(event.NewEvent(eventType, map[event.Field]string{event.RemotePeer: handle, event.Error: error, event.Index: strconv.Itoa(id)}))
|
cp.eventBus.Publish(event.NewEvent(eventType, map[event.Field]string{event.ConversationID: strconv.Itoa(ci.ID), event.RemotePeer: handle, event.Error: error, event.Index: strconv.Itoa(id)}))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -41,11 +41,14 @@ type CwtchProfileStorage struct {
|
||||||
acceptConversationStmt *sql.Stmt
|
acceptConversationStmt *sql.Stmt
|
||||||
deleteConversationStmt *sql.Stmt
|
deleteConversationStmt *sql.Stmt
|
||||||
setConversationAttributesStmt *sql.Stmt
|
setConversationAttributesStmt *sql.Stmt
|
||||||
|
setConversationACLStmt *sql.Stmt
|
||||||
|
|
||||||
channelInsertStmts map[ChannelID]*sql.Stmt
|
channelInsertStmts map[ChannelID]*sql.Stmt
|
||||||
channelUpdateMessageStmts map[ChannelID]*sql.Stmt
|
channelUpdateMessageStmts map[ChannelID]*sql.Stmt
|
||||||
channelGetMessageStmts map[ChannelID]*sql.Stmt
|
channelGetMessageStmts map[ChannelID]*sql.Stmt
|
||||||
channelGetMessageBySignatureStmts map[ChannelID]*sql.Stmt
|
channelGetMessageBySignatureStmts map[ChannelID]*sql.Stmt
|
||||||
|
channelGetCountStmts map[ChannelID]*sql.Stmt
|
||||||
|
channelGetMostRecentMessagesStmts map[ChannelID]*sql.Stmt
|
||||||
|
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
}
|
}
|
||||||
|
@ -65,6 +68,7 @@ const selectConversationSQLStmt = `select ID, Handle, Attributes, ACL, Accepted
|
||||||
const selectConversationByHandleSQLStmt = `select ID, Handle, Attributes, ACL, Accepted from conversations where Handle=(?);`
|
const selectConversationByHandleSQLStmt = `select ID, Handle, Attributes, ACL, Accepted from conversations where Handle=(?);`
|
||||||
const acceptedConversationSQLStmt = `update conversations set Accepted=true where ID=(?);`
|
const acceptedConversationSQLStmt = `update conversations set Accepted=true where ID=(?);`
|
||||||
const setConversationAttributesSQLStmt = `update conversations set Attributes=(?) where ID=(?) ;`
|
const setConversationAttributesSQLStmt = `update conversations set Attributes=(?) where ID=(?) ;`
|
||||||
|
const setConversationACLSQLStmt = `update conversations set ACL=(?) where ID=(?) ;`
|
||||||
const deleteConversationSQLStmt = `delete from conversations where ID=(?);`
|
const deleteConversationSQLStmt = `delete from conversations where ID=(?);`
|
||||||
|
|
||||||
// createTableConversationMessagesSQLStmt is a template for creating conversation based tables...
|
// createTableConversationMessagesSQLStmt is a template for creating conversation based tables...
|
||||||
|
@ -85,6 +89,12 @@ const getMessageBySignatureFromConversationSQLStmt = `select ID from channel_%d_
|
||||||
// getMessageByContentHashFromConversationSQLStmt is a template for creating conversation based tables...
|
// getMessageByContentHashFromConversationSQLStmt is a template for creating conversation based tables...
|
||||||
const getMessageByContentHashFromConversationSQLStmt = `select ID from channel_%d_%d_chat where ContentHash=(?);`
|
const getMessageByContentHashFromConversationSQLStmt = `select ID from channel_%d_%d_chat where ContentHash=(?);`
|
||||||
|
|
||||||
|
// getMessageCountFromConversationSqlStmt
|
||||||
|
const getMessageCountFromConversationSqlStmt = `select count(*) from channel_%d_%d_chat;`
|
||||||
|
|
||||||
|
// getMostRecentMessagesFromSqlStmt
|
||||||
|
const getMostRecentMessagesSqlStmt = `select ID, Body, Attributes, Signature, ContentHash from channel_%d_%d_chat order by ID desc limit (?) offset (?);`
|
||||||
|
|
||||||
// NewCwtchProfileStorage constructs a new CwtchProfileStorage from a database. It is also responsible for
|
// NewCwtchProfileStorage constructs a new CwtchProfileStorage from a database. It is also responsible for
|
||||||
// Preparing commonly used SQL Statements
|
// Preparing commonly used SQL Statements
|
||||||
func NewCwtchProfileStorage(db *sql.DB) (*CwtchProfileStorage, error) {
|
func NewCwtchProfileStorage(db *sql.DB) (*CwtchProfileStorage, error) {
|
||||||
|
@ -147,6 +157,12 @@ func NewCwtchProfileStorage(db *sql.DB) (*CwtchProfileStorage, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setConversationACLStmt, err := db.Prepare(setConversationACLSQLStmt)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error preparing query: %v %v", setConversationACLSQLStmt, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &CwtchProfileStorage{db: db,
|
return &CwtchProfileStorage{db: db,
|
||||||
insertProfileKeyValueStmt: insertProfileKeyValueStmt,
|
insertProfileKeyValueStmt: insertProfileKeyValueStmt,
|
||||||
selectProfileKeyValueStmt: selectProfileKeyStmt,
|
selectProfileKeyValueStmt: selectProfileKeyStmt,
|
||||||
|
@ -157,10 +173,13 @@ func NewCwtchProfileStorage(db *sql.DB) (*CwtchProfileStorage, error) {
|
||||||
acceptConversationStmt: acceptConversationStmt,
|
acceptConversationStmt: acceptConversationStmt,
|
||||||
deleteConversationStmt: deleteConversationStmt,
|
deleteConversationStmt: deleteConversationStmt,
|
||||||
setConversationAttributesStmt: setConversationAttributesStmt,
|
setConversationAttributesStmt: setConversationAttributesStmt,
|
||||||
|
setConversationACLStmt: setConversationACLStmt,
|
||||||
channelInsertStmts: map[ChannelID]*sql.Stmt{},
|
channelInsertStmts: map[ChannelID]*sql.Stmt{},
|
||||||
channelUpdateMessageStmts: map[ChannelID]*sql.Stmt{},
|
channelUpdateMessageStmts: map[ChannelID]*sql.Stmt{},
|
||||||
channelGetMessageStmts: map[ChannelID]*sql.Stmt{},
|
channelGetMessageStmts: map[ChannelID]*sql.Stmt{},
|
||||||
channelGetMessageBySignatureStmts: map[ChannelID]*sql.Stmt{}},
|
channelGetMessageBySignatureStmts: map[ChannelID]*sql.Stmt{},
|
||||||
|
channelGetMostRecentMessagesStmts: map[ChannelID]*sql.Stmt{},
|
||||||
|
channelGetCountStmts: map[ChannelID]*sql.Stmt{}},
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,6 +377,16 @@ func (cps *CwtchProfileStorage) DeleteConversation(id int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetConversationACL sets a new ACL on a given conversation.
|
||||||
|
func (cps *CwtchProfileStorage) SetConversationACL(id int, acl model.AccessControlList) error {
|
||||||
|
_, err := cps.setConversationACLStmt.Exec(acl, id)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error executing query: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetConversationAttribute sets a new attribute on a given conversation.
|
// SetConversationAttribute sets a new attribute on a given conversation.
|
||||||
func (cps *CwtchProfileStorage) SetConversationAttribute(id int, path attr.ScopedZonedPath, value string) error {
|
func (cps *CwtchProfileStorage) SetConversationAttribute(id int, path attr.ScopedZonedPath, value string) error {
|
||||||
ci, err := cps.GetConversation(id)
|
ci, err := cps.GetConversation(id)
|
||||||
|
@ -501,6 +530,68 @@ func (cps *CwtchProfileStorage) GetChannelMessage(conversation int, channel int,
|
||||||
return body, model.DeserializeAttributes(attributes), nil
|
return body, model.DeserializeAttributes(attributes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChannelMessageCount returns the number of messages in a channel
|
||||||
|
func (cps *CwtchProfileStorage) GetChannelMessageCount(conversation int, channel int) (int, error) {
|
||||||
|
channelID := ChannelID{Conversation: conversation, Channel: channel}
|
||||||
|
|
||||||
|
_, exists := cps.channelGetCountStmts[channelID]
|
||||||
|
if !exists {
|
||||||
|
conversationStmt, err := cps.db.Prepare(fmt.Sprintf(getMessageCountFromConversationSqlStmt, conversation, channel))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error executing transaction: %v", err)
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
cps.channelGetCountStmts[channelID] = conversationStmt
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int
|
||||||
|
err := cps.channelGetCountStmts[channelID].QueryRow().Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error executing query: %v", err)
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChannelMessageCount returns the number of messages in a channel
|
||||||
|
func (cps *CwtchProfileStorage) GetMostRecentMessages(conversation int, channel int, offset int, limit int) ([]model.ConversationMessage, error) {
|
||||||
|
channelID := ChannelID{Conversation: conversation, Channel: channel}
|
||||||
|
|
||||||
|
_, exists := cps.channelGetMostRecentMessagesStmts[channelID]
|
||||||
|
if !exists {
|
||||||
|
conversationStmt, err := cps.db.Prepare(fmt.Sprintf(getMostRecentMessagesSqlStmt, conversation, channel))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error executing transaction: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cps.channelGetMostRecentMessagesStmts[channelID] = conversationStmt
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := cps.channelGetMostRecentMessagesStmts[channelID].Query(limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error executing query: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var conversationMessages []model.ConversationMessage
|
||||||
|
defer rows.Close()
|
||||||
|
for {
|
||||||
|
result := rows.Next()
|
||||||
|
if !result {
|
||||||
|
return conversationMessages, nil
|
||||||
|
}
|
||||||
|
var id int
|
||||||
|
var body string
|
||||||
|
var attributes []byte
|
||||||
|
var sig string
|
||||||
|
var contenthash string
|
||||||
|
err = rows.Scan(&id, &body, &attributes, &sig, &contenthash)
|
||||||
|
if err != nil {
|
||||||
|
return conversationMessages, err
|
||||||
|
}
|
||||||
|
conversationMessages = append(conversationMessages, model.ConversationMessage{ID: id, Body: body, Attr: model.DeserializeAttributes(attributes), Signature: sig, ContentHash: contenthash})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Close closes the underlying database and prepared statements
|
// Close closes the underlying database and prepared statements
|
||||||
func (cps *CwtchProfileStorage) Close() {
|
func (cps *CwtchProfileStorage) Close() {
|
||||||
if cps.db != nil {
|
if cps.db != nil {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
// AccessPeeringState provides access to functions relating to the underlying connections of a peer.
|
// AccessPeeringState provides access to functions relating to the underlying connections of a peer.
|
||||||
type AccessPeeringState interface {
|
type AccessPeeringState interface {
|
||||||
GetPeerState(string) (connections.ConnectionState, bool)
|
GetPeerState(string) connections.ConnectionState
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModifyPeeringState is a meta-interface intended to restrict callers to modify-only access to connection peers
|
// ModifyPeeringState is a meta-interface intended to restrict callers to modify-only access to connection peers
|
||||||
|
@ -48,7 +48,7 @@ type ModifyServers interface {
|
||||||
type SendMessages interface {
|
type SendMessages interface {
|
||||||
SendMessage(conversation int, message string) error
|
SendMessage(conversation int, message string) error
|
||||||
SendInviteToConversation(conversationID int, inviteConversationID int) error
|
SendInviteToConversation(conversationID int, inviteConversationID int) error
|
||||||
SendScopedZonedGetValToContact(handle string, scope attr.Scope, zone attr.Zone, key string)
|
SendScopedZonedGetValToContact(conversationID int, scope attr.Scope, zone attr.Zone, key string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModifyMessages enables a caller to modify the messages in a timeline
|
// ModifyMessages enables a caller to modify the messages in a timeline
|
||||||
|
@ -106,11 +106,15 @@ type CwtchPeer interface {
|
||||||
GetConversationInfo(conversation int) (*model.Conversation, error)
|
GetConversationInfo(conversation int) (*model.Conversation, error)
|
||||||
FetchConversationInfo(handle string) (*model.Conversation, error)
|
FetchConversationInfo(handle string) (*model.Conversation, error)
|
||||||
AcceptConversation(conversation int) error
|
AcceptConversation(conversation int) error
|
||||||
|
BlockConversation(conversation int) error
|
||||||
SetConversationAttribute(conversation int, path attr.ScopedZonedPath, value string) error
|
SetConversationAttribute(conversation int, path attr.ScopedZonedPath, value string) error
|
||||||
GetConversationAttribute(conversation int, path attr.ScopedZonedPath) (string, error)
|
GetConversationAttribute(conversation int, path attr.ScopedZonedPath) (string, error)
|
||||||
DeleteConversation(conversation int) error
|
DeleteConversation(conversation int) error
|
||||||
|
|
||||||
|
// New Unified Conversation Channel Interfaces
|
||||||
GetChannelMessage(conversation int, channel int, id int) (string, model.Attributes, error)
|
GetChannelMessage(conversation int, channel int, id int) (string, model.Attributes, error)
|
||||||
|
GetChannelMessageCount(conversation int, channel int) (int, error)
|
||||||
|
GetMostRecentMessages(conversation int, channel int, offset int, limit int) ([]model.ConversationMessage, error)
|
||||||
|
|
||||||
ShareFile(fileKey string, serializedManifest string)
|
ShareFile(fileKey string, serializedManifest string)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
// Import SQL Cipher
|
|
||||||
_ "github.com/mutecomm/go-sqlcipher/v4"
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
"io"
|
"io"
|
||||||
|
|
|
@ -36,10 +36,4 @@ func ReadProfile(directory string, key [32]byte, salt [128]byte) (*model.Profile
|
||||||
return v1.ReadProfile(directory, key, salt)
|
return v1.ReadProfile(directory, key, salt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProfile creates a new profile for use in the profile store.
|
|
||||||
func NewProfile(name string) *model.Profile {
|
|
||||||
profile := model.GenerateNewProfile(name)
|
|
||||||
return profile
|
|
||||||
}
|
|
||||||
|
|
||||||
// ********* Versioning and upgrade **********
|
// ********* Versioning and upgrade **********
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
// Import SQL Cipher
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
app2 "cwtch.im/cwtch/app"
|
app2 "cwtch.im/cwtch/app"
|
||||||
"cwtch.im/cwtch/app/utils"
|
"cwtch.im/cwtch/app/utils"
|
||||||
|
@ -15,6 +16,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
|
_ "github.com/mutecomm/go-sqlcipher/v4"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
@ -31,62 +33,28 @@ var (
|
||||||
carolLines = []string{"Howdy, thanks!"}
|
carolLines = []string{"Howdy, thanks!"}
|
||||||
)
|
)
|
||||||
|
|
||||||
func printAndCountVerifedTimeline(t *testing.T, timeline []model.Message) int {
|
func waitForConnection(t *testing.T, peer peer.CwtchPeer, addr string, target connections.ConnectionState) {
|
||||||
numVerified := 0
|
|
||||||
for _, message := range timeline {
|
|
||||||
fmt.Printf("%v %v> %s\n", message.Timestamp, message.PeerID, message.Message)
|
|
||||||
numVerified++
|
|
||||||
}
|
|
||||||
return numVerified
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForPeerGroupConnection(t *testing.T, peer peer.CwtchPeer, serverAddr string) {
|
|
||||||
peerName, _ := peer.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
peerName, _ := peer.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
||||||
for {
|
for {
|
||||||
fmt.Printf("%v checking group connection...\n", peerName)
|
fmt.Printf("%v checking connection...\n", peerName)
|
||||||
state, ok := peer.GetPeerState(serverAddr)
|
state := peer.GetPeerState(addr)
|
||||||
if ok {
|
fmt.Printf("Waiting for Peer %v to %v - state: %v\n", peerName, addr, state)
|
||||||
fmt.Printf("Waiting for Peer %v to join group %v - state: %v\n", peerName, serverAddr, state)
|
if state == connections.FAILED {
|
||||||
if state == connections.FAILED {
|
t.Fatalf("%v could not connect to %v", peer.GetOnion(), addr)
|
||||||
t.Fatalf("%v could not connect to %v", peer.GetOnion(), serverAddr)
|
}
|
||||||
}
|
if state != target {
|
||||||
if state != connections.SYNCED {
|
fmt.Printf("peer %v %v waiting connect %v, currently: %v\n", peerName, peer.GetOnion(), addr, connections.ConnectionStateName[state])
|
||||||
fmt.Printf("peer %v %v waiting connect to group %v, currently: %v\n", peerName, peer.GetOnion(), serverAddr, connections.ConnectionStateName[state])
|
time.Sleep(time.Second * 5)
|
||||||
time.Sleep(time.Second * 5)
|
continue
|
||||||
continue
|
} else {
|
||||||
} else {
|
fmt.Printf("peer %v %v CONNECTED to %v\n", peerName, peer.GetOnion(), addr)
|
||||||
fmt.Printf("peer %v %v CONNECTED to group %v\n", peerName, peer.GetOnion(), serverAddr)
|
break
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second * 2)
|
time.Sleep(time.Second * 2)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) {
|
|
||||||
for {
|
|
||||||
state, ok := peera.GetPeerState(peerb.GetOnion())
|
|
||||||
if ok {
|
|
||||||
//log.Infof("Waiting for Peer %v to peer with peer: %v - state: %v\n", peera.GetProfile().Name, peerb.GetProfile().Name, state)
|
|
||||||
if state == connections.FAILED {
|
|
||||||
t.Fatalf("%v could not connect to %v", peera.GetOnion(), peerb.GetOnion())
|
|
||||||
}
|
|
||||||
if state != connections.AUTHENTICATED {
|
|
||||||
fmt.Printf("peer %v waiting connect to peer %v, currently: %v\n", peera.GetOnion(), peerb.GetOnion(), connections.ConnectionStateName[state])
|
|
||||||
time.Sleep(time.Second * 5)
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
peerAName, _ := peera.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
|
||||||
peerBName, _ := peerb.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
|
||||||
fmt.Printf("%v CONNECTED and AUTHED to %v\n", peerAName, peerBName)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCwtchPeerIntegration(t *testing.T) {
|
func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
numGoRoutinesStart := runtime.NumGoroutine()
|
numGoRoutinesStart := runtime.NumGoroutine()
|
||||||
|
|
||||||
|
@ -179,65 +147,37 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error adding conversaiton %v", alice2bobConversationID)
|
t.Fatalf("error adding conversaiton %v", alice2bobConversationID)
|
||||||
}
|
}
|
||||||
alice.PeerWithOnion(bob.GetOnion())
|
bob2aliceConversationID, err := bob.NewContactConversation(alice.GetOnion(), model.DefaultP2PAccessControl(), true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error adding conversaiton %v", bob2aliceConversationID)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("Alice peering with Carol...")
|
t.Logf("Alice peering with Carol...")
|
||||||
// Simulate Alice Adding Carol
|
// Simulate Alice Adding Carol
|
||||||
alice2carolConversationID, err := alice.NewContactConversation(carol.GetOnion(), model.DefaultP2PAccessControl(), true)
|
alice2carolConversationID, err := alice.NewContactConversation(carol.GetOnion(), model.DefaultP2PAccessControl(), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error adding conversaiton %v", alice2carolConversationID)
|
t.Fatalf("error adding conversaiton %v", alice2carolConversationID)
|
||||||
}
|
}
|
||||||
alice.PeerWithOnion(carol.GetOnion())
|
|
||||||
|
|
||||||
// Simulate Alice Creating a Group
|
|
||||||
fmt.Println("Alice joining server...")
|
|
||||||
if err := alice.AddServer(string(serverKeyBundle)); err != nil {
|
|
||||||
t.Fatalf("Failed to Add Server Bundle %v", err)
|
|
||||||
}
|
|
||||||
err = alice.JoinServer(ServerAddr)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("alice cannot join server %v %v", ServerAddr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Creating group on ", ServerAddr, "...")
|
|
||||||
aliceGroupConversationID, err := alice.StartGroup("Our Cool Testing Group", ServerAddr)
|
|
||||||
fmt.Printf("Created group: %v!\n", aliceGroupConversationID)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to init group: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Waiting for alice to join server...")
|
|
||||||
|
|
||||||
waitForPeerGroupConnection(t, alice, ServerAddr)
|
|
||||||
|
|
||||||
fmt.Println("Waiting for alice and Bob to peer...")
|
|
||||||
waitForPeerPeerConnection(t, alice, bob)
|
|
||||||
// Need to add contact else SetContactAuth fails on peer peer doesnt exist
|
|
||||||
// Normal flow would be Bob app monitors for the new connection (a new connection state change to Auth
|
|
||||||
// and the adds the user to peer, and then approves or blocks it
|
|
||||||
// Simulate Bob adding Alice
|
|
||||||
bob2aliceConversationID, err := bob.NewContactConversation(alice.GetOnion(), model.DefaultP2PAccessControl(), true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error adding conversaiton %v", bob2aliceConversationID)
|
|
||||||
}
|
|
||||||
bob.AddServer(string(serverKeyBundle))
|
|
||||||
|
|
||||||
waitForPeerPeerConnection(t, alice, carol)
|
|
||||||
// Simulate Carol adding Alice
|
|
||||||
carol2aliceConversationID, err := carol.NewContactConversation(alice.GetOnion(), model.DefaultP2PAccessControl(), true)
|
carol2aliceConversationID, err := carol.NewContactConversation(alice.GetOnion(), model.DefaultP2PAccessControl(), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error adding conversaiton %v", carol2aliceConversationID)
|
t.Fatalf("error adding conversaiton %v", carol2aliceConversationID)
|
||||||
}
|
}
|
||||||
carol.AddServer(string(serverKeyBundle))
|
|
||||||
|
|
||||||
fmt.Println("Alice and Bob getVal public.name...")
|
alice.PeerWithOnion(bob.GetOnion())
|
||||||
|
alice.PeerWithOnion(carol.GetOnion())
|
||||||
|
|
||||||
alice.SendScopedZonedGetValToContact(bob.GetOnion(), attr.PublicScope, attr.ProfileZone, constants.Name)
|
waitForConnection(t, alice, bob.GetOnion(), connections.AUTHENTICATED)
|
||||||
bob.SendScopedZonedGetValToContact(alice.GetOnion(), attr.PublicScope, attr.ProfileZone, constants.Name)
|
waitForConnection(t, alice, carol.GetOnion(), connections.AUTHENTICATED)
|
||||||
|
waitForConnection(t, bob, alice.GetOnion(), connections.AUTHENTICATED)
|
||||||
|
waitForConnection(t, carol, alice.GetOnion(), connections.AUTHENTICATED)
|
||||||
|
|
||||||
alice.SendScopedZonedGetValToContact(carol.GetOnion(), attr.PublicScope, attr.ProfileZone, constants.Name)
|
t.Logf("Alice and Bob getVal public.name...")
|
||||||
carol.SendScopedZonedGetValToContact(alice.GetOnion(), attr.PublicScope, attr.ProfileZone, constants.Name)
|
|
||||||
|
alice.SendScopedZonedGetValToContact(alice2bobConversationID, attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
|
bob.SendScopedZonedGetValToContact(bob2aliceConversationID, attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
|
|
||||||
|
alice.SendScopedZonedGetValToContact(alice2carolConversationID, attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
|
carol.SendScopedZonedGetValToContact(carol2aliceConversationID, attr.PublicScope, attr.ProfileZone, constants.Name)
|
||||||
|
|
||||||
// This used to be 10, but increasing it to 30 because this is now causing frequent issues
|
// This used to be 10, but increasing it to 30 because this is now causing frequent issues
|
||||||
// Probably related to latency/throughput problems in the underlying tor network.
|
// Probably related to latency/throughput problems in the underlying tor network.
|
||||||
|
@ -266,6 +206,35 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
}
|
}
|
||||||
fmt.Printf("Alice has carol's name as '%v'\n", carolName)
|
fmt.Printf("Alice has carol's name as '%v'\n", carolName)
|
||||||
|
|
||||||
|
// Group Testing
|
||||||
|
|
||||||
|
// Simulate Alice Creating a Group
|
||||||
|
fmt.Println("Alice joining server...")
|
||||||
|
if err := alice.AddServer(string(serverKeyBundle)); err != nil {
|
||||||
|
t.Fatalf("Failed to Add Server Bundle %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bob.AddServer(string(serverKeyBundle))
|
||||||
|
carol.AddServer(string(serverKeyBundle))
|
||||||
|
|
||||||
|
err = alice.JoinServer(ServerAddr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("alice cannot join server %v %v", ServerAddr, err)
|
||||||
|
}
|
||||||
|
waitForConnection(t, alice, ServerAddr, connections.AUTHENTICATED)
|
||||||
|
|
||||||
|
// Creating a Group
|
||||||
|
fmt.Println("Creating group on ", ServerAddr, "...")
|
||||||
|
aliceGroupConversationID, err := alice.StartGroup("Our Cool Testing Group", ServerAddr)
|
||||||
|
fmt.Printf("Created group: %v!\n", aliceGroupConversationID)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to init group: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Waiting for alice to join server...")
|
||||||
|
|
||||||
|
// Invites
|
||||||
fmt.Println("Alice inviting Bob to group...")
|
fmt.Println("Alice inviting Bob to group...")
|
||||||
err = alice.SendInviteToConversation(alice2bobConversationID, aliceGroupConversationID)
|
err = alice.SendInviteToConversation(alice2bobConversationID, aliceGroupConversationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,7 +258,7 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
t.Fatalf("alice cannot join server %v %v", ServerAddr, err)
|
t.Fatalf("alice cannot join server %v %v", ServerAddr, err)
|
||||||
}
|
}
|
||||||
bobGroupConversationID := 3
|
bobGroupConversationID := 3
|
||||||
waitForPeerGroupConnection(t, bob, ServerAddr)
|
waitForConnection(t, bob, ServerAddr, connections.SYNCED)
|
||||||
|
|
||||||
numGoRoutinesPostServerConnect := runtime.NumGoroutine()
|
numGoRoutinesPostServerConnect := runtime.NumGoroutine()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package encryptedstorage
|
package encryptedstorage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
// Import SQL Cipher
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
app2 "cwtch.im/cwtch/app"
|
app2 "cwtch.im/cwtch/app"
|
||||||
"cwtch.im/cwtch/app/utils"
|
"cwtch.im/cwtch/app/utils"
|
||||||
|
@ -11,6 +12,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
|
_ "github.com/mutecomm/go-sqlcipher/v4"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -73,7 +75,7 @@ func TestEncryptedStorage(t *testing.T) {
|
||||||
|
|
||||||
alice.PeerWithOnion(bob.GetOnion())
|
alice.PeerWithOnion(bob.GetOnion())
|
||||||
|
|
||||||
time.Sleep(time.Second * 30)
|
time.Sleep(time.Second * 40)
|
||||||
|
|
||||||
alice.SendMessage(2, "Hello Bob")
|
alice.SendMessage(2, "Hello Bob")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -104,6 +106,20 @@ func TestEncryptedStorage(t *testing.T) {
|
||||||
t.Fatalf("Alices message should have been acknowledged.")
|
t.Fatalf("Alices message should have been acknowledged.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if count, err := alice.GetChannelMessageCount(2, 0); err != nil || count != 1 {
|
||||||
|
t.Fatalf("Channel should have a single message in it. Instead returned %v %v", count, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
messages, err := alice.GetMostRecentMessages(2, 0, 0, 10)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("fetching messages over offset should not result in error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(messages) != 1 || len(messages) > 0 && messages[0].Body != "Hello Bob" {
|
||||||
|
t.Fatalf("expeced GetMostRecentMessages to return 1, instead returned: %v %v", len(messages), messages)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sub Test testing that Alice can add Bob, delete the conversation associated with Bob, and then add Bob again
|
// Sub Test testing that Alice can add Bob, delete the conversation associated with Bob, and then add Bob again
|
||||||
|
|
|
@ -18,6 +18,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
|
// Import SQL Cipher
|
||||||
|
_ "github.com/mutecomm/go-sqlcipher/v4"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
@ -30,22 +32,20 @@ import (
|
||||||
|
|
||||||
func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) {
|
func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) {
|
||||||
for {
|
for {
|
||||||
state, ok := peera.GetPeerState(peerb.GetOnion())
|
state := peera.GetPeerState(peerb.GetOnion())
|
||||||
if ok {
|
//log.Infof("Waiting for Peer %v to peer with peer: %v - state: %v\n", peera.GetProfile().Name, peerb.GetProfile().Name, state)
|
||||||
//log.Infof("Waiting for Peer %v to peer with peer: %v - state: %v\n", peera.GetProfile().Name, peerb.GetProfile().Name, state)
|
if state == connections.FAILED {
|
||||||
if state == connections.FAILED {
|
t.Fatalf("%v could not connect to %v", peera.GetOnion(), peerb.GetOnion())
|
||||||
t.Fatalf("%v could not connect to %v", peera.GetOnion(), peerb.GetOnion())
|
}
|
||||||
}
|
if state != connections.AUTHENTICATED {
|
||||||
if state != connections.AUTHENTICATED {
|
fmt.Printf("peer %v waiting connect to peer %v, currently: %v\n", peera.GetOnion(), peerb.GetOnion(), connections.ConnectionStateName[state])
|
||||||
fmt.Printf("peer %v waiting connect to peer %v, currently: %v\n", peera.GetOnion(), peerb.GetOnion(), connections.ConnectionStateName[state])
|
time.Sleep(time.Second * 5)
|
||||||
time.Sleep(time.Second * 5)
|
continue
|
||||||
continue
|
} else {
|
||||||
} else {
|
peerAName, _ := peera.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
||||||
peerAName, _ := peera.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
peerBName, _ := peerb.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
||||||
peerBName, _ := peerb.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name)
|
fmt.Printf("%v CONNECTED and AUTHED to %v\n", peerAName, peerBName)
|
||||||
fmt.Printf("%v CONNECTED and AUTHED to %v\n", peerAName, peerBName)
|
break
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -146,7 +146,7 @@ func TestFileSharing(t *testing.T) {
|
||||||
err := json.Unmarshal([]byte(messageWrapper.Data), &fileMessageOverlay)
|
err := json.Unmarshal([]byte(messageWrapper.Data), &fileMessageOverlay)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
filesharingFunctionality.DownloadFile(bob, alice.GetOnion(), "cwtch.out.png", "cwtch.out.png.manifest", fmt.Sprintf("%s.%s", fileMessageOverlay.Hash, fileMessageOverlay.Nonce))
|
filesharingFunctionality.DownloadFile(bob, 1, "cwtch.out.png", "cwtch.out.png.manifest", fmt.Sprintf("%s.%s", fileMessageOverlay.Hash, fileMessageOverlay.Nonce))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue