forked from openprivacy/libricochet-go
Merge branch 'v1-cleanup' of openprivacy/libricochet-go into master
This commit is contained in:
commit
b2f6b314fc
|
@ -15,7 +15,7 @@ pipeline:
|
|||
image: golang
|
||||
commands:
|
||||
- go list ./... | xargs go vet
|
||||
- go list ./... | grep -v "/wire/" | grep -v "/examples/" | xargs golint -set_exit_status
|
||||
- go list ./... | grep -v "/wire/" | xargs golint -set_exit_status
|
||||
units-tests:
|
||||
image: golang
|
||||
commands:
|
||||
|
|
|
@ -4,3 +4,6 @@ go-ricochet-coverage.out
|
|||
.idea
|
||||
.reviewboardrc
|
||||
/vendor/
|
||||
/testing/tor/
|
||||
/connectivity/tor/
|
||||
/tor/
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
package alicebot
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"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/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewAliceBot creates a new AliceBot and establishes a connection to the given onion server.
|
||||
func NewAliceBot(acn connectivity.ACN, onion string) AliceBot {
|
||||
alice := new(alicebot)
|
||||
alice.messages = make(map[uint32]string)
|
||||
|
||||
var err error
|
||||
alice.pub, alice.priv, err = ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
log.Errorf("[alice] error generating key: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
rc, err := goricochet.Open(acn, onion)
|
||||
if err != nil {
|
||||
log.Errorf("[alice] error connecting to echobot: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = connection.HandleOutboundConnection(rc).ProcessAuthAsV3Client(identity.InitializeV3("alice", &alice.priv, &alice.pub))
|
||||
if err != nil {
|
||||
log.Errorf("[alice] failed to authenticate connection: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
alice.rc = rc
|
||||
|
||||
ach := connection.AutoConnectionHandler{}
|
||||
ach.Init()
|
||||
|
||||
ach.RegisterChannelHandler("im.ricochet.chat", func() channels.Handler {
|
||||
chat := new(channels.ChatChannel)
|
||||
chat.Handler = alice
|
||||
return chat
|
||||
})
|
||||
|
||||
go alice.rc.Process(&ach)
|
||||
|
||||
log.Infof("[alice] requesting channel...")
|
||||
alice.rc.Do(func() error {
|
||||
chatchannel := channels.ChatChannel{}
|
||||
chatchannel.Handler = alice
|
||||
|
||||
_, err := alice.rc.RequestOpenChannel("im.ricochet.chat", &chatchannel)
|
||||
if err != nil {
|
||||
log.Errorf("failed requestopenchannel: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return alice
|
||||
}
|
||||
|
||||
type alicebot struct {
|
||||
messages map[uint32]string
|
||||
pub ed25519.PublicKey
|
||||
priv ed25519.PrivateKey
|
||||
mID int
|
||||
rc *connection.Connection
|
||||
}
|
||||
|
||||
// AliceBot is an interface for alicebot, allowing callers to send and receive messages.
|
||||
type AliceBot interface {
|
||||
channels.ChatChannelHandler
|
||||
SendMessage(string)
|
||||
}
|
||||
|
||||
// SendMessage can be called to send a message to EchoBot
|
||||
func (ab *alicebot) SendMessage(message string) {
|
||||
// The following code opens (or creates) a new im.ricochet.chat channel to the connected service
|
||||
// and sends a message.
|
||||
log.Infof("[alice] sending...")
|
||||
ab.rc.Do(func() error {
|
||||
channel := ab.rc.Channel("im.ricochet.chat", channels.Outbound)
|
||||
id, err := channels.SendMessageOnChatChannel(channel, message)
|
||||
if err == nil {
|
||||
ab.messages[id] = message
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// OpenInbound is called when EchoBot attempts to open a channel with AliceBot
|
||||
func (ab *alicebot) OpenInbound() {
|
||||
log.Infof("[alice] inbound connection established")
|
||||
}
|
||||
|
||||
// ChatMessage is called whenever AliceBot receives a message from EchoBot
|
||||
func (ab *alicebot) ChatMessage(messageID uint32, when time.Time, message string) bool {
|
||||
log.Infof("[alice] got message from echobot: %s", message)
|
||||
return true
|
||||
}
|
||||
|
||||
// ChatMessageAck is called whenever AliceBot received an acknowledgement of a previously sent message.
|
||||
func (ab *alicebot) ChatMessageAck(messageID uint32, accepted bool) {
|
||||
log.Infof("[alice] message \"%s\" ack'd", ab.messages[messageID])
|
||||
}
|
|
@ -2,10 +2,9 @@ package main
|
|||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/application/examples/echobot/alicebot"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"time"
|
||||
|
@ -15,17 +14,20 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
// EchoBotInstance is an Instance of the EchoBot Application. One is created for every connected peer.
|
||||
type EchoBotInstance struct {
|
||||
rai *application.Instance
|
||||
ra *application.RicochetApplication
|
||||
}
|
||||
|
||||
// Init establishes an EchoBotInstance
|
||||
func (ebi *EchoBotInstance) Init(rai *application.Instance, ra *application.RicochetApplication) {
|
||||
ebi.rai = rai
|
||||
ebi.ra = ra
|
||||
}
|
||||
|
||||
// We always want bidirectional chat channels
|
||||
// OpenInbound is called when AliceBot opens a ChatChannel. In this case, because we want EchoBot to respond we
|
||||
// need to open a new channel in the other direction.
|
||||
func (ebi *EchoBotInstance) OpenInbound() {
|
||||
log.Debugln("OpenInbound() ChatChannel handler called...")
|
||||
outboutChatChannel := ebi.rai.Connection.Channel("im.ricochet.chat", channels.Outbound)
|
||||
|
@ -40,39 +42,39 @@ func (ebi *EchoBotInstance) OpenInbound() {
|
|||
}
|
||||
}
|
||||
|
||||
// ChatMessage is called whenever a connected peer sends a message to EchoBot
|
||||
func (ebi *EchoBotInstance) ChatMessage(messageID uint32, when time.Time, message string) bool {
|
||||
log.Infof("message from %v - %v", ebi.rai.RemoteHostname, message)
|
||||
ebi.SendChatMessage(ebi.rai, ebi.rai.RemoteHostname+" "+message)
|
||||
return true
|
||||
}
|
||||
|
||||
// ChatMessageAck is called whenever a connected peer acknolwedges a message that EchoBot sent.
|
||||
func (ebi *EchoBotInstance) ChatMessageAck(messageID uint32, accepted bool) {
|
||||
|
||||
}
|
||||
|
||||
// SendChatMessage sends a chat message to the given echobot instance
|
||||
func (ebi *EchoBotInstance) SendChatMessage(rai *application.Instance, message string) {
|
||||
ebi.rai.Connection.Do(func() error {
|
||||
channel := ebi.rai.Connection.Channel("im.ricochet.chat", channels.Outbound)
|
||||
if channel != nil {
|
||||
chatchannel, ok := channel.Handler.(*channels.ChatChannel)
|
||||
if ok {
|
||||
chatchannel.SendMessage(message)
|
||||
}
|
||||
} //TODO: else?
|
||||
// We are swallowing the message id and the error here, in reality you will want to handle it.
|
||||
channels.SendMessageOnChatChannel(channel, message)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// main() encapsulates an entire ricochet ecosystem, from starting tor, to generating onion service keys to managing
|
||||
// the launching of both the echobot server and the alicebot peer.
|
||||
// In most systems you will only be handling one or two of these subsystems at any given time so this example might seem
|
||||
// bloated, but we have tried to highlight the most interesting aspects to allow easy application to new domains.
|
||||
func main() {
|
||||
////////////
|
||||
// SERVER //
|
||||
////////////
|
||||
|
||||
// Set up Logging.
|
||||
log.SetLevel(log.LevelInfo)
|
||||
log.AddEverythingFromPattern("connectivity")
|
||||
|
||||
echobot := new(application.RicochetApplication)
|
||||
cpubk, cprivk, err := ed25519.GenerateKey(rand.Reader)
|
||||
|
||||
// Set up Tor
|
||||
acn, err := connectivity.StartTor(".", "")
|
||||
if err != nil {
|
||||
log.Errorf("Unable to start Tor: %v", err)
|
||||
|
@ -80,13 +82,19 @@ func main() {
|
|||
}
|
||||
defer acn.Close()
|
||||
|
||||
listenService, err := acn.Listen(cprivk, application.RicochetPort)
|
||||
// Set up the Echobot Server
|
||||
echobot := new(application.RicochetApplication)
|
||||
cpubk, cprivk, err := ed25519.GenerateKey(rand.Reader)
|
||||
|
||||
// Turn on the echobot onion service in Tor.
|
||||
listenService, err := acn.Listen(cprivk, application.RicochetPort)
|
||||
if err != nil {
|
||||
log.Errorf("error setting up onion service: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// This next section looks complicated (and it is a little), but all it is doing is allowing echobot to handle
|
||||
// im.ricochet.chat type channels.
|
||||
af := application.InstanceFactory{}
|
||||
af.Init()
|
||||
af.AddHandler("im.ricochet.chat", func(rai *application.Instance) func() channels.Handler {
|
||||
|
@ -99,116 +107,21 @@ func main() {
|
|||
}
|
||||
})
|
||||
|
||||
// Thee next few lines turn on echobot and make it available to listen to new connections.
|
||||
// Note that we initialize a V3 identity for echobot.
|
||||
echobot.Init(acn, "echobot", identity.InitializeV3("echobot", &cprivk, &cpubk), af, new(application.AcceptAllContactManager))
|
||||
log.Infof("echobot listening on %v", listenService.AddressFull())
|
||||
go echobot.Run(listenService)
|
||||
|
||||
// Now we wait a little bit for everything to wire itself together.
|
||||
log.Infoln("counting to five ...")
|
||||
time.Sleep(time.Second * 5)
|
||||
|
||||
////////////
|
||||
// CLIENT //
|
||||
////////////
|
||||
|
||||
//alicebot should nominally be in another package to prevent initializing it directly
|
||||
alice := NewAliceBot(acn, listenService.AddressIdentity())
|
||||
// Finally, in these last few lines we setup an AliceBot who simply sends messages to echobot
|
||||
alice := alicebot.NewAliceBot(acn, listenService.AddressIdentity())
|
||||
alice.SendMessage("be gay")
|
||||
alice.SendMessage("do crime")
|
||||
|
||||
// stick around and see what happens
|
||||
time.Sleep(time.Second * 30)
|
||||
}
|
||||
|
||||
func NewAliceBot(acn connectivity.ACN, onion string) alicebot {
|
||||
alice := alicebot{}
|
||||
alice.messages = make(map[uint32]string)
|
||||
|
||||
var err error
|
||||
alice.pub, alice.priv, err = ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
log.Errorf("[alice] error generating key: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
rc, err := goricochet.Open(acn, onion)
|
||||
if err != nil {
|
||||
log.Errorf("[alice] error connecting to echobot: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = connection.HandleOutboundConnection(rc).ProcessAuthAsV3Client(identity.InitializeV3("alice", &alice.priv, &alice.pub))
|
||||
if err != nil {
|
||||
log.Errorf("[alice] failed to authenticate connection: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
alice.rc = rc
|
||||
|
||||
ach := connection.AutoConnectionHandler{}
|
||||
ach.Init()
|
||||
|
||||
ach.RegisterChannelHandler("im.ricochet.chat", func() channels.Handler {
|
||||
chat := new(channels.ChatChannel)
|
||||
chat.Handler = &alice
|
||||
return chat
|
||||
})
|
||||
|
||||
go alice.rc.Process(&ach)
|
||||
|
||||
log.Infof("[alice] requesting channel...")
|
||||
alice.rc.Do(func() error {
|
||||
chatchannel := channels.ChatChannel{}
|
||||
chatchannel.Handler = &alice
|
||||
|
||||
_, err := alice.rc.RequestOpenChannel("im.ricochet.chat", &chatchannel)
|
||||
if err != nil {
|
||||
log.Errorf("failed requestopenchannel: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return alice
|
||||
}
|
||||
|
||||
type alicebot struct {
|
||||
messages map[uint32]string
|
||||
pub ed25519.PublicKey
|
||||
priv ed25519.PrivateKey
|
||||
mID int
|
||||
rc *connection.Connection
|
||||
}
|
||||
|
||||
func (this *alicebot) SendMessage(message string) {
|
||||
log.Infof("[alice] sending...")
|
||||
this.rc.Do(func() error {
|
||||
channel := this.rc.Channel("im.ricochet.chat", channels.Outbound)
|
||||
if channel != nil {
|
||||
peerchannel, ok := channel.Handler.(*channels.ChatChannel)
|
||||
if ok {
|
||||
log.Infof("[alice] sending '%s' to echobot", message)
|
||||
this.messages[peerchannel.SendMessage(message)] = message
|
||||
} else {
|
||||
log.Errorf("couldn't cast channel:")
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
log.Errorf("couldn't create channel")
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (this *alicebot) OpenInbound() {
|
||||
log.Infof("[alice] inbound connection established")
|
||||
}
|
||||
|
||||
func (this *alicebot) ChatMessage(messageID uint32, when time.Time, message string) bool {
|
||||
log.Infof("[alice] got message from echobot: %s", message)
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *alicebot) ChatMessageAck(messageID uint32, accepted bool) {
|
||||
log.Infof("[alice] message \"%s\" ack'd", this.messages[messageID])
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base32"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"strings"
|
||||
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"os"
|
||||
)
|
||||
|
||||
// An example of how to setup a v3 onion service in go
|
||||
func main() {
|
||||
log.SetLevel(log.LevelInfo)
|
||||
tm, err := connectivity.StartTor(".", "")
|
||||
if err != nil {
|
||||
log.Errorf("Unable to start Tor: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer tm.Close()
|
||||
|
||||
cpubk, cprivk, _ := ed25519.GenerateKey(rand.Reader)
|
||||
onion, err := tm.Listen(cprivk, application.RicochetPort)
|
||||
utils.CheckError(err)
|
||||
defer onion.Close()
|
||||
log.Infof("Got Listener %v", onion.AddressFull())
|
||||
decodedPub, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion.AddressIdentity()))
|
||||
log.Infof("Decoded Public Key: %x %v", decodedPub[:32], err)
|
||||
log.Infof("ed25519 Public Key: %x", cpubk)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package channels
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/wire/chat"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/wire/control"
|
||||
|
@ -57,6 +58,19 @@ func (cc *ChatChannel) SendMessageWithTime(message string, when time.Time) uint3
|
|||
return messageID
|
||||
}
|
||||
|
||||
// SendMessageOnChatChannel is a wrapper function which performs some necessary boilerplate
|
||||
// to make sending messages easier.
|
||||
func SendMessageOnChatChannel(channel *Channel, message string) (uint32, error) {
|
||||
if channel != nil {
|
||||
peerchannel, ok := channel.Handler.(*ChatChannel)
|
||||
if ok {
|
||||
return peerchannel.SendMessage(message), nil
|
||||
}
|
||||
return 0, errors.New("channel is not an im.ricochet.chat channel")
|
||||
}
|
||||
return 0, errors.New("channel pointer is nil")
|
||||
}
|
||||
|
||||
// Acknowledge indicates that the given messageID was received, and whether
|
||||
// it was accepted.
|
||||
func (cc *ChatChannel) Acknowledge(messageID uint32, accepted bool) {
|
||||
|
|
|
@ -80,13 +80,8 @@ func SendMessage(rai *application.Instance, message string) error {
|
|||
|
||||
log.Infof("Finding Chat Channel")
|
||||
channel := rai.Connection.Channel("im.ricochet.chat", channels.Outbound)
|
||||
if channel != nil {
|
||||
log.Infof("Found Chat Channel")
|
||||
chatchannel, ok := channel.Handler.(*channels.ChatChannel)
|
||||
if ok {
|
||||
chatchannel.SendMessage(message)
|
||||
}
|
||||
} else {
|
||||
_, err := channels.SendMessageOnChatChannel(channel, message)
|
||||
if err != nil {
|
||||
log.Infof("Could not find chat channel")
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -12,6 +12,4 @@ echo ""
|
|||
echo "Linting:"
|
||||
|
||||
# Ignore wire packages as they are autogenerated
|
||||
# Ignore examples as they are illustrative
|
||||
# TODO Consider Renaming ApplicationInstance and ApplicationInstanceFactory to remove the last grep
|
||||
go list ./... | grep -v "/wire/" | grep -v "/examples/" | xargs golint
|
||||
go list ./... | grep -v "/wire/" | xargs golint
|
Loading…
Reference in New Issue