/* Copyright © 2024 Raul */ package cmd import ( "bufio" "crypto/tls" "fmt" "io" "log" "net" "os" "runtime" "strings" "time" "github.com/jroimartin/gocui" "github.com/nsf/termbox-go" ) type Message struct { Contents string //Date time.Time Server net.Conn } type ProfileData struct { Username string } var Profile ProfileData func (m Message) toSend() { m.Server.Write([]byte(m.Contents)) } var ( serverPort string = "1302" serverIP string data Message clientInsecure bool ) func startSecureConnection() (net.Conn, error) { conf := &tls.Config{ InsecureSkipVerify: true, } conn, err := tls.Dial("tcp", serverIP+":"+serverPort, conf) return conn, err } func startInsecureConnection() (net.Conn, error) { conn, err := net.Dial("tcp", serverIP+":"+serverPort) return conn, err } func Client() { var conn net.Conn var err error if clientInsecure == true { fmt.Println("WARNING: Starting unencrypted connection!") conn, err = startInsecureConnection() } else { conn, err = startSecureConnection() } if err != nil { log.Fatalf("Error occurred trying to connect to server: %v\n", err) } defer conn.Close() data.Server = conn nameRequest, b, err := receiveMessage(conn) if err != nil { log.Fatalf("Error occurred reading from server while requesting name: %v\n", err) } fmt.Print(nameRequest) test := make([]byte, b) copy(test, "Password: ") if nameRequest == string(test) { pass, err := scanLine() if err != nil { log.Fatal(err) } conn.Write([]byte(pass)) nameRequest, _, err := receiveMessage(conn) if err != nil { if err != io.EOF { log.Fatalf("Error occurred reading from server while requesting name: %v\n", err) } os.Exit(1) } fmt.Print(nameRequest) sendName(conn) } else { sendName(conn) } GUI() } //////////////////////////////////////////////////// // REFACTORING BELOW // //////////////////////////////////////////////////// func listenMessages(g *gocui.Gui) { time.Sleep(time.Millisecond * 50) chatbox, err := g.View("chatbox") if err != nil { log.Panicln(err) } for { messageFromServer, _, err := receiveMessage(data.Server) if err != nil { // What the hell is this if err == gocui.ErrQuit { g.Close() } else { g.Close() os.Exit(1) } } formattedMessage := strings.TrimRight(messageFromServer, "\n") fmt.Fprintln(chatbox, formattedMessage) termbox.Interrupt() } } func receiveMessage(conn net.Conn) (s string, b int, err error) { serverMessage := make([]byte, 2048) n, err := conn.Read(serverMessage) if err != nil { return "", 0, err } serverMessageReduced := make([]byte, n) copy(serverMessageReduced, serverMessage) finalMessage := string(serverMessageReduced) serverMessage = nil return finalMessage, n, nil } func scanLine() (line string, err error) { switch runtime.GOOS { case "linux": message, err := bufio.NewReader(os.Stdin).ReadString('\n') if err != nil { return "", err } line = message case "windows": message, err := bufio.NewReader(os.Stdin).ReadString('\r') if err != nil { return "", err } line = message } return line, nil } func sendName(conn net.Conn) { message, err := scanLine() if err != nil { log.Fatalf("Error occurred sending message to server: %v\n", err) } Profile.Username = strings.TrimRight(message, "\n") if _, err := conn.Write([]byte(message)); err != nil { log.Fatalf("Error occurred writing to server: %v\n", err) } } func GUI() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) } defer g.Close() g.SetManagerFunc(layout) g.Mouse = true g.Cursor = true initKeybindings(g) go listenMessages(g) if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func quit(*gocui.Gui, *gocui.View) error { return gocui.ErrQuit } func sendToServer(g *gocui.Gui, v *gocui.View) error { textarea, err := g.View("textarea") if err != nil { log.Panicln(err) } message := textarea.Buffer() msg := string(message) if msg == "" { return nil } if msg == "" { return nil } data.Contents = msg data.toSend() textarea.Clear() textarea.SetCursor(0, 0) return nil } func scrollView(v *gocui.View, dy int) error { if v != nil { v.Autoscroll = false ox, oy := v.Origin() if err := v.SetOrigin(ox, oy+dy); err != nil { return err } } return nil } func autoscroll(g *gocui.Gui, v *gocui.View) error { chatbox, err := g.View("chatbox") if err != nil { log.Panicf("Error happened setting autoscroll: %v\n", err) } chatbox.Autoscroll = true return nil } func initKeybindings(g *gocui.Gui) error { if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } // if err := g.SetKeybinding("button", gocui.MouseLeft, gocui.ModNone, sendToServer); err != nil { // log.Panicln(err) // } if err := g.SetKeybinding("textarea", gocui.KeyCtrlA, gocui.ModNone, autoscroll); err != nil { log.Panicln(err) } if err := g.SetKeybinding("textarea", gocui.KeyEnter, gocui.ModNone, sendToServer); err != nil { log.Panicln(err) } if err := g.SetKeybinding("chatbox", gocui.MouseWheelUp, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { scrollView(v, -1) return nil }); err != nil { log.Panicln(err) } if err := g.SetKeybinding("chatbox", gocui.MouseWheelDown, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { scrollView(v, 1) return nil }); err != nil { log.Panicln(err) } return nil } func layout(g *gocui.Gui) error { maxX, maxY := g.Size() if chatbox, err := g.SetView("chatbox", 2, 1, maxX-2, maxY-5); err != nil { if err != gocui.ErrUnknownView { return err } chatbox.Autoscroll = true chatbox.Title = "Chat Box (Find source at https://git.bulgariu.xyz/raul/mini-chat!)" } // if button, err := g.SetView("button", maxX/2+32, maxY-4, maxX-28, maxY-2); err != nil { // if err != gocui.ErrUnknownView { // return err // } // // button.Wrap = true // button.Frame = true // fmt.Fprintln(button, "Send it") // } if textarea, err := g.SetView("textarea", 2, maxY-4, maxX-2, maxY-2); err != nil { if err != gocui.ErrUnknownView { return err } if _, err := g.SetCurrentView("textarea"); err != nil { log.Panicln(err) } textarea.Title = "(" + Profile.Username + ") " + "Send message" + " (Ctrl+A: Autoscroll)" textarea.Wrap = true textarea.Editable = true } return nil }