diff --git a/application/examples/echobot-deprecated/main.go b/application/examples/echobot-deprecated/main.go new file mode 100644 index 0000000..457af6f --- /dev/null +++ b/application/examples/echobot-deprecated/main.go @@ -0,0 +1,90 @@ +package main + +import ( + "git.openprivacy.ca/openprivacy/libricochet-go/application" + "git.openprivacy.ca/openprivacy/libricochet-go/channels" + "git.openprivacy.ca/openprivacy/libricochet-go/utils" + "log" + "time" +) + +type EchoBotInstance struct { + rai *application.ApplicationInstance + ra *application.RicochetApplication +} + +func (ebi *EchoBotInstance) Init(rai *application.ApplicationInstance, ra *application.RicochetApplication) { + ebi.rai = rai + ebi.ra = ra +} + +// We always want bidirectional chat channels +func (ebi *EchoBotInstance) OpenInbound() { + log.Println("OpenInbound() ChatChannel handler called...") + outboutChatChannel := ebi.rai.Connection.Channel("im.ricochet.chat", channels.Outbound) + if outboutChatChannel == nil { + ebi.rai.Connection.Do(func() error { + ebi.rai.Connection.RequestOpenChannel("im.ricochet.chat", + &channels.ChatChannel{ + Handler: ebi, + }) + return nil + }) + } +} + +func (ebi *EchoBotInstance) ChatMessage(messageID uint32, when time.Time, message string) bool { + log.Printf("message from %v - %v", ebi.rai.RemoteHostname, message) + go ebi.ra.Broadcast(func(rai *application.ApplicationInstance) { + ebi.SendChatMessage(rai, ebi.rai.RemoteHostname+" "+message) + }) + return true +} + +func (ebi *EchoBotInstance) ChatMessageAck(messageID uint32, accepted bool) { + +} + +func (ebi *EchoBotInstance) SendChatMessage(rai *application.ApplicationInstance, message string) { + rai.Connection.Do(func() error { + channel := rai.Connection.Channel("im.ricochet.chat", channels.Outbound) + if channel != nil { + chatchannel, ok := channel.Handler.(*channels.ChatChannel) + if ok { + chatchannel.SendMessage(message) + } + } + return nil + }) +} + +func main() { + echobot := new(application.RicochetApplication) + pk, err := utils.LoadPrivateKeyFromFile("./testing/private_key") + + if err != nil { + log.Fatalf("error reading private key file: %v", err) + } + + l, err := application.SetupOnion("127.0.0.1:9051", "tcp4", "", pk, 9878) + + if err != nil { + log.Fatalf("error setting up onion service: %v", err) + } + + af := application.ApplicationInstanceFactory{} + af.Init() + af.AddHandler("im.ricochet.chat", func(rai *application.ApplicationInstance) func() channels.Handler { + ebi := new(EchoBotInstance) + ebi.Init(rai, echobot) + return func() channels.Handler { + chat := new(channels.ChatChannel) + chat.Handler = ebi + return chat + } + }) + + echobot.Init("echobot", pk, af, new(application.AcceptAllContactManager)) + log.Printf("echobot listening on %s", l.Addr().String()) + echobot.Run(l) +} diff --git a/application/examples/echobot/main.go b/application/examples/echobot/main.go index 457af6f..d1af675 100644 --- a/application/examples/echobot/main.go +++ b/application/examples/echobot/main.go @@ -3,9 +3,14 @@ package main import ( "git.openprivacy.ca/openprivacy/libricochet-go/application" "git.openprivacy.ca/openprivacy/libricochet-go/channels" - "git.openprivacy.ca/openprivacy/libricochet-go/utils" + "golang.org/x/crypto/ed25519" "log" "time" + "crypto/rand" + "git.openprivacy.ca/openprivacy/libricochet-go/utils" + "git.openprivacy.ca/openprivacy/libricochet-go/identity" + "git.openprivacy.ca/openprivacy/libricochet-go" + "git.openprivacy.ca/openprivacy/libricochet-go/connection" ) type EchoBotInstance struct { @@ -59,14 +64,18 @@ func (ebi *EchoBotInstance) SendChatMessage(rai *application.ApplicationInstance } func main() { + //////////// + // SERVER // + //////////// + echobot := new(application.RicochetApplication) - pk, err := utils.LoadPrivateKeyFromFile("./testing/private_key") + cpubk, cprivk, err := ed25519.GenerateKey(rand.Reader) if err != nil { - log.Fatalf("error reading private key file: %v", err) + log.Fatalf("error generating random key: %v", err) } - l, err := application.SetupOnion("127.0.0.1:9051", "tcp4", "", pk, 9878) + l, err := application.SetupOnionV3("127.0.0.1:9051", "tcp4", "", cprivk, utils.GetTorV3Hostname(cpubk), 9878) if err != nil { log.Fatalf("error setting up onion service: %v", err) @@ -84,7 +93,112 @@ func main() { } }) - echobot.Init("echobot", pk, af, new(application.AcceptAllContactManager)) + echobot.InitV3("echobot", identity.InitializeV3("echobot", &cprivk, &cpubk), af, new(application.AcceptAllContactManager)) log.Printf("echobot listening on %s", l.Addr().String()) - echobot.Run(l) + go echobot.Run(l) + + log.Printf("counting to five...") + time.Sleep(time.Second * 5) + + //////////// + // CLIENT // + //////////// + + //alicebot should nominally be in another package to prevent initializing it directly + alice := NewAliceBot(l.Addr().String()[:56]) + alice.SendMessage("be gay") + alice.SendMessage("do crime") + + // stick around and see what happens + time.Sleep(time.Second * 30) } + + +func NewAliceBot(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.Fatalf("[alice] error generating key: %v", err) + } + + rc, err := goricochet.Open(onion) + if err != nil { + log.Fatalf("[alice] error connecting to echobot: %v", err) + } + + rc.TraceLog(false) + _, err = connection.HandleOutboundConnection(rc).ProcessAuthAsV3Client(identity.InitializeV3("alice", &alice.priv, &alice.pub)) + if err != nil { + log.Fatalf("[alice] failed to authenticate connection: %v", err) + } + + 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.Printf("[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.Fatalf("failed requestopenchannel: %v", err) + } + 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.Printf("[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.Printf("[alice] sending '%s' to echobot", message) + this.messages[peerchannel.SendMessage(message)] = message + } else { + log.Fatalf("couldn't cast channel:") + } + } else { + log.Fatalf("couldn't create channel") + } + return nil + }) +} + +func (this *alicebot) OpenInbound() { + log.Printf("[alice] inbound connection established") +} + +func (this *alicebot) ChatMessage(messageID uint32, when time.Time, message string) bool { + log.Printf("[alice] got message from echobot: %s", message) + return true +} + +func (this *alicebot) ChatMessageAck(messageID uint32, accepted bool) { + log.Printf("[alice] message \"%s\" ack'd", this.messages[messageID]) +} \ No newline at end of file diff --git a/channels/chatchannel.go b/channels/chatchannel.go index 42c1f24..b89983b 100644 --- a/channels/chatchannel.go +++ b/channels/chatchannel.go @@ -91,7 +91,7 @@ func (cc *ChatChannel) Bidirectional() bool { // RequiresAuthentication - chat channels require hidden service auth func (cc *ChatChannel) RequiresAuthentication() string { - return "im.ricochet.auth.hidden-service" + return "im.ricochet.auth.3dh" } // OpenInbound is the first method called for an inbound channel request.