package main import ( "crypto/rsa" "fmt" "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/data/binding" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "image/color" "regexp" "strings" ) type UserData struct { ServerUrl string Username string ServerKey []byte UserKey []byte ParsedUserKey *rsa.PublicKey } const DefaultPort = "12345" func parseServerUrl(url string) (string, error) { url = strings.TrimRight(url, "/") urlFormat, err := regexp.Compile("(.*://)?((.*):(\\d+)|(.*))") if err != nil { return "", err } matches := urlFormat.FindStringSubmatch(url) host := "" port := "" if matches[5] != "" { host = matches[5] port = DefaultPort } else { host = matches[3] port = matches[4] } addr := fmt.Sprintf("http://%s:%s", host, port) return addr, nil } func chatLayout(window fyne.Window, user UserData) { window.SetTitle("Сообщения") storage.binding = binding.BindStringList(&storage.data) go runClient(user) list := widget.NewListWithData(storage.binding, func() fyne.CanvasObject { return widget.NewLabel("template") }, func(i binding.DataItem, o fyne.CanvasObject) { o.(*widget.Label).Bind(i.(binding.String)) }) entry := widget.NewEntry() submit := widget.NewButton("Отправить", func() { go sendMessage(user, entry.Text) entry.SetText("") storage.RLock() lastIdx := storage.binding.Length() - 1 list.Select(lastIdx) list.Unselect(lastIdx) storage.RUnlock() }) window.SetContent(container.NewBorder( nil, container.NewBorder(nil, nil, nil, submit, entry), nil, nil, list, )) } func loginLayout(window fyne.Window, user UserData) { window.SetTitle("Войти в чат") keyIsSetup := false label := canvas.NewText("Chat", color.White) label.TextSize = 40 urlLabel := widget.NewLabel("URL: ") urlEntry := widget.NewEntry() urlEntry.Text = "localhost" loginLabel := widget.NewLabel("Логин: ") loginEntry := widget.NewEntry() chooseKey := widget.NewButton("Выбрать ключ для подписи", func() { dialog.ShowFileOpen(func(closer fyne.URIReadCloser, err error) { if closer == nil { return } bufSize := 4096 data := make([]byte, 0) buf := make([]byte, bufSize) n := bufSize for n >= bufSize { n, _ = closer.Read(buf) data = append(data, buf...) } user.ParsedUserKey, err = parseKey(data) if err != nil { dialog.ShowInformation("Ошибка", "Выбранный файл не является ключом", window) return } user.UserKey = data user.ServerKey = nil keyIsSetup = true _ = closer.Close() }, window) }) loginButton := widget.NewButton("Войти", func() { if !keyIsSetup { dialog.ShowInformation("Ошибка", "Не выбран ключ", window) return } if len(loginEntry.Text) == 0 { dialog.ShowInformation("Ошибка", "Логин не может быть пустым", window) return } user.Username = loginEntry.Text if len(urlEntry.Text) == 0 { dialog.ShowInformation("Ошибка", "URL не может быть пустым", window) return } url, err := parseServerUrl(urlEntry.Text) if err != nil { dialog.ShowInformation("Ошибка", err.Error(), window) } user.ServerUrl = url err = pingServer(user) if err != nil { dialog.ShowInformation("Ошибка", "Нет связи с указанным сервером", window) return } _, err = tryAuth(user) if err != nil { dialog.ShowInformation("Ошибка", fmt.Sprint(err), window) return } chatLayout(window, user) }) back := widget.NewButton("Назад", func() { startLayout(window, user) }) mainContainer := container.NewCenter(container.NewVBox( container.NewCenter(label), layout.NewSpacer(), container.NewBorder(nil, nil, urlLabel, nil, urlEntry), container.NewBorder(nil, nil, loginLabel, nil, loginEntry), chooseKey, loginButton, back, )) window.SetContent(mainContainer) } func registerLayout(window fyne.Window, user UserData) { window.SetTitle("Регистрация") keyIsSetup := false label := canvas.NewText("Chat", color.White) label.TextSize = 40 urlLabel := widget.NewLabel("URL: ") urlEntry := widget.NewEntry() urlEntry.Text = "localhost" loginLabel := widget.NewLabel("Логин: ") loginEntry := widget.NewEntry() generateKeysBtn := widget.NewButton("Сгенерировать ключи подписи", func() { dialog.ShowFileSave(func(closer fyne.URIWriteCloser, err error) { if closer == nil { return } private, public, err := generateKeys() if err != nil { dialog.ShowInformation("Ошибка", fmt.Sprint(err), window) return } user.ServerKey = private user.UserKey = public user.ParsedUserKey, _ = parseKey(user.UserKey) keyIsSetup = true _, _ = closer.Write(user.UserKey) _ = closer.Close() }, window) }) registerButton := widget.NewButton("Зарегистрироваться", func() { if !keyIsSetup { dialog.ShowInformation("Ошибка", "Не выбран ключ", window) return } if len(loginEntry.Text) == 0 { dialog.ShowInformation("Ошибка", "Логин не может быть пустым", window) return } user.Username = loginEntry.Text if len(urlEntry.Text) == 0 { dialog.ShowInformation("Ошибка", "URL не может быть пустым", window) return } url, err := parseServerUrl(urlEntry.Text) if err != nil { dialog.ShowInformation("Ошибка", err.Error(), window) } user.ServerUrl = url err = pingServer(user) if err != nil { dialog.ShowInformation("Ошибка", "Нет связи с указанным сервером", window) return } _, err = register(user, string(user.ServerKey)) if err != nil { dialog.ShowInformation("Ошибка", fmt.Sprint(err), window) return } chatLayout(window, user) }) back := widget.NewButton("Назад", func() { startLayout(window, user) }) window.SetContent(container.NewCenter(container.NewVBox( container.NewCenter(label), layout.NewSpacer(), container.NewBorder(nil, nil, urlLabel, nil, urlEntry), container.NewBorder(nil, nil, loginLabel, nil, loginEntry), generateKeysBtn, registerButton, back, ))) } func startLayout(window fyne.Window, user UserData) { window.SetTitle("Chat") label := canvas.NewText("Chat", color.White) label.TextSize = 40 login := widget.NewButton("Войти", func() { loginLayout(window, user) }) signup := widget.NewButton("Зарегистрироваться", func() { registerLayout(window, user) }) mainContainer := container.NewCenter(container.NewVBox( container.NewCenter(label), layout.NewSpacer(), login, signup)) window.SetContent(mainContainer) } func main() { myApp := app.New() myApp.Settings().SetTheme(theme.DarkTheme()) window := myApp.NewWindow("") window.Resize(fyne.Size{Width: 640, Height: 480}) var user UserData startLayout(window, user) window.ShowAndRun() }