package client import ( "encoding/json" "git.mascherari.press/cwtch/model" "git.mascherari.press/cwtch/protocol" "github.com/s-rah/go-ricochet" "github.com/s-rah/go-ricochet/application" "github.com/s-rah/go-ricochet/channels" "github.com/s-rah/go-ricochet/connection" "github.com/s-rah/go-ricochet/identity" "io/ioutil" "sync" ) type CwtchPeerInstance struct { rai *application.ApplicationInstance ra *application.RicochetApplication } func (cpi *CwtchPeerInstance) Init(rai *application.ApplicationInstance, ra *application.RicochetApplication) { cpi.rai = rai cpi.ra = ra } type CwtchPeer struct { connection.AutoConnectionHandler Profile *model.Profile PendingContacts []string PendingInvites map[string][]string mutex sync.Mutex Log chan string } func NewCwtchPeer(name string) *CwtchPeer { peer := new(CwtchPeer) peer.Profile = model.GenerateNewProfile(name) peer.PendingInvites = make(map[string][]string) peer.Log = make(chan string) peer.Init() return peer } func (cp *CwtchPeer) Save(profilefile string) error { cp.mutex.Lock() bytes, _ := json.Marshal(cp) err := ioutil.WriteFile(profilefile, bytes, 0600) cp.mutex.Unlock() return err } func LoadCwtchPeer(profilefile string) (*CwtchPeer, error) { bytes, _ := ioutil.ReadFile(profilefile) peer := new(CwtchPeer) err := json.Unmarshal(bytes, &peer) return peer, err } // AddContactRequest is the entry point for CwtchPeer relationships func (cp *CwtchPeer) AddContactRequest(onion string) { cp.mutex.Lock() cp.PendingContacts = append(cp.PendingContacts, onion) go cp.EstablishContact(onion) cp.mutex.Unlock() } // InviteOnionToGroup kicks off the invite process func (cp *CwtchPeer) InviteOnionToGroup(onion string, groupid string) { cp.mutex.Lock() cp.PendingInvites[onion] = append(cp.PendingInvites[onion], groupid) cp.mutex.Unlock() } func (cp *CwtchPeer) PeerListen(onion string) { } func (cp *CwtchPeer) JoinServer(onion string) { } func (cp *CwtchPeer) SendMessageToGroup(groupid string, message string) { // Lookup Group // Lookup Sever Connection // If no server connection, spin off server connection // Else group.EncryptMessage(message) and send result to server } func (cp *CwtchPeer) Listen() error { cwtchpeer := new(application.RicochetApplication) l, err := application.SetupOnion("127.0.0.1:9051", "tcp4", "", cp.Profile.OnionPrivateKey, 9878) if err != nil { return err } af := application.ApplicationInstanceFactory{} af.Init() af.AddHandler("im.cwtch.peer", func(rai *application.ApplicationInstance) func() channels.Handler { cpi := new(CwtchPeerInstance) cpi.Init(rai, cwtchpeer) return func() channels.Handler { chat := new(protocol.CwtchPeerChannel) chat.Handler = &CwtchPeerHandler{Onion: rai.RemoteHostname, Peer: cp} return chat } }) cwtchpeer.Init(cp.Profile.OnionPrivateKey, af, new(application.AcceptAllContactManager)) cp.Log <- "Running cwtch peer on " + l.Addr().String() cwtchpeer.Run(l) return nil } func (cp *CwtchPeer) EstablishContact(onion string) { rc, err := goricochet.Open(onion) if err == nil { cp.Log <- "Connected to " + onion cp.RegisterChannelHandler("im.cwtch.peer", func() channels.Handler { peer := new(protocol.CwtchPeerChannel) peer.Handler = &CwtchPeerHandler{Onion: onion, Peer: cp} return peer }) if err == nil { _, err := connection.HandleOutboundConnection(rc).ProcessAuthAsClient(identity.Initialize("", cp.Profile.OnionPrivateKey)) if err == nil { cp.Log <- "Authed to " + onion go func() { rc.Do(func() error { rc.RequestOpenChannel("im.cwtch.peer", &protocol.CwtchPeerChannel{ Handler: &CwtchPeerHandler{Onion: onion, Peer: cp}, }) return nil }) sendIdentity := func(message []byte) { rc.Do(func() error { channel := rc.Channel("im.cwtch.peer", channels.Outbound) if channel != nil { cwtchchannel, ok := channel.Handler.(*protocol.CwtchPeerChannel) if ok { cwtchchannel.SendMessage(message) } } else { cp.Log <- "Error finding cwtch channel " + onion } return nil }) } sendIdentity(cp.Profile.GetCwtchIdentityPacket()) }() rc.Process(cp) } } } // onion is offline // TODO Sleep, Wake, Retry (assuming contact still exists) } type CwtchPeerHandler struct { Onion string Peer *CwtchPeer } func (cph *CwtchPeerHandler) ClientIdentity(ci *protocol.CwtchIdentity) { cph.Peer.Log <- "Received Client Identity from " + cph.Onion + " " + ci.String() cph.Peer.Profile.AddCwtchIdentity(cph.Onion, *ci) } func (cph *CwtchPeerHandler) HandleGroupInvite() { }