ep2pchat/main.go
2018-06-12 16:59:50 -04:00

246 lines
4.8 KiB
Go

package main
import (
"bytes"
"context"
"encoding/base64"
"flag"
"fmt"
"io"
"net"
"os"
"unicode"
"github.com/jroimartin/gocui"
"gitlab.com/tblyler/ep2pchat/client"
"gitlab.com/tblyler/ep2pchat/server"
"gitlab.com/tblyler/ep2pchat/signer"
)
func logErrConn(conn net.Conn, msgs ...interface{}) {
fmt.Fprint(os.Stderr, "["+conn.RemoteAddr().String()+"] ")
fmt.Fprintln(os.Stderr, msgs...)
}
func logConn(conn net.Conn, msgs ...interface{}) {
fmt.Print("[" + conn.RemoteAddr().String() + "] ")
fmt.Println(msgs...)
}
func logErr(msgs ...interface{}) {
fmt.Fprintln(os.Stderr, msgs...)
}
func log(msgs ...interface{}) {
fmt.Println(msgs...)
}
func main() {
keyStr := ""
host := "0.0.0.0:0"
isServer := false
flag.StringVar(&keyStr, "key", keyStr, "256-bit binary base64-encoded key value, generates otherwise")
flag.StringVar(&host, "host", host, "the host to connect to or listen on")
flag.BoolVar(&isServer, "server", isServer, "determines whether or not to act as a server")
flag.Parse()
err := func() (err error) {
var sign signer.Signer
if keyStr == "" {
log("not using encryption nor signing")
sign = signer.NewClearText()
} else {
key, err := base64.StdEncoding.DecodeString(keyStr)
if err != nil || len(key) != signer.KeyLength {
logErr("Failed to decode 256-bit key from base64")
return err
}
var keyArray [signer.KeyLength]byte
copy(keyArray[:], key)
sign = signer.NewSecretBox(keyArray)
}
if isServer {
listener, err := net.Listen("tcp", host)
if err != nil {
logErr("Failed to listen on address ", host)
return err
}
defer listener.Close()
log("Listening on ", listener.Addr().String())
server := server.NewServer(listener, sign, context.Background())
return server.Serve()
}
conn, err := net.Dial("tcp", host)
if err != nil {
logErr("Failed to connect to address ", host)
return err
}
defer conn.Close()
client := client.NewClient(conn, context.Background())
gui, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
logErr("Failed to initialze UI")
return err
}
defer gui.Close()
gui.Cursor = true
gui.SetManagerFunc(func(gui *gocui.Gui) error {
maxX, maxY := gui.Size()
v, err := gui.SetView("chat", 1, 1, maxX-1, maxY-5)
if err != nil {
if err != gocui.ErrUnknownView {
return err
}
if _, err := gui.SetCurrentView("chat"); err != nil {
return err
}
v.Wrap = true
v.Autoscroll = true
}
v, err = gui.SetView("input", 1, maxY-4, maxX-1, maxY-1)
if err != nil {
if err != gocui.ErrUnknownView {
return err
}
if _, err := gui.SetCurrentView("input"); err != nil {
return err
}
v.Editable = true
v.Wrap = true
v.Autoscroll = true
}
return nil
})
gui.SetKeybinding("input", gocui.KeyCtrlW, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
buffer := v.Buffer()
foundFirstSpace := false
foundFirstChar := false
for i := len(buffer) - 1; i >= 0; i-- {
if unicode.IsSpace(rune(buffer[i])) {
if foundFirstSpace && foundFirstChar {
break
}
foundFirstSpace = true
} else {
foundFirstChar = true
}
v.EditDelete(true)
}
return nil
})
gui.SetKeybinding("input", gocui.KeyCtrlL, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
chat, err := g.View("chat")
if err != nil {
return nil
}
chat.SetCursor(0, 0)
chat.Clear()
return nil
})
gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
})
gui.SetKeybinding("input", gocui.KeyCtrlU, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
v.SetCursor(0, 0)
v.Clear()
return nil
})
gui.SetKeybinding("input", gocui.KeyEnter, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
msg := v.ViewBuffer()
if msg == "" {
return nil
}
encMsg, err := sign.Encode([]byte(msg))
if err != nil {
logErr(err)
return gocui.ErrQuit
}
err = client.SendMsg(encMsg)
if err != nil {
logErr(err)
return gocui.ErrQuit
}
v.SetCursor(0, 0)
v.Clear()
return nil
})
go func() {
for {
encMsg, err := client.GetMsg()
if err != nil {
logErr(err)
break
}
msg, err := sign.Decode(encMsg)
if err != nil {
logErr(err)
break
}
gui.Update(func(gui *gocui.Gui) error {
v, err := gui.View("chat")
if err != nil {
gui.Close()
return err
}
_, err = io.Copy(v, bytes.NewReader(msg))
if err != nil {
logErr(err)
return err
}
return nil
})
if err != nil {
break
}
}
gui.Close()
}()
err = gui.MainLoop()
if err != nil && err != gocui.ErrQuit {
return err
}
return nil
}()
if err != nil {
logErr(err)
os.Exit(1)
}
}