forked from cwtch.im/cwtch
176 lines
4.6 KiB
Go
176 lines
4.6 KiB
Go
|
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() {
|
||
|
}
|