diff --git a/go-client/cmd/awaken/awaken.go b/go-client/cmd/awaken/awaken.go index 52f1a0eb3..1532d7c2e 100755 --- a/go-client/cmd/awaken/awaken.go +++ b/go-client/cmd/awaken/awaken.go @@ -16,6 +16,7 @@ func main() { p := &awaken.Info{} if len(os.Args) != 1 { global.LOG = logger.InitLogger() + defer global.LOG.Sync() zap.ReplaceGlobals(global.LOG) base64String := strings.TrimPrefix(os.Args[1], "jms://") decoded, _ := base64.StdEncoding.DecodeString(base64String) diff --git a/go-client/config.json b/go-client/config.json index 8d2c6bcba..481604037 100644 --- a/go-client/config.json +++ b/go-client/config.json @@ -1,6 +1,6 @@ { "filename": "Jumpserve Clients Config", - "version": 7, + "version": 8, "windowBounds": { "width": 1280, "height": 800 @@ -155,6 +155,25 @@ "is_internal": false, "is_default": false, "is_set": false + }, + { + "name": "realvnc", + "display_name": "RealVNC Viewer", + "protocol": [ + "vnc" + ], + "comment": { + "zh": "RealVNC Viewer 是 RealVNC 提供的远程桌面连接工具。", + "en": "RealVNC Viewer is a remote desktop connection tool provided by RealVNC." + }, + "download_url": "https://www.realvnc.com/en/connect/download/viewer/", + "type": "remotedesktop", + "path": "C:\\Program Files\\RealVNC\\VNC Viewer\\vncviewer.exe", + "arg_format": "{file}", + "match_first": [], + "is_internal": false, + "is_default": false, + "is_set": false } ], "filetransfer": [ diff --git a/go-client/pkg/autoit/goautoit.go b/go-client/pkg/autoit/goautoit.go index 846e571f9..3f1e4d1bb 100644 --- a/go-client/pkg/autoit/goautoit.go +++ b/go-client/pkg/autoit/goautoit.go @@ -30,14 +30,17 @@ type POINT struct { } var ( - dll64 *syscall.LazyDLL - controlClick *syscall.LazyProc - controlSend *syscall.LazyProc - controlSetText *syscall.LazyProc - run *syscall.LazyProc - winWaitActive *syscall.LazyProc - winWait *syscall.LazyProc - send *syscall.LazyProc + dll64 *syscall.LazyDLL + controlClick *syscall.LazyProc + controlSend *syscall.LazyProc + controlGetFocus *syscall.LazyProc + controlGetText *syscall.LazyProc + controlSetText *syscall.LazyProc + run *syscall.LazyProc + winActivate *syscall.LazyProc + winWaitActive *syscall.LazyProc + winWait *syscall.LazyProc + send *syscall.LazyProc ) func LoadAuto() { @@ -48,8 +51,11 @@ func LoadAuto() { dll64 := syscall.NewLazyDLL(filepath.Join(filepath.Dir(os.Args[0]), autoItX3)) controlClick = dll64.NewProc("AU3_ControlClick") controlSend = dll64.NewProc("AU3_ControlSend") + controlGetFocus = dll64.NewProc("AU3_ControlGetFocus") + controlGetText = dll64.NewProc("AU3_ControlGetText") controlSetText = dll64.NewProc("AU3_ControlSetText") run = dll64.NewProc("AU3_Run") + winActivate = dll64.NewProc("AU3_WinActivate") winWaitActive = dll64.NewProc("AU3_WinWaitActive") winWait = dll64.NewProc("AU3_WinWait") send = dll64.NewProc("AU3_Send") @@ -117,6 +123,24 @@ func WinWait(szTitle string, args ...interface{}) int { return int(handle) } +// WinActivate -- activate a window. +func WinActivate(szTitle string, args ...interface{}) int { + var szText string + var ok bool + if len(args) == 0 { + szText = "" + } else if len(args) == 1 { + if szText, ok = args[0].(string); !ok { + panic("szText must be a string") + } + } else { + panic("Too more parameter") + } + + ret, _, _ := winActivate.Call(strPtr(szTitle), strPtr(szText)) + return int(ret) +} + // ControlClick -- Sends a mouse click command to a given control. func ControlClick(title, text, control string, args ...interface{}) int { var button string @@ -220,13 +244,24 @@ func ControlSend(title, text, control, sendText string, args ...interface{}) int // ControlSetText -- Sets text of a control. func ControlSetText(title, text, control, newText string) int { - ret, _, lastErr := controlSetText.Call(strPtr(title), strPtr(text), strPtr(control), strPtr(newText)) - if int(ret) == 0 { - println(lastErr) - } + ret, _, _ := controlSetText.Call(strPtr(title), strPtr(text), strPtr(control), strPtr(newText)) return int(ret) } +// ControlGetFocus -- Returns the Control Identifier of the focused control. +func ControlGetFocus(title, text string) string { + buf := make([]uint16, 4096) + controlGetFocus.Call(strPtr(title), strPtr(text), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) + return syscall.UTF16ToString(buf) +} + +// ControlGetText -- Returns text from a control. +func ControlGetText(title, text, control string) string { + buf := make([]uint16, 4096) + controlGetText.Call(strPtr(title), strPtr(text), strPtr(control), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) + return syscall.UTF16ToString(buf) +} + // Send -- Send simulates input on the keyboard // flag: 0: normal, 1: raw func Send(key string, args ...interface{}) { diff --git a/go-client/pkg/awaken/awaken.go b/go-client/pkg/awaken/awaken.go index 97bff5d93..bfa51c6f6 100755 --- a/go-client/pkg/awaken/awaken.go +++ b/go-client/pkg/awaken/awaken.go @@ -95,7 +95,7 @@ func reportErrorf(format string, args ...interface{}) { } func removeCurRdpVncFile() { - re := regexp.MustCompile(`(?i)\.(rdp|vncpaxx)$`) + re := regexp.MustCompile(`(?i)\.(rdp|vnc|vncpaxx)$`) dir, _ := os.UserConfigDir() rd, _ := ioutil.ReadDir(filepath.Join(dir, "jumpserver-client")) for _, v := range rd { @@ -127,6 +127,7 @@ func (r *Rouse) HandleRDP(appConfig *config.AppConfig) { } func (r *Rouse) HandleVNC(appConfig *config.AppConfig) { + removeCurRdpVncFile() cmd := handleVNC(r, appConfig) if cmd != nil { if err := cmd.Run(); err != nil { diff --git a/go-client/pkg/awaken/awaken_windows.go b/go-client/pkg/awaken/awaken_windows.go index 488b83549..a7e0fd138 100755 --- a/go-client/pkg/awaken/awaken_windows.go +++ b/go-client/pkg/awaken/awaken_windows.go @@ -122,6 +122,51 @@ func handleVNC(r *Rouse, cfg *config.AppConfig) *exec.Cmd { "host": r.Host, "port": strconv.Itoa(r.Port), } + if appItem.Name == "realvnc" { + dir, _ := os.UserConfigDir() + currentPath := filepath.Join(dir, "jumpserver-client") + EnsureDirExist(currentPath) + files, _ := ioutil.ReadDir(currentPath) + for _, file := range files { + if !file.IsDir() && strings.EqualFold(filepath.Ext(file.Name()), ".vnc") { + os.Remove(filepath.Join(currentPath, file.Name())) + } + } + filePath := filepath.Join(currentPath, r.getName()+".vnc") + content := fmt.Sprintf("[Connection]\nHost=%s:%s\nWarnUnencrypted=0\nUserName=%s\n", r.Host, strconv.Itoa(r.Port), r.getUserName()) + if err := ioutil.WriteFile(filePath, []byte(content), os.ModePerm); err != nil { + global.LOG.Error(err.Error()) + return nil + } + autoit.LoadAuto() + autoit.Run(appItem.Path + " -VerifyId \"0\" \"" + filePath + "\"") + winTitle := "Authentication" + active := false + for i := 0; i <= 30; i++ { + ret := autoit.WinWaitActive(winTitle, "", 1) + time.Sleep(300 * time.Millisecond) + if ret != 0 { + active = true + break + } + autoit.WinActivate(winTitle, "") + } + if !active { + autoit.WinActivate(winTitle, "") + time.Sleep(500 * time.Millisecond) + } + focusedControl := autoit.ControlGetFocus(winTitle, "") + focusedText := strings.TrimSpace(autoit.ControlGetText(winTitle, "", focusedControl)) + if focusedControl != "" && focusedText == strings.TrimSpace(r.getUserName()) { + autoit.Send("{TAB}") + time.Sleep(300 * time.Millisecond) + } + autoit.Send("^a") + time.Sleep(100 * time.Millisecond) + autoit.Send(r.Value, 1) + autoit.Send("{ENTER}") + return exec.Command("cmd", "/C", "exit", "0") + } if len(appItem.AutoIt) == 0 { commands := getCommandFromArgs(connectMap, appItem.ArgFormat) cmd := exec.Command(appItem.Path, strings.Split(commands, " ")...) diff --git a/ui/components/SettingItems/settingItems.vue b/ui/components/SettingItems/settingItems.vue index 44a6f00b6..0f4995ada 100644 --- a/ui/components/SettingItems/settingItems.vue +++ b/ui/components/SettingItems/settingItems.vue @@ -24,6 +24,7 @@ const imagesMap: Record = { mstsc: getImageByName("mstsc"), terminal: getImageByName("terminal"), vncviewer: getImageByName("realvnc"), + realvnc: getImageByName("realvnc"), tigervnc: getImageByName("tigerVnc"), securefx: getImageByName("securecrt"), securecrt: getImageByName("securecrt"),