package main
import (
"fmt"
"os"
"os/exec"
"regexp"
"github.com/lxn/walk"
"github.com/lxn/walk/declarative"
)
// RemoteSSHShell struct type to store ssh data
type RemoteSSHShell struct {
AuthMthd int
HostAddr string
HostPort int
PreVTKEY string // private key path
PassPhrase string
PassWord string
UserName string
}
func startSSHsession(owner walk.Form, sendipaddr string) (int, error) {
const regexSingle = `^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$`
var sshClientguiwindow struct {
dlgSSHAccwin *walk.Dialog
dbindersshx *walk.DataBinder
acceptPB, cancelPB *walk.PushButton
mapLineEd, pkLineEd, pKandPHm *walk.LineEdit
passGBOXm, pKeyGBOXm *walk.GroupBox
}
sshstatset := &RemoteSSHShell{
AuthMthd: 1,
HostPort: 22,
UserName: "root",
HostAddr: sendipaddr,
}
return declarative.Dialog{
AssignTo: &sshClientguiwindow.dlgSSHAccwin,
FixedSize: true,
Title: "SSH Client",
DefaultButton: &sshClientguiwindow.acceptPB,
CancelButton: &sshClientguiwindow.cancelPB,
DataBinder: declarative.DataBinder{
AssignTo: &sshClientguiwindow.dbindersshx,
Name: "sshClient",
DataSource: sshstatset,
ErrorPresenter: declarative.ToolTipErrorPresenter{},
},
MinSize: declarative.Size{Width: 320, Height: 300},
Layout: declarative.VBox{},
Children: []declarative.Widget{
declarative.GroupBox{
Title: "SSH Client Options",
Layout: declarative.HBox{},
Children: []declarative.Widget{
declarative.Composite{
Layout: declarative.Grid{Columns: 1},
Children: []declarative.Widget{
declarative.GroupBox{
Title: "Host Address To Connect",
Layout: declarative.Grid{Columns: 4},
Children: []declarative.Widget{
declarative.LineEdit{
ToolTipText: "username for ssh connection",
CueBanner: "User",
MaxSize: declarative.Size{Width: 55},
Text: declarative.Bind("UserName"),
},
declarative.LineEdit{
AssignTo: &sshClientguiwindow.mapLineEd,
ToolTipText: "format: IP|Domain",
CueBanner: "IP or Domain",
Text: declarative.Bind("HostAddr"),
},
declarative.NumberEdit{
ToolTipText: "port number",
MaxSize: declarative.Size{Width: 40},
MinSize: declarative.Size{Width: 40},
Value: declarative.Bind("HostPort"),
MinValue: 1,
MaxValue: 65535,
},
},
},
declarative.RadioButtonGroupBox{
Title: "Authentication",
Layout: declarative.HBox{},
DataMember: "AuthMthd",
Buttons: []declarative.RadioButton{
{MaxSize: declarative.Size{Width: 90}, Text: "Password", Value: 1, OnClicked: func() {
sshClientguiwindow.passGBOXm.SetVisible(true)
sshClientguiwindow.pKeyGBOXm.SetVisible(false)
sshClientguiwindow.pKandPHm.SetVisible(false)
}},
{MaxSize: declarative.Size{Width: 90}, Text: "PrivateK", Value: 2, OnClicked: func() {
sshClientguiwindow.passGBOXm.SetVisible(false)
sshClientguiwindow.pKeyGBOXm.SetVisible(true)
sshClientguiwindow.pKandPHm.SetVisible(false)
}},
{MaxSize: declarative.Size{Width: 90}, Text: "PK+PasPh", Value: 3, OnClicked: func() {
sshClientguiwindow.passGBOXm.SetVisible(false)
sshClientguiwindow.pKeyGBOXm.SetVisible(true)
sshClientguiwindow.pKandPHm.SetVisible(true)
}},
},
},
declarative.GroupBox{
AssignTo: &sshClientguiwindow.passGBOXm,
Title: "Login With Password",
Layout: declarative.Grid{Columns: 1},
Children: []declarative.Widget{
declarative.LineEdit{
CueBanner: "Password ...",
PasswordMode: true,
Text: declarative.Bind("PassWord"),
},
},
},
declarative.GroupBox{
AssignTo: &sshClientguiwindow.pKeyGBOXm,
Title: "Login With Private Key",
Visible: false,
Layout: declarative.VBox{},
Children: []declarative.Widget{
declarative.LineEdit{
AssignTo: &sshClientguiwindow.pkLineEd,
CueBanner: "Private Key File Path ...",
ReadOnly: true,
Text: declarative.Bind("PreVTKEY"),
},
declarative.PushButton{
Text: "Open Key File",
ToolTipText: "open ssh key file",
OnClicked: func() {
filePth, err := openPkFile(sshClientguiwindow.dlgSSHAccwin)
if err != nil {
walk.MsgBox(sshClientguiwindow.dlgSSHAccwin, "OS file error", fmt.Sprint(err), walk.MsgBoxIconError)
return
}
sshClientguiwindow.pkLineEd.SetText(filePth)
},
},
declarative.LineEdit{
AssignTo: &sshClientguiwindow.pKandPHm,
CueBanner: "Passphrase for Key ...",
PasswordMode: true,
Text: declarative.Bind("PassPhrase"),
},
},
},
},
},
},
},
declarative.Composite{
Layout: declarative.HBox{},
Children: []declarative.Widget{
declarative.HSpacer{},
declarative.PushButton{
AssignTo: &sshClientguiwindow.acceptPB,
Text: "Connect",
OnClicked: func() {
if err := sshClientguiwindow.dbindersshx.Submit(); err != nil {
return
}
if sshstatset.HostAddr == "" {
sshClientguiwindow.mapLineEd.SetFocus()
return
}
if match, _ := regexp.MatchString(regexSingle, sshstatset.HostAddr); !match {
walk.MsgBox(sshClientguiwindow.dlgSSHAccwin, "Host syntax error", "Invalid IPv4 or Domain address, format: IPv4|Domain\nExample: slc.snix.ir", walk.MsgBoxIconError)
return
}
if exeErr := exec.Command("cmd", "/c", "start",
os.Args[0],
"-auth", fmt.Sprint(sshstatset.AuthMthd),
"-addr", fmt.Sprintf("%v:%v", sshstatset.HostAddr, sshstatset.HostPort),
"-user", sshstatset.UserName,
"-pass", sshstatset.PassWord,
"-prvk", sshstatset.PreVTKEY,
"-prph", sshstatset.PassPhrase).Start(); exeErr != nil {
walk.MsgBox(sshClientguiwindow.dlgSSHAccwin, "Internal System Err", fmt.Sprintf("%v", exeErr), walk.MsgBoxIconError)
return
}
},
},
declarative.PushButton{
AssignTo: &sshClientguiwindow.cancelPB,
Text: "Cancel",
OnClicked: func() { sshClientguiwindow.dlgSSHAccwin.Cancel() },
},
},
},
},
}.Run(owner)
}
func openPkFile(dlgwinad walk.Form) (string, error) {
openSSHfile := new(walk.FileDialog)
openSSHfile.Filter = "PEM Files (*.pem)|*.pem|All Files (*.*)|*.*"
openSSHfile.Title = "Open SSH Key File"
if okfile, err := openSSHfile.ShowOpen(dlgwinad); err != nil {
return openSSHfile.FilePath, err
} else if !okfile {
return openSSHfile.FilePath, err
}
return openSSHfile.FilePath, nil
}