libricochet-go/application/application.go

143 lines
3.5 KiB
Go
Raw Normal View History

2017-05-02 23:33:51 +00:00
package application
import (
"crypto/rsa"
2018-06-08 21:54:31 +00:00
"git.openprivacy.ca/openprivacy/libricochet-go"
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"log"
"net"
"sync"
2017-05-02 23:33:51 +00:00
)
// RicochetApplication bundles many useful constructs that are
// likely standard in a ricochet application
type RicochetApplication struct {
contactManager ContactManagerInterface
privateKey *rsa.PrivateKey
name string
l net.Listener
instances []*ApplicationInstance
lock sync.Mutex
aif ApplicationInstanceFactory
2017-05-02 23:33:51 +00:00
}
func (ra *RicochetApplication) Init(name string, pk *rsa.PrivateKey, af ApplicationInstanceFactory, cm ContactManagerInterface) {
ra.name = name
ra.privateKey = pk
ra.aif = af
ra.contactManager = cm
}
2018-01-10 01:31:54 +00:00
// TODO: Reimplement OnJoin, OnLeave Events.
func (ra *RicochetApplication) handleConnection(conn net.Conn) {
rc, err := goricochet.NegotiateVersionInbound(conn)
if err != nil {
log.Printf("There was an error")
conn.Close()
return
}
ich := connection.HandleInboundConnection(rc)
err = ich.ProcessAuthAsServer(identity.Initialize(ra.name, ra.privateKey), ra.contactManager.LookupContact)
if err != nil {
log.Printf("There was an error")
conn.Close()
return
}
rc.TraceLog(true)
rai := ra.aif.GetApplicationInstance(rc)
ra.lock.Lock()
ra.instances = append(ra.instances, rai)
ra.lock.Unlock()
rc.Process(rai)
// rc.Process ends when the connection ends.
// Remove it from the application's list of instances
ra.lock.Lock()
for i, x := range ra.instances {
if x == rai {
ra.instances = append(ra.instances[:i], ra.instances[i+1:]...)
break
}
}
ra.lock.Unlock()
}
func (ra *RicochetApplication) HandleApplicationInstance(rai *ApplicationInstance) {
ra.lock.Lock()
ra.instances = append(ra.instances, rai)
ra.lock.Unlock()
}
// Open a connection to another Ricochet peer at onionAddress. If they are unknown to use, use requestMessage (otherwise can be blank)
func (ra *RicochetApplication) Open(onionAddress string, requestMessage string) (*ApplicationInstance, error) {
rc, err := goricochet.Open(onionAddress)
rc.TraceLog(true)
if err != nil {
log.Printf("Error in application.Open(): %v\n", err)
return nil, err
}
known, err := connection.HandleOutboundConnection(rc).ProcessAuthAsClient(identity.Initialize(ra.name, ra.privateKey))
rai := ra.aif.GetApplicationInstance(rc)
go rc.Process(rai)
if !known {
err := rc.Do(func() error {
_, err := rc.RequestOpenChannel("im.ricochet.contact.request",
&channels.ContactRequestChannel{
Handler: new(AcceptAllContactHandler),
Name: ra.name,
Message: requestMessage,
})
return err
})
if err != nil {
log.Printf("could not contact %s", err)
}
}
ra.HandleApplicationInstance(rai)
return rai, nil
}
func (ra *RicochetApplication) Broadcast(do func(rai *ApplicationInstance)) {
ra.lock.Lock()
for _, rai := range ra.instances {
do(rai)
}
ra.lock.Unlock()
}
func (ra *RicochetApplication) Shutdown() {
ra.lock.Lock()
ra.l.Close()
for _, instance := range ra.instances {
instance.Connection.Conn.Close()
}
ra.lock.Unlock()
}
func (ra *RicochetApplication) ConnectionCount() int {
return len(ra.instances)
2017-07-04 18:39:19 +00:00
}
func (ra *RicochetApplication) Run(l net.Listener) {
if ra.privateKey == nil || ra.contactManager == nil {
return
}
2017-07-04 18:39:19 +00:00
ra.l = l
var err error
2017-07-04 18:39:19 +00:00
for err == nil {
conn, err := ra.l.Accept()
if err == nil {
go ra.handleConnection(conn)
2017-07-04 18:39:19 +00:00
} else {
return
}
}
}