diff --git a/.gitignore b/.gitignore index 96785e3..f93f30c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ *private_key* *.messages *.test +*test_* +*_test* diff --git a/app/app.go b/app/app.go index c2ee00b..16b8ff0 100644 --- a/app/app.go +++ b/app/app.go @@ -3,24 +3,38 @@ package app import ( "git.mascherari.press/cwtch/model" "git.mascherari.press/cwtch/peer" + "log" ) type Application struct { - Peer *peer.CwtchPeer + Peer *peer.CwtchPeer } func (app *Application) NewProfile(name string, filename string) error { profile := peer.NewCwtchPeer(name) app.Peer = profile - return profile.Save(filename) + err := profile.Save(filename) + if err == nil { + go func() { + err := app.Peer.Listen() + if err != nil { + log.Panic(err) + } + }() + } + return err } - func (app *Application) SetProfile(filename string) error { - profile,err := peer.LoadCwtchPeer(filename) + profile, err := peer.LoadCwtchPeer(filename) app.Peer = profile if err == nil { - app.Peer.Listen() + go func() { + err := app.Peer.Listen() + if err != nil { + log.Panic(err) + } + }() } return err } @@ -33,12 +47,6 @@ func (app *Application) SendMessageToPeer(onion string) { } -func (app *Application) GetPeers() []model.PublicProfile { - - return nil -} - func (app *Application) GetNewMessages() []model.Message { return nil } - diff --git a/app/cli/main.go b/app/cli/main.go index f990244..abe3d7b 100644 --- a/app/cli/main.go +++ b/app/cli/main.go @@ -1,17 +1,18 @@ package main import ( - "fmt" "bufio" + "fmt" + app2 "git.mascherari.press/cwtch/app" "os" "strings" - app2 "git.mascherari.press/cwtch/app" ) func main() { quit := false app := app2.Application{} + profilefile := "" for !quit { reader := bufio.NewReader(os.Stdin) @@ -40,6 +41,7 @@ func main() { case "loadprofile": if len(commands) == 2 { err := app.SetProfile(commands[1]) + profilefile = commands[1] if err == nil { fmt.Printf("Loaded profile for %v\n", commands[1]) } else { @@ -55,7 +57,25 @@ func main() { } else { fmt.Printf("Profile needs to be set") } + case "invite": + if len(commands) == 2 { + fmt.Printf("Inviting cwtch:%v\n", commands[1]) + app.PeerRequest(commands[1]) + } else { + fmt.Printf("Error inviting peer, usage: invite [onion]\n") + } + case "peers": + peers := app.Peer.GetPeers() + for p, s := range peers { + fmt.Printf("Name: %v Status: %v\n", p, s) + } + case "contacts": + for _, c := range app.Peer.Profile.Contacts { + fmt.Printf("Name: %v, Onion: %v, Trusted: %v\n", c.Name, c.Onion, c.Trusted) + } + case "save": + app.Peer.Save(profilefile) } } -} \ No newline at end of file +} diff --git a/model/profile.go b/model/profile.go index 8a6e109..049e94a 100644 --- a/model/profile.go +++ b/model/profile.go @@ -21,6 +21,7 @@ type PublicProfile struct { Ed25519PublicKey ed25519.PublicKey Trusted bool Blocked bool + Onion string } // Profile encapsulates all the attributes necessary to be a Cwtch Peer. @@ -29,7 +30,6 @@ type Profile struct { Contacts map[string]PublicProfile Ed25519PrivateKey ed25519.PrivateKey OnionPrivateKey *rsa.PrivateKey - Onion string Groups map[string]*Group } @@ -71,7 +71,7 @@ func (p *Profile) GetCwtchIdentityPacket() (message []byte) { // AddCwtchIdentity takes a wire message and if it is a CwtchIdentity message adds the identity as a contact // otherwise returns an error func (p *Profile) AddCwtchIdentity(onion string, ci *protocol.CwtchIdentity) { - p.AddContact(onion, PublicProfile{Name: ci.GetName(), Ed25519PublicKey: ci.GetEd25519PublicKey()}) + p.AddContact(onion, PublicProfile{Name: ci.GetName(), Ed25519PublicKey: ci.GetEd25519PublicKey(), Onion: onion}) } // AddContact allows direct manipulation of cwtch contacts diff --git a/peer/connections/connectionsmanager.go b/peer/connections/connectionsmanager.go index 0b0a4ce..f1515b4 100644 --- a/peer/connections/connectionsmanager.go +++ b/peer/connections/connectionsmanager.go @@ -38,6 +38,16 @@ func (m *Manager) ManageServerConnection(host string, handler func(string, *prot m.lock.Unlock() } +func (m *Manager) GetPeers() map[string]ConnectionState { + rm := make(map[string]ConnectionState) + m.lock.Lock() + for onion, ppc := range m.peerConnections { + rm[onion] = ppc.GetState() + } + m.lock.Unlock() + return rm +} + func (m *Manager) GetPeerPeerConnectionForOnion(host string) (ppc *PeerPeerConnection) { m.lock.Lock() ppc = m.peerConnections[host] diff --git a/peer/connections/peerpeerconnection.go b/peer/connections/peerpeerconnection.go index 8d4639e..76e51af 100644 --- a/peer/connections/peerpeerconnection.go +++ b/peer/connections/peerpeerconnection.go @@ -41,6 +41,10 @@ func (ppc *PeerPeerConnection) HandleGroupInvite(gci *protocol.GroupChatInvite) ppc.profile.ProcessInvite(gci, ppc.PeerHostname) } +func (ppc *PeerPeerConnection) GetClientIdentityPacket() []byte { + return nil +} + func (ppc *PeerPeerConnection) SendGroupInvite(invite []byte) { ppc.connection.Do(func() error { channel := ppc.connection.Channel("im.cwtch.peer", channels.Outbound) @@ -56,9 +60,10 @@ func (ppc *PeerPeerConnection) SendGroupInvite(invite []byte) { // Run manages the setup and teardown of a peer->peer connection func (ppc *PeerPeerConnection) Run() error { + ppc.state = CONNECTING rc, err := goricochet.Open(ppc.PeerHostname) if err == nil { - rc.TraceLog(true) + rc.TraceLog(false) ppc.connection = *rc ppc.state = CONNECTED _, err := connection.HandleOutboundConnection(&ppc.connection).ProcessAuthAsClient(identity.Initialize(ppc.profile.Name, ppc.profile.OnionPrivateKey)) diff --git a/peer/connections/peerpeerconnection_test.go b/peer/connections/peerpeerconnection_test.go index b8d7b98..405beaa 100644 --- a/peer/connections/peerpeerconnection_test.go +++ b/peer/connections/peerpeerconnection_test.go @@ -74,6 +74,10 @@ func (tp *TestPeer) HandleGroupInvite(gci *protocol.GroupChatInvite) { tp.ReceivedGroupInvite = true } +func (tp *TestPeer) GetClientIdentityPacket() []byte { + return nil +} + func TestPeerPeerConnection(t *testing.T) { profile := model.GenerateNewProfile("sarah") ppc := NewPeerPeerConnection("127.0.0.1:5452|kwke2hntvyfqm7dr", profile) diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index f23ed9e..1641710 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -11,6 +11,7 @@ import ( "github.com/s-rah/go-ricochet/channels" "github.com/s-rah/go-ricochet/connection" "io/ioutil" + "log" "sync" ) @@ -30,14 +31,26 @@ type CwtchPeer struct { mutex sync.Mutex Log chan string `json:"-"` connectionsManager *connections.Manager + profilefile string +} + +func (cp *CwtchPeer) setup() { + cp.Log = make(chan string) + cp.connectionsManager = connections.NewConnectionsManager() + cp.Init() + + go cp.connectionsManager.AttemptReconnections() + + for onion := range cp.Profile.Contacts { + cp.PeerWithOnion(onion) + } + } func NewCwtchPeer(name string) *CwtchPeer { cp := new(CwtchPeer) cp.Profile = model.GenerateNewProfile(name) - cp.Log = make(chan string) - cp.connectionsManager = connections.NewConnectionsManager() - cp.Init() + cp.setup() return cp } @@ -45,6 +58,7 @@ func (cp *CwtchPeer) Save(profilefile string) error { cp.mutex.Lock() bytes, _ := json.Marshal(cp) err := ioutil.WriteFile(profilefile, bytes, 0600) + cp.profilefile = profilefile cp.mutex.Unlock() return err } @@ -53,6 +67,8 @@ func LoadCwtchPeer(profilefile string) (*CwtchPeer, error) { bytes, _ := ioutil.ReadFile(profilefile) cp := new(CwtchPeer) err := json.Unmarshal(bytes, &cp) + cp.setup() + cp.profilefile = profilefile return cp, err } @@ -90,6 +106,10 @@ func (cp *CwtchPeer) SendMessageToGroup(groupid string, message string) { psc.SendGroupMessage(gm) } +func (cp *CwtchPeer) GetPeers() map[string]connections.ConnectionState { + return cp.connectionsManager.GetPeers() +} + func (cp *CwtchPeer) Listen() error { cwtchpeer := new(application.RicochetApplication) l, err := application.SetupOnion("127.0.0.1:9051", "tcp4", "", cp.Profile.OnionPrivateKey, 9878) @@ -111,7 +131,7 @@ func (cp *CwtchPeer) Listen() error { }) cwtchpeer.Init(cp.Profile.OnionPrivateKey, af, new(application.AcceptAllContactManager)) - cp.Log <- "Running cwtch peer on " + l.Addr().String() + log.Printf("Running cwtch peer on %v", l.Addr().String()) cwtchpeer.Run(l) return nil } @@ -132,8 +152,9 @@ type CwtchPeerHandler struct { } func (cph *CwtchPeerHandler) ClientIdentity(ci *protocol.CwtchIdentity) { - cph.Peer.Log <- "Received Client Identity from " + cph.Onion + " " + ci.String() + log.Printf("Received Client Identity from %v %v\n", cph.Onion, ci.String()) cph.Peer.Profile.AddCwtchIdentity(cph.Onion, ci) + cph.Peer.Save(cph.Peer.profilefile) } func (cph *CwtchPeerHandler) HandleGroupInvite(gci *protocol.GroupChatInvite) { @@ -142,3 +163,7 @@ func (cph *CwtchPeerHandler) HandleGroupInvite(gci *protocol.GroupChatInvite) { func (cph *CwtchPeerHandler) HandleGroupMessage(gm *protocol.GroupMessage) { } + +func (cph *CwtchPeerHandler) GetClientIdentityPacket() []byte { + return cph.Peer.Profile.GetCwtchIdentityPacket() +} diff --git a/peer/peer/peer_channel.go b/peer/peer/peer_channel.go index da6ef63..f78350f 100644 --- a/peer/peer/peer_channel.go +++ b/peer/peer/peer_channel.go @@ -25,6 +25,7 @@ type CwtchPeerChannel struct { type CwtchPeerChannelHandler interface { ClientIdentity(*protocol.CwtchIdentity) HandleGroupInvite(*protocol.GroupChatInvite) + GetClientIdentityPacket() []byte } // SendMessage sends a raw message on this channel @@ -99,6 +100,10 @@ func (cpc *CwtchPeerChannel) Packet(data []byte) { if err == nil { if cpp.GetCwtchIdentify() != nil { cpc.Handler.ClientIdentity(cpp.GetCwtchIdentify()) + pkt := cpc.Handler.GetClientIdentityPacket() + if pkt != nil { + cpc.SendMessage(pkt) + } } else if cpp.GetGroupChatInvite() != nil { cpc.Handler.HandleGroupInvite(cpp.GetGroupChatInvite()) } diff --git a/peer/peer/peer_channel_test.go b/peer/peer/peer_channel_test.go index c7d7af3..fb1b45b 100644 --- a/peer/peer/peer_channel_test.go +++ b/peer/peer/peer_channel_test.go @@ -48,6 +48,10 @@ func (th *TestHandler) HandleGroupInvite(ci *protocol.GroupChatInvite) { //} } +func (th *TestHandler) GetClientIdentityPacket() []byte { + return nil +} + func TestPeerChannel(t *testing.T) { th := new(TestHandler) cpc := new(CwtchPeerChannel)