diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5c7247b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,7 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [] +} \ No newline at end of file diff --git a/client.go b/client.go index c6abadc..a7a048a 100644 --- a/client.go +++ b/client.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "time" ) @@ -10,18 +11,25 @@ type ChatMessage struct { User string `json:"user"` } -var lastTimestamp int +var lastTimestamp = map[string]int{} +var initalizedChannels = map[string]bool{} func runClientSender(msg string) { - msgJson := ChatMessage{Message: msg, User: *user} + chanNum := 0 + fmt.Sscanf(activeChannel,"channel %d",&chanNum)//parce activeChannel number to chanNum + pass := channelPass(chanNum) + msgJson := ChatMessage{Message: msg, User: currentUser} jsonBytes, _ := json.Marshal(msgJson) - hash := passHash(*pass) - sendBytes(append(hash, encryptToBytes(jsonBytes, []byte(*pass))...), *ip) + hash := passHash(pass) + sendBytes(append(hash, encryptToBytes(jsonBytes, []byte(currentPass))...), *ip) } -func runClientListener() { +func runClientListener(chanNum int) { + channel := currentChannel(chanNum) + name := currentChannel(chanNum) + pass := channelPass(chanNum) for { - passwordHashBytes := passHash(*pass) + passwordHashBytes := passHash(pass) responseBytes := sendBytes(passwordHashBytes, *ip) responseStr := decryptFromBytes(responseBytes, passwordHashBytes) var response MsgRecord @@ -31,22 +39,31 @@ func runClientListener() { continue } var incomingMsgJson ChatMessage - msgTextStr := decryptUsingPass(response.LastMsgEncrypted, *pass) + msgTextStr := decryptUsingPass(response.LastMsgEncrypted, pass) json.Unmarshal([]byte(msgTextStr), &incomingMsgJson) - if response.LastMsgTimestamp != lastTimestamp { - if incomingMsgJson.Message == "" && incomingMsgJson.User == "" { - tuiPrint("Chat begins here") + if response.LastMsgTimestamp != lastTimestamp[name] { + if incomingMsgJson.Message == "" && incomingMsgJson.User == "" && !initalizedChannels[channel]{ + tuiPrint(channel,name,"Chat begins here") + initalizedChannels[channel] = true } else { - tuiPrint(incomingMsgJson.User + ": " + incomingMsgJson.Message) + tuiPrint(channel,name,incomingMsgJson.User + ": " + incomingMsgJson.Message) + } + if response.LastMsgTimestamp != lastTimestamp[name]{//make match global + lastTimestamp[name] = response.LastMsgTimestamp } - lastTimestamp = response.LastMsgTimestamp } - time.Sleep(1 * time.Second) + time.Sleep(300 * time.Millisecond) + } +} + +func listenClient(){//listens on all channels + for i := 0; i < listNum; i++{ + go runClientListener(i) } } func runClient() { initTUI(runClientSender) - go runClientListener() + //go runClientListener() runTUI() } diff --git a/encryption.go b/encryption.go index c4d98ee..bcba828 100644 --- a/encryption.go +++ b/encryption.go @@ -4,7 +4,7 @@ import ( "crypto/rand" "crypto/sha256" - "golang.org/x/crypto/chacha20poly1305" + "golang.org/x/crypto/chacha20poly1305"//if throwing errors, use "go get golang.com/..." "golang.org/x/crypto/scrypt" ) diff --git a/main.go b/main.go index 051178b..0708f27 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,7 @@ func main() { enableKernelReplies(true) } else if *server { enableKernelReplies(false) - runServer() + listenForPackets() } else { runClient() } diff --git a/networking.go b/networking.go index 118541e..e24e112 100644 --- a/networking.go +++ b/networking.go @@ -23,7 +23,7 @@ func makeChecksum(b []byte) uint16 { func enableKernelReplies(val bool) { option := "0" if !val { - option = "1" + option = "1" //1 makes system ignore incoming pings } err := os.WriteFile("/proc/sys/net/ipv4/icmp_echo_ignore_all", []byte(option), 0644) processErr(err) @@ -42,10 +42,10 @@ func sendBytes(data []byte, dest string) []byte { defer c.Close() dst, _ := net.ResolveIPAddr("ip4", dest) - c.WriteTo(buf, dst) + c.WriteTo(buf, dst)//Send packets recv := make([]byte, 1500) - c.SetReadDeadline(time.Now().Add(3 * time.Second)) + c.SetReadDeadline(time.Now().Add(1 * time.Second)) for { n, addr, err := c.ReadFrom(recv) if err != nil { diff --git a/server.go b/server.go index 4cb5bb5..88048aa 100644 --- a/server.go +++ b/server.go @@ -39,7 +39,3 @@ func sendReply(ip string, incomingPayload []byte) []byte { recordJson, _ := json.Marshal(record) return encryptToBytes(recordJson, key) } - -func runServer() { - listenForPackets() -} diff --git a/tui.go b/tui.go index 3ba46bb..46faf9f 100644 --- a/tui.go +++ b/tui.go @@ -2,34 +2,91 @@ package main import ( "fmt" - "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) var ( app *tview.Application - msgView *tview.TextView + msgView map[string]*tview.TextView + listNum int = 5 + activeChannel string = "channel 0" + currentUser string//both of these are assigned in sign in window + currentPass string//passed tp client.go ) func initTUI(onSend func(string)) { app = tview.NewApplication() + pages := tview.NewPages() + msgView = make(map[string]*tview.TextView) + chanPages := tview.NewPages() + var firstInput *tview.InputField + + for i := 0; i < listNum; i++ {//initalize pages on per-channel basis + name := fmt.Sprintf("channel %d", i) + view, prim, input:= channel(name, onSend,)//channel returns TextView and Primitive + msgView[name] = view + if i == 0{ + firstInput = input// + } + chanPages.AddPage(name,prim,true,i == 0) + } + + list := tview.NewList()//initalize list + for i := 0; i < listNum; i++ { + name := fmt.Sprintf("channel %d", i) + list.AddItem(name,"",0, func(){ + activeChannel = name + chanPages.SwitchToPage(name)//switches chan pages, not page + }) + } + + +chat := tview.NewFlex().SetDirection(tview.FlexRow).//adds everything to flex, to be refrenced later + AddItem(chanPages,0,1, true) +mainView := tview.NewFlex(). + AddItem(list,20,0,false). + AddItem(chat,0,1,true) + + signIn := tview.NewForm()//input form, ran on launch + signIn.AddInputField("username", "", 20, nil, nil) + signIn.AddInputField("password", "", 20, nil, nil) + signIn.AddDropDown("username color",[]string{"red","blue","green"},0,nil) + signIn.AddButton("enter", func() { + currentUser = signIn.GetFormItemByLabel("username").(*tview.InputField).GetText() + currentPass = signIn.GetFormItemByLabel("password").(*tview.InputField).GetText() + _, color := signIn.GetFormItemByLabel("username color").(*tview.DropDown).GetCurrentOption() + _ = color //negate unused var + //fmt.Println(currentUser,currentPass,color) + go listenClient() + //go runClientListener() + pages.SwitchToPage("main")//switch to other pages + app.SetFocus(firstInput)//focus to whatever is selected + }) + signIn.SetBorder(true).SetTitle("enter details").SetTitleAlign(tview.AlignLeft) + + pages.AddPage("signIn",signIn,true,true) + pages.AddPage("main",mainView,true,false)//unable to be reached unless with sign in form + +app.SetRoot(pages,true).SetFocus(signIn) //puts the user in sign in to start +} - msgView = tview.NewTextView(). +func channel(name string, onSend func(string)) (*tview.TextView, tview.Primitive, *tview.InputField){ + textView := tview.NewTextView(). SetScrollable(true). - SetDynamicColors(true). - ScrollToEnd() - msgView.SetBackgroundColor(tcell.ColorDefault) - msgView.SetBorderPadding(0, 0, 1, 1) - msgView.SetBorder(true) - msgView.SetTitle(" PingChat v2 ") - - inputBox := tview.NewInputField() + SetDynamicColors(true) + textView.ScrollToEnd() + textView.SetBackgroundColor(tcell.ColorDefault) + textView.SetBorderPadding(0, 0, 1, 1) + textView.SetBorder(true) + textView.SetTitle(" PingChat v2 ") + + inputBox := tview.NewInputField() inputBox.SetBorder(true) inputBox.SetBackgroundColor(tcell.ColorDefault) inputBox.SetFieldBackgroundColor(tcell.ColorDefault) - inputBox.SetDoneFunc(func(key tcell.Key) { + inputBox.SetDoneFunc(func(key tcell.Key) {//take input from inputBox, sends off if key != tcell.KeyEnter { return } @@ -41,20 +98,40 @@ func initTUI(onSend func(string)) { go onSend(text) }) - flex := tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(msgView, 0, 1, false). + flex := tview.NewFlex().SetDirection(tview.FlexRow).//puts text view and input into respective positions + AddItem(textView, 0, 1, false). AddItem(inputBox, 3, 1, true) - app.SetRoot(flex, true).SetFocus(inputBox) + + return textView, flex, inputBox } -func tuiPrint(line string) { +func currentChannel(chanNum int)(chanName string){ + return fmt.Sprintf("channel %d",chanNum) +} + +func channelPass(channelNum int)(chanPas string){ + return fmt.Sprintf("%s%d",currentPass,channelNum) +} + +func tuiPrint(channel string, name string, line string) { + view := msgView[channel]//updates with new messages + //if view == nil { + // return + //} app.QueueUpdateDraw(func() { - fmt.Fprintf(msgView, "%s\n", line) + fmt.Fprintf(view, "%s\n",name,line) }) } -func runTUI() { +func runTUI() {//throwing errors if err := app.Run(); err != nil { panic(err) } } + +func ScrollToMessage(){ + textView := msgView[activeChannel] + app.QueueUpdateDraw(func(){ + textView.ScrollToEnd() + }) +}