tricrypt/cmd/cfyne/main.go

649 lines
19 KiB
Go
Raw Normal View History

2024-07-12 19:33:27 +02:00
package main
import (
"crypto/tls"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"log"
"net"
"os"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
com "git.qowevisa.me/Qowevisa/gotell/communication"
"git.qowevisa.me/Qowevisa/gotell/env"
"git.qowevisa.me/Qowevisa/gotell/extfyne/widgets"
)
func updateTime(clock *widget.Label) {
formatted := time.Now().Format("Time: 03:04:05")
clock.SetText(formatted)
}
var globalApp *MutableApplication
var globalCfg *FyneConfig
var (
ecdhKeyReceived = false
ecdhKeySent = false
)
var (
globalClipboardChannel = make(chan string)
)
func main() {
tlepCenter.Init()
userCenter.Init()
loadingFileName := env.ServerFullchainFileName
cert, err := os.ReadFile(loadingFileName)
if err != nil {
log.Fatalf("client: load root cert: %s", err)
}
log.Printf("Certificate %s loaded successfully!\n", loadingFileName)
//
roots := x509.NewCertPool()
if ok := roots.AppendCertsFromPEM(cert); !ok {
log.Fatalf("client: failed to parse root certificate")
}
config := &tls.Config{
RootCAs: roots,
}
host := "chat.qowevisa.click"
port := 3232
service := fmt.Sprintf("%s:%d", host, port)
log.Printf("client: connecting to %s", service)
conn, err := tls.Dial("tcp", service, config)
if err != nil {
log.Fatalf("client: dial: %s", err)
}
defer conn.Close()
log.Printf("client: connected to %s", service)
//
a := app.New()
w := a.NewWindow("gotell-fyne")
// w.Resize(fyne.NewSize(640, 400))
// dispatch clipboardChannel
go func(w fyne.Window) {
for val := range globalClipboardChannel {
w.Clipboard().SetContent(val)
}
}(w)
clock := widget.NewLabel("")
go func() {
for range time.Tick(time.Second) {
updateTime(clock)
}
}()
btn := widget.NewButtonWithIcon("testBtn", theme.AccountIcon(), func() {
fmt.Println("I'm pressed!")
})
mainContainer := container.New(layout.NewVBoxLayout(), clock, btn)
w.SetContent(mainContainer)
// w.SetContent(widget.NewLabel("Hello World!"))
// w2 := a.NewWindow("Larger")
// w2.SetContent(widget.NewLabel("More content"))
// w2.Resize(fyne.NewSize(100, 100))
// w2.Show()
intercomFromServer := make(chan *com.Message, 16)
intercomToServer := make(chan *com.Message, 16)
actionsChannel := make(chan int)
cfg := FyneConfig{
App: a,
Window: w,
}
globalCfg = &cfg
go readFromServer(conn, intercomFromServer)
go analyzeMessages(intercomFromServer, actionsChannel)
go actOnActions(cfg, actionsChannel, intercomToServer)
go readFromIntercom(intercomToServer, conn)
w.ShowAndRun()
}
var r com.RegisteredUser
var tmpLink *com.Link
var tmpNick string
func readFromServer(conn net.Conn, intercom chan *com.Message) {
buf := make([]byte, 70000)
for {
err := conn.SetDeadline(time.Now().Add(1 * time.Minute))
if err != nil {
log.Printf("ERROR: conn.SetDeadline: %v", err)
continue
}
n, err := conn.Read(buf)
if err != nil {
log.Printf("ERROR: client: read: %s", err)
if errors.Is(err, net.ErrClosed) {
log.Printf("caught ErrClosed!")
}
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
log.Println("caught! Read timeout occurred")
continue
}
return
}
msg, err := com.Decode(buf[:n])
if err != nil {
log.Printf("ERROR: %#v\n", err)
continue
}
if msg == nil {
continue
}
log.Printf("client: readServer: received message from server: %v", *msg)
switch msg.ID {
case com.ID_SERVER_APPROVE_CLIENT_NICKNAME:
newID := binary.BigEndian.Uint16(msg.Data)
msg.FromID = newID
msg.Data = []byte{}
r.ID = newID
if tmpNick != "" {
r.Name = tmpNick
}
r.IsRegistered = true
case com.ID_SERVER_APPROVE_CLIENT_LINK:
if tmpLink == nil {
continue
}
msg.ToID = tmpLink.UseCount
msg.Data = tmpLink.Data
// Crypto stuff
case com.ID_CLIENT_SEND_CLIENT_ECDH_PUBKEY:
t, err := tlepCenter.GetTLEP(msg.FromID)
if err != nil {
log.Printf("ERROR: tlep: GetTLEP: %v\n", err)
continue
}
err = t.ECDHApplyOtherKeyBytes(msg.Data)
if err != nil {
log.Printf("ERROR: tlep: ECDHApplyOtherKeyBytes: %v\n", err)
continue
}
fromName, err := userCenter.GetName(msg.FromID)
if err != nil {
log.Printf("ERROR: userCenter: GetName: %v\n", err)
} else {
msg.Data = []byte(fromName)
}
case com.ID_CLIENT_SEND_CLIENT_CBES_SPECS:
t, err := tlepCenter.GetTLEP(msg.FromID)
if err != nil {
log.Printf("ERROR: tlep: GetTLEP: %v\n", err)
continue
}
cbes, err := t.DecryptMessageEA(msg.Data)
if err != nil {
log.Printf("ERROR: tlep: DecryptMessageEA: %v\n", err)
continue
}
err = t.CBESSetFromBytes(cbes)
if err != nil {
log.Printf("ERROR: tlep: CBESSetFromBytes: %v\n", err)
continue
}
fromName, err := userCenter.GetName(msg.FromID)
if err != nil {
log.Printf("ERROR: userCenter: GetName: %v\n", err)
} else {
msg.Data = []byte(fromName)
}
// message
case com.ID_CLIENT_SEND_CLIENT_MESSAGE:
t, err := tlepCenter.GetTLEP(msg.FromID)
if err != nil {
log.Printf("ERROR: tlep: GetTLEP: %v\n", err)
continue
}
decrypedMsg, err := t.DecryptMessageAtMax(msg.Data)
if err != nil {
log.Printf("ERROR: tlep: DecryptMessageAtMax: %v\n", err)
continue
}
msg.Data = decrypedMsg
// switch
}
// user stuff
switch msg.ID {
case com.ID_CLIENT_ASK_CLIENT_HANDSHAKE,
com.ID_CLIENT_APPROVE_CLIENT_HANDSHAKE:
userCenter.AddUser(string(msg.Data), msg.FromID)
}
log.Printf("client: readServer: sending message to websocket: %v", *msg)
intercom <- msg
}
}
func analyzeMessages(intercomFromServer chan *com.Message, actionsChannel chan int) {
for msg := range intercomFromServer {
if msg == nil {
log.Printf("ERROR: msg is nil")
continue
}
log.Printf("Handling %v from server", *msg)
switch msg.ID {
case com.ID_SERVER_ASK_CLIENT_NICKNAME:
actionsChannel <- ACTION_SET_SCENE_NICKNAME
case com.ID_SERVER_APPROVE_CLIENT_NICKNAME:
actionsChannel <- ACTION_SET_SCENE_MAIN
case com.ID_SERVER_DECLINE_CLIENT_NICKNAME:
// TODO
// Link stuff
case com.ID_SERVER_APPROVE_CLIENT_LINK:
globalApp.ArrayBundle.LinksMuAr.Add(*tmpLink)
globalApp.Tabs.LinkTab.Content.Refresh()
case com.ID_SERVER_SEND_CLIENT_ANOTHER_ID:
selTab := globalApp.Tabs.AppTabs.Selected()
id := msg.ToID
userStr := getStdUserName(id, string(msg.Data))
globalApp.Widgets.UsersSelect.Options = append(globalApp.Widgets.UsersSelect.Options, userStr)
globalApp.Tabs.ConnsNoty++
globalApp.ArrayBundle.UsersOpts[userStr] = &UserOptions{
Status: USER_STATUS_VISIBLE,
ToID: id,
}
_, exists := globalApp.ArrayBundle.UserShortcuts[id]
if !exists {
globalApp.ArrayBundle.UserShortcuts[id] = userStr
globalApp.ArrayBundle.UserShortcutsRev[userStr] = id
}
if selTab != globalApp.Tabs.ConnectionTab {
updateTabsBasedOnNotyVals(&globalApp.Tabs)
}
actionsChannel <- ACTION_UPDATE_USER_OPTS_CONTS
case com.ID_CLIENT_ASK_CLIENT_HANDSHAKE:
selTab := globalApp.Tabs.AppTabs.Selected()
id := msg.FromID
userStr := getStdUserName(id, string(msg.Data))
globalApp.Widgets.UsersSelect.Options = append(globalApp.Widgets.UsersSelect.Options, userStr)
globalApp.Tabs.ConnsNoty++
globalApp.ArrayBundle.UsersOpts[userStr] = &UserOptions{
Status: USER_STATUS_HANDSHAKE_INIT,
ToID: id,
}
_, exists := globalApp.ArrayBundle.UserShortcuts[id]
if !exists {
globalApp.ArrayBundle.UserShortcuts[id] = userStr
globalApp.ArrayBundle.UserShortcutsRev[userStr] = id
}
if selTab != globalApp.Tabs.ConnectionTab {
updateTabsBasedOnNotyVals(&globalApp.Tabs)
}
actionsChannel <- ACTION_UPDATE_USER_OPTS_CONTS
case com.ID_CLIENT_APPROVE_CLIENT_HANDSHAKE:
id := msg.FromID
userStr := getStdUserName(id, string(msg.Data))
var shortcut string
val, exists := globalApp.ArrayBundle.UserShortcuts[id]
if !exists {
globalApp.ArrayBundle.UserShortcuts[id] = userStr
globalApp.ArrayBundle.UserShortcutsRev[userStr] = id
shortcut = userStr
} else {
shortcut = val
}
u, exists := globalApp.ArrayBundle.UsersOpts[shortcut]
if !exists {
log.Printf("WARNING!!: UserOpts[%s] NOT exists!", shortcut)
continue
}
u.Status = USER_STATUS_HANDSHAKE_ACCEPTED
actionsChannel <- ACTION_UPDATE_USER_OPTS_CONTS
case com.ID_CLIENT_SEND_CLIENT_ECDH_PUBKEY:
id := msg.FromID
if ecdhKeySent {
val, exists := globalApp.ArrayBundle.UserShortcuts[id]
if !exists {
log.Printf("WARNING!!: UserShortcuts[%d] NOT exists!", id)
continue
}
u, exists := globalApp.ArrayBundle.UsersOpts[val]
if !exists {
log.Printf("WARNING!!: UserOpts[%s] NOT exists!", val)
continue
}
u.Status = USER_STATUS_ECDH_ESTABLISHED
actionsChannel <- ACTION_UPDATE_USER_OPTS_CONTS
}
ecdhKeyReceived = true
case com.ID_CLIENT_SEND_CLIENT_CBES_SPECS:
id := msg.FromID
val, exists := globalApp.ArrayBundle.UserShortcuts[id]
if !exists {
log.Printf("WARNING!!: UserShortcuts[%d] NOT exists!", id)
continue
}
u, exists := globalApp.ArrayBundle.UsersOpts[val]
if !exists {
log.Printf("WARNING!!: UserOpts[%s] NOT exists!", val)
continue
}
u.Status = USER_STATUS_ECDH_CBES_ESTABLISHED
actionsChannel <- ACTION_UPDATE_USER_OPTS_CONTS
//
log.Printf("Checkin Messages tab")
userStr := getStdUserName(id, string(msg.Data))
var shortcut string
if !exists {
globalApp.ArrayBundle.UserShortcuts[id] = userStr
shortcut = userStr
} else {
shortcut = val
}
//
haveUser := false
for _, v := range globalApp.Widgets.MessagesSelect.Options {
if v == shortcut {
haveUser = true
break
}
}
log.Printf("MessagesSelect.Options1 are %v\n", globalApp.Widgets.MessagesSelect.Options)
log.Printf("have user is : %t\n", haveUser)
if !haveUser {
log.Printf("MessagesSelect.Options1.5 shortcut is '%s'\n", shortcut)
// LOL wtf
// globalApp.Widgets.MessagesSelect.Options = append(globalApp.Widgets.UsersSelect.Options, shortcut)
globalApp.Widgets.MessagesSelect.Options = append(globalApp.Widgets.MessagesSelect.Options, shortcut)
log.Printf("MessagesSelect.Options2 are %v\n", globalApp.Widgets.MessagesSelect.Options)
globalApp.Tabs.UsersNoty++
globalApp.Widgets.MessagesSelect.Refresh()
updateTabsBasedOnNotyVals(&globalApp.Tabs)
}
case com.ID_CLIENT_SEND_CLIENT_MESSAGE:
id := msg.FromID
shortcut, exists := globalApp.ArrayBundle.UserShortcuts[id]
if !exists {
log.Printf("WARNING: UserShortcuts[%d] NOT exists!", id)
continue
}
msgs, exists := globalApp.ArrayBundle.Messages[shortcut]
mBoardMsg := widgets.MBoardMessage{
LeftAlign: true,
Data: widget.NewLabel(string(msg.Data)),
}
if !exists {
tmp := CreateMutexArray[widgets.MBoardMessage]()
tmp.Add(mBoardMsg)
globalApp.ArrayBundle.Messages[shortcut] = &tmp
globalApp.Widgets.MBoardCaretacker.Refresh()
globalApp.Tabs.UsersTab.Content.Refresh()
} else {
msgs.Add(mBoardMsg)
globalApp.Widgets.MBoardCaretacker.Refresh()
globalApp.Tabs.UsersTab.Content.Refresh()
}
haveUser := false
for _, v := range globalApp.Widgets.MessagesSelect.Options {
if v == shortcut {
haveUser = true
break
}
}
if !haveUser {
globalApp.Widgets.MessagesSelect.Options = append(globalApp.Widgets.MessagesSelect.Options, shortcut)
globalApp.Tabs.UsersNoty++
updateTabsBasedOnNotyVals(&globalApp.Tabs)
globalApp.Widgets.MessagesSelect.Refresh()
}
//
mBoard, exists := globalApp.Widgets.MBoardMap[shortcut]
if exists {
mBoard.Add(mBoardMsg)
}
// TODO
default:
log.Printf("Can not handle msg with id %d", msg.ID)
log.Printf("Msg is different : %v", *msg)
}
}
}
func actOnActions(cfg FyneConfig, actionsChannel chan int, intercomToServer chan *com.Message) {
for action := range actionsChannel {
log.Printf("Receive action %d", action)
switch action {
case ACTION_SET_SCENE_NICKNAME:
// cfg.Window.Content()
scene := GetNicknameScene(intercomToServer)
cfg.Window.Resize(fyne.NewSize(640, 400))
cfg.Window.SetContent(scene)
case ACTION_SET_SCENE_MAIN:
scene, app := GetMainScene(intercomToServer, MainSceneConfig{
UserNickcname: tmpNick,
UserNameSet: true,
UserId: r.ID,
UserIdSet: true,
})
globalApp = app
globalApp.App.App = cfg.App
globalApp.App.Window = cfg.Window
// shortcuts
ctrl1 := &desktop.CustomShortcut{KeyName: fyne.Key1, Modifier: fyne.KeyModifierControl}
ctrl2 := &desktop.CustomShortcut{KeyName: fyne.Key2, Modifier: fyne.KeyModifierControl}
ctrl3 := &desktop.CustomShortcut{KeyName: fyne.Key3, Modifier: fyne.KeyModifierControl}
cfg.Window.Canvas().AddShortcut(ctrl1, func(shortcut fyne.Shortcut) {
app.Tabs.AppTabs.Select(app.Tabs.LinkTab)
})
cfg.Window.Canvas().AddShortcut(ctrl2, func(shortcut fyne.Shortcut) {
app.Tabs.AppTabs.Select(app.Tabs.ConnectionTab)
})
cfg.Window.Canvas().AddShortcut(ctrl3, func(shortcut fyne.Shortcut) {
app.Tabs.AppTabs.Select(app.Tabs.UsersTab)
})
// cfg.Window.Resize(fyne.NewSize(640, 400))
cfg.Window.SetContent(scene)
// ACTION_UPDATE_USER_OPTS_CONTS
case ACTION_UPDATE_USER_OPTS_CONTS:
for _, u := range globalApp.ArrayBundle.UsersOpts {
switch u.Status {
case USER_STATUS_VISIBLE:
btn := widget.NewButton("Init Handhake", func() {})
btn.OnTapped = func() {
intercomToServer <- &com.Message{
ID: com.ID_CLIENT_ASK_CLIENT_HANDSHAKE,
ToID: u.ToID,
}
btn.Hide()
}
u.OptsContainer = container.NewGridWithRows(1, btn)
cTaker := globalApp.Widgets.UserOptsCaretaker
cTaker.RemoveAll()
cTaker.Add(u.OptsContainer)
case USER_STATUS_HANDSHAKE_INIT:
btn := widget.NewButton("Accept Handhake", func() {})
btn.OnTapped = func() {
intercomToServer <- &com.Message{
ID: com.ID_CLIENT_APPROVE_CLIENT_HANDSHAKE,
ToID: u.ToID,
}
btn.Hide()
u.Status = USER_STATUS_HANDSHAKE_ACCEPTED
actionsChannel <- ACTION_UPDATE_USER_OPTS_CONTS
}
u.OptsContainer = container.NewGridWithRows(1, btn)
cTaker := globalApp.Widgets.UserOptsCaretaker
cTaker.RemoveAll()
cTaker.Add(u.OptsContainer)
case USER_STATUS_HANDSHAKE_ACCEPTED:
btn := widget.NewButton("Send ECDH PubKey", func() {})
btn.OnTapped = func() {
intercomToServer <- &com.Message{
ID: com.ID_CLIENT_SEND_CLIENT_ECDH_PUBKEY,
ToID: u.ToID,
}
btn.Hide()
if ecdhKeyReceived {
u.Status = USER_STATUS_ECDH_ESTABLISHED
actionsChannel <- ACTION_UPDATE_USER_OPTS_CONTS
}
ecdhKeySent = true
}
u.OptsContainer = container.NewGridWithRows(1, btn)
cTaker := globalApp.Widgets.UserOptsCaretaker
cTaker.RemoveAll()
cTaker.Add(u.OptsContainer)
case USER_STATUS_ECDH_ESTABLISHED:
btn := widget.NewButton("Send CBES Specs", func() {})
btn.OnTapped = func() {
intercomToServer <- &com.Message{
ID: com.ID_CLIENT_SEND_CLIENT_CBES_SPECS,
ToID: u.ToID,
}
btn.Hide()
}
u.OptsContainer = container.NewGridWithRows(1, btn)
cTaker := globalApp.Widgets.UserOptsCaretaker
cTaker.RemoveAll()
cTaker.Add(u.OptsContainer)
case USER_STATUS_ECDH_CBES_ESTABLISHED:
btn := widget.NewButton("TBC", func() {})
btn.OnTapped = func() {
log.Println("TODO:: 1001")
}
u.OptsContainer = container.NewGridWithRows(1, btn)
cTaker := globalApp.Widgets.UserOptsCaretaker
cTaker.RemoveAll()
cTaker.Add(u.OptsContainer)
default:
}
}
// globalApp.Tabs.ConnectionTab.Content.Refresh()
// ACTION_UPDATE_USER_OPTS_CONTS
default:
}
}
}
func readFromIntercom(intercom chan *com.Message, conn net.Conn) {
for msg := range intercom {
log.Printf("client: received message from Intercom: %v", msg)
msg.Version = com.V1
switch msg.ID {
case com.ID_CLIENT_SEND_SERVER_NICKNAME:
tmpNick = string(msg.Data)
case com.ID_CLIENT_SEND_SERVER_LINK:
if !r.IsRegistered {
continue
}
l, err := r.GenerateLink(msg.ToID)
if err != nil {
log.Printf("Error: link: %v", err)
continue
}
tmpLink = &l
answ, err := com.ClientSendServerLink(r.ID, l)
if err != nil {
log.Printf("Error: com: %v", err)
continue
}
log.Printf("client: readWS: sending data to server: %v", answ)
conn.Write(answ)
continue
case com.ID_CLIENT_ASK_CLIENT_HANDSHAKE,
com.ID_CLIENT_APPROVE_CLIENT_HANDSHAKE,
com.ID_CLIENT_DECLINE_CLIENT_HANDSHAKE,
com.ID_CLIENT_SEND_CLIENT_ECDH_PUBKEY,
com.ID_CLIENT_SEND_CLIENT_CBES_SPECS,
com.ID_CLIENT_SEND_CLIENT_MKLG_FINGERPRINT,
com.ID_CLIENT_DECLINE_CLIENT_MKLG_FINGERPRINT,
com.ID_CLIENT_SEND_CLIENT_MESSAGE:
if !r.IsRegistered {
continue
}
msg.FromID = r.ID
// switch
}
// user stuff
switch msg.ID {
case com.ID_CLIENT_ASK_CLIENT_HANDSHAKE,
com.ID_CLIENT_APPROVE_CLIENT_HANDSHAKE:
if r.IsRegistered {
msg.Data = []byte(r.Name)
}
}
// Crypto stuff
switch msg.ID {
case com.ID_CLIENT_ASK_CLIENT_HANDSHAKE,
com.ID_CLIENT_APPROVE_CLIENT_HANDSHAKE:
err := tlepCenter.AddTLEP(msg.ToID, fmt.Sprintf("%s-%d", r.Name, msg.ToID))
if err != nil {
log.Printf("ERROR: tlepCenter.AddUser: %v\n", err)
}
case com.ID_CLIENT_SEND_CLIENT_ECDH_PUBKEY:
t, err := tlepCenter.GetTLEP(msg.ToID)
if err != nil {
log.Printf("ERROR: tlep: GetTLEP: %v\n", err)
continue
}
key, err := t.ECDHGetPublicKey()
if err != nil {
log.Printf("ERROR: tlep: ECDHGetPublicKey: %v\n", err)
continue
}
msg.Data = key
case com.ID_CLIENT_SEND_CLIENT_CBES_SPECS:
t, err := tlepCenter.GetTLEP(msg.ToID)
if err != nil {
log.Printf("ERROR: tlep: GetTLEP: %v\n", err)
continue
}
err = t.CBESInitRandom()
if err != nil {
log.Printf("ERROR: tlep: CBESInitRandom: %v\n", err)
continue
}
cbes, err := t.CBESGetBytes()
if err != nil {
log.Printf("ERROR: tlep: ECDHGetPublicKey: %v\n", err)
continue
}
cbesEAEncr, err := t.EncryptMessageEA(cbes)
if err != nil {
log.Printf("ERROR: tlep: EncryptMessageEA: %v\n", err)
continue
}
msg.Data = cbesEAEncr
// message
case com.ID_CLIENT_SEND_CLIENT_MESSAGE:
t, err := tlepCenter.GetTLEP(msg.ToID)
if err != nil {
log.Printf("ERROR: tlep: GetTLEP: %v\n", err)
continue
}
encrypedMsg, err := t.EncryptMessageAtMax(msg.Data)
if err != nil {
log.Printf("ERROR: tlep: EncryptMessageAtMax: %v\n", err)
continue
}
msg.Data = encrypedMsg
// switch
}
encodedMsg, err := msg.Bytes()
if err != nil {
log.Printf("Encoding error: %s", err)
continue
}
log.Printf("client: readWS: sending data to server: %v", encodedMsg)
conn.Write(encodedMsg)
}
}