diff --git a/src/app/common/elements/index.tsx b/src/app/common/elements/index.tsx
index 30895855..7096d328 100644
--- a/src/app/common/elements/index.tsx
+++ b/src/app/common/elements/index.tsx
@@ -13,7 +13,6 @@ export { NumberField } from "./numberfield";
export { PasswordField } from "./passwordfield";
export { ResizableSidebar } from "./resizablesidebar";
export { SettingsError } from "./settingserror";
-export { ShowWaveShellInstallPrompt } from "./showwaveshellinstallprompt";
export { Status } from "./status";
export { TextField } from "./textfield";
export { Toggle } from "./toggle";
diff --git a/src/app/common/elements/showwaveshellinstallprompt.tsx b/src/app/common/elements/showwaveshellinstallprompt.tsx
deleted file mode 100644
index 647cd74d..00000000
--- a/src/app/common/elements/showwaveshellinstallprompt.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2023, Command Line Inc.
-// SPDX-License-Identifier: Apache-2.0
-
-import { GlobalModel } from "@/models";
-import * as appconst from "@/app/appconst";
-
-function ShowWaveShellInstallPrompt(callbackFn: () => void) {
- let message: string = `
-In order to use Wave's advanced features like unified history and persistent sessions, Wave installs a small, open-source helper program called WaveShell on your remote machine. WaveShell does not open any external ports and only communicates with your *local* Wave terminal instance over ssh. For more information please see [the docs](https://docs.waveterm.dev/reference/waveshell).
- `;
- message = message.trim();
- let prtn = GlobalModel.showAlert({
- message: message,
- confirm: true,
- markdown: true,
- confirmflag: appconst.ConfirmKey_HideShellPrompt,
- });
- prtn.then((confirm) => {
- if (!confirm) {
- return;
- }
- if (callbackFn) {
- callbackFn();
- }
- });
-}
-
-export { ShowWaveShellInstallPrompt };
diff --git a/src/app/common/modals/createremoteconn.tsx b/src/app/common/modals/createremoteconn.tsx
index 297e6797..56f4e6ab 100644
--- a/src/app/common/modals/createremoteconn.tsx
+++ b/src/app/common/modals/createremoteconn.tsx
@@ -7,16 +7,7 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator";
import { If } from "tsx-control-statements/components";
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
-import {
- Modal,
- TextField,
- NumberField,
- InputDecoration,
- Dropdown,
- PasswordField,
- Tooltip,
- ShowWaveShellInstallPrompt,
-} from "@/elements";
+import { Modal, TextField, NumberField, InputDecoration, Dropdown, PasswordField, Tooltip } from "@/elements";
import * as util from "@/util/util";
import "./createremoteconn.less";
@@ -73,12 +64,7 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
}
@boundMethod
- handleOk(): void {
- ShowWaveShellInstallPrompt(this.submitRemote);
- }
-
- @boundMethod
- submitRemote(): void {
+ handleSubmitRemote(): void {
mobx.action(() => {
this.errorStr.set(null);
})();
@@ -275,7 +261,7 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
{ value: "none", label: "none" },
{ value: "key", label: "key" },
{ value: "password", label: "password" },
- { value: "key+password", label: "key+password" },
+ { value: "key+password", label: "key+passphrase" },
]}
value={this.tempAuthMode.get()}
onChange={(val: string) => {
@@ -288,17 +274,18 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
message={
-
- none - no authentication, or authentication is already
- configured in your ssh config.
+ none - no authentication details are stored.
-
- key - use a private key.
+ key - provide a custom private key for authentication.
-
- password - use a password.
+ password - provide a password (to save) for
+ authentication.
-
- key+password - use a key with a passphrase.
+ key+passphrase - provide a custom private key with a
+ passphrase (to save) for authentication.
}
@@ -374,7 +361,7 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
Error: {this.getErrorStr()}
-
+
);
}
diff --git a/src/app/common/modals/userinput.less b/src/app/common/modals/userinput.less
index f86c11ca..fd6b3e6f 100644
--- a/src/app/common/modals/userinput.less
+++ b/src/app/common/modals/userinput.less
@@ -3,10 +3,14 @@
.wave-modal-content {
.wave-modal-body {
- padding: 20px 20px;
+ padding: 0px 20px 0px 20px;
- .userinput-query {
- margin-bottom: 10px;
+ .wave-modal-dialog {
+ padding: 20px 0px 20px 0px;
+
+ .userinput-query {
+ margin-bottom: 10px;
+ }
}
}
}
diff --git a/src/app/common/modals/userinput.tsx b/src/app/common/modals/userinput.tsx
index 47a3c55a..19e0bbee 100644
--- a/src/app/common/modals/userinput.tsx
+++ b/src/app/common/modals/userinput.tsx
@@ -1,7 +1,7 @@
import * as React from "react";
import { GlobalModel } from "@/models";
import { Choose, When, If } from "tsx-control-statements/components";
-import { Modal, PasswordField, Markdown } from "@/elements";
+import { Modal, PasswordField, Markdown, Checkbox } from "@/elements";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
import "./userinput.less";
@@ -9,6 +9,7 @@ import "./userinput.less";
export const UserInputModal = (userInputRequest: UserInputRequest) => {
const [responseText, setResponseText] = React.useState("");
const [countdown, setCountdown] = React.useState(Math.floor(userInputRequest.timeoutms / 1000));
+ const checkboxStatus = React.useRef(false);
const handleSendCancel = React.useCallback(() => {
GlobalModel.sendUserInput({
@@ -24,16 +25,19 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
type: "userinputresp",
requestid: userInputRequest.requestid,
text: responseText,
+ checkboxstat: checkboxStatus.current,
});
GlobalModel.remotesModel.closeModal();
}, [responseText, userInputRequest]);
const handleSendConfirm = React.useCallback(
(response: boolean) => {
+ console.log(`checkbox ${checkboxStatus}\n\n`);
GlobalModel.sendUserInput({
type: "userinputresp",
requestid: userInputRequest.requestid,
confirm: response,
+ checkboxstat: checkboxStatus.current,
});
GlobalModel.remotesModel.closeModal();
},
@@ -71,23 +75,32 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
-
-
-
-
-
{userInputRequest.querytext}
+
+
+
+
+
+ {userInputRequest.querytext}
+
+
+
+ handleTextKeyDown(e)}
+ />
+
+
-
-
- handleTextKeyDown(e)}
- />
-
-
+
+ (checkboxStatus.current = !checkboxStatus.current)}
+ label={userInputRequest.checkboxmsg}
+ className="checkbox-text"
+ />
+
diff --git a/src/app/common/modals/viewremoteconndetail.less b/src/app/common/modals/viewremoteconndetail.less
index aee472d8..44d47417 100644
--- a/src/app/common/modals/viewremoteconndetail.less
+++ b/src/app/common/modals/viewremoteconndetail.less
@@ -1,5 +1,6 @@
.rconndetail-modal {
- width: 631px;
+ width: auto;
+ max-width: 80vw;
min-height: 565px;
.wave-modal-content {
diff --git a/src/app/connections/connections.tsx b/src/app/connections/connections.tsx
index f5c715b8..9cc73462 100644
--- a/src/app/connections/connections.tsx
+++ b/src/app/connections/connections.tsx
@@ -8,7 +8,7 @@ import { boundMethod } from "autobind-decorator";
import { If, For } from "tsx-control-statements/components";
import cn from "classnames";
import { GlobalModel, RemotesModel, GlobalCommandRunner } from "@/models";
-import { Button, Status, ShowWaveShellInstallPrompt } from "@/common/elements";
+import { Button, Status } from "@/common/elements";
import * as util from "@/util/util";
import "./connections.less";
@@ -71,14 +71,9 @@ class ConnectionsView extends React.Component<{ model: RemotesModel }, { hovered
GlobalModel.remotesModel.openAddModal({ remoteedit: true });
}
- @boundMethod
- importSshConfig(): void {
- GlobalCommandRunner.importSshConfig();
- }
-
@boundMethod
handleImportSshConfig(): void {
- ShowWaveShellInstallPrompt(this.importSshConfig);
+ GlobalCommandRunner.importSshConfig();
}
@boundMethod
diff --git a/src/types/custom.d.ts b/src/types/custom.d.ts
index 0c5018b8..4395ca3b 100644
--- a/src/types/custom.d.ts
+++ b/src/types/custom.d.ts
@@ -666,6 +666,7 @@ declare global {
title: string;
markdown: boolean;
timeoutms: number;
+ checkboxmsg: string;
};
type UserInputResponsePacket = {
@@ -674,6 +675,7 @@ declare global {
text?: string;
confirm?: boolean;
errormsg?: string;
+ checkboxstat?: boolean;
};
type RenderModeType = "normal" | "collapsed" | "expanded";
diff --git a/wavesrv/pkg/cmdrunner/cmdrunner.go b/wavesrv/pkg/cmdrunner/cmdrunner.go
index 9291203a..3af7379b 100644
--- a/wavesrv/pkg/cmdrunner/cmdrunner.go
+++ b/wavesrv/pkg/cmdrunner/cmdrunner.go
@@ -2202,7 +2202,7 @@ func NewHostInfo(hostName string) (*HostInfoType, error) {
func RemoteConfigParseCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) {
home := base.GetHomeDir()
localConfig := filepath.Join(home, ".ssh", "config")
- systemConfig := filepath.Join("/", "ssh", "config")
+ systemConfig := filepath.Join("/etc", "ssh", "config")
sshConfigFiles := []string{localConfig, systemConfig}
ssh_config.ReloadConfigs()
hostPatterns, hostPatternsErr := resolveSshConfigPatterns(sshConfigFiles)
diff --git a/wavesrv/pkg/remote/remote.go b/wavesrv/pkg/remote/remote.go
index 9d51f9d7..07f61dec 100644
--- a/wavesrv/pkg/remote/remote.go
+++ b/wavesrv/pkg/remote/remote.go
@@ -1202,14 +1202,54 @@ func (msh *MShellProc) RunInstall(autoInstall bool) {
msh.WriteToPtyBuffer("*error: cannot install on archived remote\n")
return
}
- if autoInstall {
+
+ var makeClientCtx context.Context
+ var makeClientCancelFn context.CancelFunc
+ msh.WithLock(func() {
+ makeClientCtx, makeClientCancelFn = context.WithCancel(context.Background())
+ msh.MakeClientCancelFn = makeClientCancelFn
+ msh.MakeClientDeadline = nil
+ go msh.NotifyRemoteUpdate()
+ })
+ defer makeClientCancelFn()
+ clientData, err := sstore.EnsureClientData(makeClientCtx)
+ if err != nil {
+ msh.WriteToPtyBuffer("*error: cannot obtain client data: %v", err)
+ return
+ }
+ hideShellPrompt := clientData.ClientOpts.ConfirmFlags["hideshellprompt"]
+ baseStatus := msh.GetStatus()
+
+ if baseStatus == StatusConnected {
+ ctx, cancelFn := context.WithTimeout(makeClientCtx, 60*time.Second)
+ defer cancelFn()
+ request := &userinput.UserInputRequestType{
+ ResponseType: "confirm",
+ QueryText: "Waveshell is running on your connection and must be restarted to re-install. Would you like to continue?",
+ Title: "Restart Waveshell",
+ }
+ response, err := userinput.GetUserInput(ctx, scbus.MainRpcBus, request)
+ if err != nil {
+ if err == context.Canceled {
+ msh.WriteToPtyBuffer("installation canceled by user\n")
+ } else {
+ msh.WriteToPtyBuffer("timed out waiting for user input\n")
+ }
+ return
+ }
+ if !response.Confirm {
+ msh.WriteToPtyBuffer("installation canceled by user\n")
+ return
+ }
+ } else if !hideShellPrompt {
+ ctx, cancelFn := context.WithTimeout(makeClientCtx, 60*time.Second)
+ defer cancelFn()
request := &userinput.UserInputRequestType{
ResponseType: "confirm",
QueryText: "Waveshell must be reinstalled on the connection to continue. Would you like to install it?",
Title: "Install Waveshell",
+ CheckBoxMsg: "Don't show me this again",
}
- ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
- defer cancelFn()
response, err := userinput.GetUserInput(ctx, scbus.MainRpcBus, request)
if err != nil {
var errMsg error
@@ -1234,28 +1274,24 @@ func (msh *MShellProc) RunInstall(autoInstall bool) {
})
return
}
- }
- baseStatus := msh.GetStatus()
- if baseStatus == StatusConnected {
- request := &userinput.UserInputRequestType{
- ResponseType: "confirm",
- QueryText: "Waveshell is running on your connection and must be restarted to re-install. Would you like to continue?",
- Title: "Restart Waveshell",
- }
- ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
- defer cancelFn()
- response, err := userinput.GetUserInput(ctx, scbus.MainRpcBus, request)
- if err != nil {
- if err == context.Canceled {
- msh.WriteToPtyBuffer("installation canceled by user\n")
- } else {
- msh.WriteToPtyBuffer("timed out waiting for user input\n")
+ if response.CheckboxStat {
+ clientData.ClientOpts.ConfirmFlags["hideshellprompt"] = true
+ err = sstore.SetClientOpts(makeClientCtx, clientData.ClientOpts)
+ if err != nil {
+ msh.WriteToPtyBuffer("*error, %s\n", err)
+ msh.setErrorStatus(err)
+ return
}
- return
- }
- if !response.Confirm {
- msh.WriteToPtyBuffer("installation canceled by user\n")
- return
+
+ //reload updated clientdata before sending
+ clientData, err = sstore.EnsureClientData(makeClientCtx)
+ if err != nil {
+ msh.WriteToPtyBuffer("*error, %s\n", err)
+ msh.setErrorStatus(err)
+ return
+ }
+ update := scbus.MakeUpdatePacket()
+ update.AddUpdate(*clientData)
}
}
curStatus := msh.GetInstallStatus()
@@ -1267,14 +1303,14 @@ func (msh *MShellProc) RunInstall(autoInstall bool) {
msh.WriteToPtyBuffer("*error: cannot install on a local remote\n")
return
}
- _, err := shellapi.MakeShellApi(packet.ShellType_bash)
+ _, err = shellapi.MakeShellApi(packet.ShellType_bash)
if err != nil {
msh.WriteToPtyBuffer("*error: %v\n", err)
return
}
if msh.Client == nil {
remoteDisplayName := fmt.Sprintf("%s [%s]", remoteCopy.RemoteAlias, remoteCopy.RemoteCanonicalName)
- client, err := ConnectToClient(remoteCopy.SSHOpts, remoteDisplayName)
+ client, err := ConnectToClient(makeClientCtx, remoteCopy.SSHOpts, remoteDisplayName)
if err != nil {
statusErr := fmt.Errorf("ssh cannot connect to client: %w", err)
msh.setInstallErrorStatus(statusErr)
@@ -1481,7 +1517,7 @@ func (msh *MShellProc) getActiveShellTypes(ctx context.Context) ([]string, error
return utilfn.CombineStrArrays(rtn, activeShells), nil
}
-func (msh *MShellProc) createWaveshellSession(remoteCopy sstore.RemoteType) (shexec.ConnInterface, error) {
+func (msh *MShellProc) createWaveshellSession(clientCtx context.Context, remoteCopy sstore.RemoteType) (shexec.ConnInterface, error) {
msh.WithLock(func() {
msh.Err = nil
msh.ErrNoInitPk = false
@@ -1510,7 +1546,7 @@ func (msh *MShellProc) createWaveshellSession(remoteCopy sstore.RemoteType) (she
wsSession = shexec.CmdWrap{Cmd: ecmd}
} else if msh.Client == nil {
remoteDisplayName := fmt.Sprintf("%s [%s]", remoteCopy.RemoteAlias, remoteCopy.RemoteCanonicalName)
- client, err := ConnectToClient(remoteCopy.SSHOpts, remoteDisplayName)
+ client, err := ConnectToClient(clientCtx, remoteCopy.SSHOpts, remoteDisplayName)
if err != nil {
return nil, fmt.Errorf("ssh cannot connect to client: %w", err)
}
@@ -1559,16 +1595,6 @@ func (NewLauncher) Launch(msh *MShellProc, interactive bool) {
msh.WriteToPtyBuffer("remote is trying to install, cancel install before trying to connect again\n")
return
}
- msh.WriteToPtyBuffer("connecting to %s...\n", remoteCopy.RemoteCanonicalName)
- wsSession, err := msh.createWaveshellSession(remoteCopy)
- if err != nil {
- msh.WriteToPtyBuffer("*error, %s\n", err.Error())
- msh.setErrorStatus(err)
- msh.WithLock(func() {
- msh.Client = nil
- })
- return
- }
var makeClientCtx context.Context
var makeClientCancelFn context.CancelFunc
msh.WithLock(func() {
@@ -1578,6 +1604,16 @@ func (NewLauncher) Launch(msh *MShellProc, interactive bool) {
go msh.NotifyRemoteUpdate()
})
defer makeClientCancelFn()
+ msh.WriteToPtyBuffer("connecting to %s...\n", remoteCopy.RemoteCanonicalName)
+ wsSession, err := msh.createWaveshellSession(makeClientCtx, remoteCopy)
+ if err != nil {
+ msh.WriteToPtyBuffer("*error, %s\n", err.Error())
+ msh.setErrorStatus(err)
+ msh.WithLock(func() {
+ msh.Client = nil
+ })
+ return
+ }
cproc, err := shexec.MakeClientProc(makeClientCtx, wsSession)
msh.WithLock(func() {
msh.MakeClientCancelFn = nil
diff --git a/wavesrv/pkg/remote/sshclient.go b/wavesrv/pkg/remote/sshclient.go
index ed770ca2..7eff78ef 100644
--- a/wavesrv/pkg/remote/sshclient.go
+++ b/wavesrv/pkg/remote/sshclient.go
@@ -65,7 +65,7 @@ func createDummySigner() ([]ssh.Signer, error) {
// they were successes. An error in this function prevents any other
// keys from being attempted. But if there's an error because of a dummy
// file, the library can still try again with a new key.
-func createPublicKeyCallback(sshKeywords *SshKeywords, passphrase string) func() ([]ssh.Signer, error) {
+func createPublicKeyCallback(connCtx context.Context, sshKeywords *SshKeywords, passphrase string) func() ([]ssh.Signer, error) {
var identityFiles []string
existingKeys := make(map[string][]byte)
@@ -124,7 +124,7 @@ func createPublicKeyCallback(sshKeywords *SshKeywords, passphrase string) func()
QueryText: fmt.Sprintf("Enter passphrase for the SSH key: %s", identityFile),
Title: "Publickey Auth + Passphrase",
}
- ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
+ ctx, cancelFn := context.WithTimeout(connCtx, 60*time.Second)
defer cancelFn()
response, err := userinput.GetUserInput(ctx, scbus.MainRpcBus, request)
if err != nil {
@@ -150,11 +150,11 @@ func createDefaultPasswordCallbackPrompt(password string) func() (secret string,
}
}
-func createInteractivePasswordCallbackPrompt(remoteDisplayName string) func() (secret string, err error) {
+func createInteractivePasswordCallbackPrompt(connCtx context.Context, remoteDisplayName string) func() (secret string, err error) {
return func() (secret string, err error) {
// limited to 15 seconds for some reason. this should be investigated more
// in the future
- ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
+ ctx, cancelFn := context.WithTimeout(connCtx, 60*time.Second)
defer cancelFn()
queryText := fmt.Sprintf(
"Password Authentication requested from connection \n"+
@@ -174,13 +174,13 @@ func createInteractivePasswordCallbackPrompt(remoteDisplayName string) func() (s
}
}
-func createCombinedPasswordCallbackPrompt(password string, remoteDisplayName string) func() (secret string, err error) {
+func createCombinedPasswordCallbackPrompt(connCtx context.Context, password string, remoteDisplayName string) func() (secret string, err error) {
var once sync.Once
return func() (secret string, err error) {
var prompt func() (secret string, err error)
once.Do(func() { prompt = createDefaultPasswordCallbackPrompt(password) })
if prompt == nil {
- prompt = createInteractivePasswordCallbackPrompt(remoteDisplayName)
+ prompt = createInteractivePasswordCallbackPrompt(connCtx, remoteDisplayName)
}
return prompt()
}
@@ -199,14 +199,14 @@ func createNaiveKbdInteractiveChallenge(password string) func(name, instruction
}
}
-func createInteractiveKbdInteractiveChallenge(remoteName string) func(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
+func createInteractiveKbdInteractiveChallenge(connCtx context.Context, remoteName string) func(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
return func(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
if len(questions) != len(echos) {
return nil, fmt.Errorf("bad response from server: questions has len %d, echos has len %d", len(questions), len(echos))
}
for i, question := range questions {
echo := echos[i]
- answer, err := promptChallengeQuestion(question, echo, remoteName)
+ answer, err := promptChallengeQuestion(connCtx, question, echo, remoteName)
if err != nil {
return nil, err
}
@@ -216,10 +216,10 @@ func createInteractiveKbdInteractiveChallenge(remoteName string) func(name, inst
}
}
-func promptChallengeQuestion(question string, echo bool, remoteName string) (answer string, err error) {
+func promptChallengeQuestion(connCtx context.Context, question string, echo bool, remoteName string) (answer string, err error) {
// limited to 15 seconds for some reason. this should be investigated more
// in the future
- ctx, cancelFn := context.WithTimeout(context.Background(), 60*time.Second)
+ ctx, cancelFn := context.WithTimeout(connCtx, 60*time.Second)
defer cancelFn()
queryText := fmt.Sprintf(
"Keyboard Interactive Authentication requested from connection \n"+
@@ -238,13 +238,13 @@ func promptChallengeQuestion(question string, echo bool, remoteName string) (ans
return response.Text, nil
}
-func createCombinedKbdInteractiveChallenge(password string, remoteName string) ssh.KeyboardInteractiveChallenge {
+func createCombinedKbdInteractiveChallenge(connCtx context.Context, password string, remoteName string) ssh.KeyboardInteractiveChallenge {
var once sync.Once
return func(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
var challenge ssh.KeyboardInteractiveChallenge
once.Do(func() { challenge = createNaiveKbdInteractiveChallenge(password) })
if challenge == nil {
- challenge = createInteractiveKbdInteractiveChallenge(remoteName)
+ challenge = createInteractiveKbdInteractiveChallenge(connCtx, remoteName)
}
return challenge(name, instruction, questions, echos)
}
@@ -505,7 +505,20 @@ func createHostKeyCallback(opts *sstore.SSHOpts) (ssh.HostKeyCallback, error) {
return waveHostKeyCallback, nil
}
-func ConnectToClient(opts *sstore.SSHOpts, remoteDisplayName string) (*ssh.Client, error) {
+func DialContext(ctx context.Context, network string, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
+ d := net.Dialer{Timeout: config.Timeout}
+ conn, err := d.DialContext(ctx, network, addr)
+ if err != nil {
+ return nil, err
+ }
+ c, chans, reqs, err := ssh.NewClientConn(conn, addr, config)
+ if err != nil {
+ return nil, err
+ }
+ return ssh.NewClient(c, chans, reqs), nil
+}
+
+func ConnectToClient(connCtx context.Context, opts *sstore.SSHOpts, remoteDisplayName string) (*ssh.Client, error) {
sshConfigKeywords, err := findSshConfigKeywords(opts.SSHHost)
if err != nil {
return nil, err
@@ -516,9 +529,9 @@ func ConnectToClient(opts *sstore.SSHOpts, remoteDisplayName string) (*ssh.Clien
return nil, err
}
- publicKeyCallback := ssh.PublicKeysCallback(createPublicKeyCallback(sshKeywords, opts.SSHPassword))
- keyboardInteractive := ssh.KeyboardInteractive(createCombinedKbdInteractiveChallenge(opts.SSHPassword, remoteDisplayName))
- passwordCallback := ssh.PasswordCallback(createCombinedPasswordCallbackPrompt(opts.SSHPassword, remoteDisplayName))
+ publicKeyCallback := ssh.PublicKeysCallback(createPublicKeyCallback(connCtx, sshKeywords, opts.SSHPassword))
+ keyboardInteractive := ssh.KeyboardInteractive(createCombinedKbdInteractiveChallenge(connCtx, opts.SSHPassword, remoteDisplayName))
+ passwordCallback := ssh.PasswordCallback(createCombinedPasswordCallbackPrompt(connCtx, opts.SSHPassword, remoteDisplayName))
// batch mode turns off interactive input. this means the number of
// attemtps must drop to 1 with this setup
@@ -566,7 +579,7 @@ func ConnectToClient(opts *sstore.SSHOpts, remoteDisplayName string) (*ssh.Clien
HostKeyCallback: hostKeyCallback,
}
networkAddr := sshKeywords.HostName + ":" + sshKeywords.Port
- return ssh.Dial("tcp", networkAddr, clientConfig)
+ return DialContext(connCtx, "tcp", networkAddr, clientConfig)
}
type SshKeywords struct {
diff --git a/wavesrv/pkg/userinput/userinput.go b/wavesrv/pkg/userinput/userinput.go
index 0ab0fe41..50212acb 100644
--- a/wavesrv/pkg/userinput/userinput.go
+++ b/wavesrv/pkg/userinput/userinput.go
@@ -21,6 +21,7 @@ type UserInputRequestType struct {
Title string `json:"title"`
Markdown bool `json:"markdown"`
TimeoutMs int `json:"timeoutms"`
+ CheckBoxMsg string `json:"checkboxmsg"`
}
func (*UserInputRequestType) GetType() string {
@@ -39,11 +40,12 @@ const UserInputResponsePacketStr = "userinputresp"
// An RpcResponse for user input requests
type UserInputResponsePacketType struct {
- Type string `json:"type"`
- RequestId string `json:"requestid"`
- Text string `json:"text,omitempty"`
- Confirm bool `json:"confirm,omitempty"`
- ErrorMsg string `json:"errormsg,omitempty"`
+ Type string `json:"type"`
+ RequestId string `json:"requestid"`
+ Text string `json:"text,omitempty"`
+ Confirm bool `json:"confirm,omitempty"`
+ ErrorMsg string `json:"errormsg,omitempty"`
+ CheckboxStat bool `json:"checkboxstat,omitempty"`
}
func (*UserInputResponsePacketType) GetType() string {