Created stable-ish version of cfyne

This commit is contained in:
qowevisa 2024-07-12 20:33:27 +03:00
parent 31b2707c23
commit feb5d22b2a
10 changed files with 1384 additions and 0 deletions

7
cmd/cfyne/actions.go Normal file
View File

@ -0,0 +1,7 @@
package main
const (
ACTION_SET_SCENE_NICKNAME = 1 + iota
ACTION_SET_SCENE_MAIN
ACTION_UPDATE_USER_OPTS_CONTS
)

61
cmd/cfyne/conUsers.go Normal file
View File

@ -0,0 +1,61 @@
package main
import (
"log"
"sync"
)
var (
userCenter UserCenter
)
type UserCenter struct {
UsersSTOI map[string]uint16
UsersITOS map[uint16]string
Mu sync.Mutex
}
func (u *UserCenter) Init() {
u.UsersSTOI = make(map[string]uint16)
u.UsersITOS = make(map[uint16]string)
}
func (u *UserCenter) AddUser(name string, id uint16) error {
u.Mu.Lock()
defer u.Mu.Unlock()
_, alreadyHave := u.UsersSTOI[name]
if alreadyHave {
return ERROR_ALREADY_HAVE
}
log.Printf("Users: add %s with %d id\n", name, id)
u.UsersITOS[id] = name
u.UsersSTOI[name] = id
return nil
}
func (u *UserCenter) DeleteIfHaveOne(id uint16) {
name, found := u.UsersITOS[id]
if !found {
log.Printf("User with %d id is not found; Can not delete\n", id)
return
}
delete(u.UsersITOS, id)
delete(u.UsersSTOI, name)
log.Printf("User with %s name and %d id was found; User is deleted\n", name, id)
}
func (u *UserCenter) GetID(name string) (uint16, error) {
id, have := u.UsersSTOI[name]
if !have {
return 0, ERROR_DONT_HAVE
}
return id, nil
}
func (u *UserCenter) GetName(id uint16) (string, error) {
name, have := u.UsersITOS[id]
if !have {
return "", ERROR_DONT_HAVE
}
return name, nil
}

15
cmd/cfyne/funcs.go Normal file
View File

@ -0,0 +1,15 @@
package main
import (
// "fyne.io/fyne/v2/widget"
com "git.qowevisa.me/Qowevisa/gotell/communication"
)
func getSendMessageFuncToIntercom(intercom chan *com.Message, id uint8, data []byte) func() {
return func() {
intercom <- &com.Message{
ID: id,
Data: data,
}
}
}

31
cmd/cfyne/linksmuar.go Normal file
View File

@ -0,0 +1,31 @@
package main
import (
com "git.qowevisa.me/Qowevisa/gotell/communication"
"sync"
)
// Non-Generic variant
type MutexLinksArray struct {
Ar []com.Link
Mu sync.RWMutex
}
func CreateMutexLinksArray() *MutexLinksArray {
var tmp []com.Link
return &MutexLinksArray{
Ar: tmp,
}
}
func (ma *MutexLinksArray) Add(v com.Link) {
ma.Mu.Lock()
ma.Ar = append(ma.Ar, v)
ma.Mu.Unlock()
}
func (ma *MutexLinksArray) GetArray() []com.Link {
ma.Mu.RLock()
defer ma.Mu.RUnlock()
return ma.Ar
}

648
cmd/cfyne/main.go Normal file
View File

@ -0,0 +1,648 @@
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)
}
}

377
cmd/cfyne/scenes.go Normal file
View File

@ -0,0 +1,377 @@
package main
import (
"errors"
"fmt"
"log"
"strconv"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
// "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
com "git.qowevisa.me/Qowevisa/gotell/communication"
"git.qowevisa.me/Qowevisa/gotell/extfyne/layouts"
"git.qowevisa.me/Qowevisa/gotell/extfyne/widgets"
)
func GetNicknameScene(intercom chan *com.Message) *fyne.Container {
// button := widget.NewButtonWithIcon(
// "Send",
// theme.ConfirmIcon(),
// getSendMessageFuncToIntercom(intercom, com.ID_CLIENT_SEND_SERVER_NICKNAME, []byte("test")))
entry := widget.NewEntry()
entry.SetPlaceHolder("Enter Nickname:")
btn := widget.NewButtonWithIcon("Send", theme.MailSendIcon(), func() {
log.Println("Nickname submitted:", entry.Text)
intercom <- &com.Message{
ID: com.ID_CLIENT_SEND_SERVER_NICKNAME,
Data: []byte(entry.Text),
}
})
contentInner := container.New(layout.NewVBoxLayout(), entry, btn)
return container.NewGridWithRows(3,
layout.NewSpacer(),
container.NewGridWithColumns(3,
layout.NewSpacer(),
contentInner,
layout.NewSpacer(),
),
layout.NewSpacer(),
)
}
func GetMainScene(intercom chan *com.Message, cfg MainSceneConfig) (*fyne.Container, *MutableApplication) {
headerWidget := widget.NewLabel(fmt.Sprintf("Hello, %s! Your ID: %d", cfg.UserNickcname, cfg.UserId))
header := container.NewGridWithColumns(3,
layout.NewSpacer(),
headerWidget,
layout.NewSpacer(),
)
linksSS, linksArr := GetLinksSubScene(intercom, header)
linksTab := container.NewTabItemWithIcon("Links", theme.MailComposeIcon(), linksSS)
conSS, conBundle, conWidgets := GetConnectionsSubScene(intercom, header)
connsTab := container.NewTabItemWithIcon("Connections", theme.ComputerIcon(), conSS)
userSS, userBundle, userWidgets := GetUsersSubScene(intercom, header, conBundle.UserShortcutsRev)
usersTab := container.NewTabItemWithIcon("Users", theme.AccountIcon(), userSS)
tabs := container.NewAppTabs(
linksTab,
connsTab,
usersTab,
)
tabs.SetTabLocation(container.TabLocationLeading)
return container.NewGridWithColumns(1,
tabs),
&MutableApplication{
Tabs: MutableStructAboutTabs{
AppTabs: tabs,
LinkTab: linksTab,
LinksBT: "Links",
LinksNoty: 0,
ConnectionTab: connsTab,
ConnsBT: "Connections",
ConnsNoty: 0,
UsersTab: usersTab,
UsersBT: "Users",
UsersNoty: 0,
},
ArrayBundle: BundleOfMutexArrays{
LinksMuAr: linksArr,
UsersMuAr: conBundle.UsersMuAr,
UsersOpts: conBundle.UsersOpts,
UserShortcuts: conBundle.UserShortcuts,
UserShortcutsRev: conBundle.UserShortcutsRev,
Messages: userBundle.Messages,
},
Widgets: MutableStructAboutWidgets{
UsersSelect: conWidgets.UsersSelect,
UserOptsCaretaker: conWidgets.UserOptsCaretaker,
MessagesSelect: userWidgets.MessagesSelect,
MBoardCaretacker: userWidgets.MBoardCaretacker,
MsgButton: userWidgets.MsgButton,
MBoardMap: userWidgets.MBoardMap,
},
}
}
func GetLinksSubScene(intercom chan *com.Message, header *fyne.Container) (*fyne.Container, *MutexLinksArray) {
entry := widget.NewEntry()
entry.SetPlaceHolder("Count:")
entry.Validator = func(s string) error {
val, err := strconv.ParseUint(s, 10, 16)
if err == nil && val == 0 {
return errors.New("Use count can't be 0")
}
return err
}
btn := widget.NewButtonWithIcon("Generate", theme.MailSendIcon(), func() {
count, err := strconv.ParseUint(entry.Text, 10, 16)
if err != nil || count == 0 {
log.Printf("dafuq: GetLinksSubScene: %v ; %d\n", err, count)
return
}
intercom <- &com.Message{
ID: com.ID_CLIENT_SEND_SERVER_LINK,
ToID: uint16(count),
}
})
entry.SetOnValidationChanged(func(err error) {
if err != nil {
btn.Hide()
} else {
if btn.Hidden {
btn.Show()
}
}
})
contentInner := container.NewGridWithColumns(2, entry, btn)
// /
linksMutAt := CreateMutexLinksArray()
linksList := widget.NewList(func() int {
return len(linksMutAt.GetArray())
}, func() fyne.CanvasObject {
return widget.NewLabel("template")
}, func(lii widget.ListItemID, co fyne.CanvasObject) {
link := linksMutAt.Ar[lii]
str := fmt.Sprintf("Count: %d ; %s", link.UseCount, link.Data)
co.(*widget.Label).SetText(str)
},
)
linksList.OnSelected = func(id widget.ListItemID) {
link := linksMutAt.Ar[id]
globalClipboardChannel <- string(link.Data)
}
// //
linksGetEntry := widget.NewEntry()
linksGetEntry.SetPlaceHolder("Link:")
linksGetEntry.Validator = func(s string) error {
validated, err := com.IsThisALinkData(s)
if err != nil {
return err
}
if !validated {
return errors.New("Link is not validated")
}
return nil
}
linksGetBtn := widget.NewButtonWithIcon("Get", theme.MailReplyIcon(), func() {
link := linksGetEntry.Text
validated, err := com.IsThisALinkData(link)
if err != nil {
log.Printf("linksGetBtn: Error: %v\n", err)
return
}
if !validated {
log.Printf("linksGetBtn: Error: link %v not validated\n", link)
return
}
intercom <- &com.Message{
ID: com.ID_CLIENT_ASK_SERVER_LINK,
Data: []byte(link),
}
})
linksGetEntry.SetOnValidationChanged(func(err error) {
if err != nil {
linksGetBtn.Hide()
} else {
if linksGetBtn.Hidden {
linksGetBtn.Show()
}
}
})
contentInner2 := container.NewGridWithColumns(2, linksGetEntry, linksGetBtn)
// /
// //
return container.NewGridWithRows(3,
header,
container.New(
layouts.NewVariableGridWithColumns(
6, []int{2, 1, 3}),
container.NewGridWithRows(3,
container.NewGridWithColumns(3,
layout.NewSpacer(),
widget.NewLabel("Create link:"),
layout.NewSpacer()),
contentInner,
layout.NewSpacer(),
),
layout.NewSpacer(),
linksList,
),
container.New(
layouts.NewVariableGridWithColumns(
6, []int{2, 4}),
container.NewGridWithRows(3,
container.NewGridWithColumns(3,
layout.NewSpacer(),
widget.NewLabel("Get user from link:"),
layout.NewSpacer()),
contentInner2,
layout.NewSpacer(),
),
layout.NewSpacer(),
),
), linksMutAt
}
func GetConnectionsSubScene(intercom chan *com.Message, header *fyne.Container) (*fyne.Container, BundleOfMutexArrays, MutableStructAboutWidgets) {
optionsHeaderLabel := widget.NewLabel("Options for user: ")
usersMuAr := CreateMutexStringArray()
usersOptions := make(map[string]*UserOptions)
userShortcuts := make(map[uint16]string)
userShortcutsRev := make(map[string]uint16)
userOptsCaretaker := container.NewGridWithRows(1)
optsContainer := container.NewGridWithRows(2,
optionsHeaderLabel,
userOptsCaretaker,
)
userSelect := widget.NewSelect(usersMuAr.Ar, func(s string) {
optionsHeaderLabel.SetText(fmt.Sprintf("Options for user: %s", s))
uOpts, exists := usersOptions[s]
if !exists {
log.Printf("GetConnectionsSubScene::1 : TODO!!\n")
// TODO
return
}
userOptsCaretaker.RemoveAll()
userOptsCaretaker.Add(uOpts.OptsContainer)
optsContainer.Refresh()
})
contentInner := container.New(layout.NewVBoxLayout(), userSelect, optsContainer)
return container.New(
layout.NewVBoxLayout(),
header,
container.New(
layout.NewHBoxLayout(),
layout.NewSpacer(),
contentInner,
layout.NewSpacer(),
),
), BundleOfMutexArrays{
UsersMuAr: usersMuAr,
UsersOpts: usersOptions,
UserShortcuts: userShortcuts,
UserShortcutsRev: userShortcutsRev,
}, MutableStructAboutWidgets{
UsersSelect: userSelect,
UserOptsCaretaker: userOptsCaretaker,
}
}
func GetUsersSubScene(intercom chan *com.Message, header *fyne.Container, userShortcutsRev map[string]uint16) (*fyne.Container, BundleOfMutexArrays, MutableStructAboutWidgets) {
msgHeader := widget.NewLabel("Messages With User: ")
usersMessages := make(map[string]*MutexArray[widgets.MBoardMessage])
usersMBoards := make(map[string]*widgets.MessageBoard)
msgEntry := widget.NewEntry()
msgEntry.SetPlaceHolder("Message...")
userMsgSendBtn := widget.NewButtonWithIcon("Send", theme.MailSendIcon(), func() {})
mBoardCareTaker := container.NewGridWithRows(1)
userMsgsSelect := widget.NewSelect([]string{}, func(s string) {
msgHeader.SetText(fmt.Sprintf("Messages With User: %s", s))
msgsMuAr, exists := usersMessages[s]
if !exists {
tmp := CreateMutexArray[widgets.MBoardMessage]()
usersMessages[s] = &tmp
msgsMuAr = &tmp
return
}
mBoardCareTaker.RemoveAll()
mmBoard := widgets.NewMessageBoard(msgsMuAr.Ar)
usersMBoards[s] = mmBoard
mBoardCareTaker.Add(mmBoard)
mBoardCareTaker.Refresh()
id, exists := userShortcutsRev[s]
if !exists {
log.Printf("GetUsersSubScene::2 : TODO!!\n")
// TODO
return
}
userMsgSendBtn.Text = fmt.Sprintf("%s", s)
userMsgSendBtn.OnTapped = func() {
intercom <- &com.Message{
ID: com.ID_CLIENT_SEND_CLIENT_MESSAGE,
ToID: id,
Data: []byte(msgEntry.Text),
}
msg := widgets.MBoardMessage{
LeftAlign: false,
Data: widget.NewLabel(msgEntry.Text),
}
msgsMuAr.Add(msg)
mmBoard.Add(msg)
msgEntry.Text = ""
msgEntry.Refresh()
mBoardCareTaker.Refresh()
mmBoard.Refresh()
}
userMsgSendBtn.Refresh()
})
// entryWithButton := container.NewHBox(
// layout.NewSpacer(),
// container.NewPadded(msgEntry),
// userMsgSendBtn,
// )
entryWithButton := container.New(
layouts.NewEntryBtn7030(),
msgEntry,
userMsgSendBtn,
)
fs := globalCfg.Window.Canvas().Size()
scene := container.New(
layout.NewVBoxLayout(),
header,
container.NewGridWithColumns(2,
container.New(
layout.NewVBoxLayout(),
layout.NewSpacer(),
container.New(
layout.NewHBoxLayout(),
userMsgsSelect,
msgHeader,
),
),
container.New(
layout.NewVBoxLayout(),
container.New(
layouts.NewFullWidthWithSize(fyne.NewSize(0, fs.Height*0.8)),
mBoardCareTaker,
),
layout.NewSpacer(),
entryWithButton,
),
),
)
// container.New(
// layouts.NewVariableGridWithRows(3, []int{1, 2}),
// header,
// container.New(
// layouts.NewVariableGridWithColumns(4, []int{1, 3}),
// container.NewGridWithColumns(3,
// layout.NewSpacer(),
// container.NewVBox(userMsgsSelect, msgHeader),
// layout.NewSpacer()),
// container.New(
// layouts.NewVariableGridWithRows(6, []int{5, 1}),
// mBoardCareTaker,
// container.NewHBox(msgEntry, userMsgSendBtn),
// ),
// ),
// )
return scene, BundleOfMutexArrays{
Messages: usersMessages,
}, MutableStructAboutWidgets{
MessagesSelect: userMsgsSelect,
MBoardCaretacker: mBoardCareTaker,
MsgButton: userMsgSendBtn,
MBoardMap: usersMBoards,
}
}

66
cmd/cfyne/tleps.go Normal file
View File

@ -0,0 +1,66 @@
package main
import (
"errors"
"log"
"sync"
"git.qowevisa.me/Qowevisa/gotell/gmyerr"
"git.qowevisa.me/Qowevisa/gotell/tlep"
)
var (
tlepCenter TlepCenter
)
type TlepCenter struct {
TLEPs map[uint16]*tlep.TLEP
Mu sync.Mutex
}
func (t *TlepCenter) Init() {
t.TLEPs = make(map[uint16]*tlep.TLEP)
}
var (
ERROR_ALREADY_HAVE = errors.New("Already taken")
ERROR_DONT_HAVE = errors.New("Not found")
)
func (t *TlepCenter) AddTLEP(id uint16, name string) error {
t.Mu.Lock()
defer t.Mu.Unlock()
_, alreadyHave := t.TLEPs[id]
if alreadyHave {
return ERROR_ALREADY_HAVE
}
val, err := tlep.InitTLEP(name)
if err != nil {
return gmyerr.WrapPrefix("tlep.InitTLEP", err)
}
val.Debug = true
t.TLEPs[id] = val
log.Printf("TLEPs: add %p for %d id\n", val, id)
return nil
}
func (t *TlepCenter) DeleteIfHaveOne(id uint16) {
t.Mu.Lock()
defer t.Mu.Unlock()
val, found := t.TLEPs[id]
if !found {
log.Printf("TLEP with %d id is not found; Can not delete\n", id)
return
}
delete(t.TLEPs, id)
log.Printf("TLEP with %v val and %d id was found; TLEP is deleted\n", val, id)
}
func (t *TlepCenter) GetTLEP(id uint16) (*tlep.TLEP, error) {
log.Printf("Getting tlep by id = %d\n", id)
name, have := t.TLEPs[id]
if !have {
return nil, ERROR_DONT_HAVE
}
return name, nil
}

125
cmd/cfyne/types.go Normal file
View File

@ -0,0 +1,125 @@
package main
import (
"sync"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"git.qowevisa.me/Qowevisa/gotell/extfyne/widgets"
// com "git.qowevisa.me/Qowevisa/gotell/communication"
)
type FyneConfig struct {
App fyne.App
Window fyne.Window
}
type MainSceneConfig struct {
UserNickcname string
// Just for my calmness
UserNameSet bool
UserId uint16
// Just for my calmness
UserIdSet bool
}
type MutableStructAboutTabs struct {
AppTabs *container.AppTabs
LinkTab *container.TabItem
LinksBT string
LinksNoty uint
ConnectionTab *container.TabItem
ConnsBT string
ConnsNoty uint
UsersTab *container.TabItem
UsersBT string
UsersNoty uint
}
type MutableStructAboutWidgets struct {
UsersSelect *widget.Select
UserOptsCaretaker *fyne.Container
MessagesSelect *widget.Select
MBoardCaretacker *fyne.Container
MsgButton *widget.Button
MBoardMap map[string]*widgets.MessageBoard
}
type UserOptions struct {
OptsContainer *fyne.Container
Status int
ToID uint16
Name string
}
type BundleOfMutexArrays struct {
LinksMuAr *MutexLinksArray
UsersMuAr *MutexStringArray
UsersOpts map[string]*UserOptions
UserShortcuts map[uint16]string
UserShortcutsRev map[string]uint16
Messages map[string]*MutexArray[widgets.MBoardMessage]
}
type MutableApp struct {
App fyne.App
Window fyne.Window
}
type MutableApplication struct {
Tabs MutableStructAboutTabs
Widgets MutableStructAboutWidgets
ArrayBundle BundleOfMutexArrays
App MutableApp
}
// I don't know if it is a good idea
type MutexArray[T any] struct {
Ar []T
Mu sync.RWMutex
}
func CreateMutexArray[T any]() MutexArray[T] {
var tmp []T
return MutexArray[T]{
Ar: tmp,
}
}
func (ma *MutexArray[T]) Add(v T) {
ma.Mu.Lock()
ma.Ar = append(ma.Ar, v)
ma.Mu.Unlock()
}
func (ma *MutexArray[T]) GetArray() []T {
ma.Mu.RLock()
defer ma.Mu.RUnlock()
return ma.Ar
}
// Non-Generic variant
type MutexStringArray struct {
Ar []string
Mu sync.RWMutex
}
func CreateMutexStringArray() *MutexStringArray {
var tmp []string
return &MutexStringArray{
Ar: tmp,
}
}
func (ma *MutexStringArray) Add(v string) {
ma.Mu.Lock()
ma.Ar = append(ma.Ar, v)
ma.Mu.Unlock()
}
func (ma *MutexStringArray) GetArray() []string {
ma.Mu.RLock()
defer ma.Mu.RUnlock()
return ma.Ar
}

View File

@ -0,0 +1,36 @@
package main
import (
// "log"
//
// "fyne.io/fyne/v2"
// "fyne.io/fyne/v2/container"
// "fyne.io/fyne/v2/widget"
// com "git.qowevisa.me/Qowevisa/gotell/communication"
)
const (
USER_STATUS_NOT_VISIBLE = 0 + iota
USER_STATUS_VISIBLE
USER_STATUS_HANDSHAKE_INIT
USER_STATUS_HANDSHAKE_ACCEPTED
USER_STATUS_ECDH_ESTABLISHED
USER_STATUS_ECDH_CBES_ESTABLISHED
USER_STATUS_ECDH_CBES_MKLG_ESTABLISHED
)
// func getUserOptsContainer(userStatus int, intercom chan *com.Message) *fyne.Container {
// switch {
// case USER_STATUS_HANDSHAKE_INIT:
// btn := widget.NewButton("Init Hadnshake", func() {
// intercom <- &com.Message{
// ID: ID_CLIENT_ASK_CLIENT_HANDSHAKE,
// ToID: ,
// }
// })
// return container.NewGridWithRows(1, )
// default:
// log.Printf("ERROR: getUserOptsContainer: status %d was not handled!", userStatus)
// return container.NewGridWithRows(1, widget.NewLabel("ERROR: 500"))
// }
// }

18
cmd/cfyne/util.go Normal file
View File

@ -0,0 +1,18 @@
package main
import "fmt"
func getStdUserName(userID uint16, userName string) string {
return fmt.Sprintf("User: %s; ID: %d", userName, userID)
}
func getTabNameBasedOnBaseTextAndNoty(baseText string, noty uint) string {
return fmt.Sprintf("%s (%d)", baseText, noty)
}
func updateTabsBasedOnNotyVals(tabs *MutableStructAboutTabs) {
tabs.LinkTab.Text = getTabNameBasedOnBaseTextAndNoty(tabs.LinksBT, tabs.LinksNoty)
tabs.ConnectionTab.Text = getTabNameBasedOnBaseTextAndNoty(tabs.ConnsBT, tabs.ConnsNoty)
tabs.UsersTab.Text = getTabNameBasedOnBaseTextAndNoty(tabs.UsersBT, tabs.UsersNoty)
tabs.AppTabs.Refresh()
}