diff --git a/tools/cmd/at/main.go b/tools/cmd/at/main.go index 5f2e51a2f..27acd4b4d 100644 --- a/tools/cmd/at/main.go +++ b/tools/cmd/at/main.go @@ -22,7 +22,6 @@ import ( "kitty/tools/tty" "kitty/tools/tui" "kitty/tools/utils" - "kitty/tools/wcswidth" ) var ProtocolVersion [3]int = [3]int{0, 20, 0} @@ -75,78 +74,23 @@ type serializer_func func(rc *utils.RemoteControlCmd) ([]byte, error) var serializer serializer_func = simple_serializer -func create_serializer(password string, encoded_pubkey string, response_timeout float64) (ans serializer_func, timeout float64, err error) { - timeout = response_timeout +func create_serializer(password string, encoded_pubkey string, io_data *rc_io_data) (err error) { + io_data.serializer = simple_serializer if password != "" { encryption_version, pubkey, err := get_pubkey(encoded_pubkey) if err != nil { - return nil, timeout, err + return err } - ans = func(rc *utils.RemoteControlCmd) (ans []byte, err error) { + io_data.serializer = func(rc *utils.RemoteControlCmd) (ans []byte, err error) { ec, err := crypto.Encrypt_cmd(rc, global_options.password, pubkey, encryption_version) ans, err = json.Marshal(ec) return } - if timeout < 120 { - timeout = 120 + if io_data.timeout < 120*time.Second { + io_data.timeout = 120 * time.Second } - return ans, timeout, nil } - return simple_serializer, timeout, nil -} - -type IOAbstraction interface { - WriteAllWithTimeout(b []byte, d time.Duration) (n int, err error) - WriteFromReader(r utils.Reader, read_timeout time.Duration, write_timeout time.Duration) (n int, err error) - ReadWithTimeout(b []byte, d time.Duration) (n int, err error) -} - -func do_io(device IOAbstraction, input utils.Reader, no_response bool, response_timeout time.Duration) (serialized_response []byte, err error) { - - _, err = device.WriteAllWithTimeout([]byte("\x1bP@kitty-cmd"), 2*time.Second) - if err != nil { - return - } - _, err = device.WriteFromReader(input, 2*time.Second, 2*time.Second) - if err != nil { - return - } - _, err = device.WriteAllWithTimeout([]byte("\x1b\\"), 2*time.Second) - if err != nil { - return - } - if no_response { - return - } - - response_received := false - cmd_prefix := []byte("@kitty-cmd") - - handle_dcs := func(b []byte) error { - if bytes.HasPrefix(b, cmd_prefix) { - response_received = true - } - serialized_response = b[len(cmd_prefix):] - return nil - } - - var p = wcswidth.EscapeCodeParser{HandleDCS: handle_dcs} - buf := make([]byte, 0, utils.DEFAULT_IO_BUFFER_SIZE) - - for !response_received { - buf = buf[:cap(buf)] - var n int - n, err = device.ReadWithTimeout(buf, response_timeout) - if err != nil { - if err == os.ErrDeadlineExceeded { - err = fmt.Errorf("Timed out while waiting for a response from kitty") - } - return - } - buf = buf[:n] - p.Parse(buf) - } - return + return nil } type ResponseData struct { @@ -176,40 +120,24 @@ type Response struct { Traceback string `json:"tb,omitempty"` } -func get_response(rc *utils.RemoteControlCmd, timeout float64) (ans *Response, err error) { - serializer, timeout, err = create_serializer(global_options.password, "", timeout) - if err != nil { - return - } - d, err := serializer(rc) - if err != nil { - return - } - var device IOAbstraction - if global_options.to_network == "" { - var term *tty.Term - term, err = tty.OpenControllingTerm(tty.SetRaw) - if err != nil { - return - } - defer term.RestoreAndClose() - device = term - } else { - err = fmt.Errorf("TODO: Implement socket IO") - return - } - r := utils.BytesReader{Data: d} - serialized_response, err := do_io(device, &r, rc.NoResponse, time.Duration(timeout*float64(time.Second))) +type rc_io_data struct { + cmd *cobra.Command + rc *utils.RemoteControlCmd + serializer serializer_func + next_block func(rc *utils.RemoteControlCmd, serializer serializer_func) (b []byte, err error) + send_keypresses bool + string_response_is_err bool + timeout time.Duration +} + +func get_response(do_io func(io_data *rc_io_data) ([]byte, error), io_data *rc_io_data) (ans *Response, err error) { + serialized_response, err := do_io(io_data) if err != nil { if err == os.ErrDeadlineExceeded { - rc.Payload = nil - rc.CancelAsync = true - rc.NoResponse = true - d, err = serializer(rc) - if err != nil { - return - } - _, err = do_io(device, &r, rc.NoResponse, 0) + io_data.rc.Payload = nil + io_data.rc.CancelAsync = true + io_data.rc.NoResponse = true + _, err = do_io(io_data) } return } @@ -227,12 +155,24 @@ func get_response(rc *utils.RemoteControlCmd, timeout float64) (ans *Response, e return } -func send_rc_command(cmd *cobra.Command, rc *utils.RemoteControlCmd, timeout float64, string_response_is_err bool) (err error) { - err = setup_global_options(cmd) +func send_rc_command(io_data *rc_io_data) (err error) { + err = setup_global_options(io_data.cmd) if err != nil { return err } - response, err := get_response(rc, timeout) + err = create_serializer(global_options.password, "", io_data) + if err != nil { + return + } + var response *Response + if global_options.to_network == "" { + response, err = get_response(do_tty_io, io_data) + if err != nil { + return + } + } else { + return fmt.Errorf("TODO: Implement socket IO") + } if err != nil || response == nil { return err } @@ -242,7 +182,7 @@ func send_rc_command(cmd *cobra.Command, rc *utils.RemoteControlCmd, timeout flo } return fmt.Errorf("%s", response.Error) } - if response.Data.is_string && string_response_is_err { + if response.Data.is_string && io_data.string_response_is_err { return fmt.Errorf("%s", response.Data.as_str) } fmt.Println(strings.TrimRight(response.Data.as_str, "\n \t")) diff --git a/tools/cmd/at/main_test.go b/tools/cmd/at/main_test.go index 755ff7b44..38f53380f 100644 --- a/tools/cmd/at/main_test.go +++ b/tools/cmd/at/main_test.go @@ -37,7 +37,8 @@ func TestCommandToJSON(t *testing.T) { } func TestRCSerialization(t *testing.T) { - serializer, _, err := create_serializer("", "", 0) + io_data := rc_io_data{} + err := create_serializer("", "", &io_data) if err != nil { t.Fatal(err) } @@ -46,7 +47,7 @@ func TestRCSerialization(t *testing.T) { Cmd: "test", Version: ver, } simple := func(expected string) { - actual, err := serializer(&rc) + actual, err := io_data.serializer(&rc) if err != nil { t.Fatal(err) } @@ -64,11 +65,11 @@ func TestRCSerialization(t *testing.T) { if err != nil { t.Fatal(err) } - serializer, _, err = create_serializer("tpw", pubkey, 0) + err = create_serializer("tpw", pubkey, &io_data) if err != nil { t.Fatal(err) } - raw, err := serializer(&rc) + raw, err := io_data.serializer(&rc) var ec utils.EncryptedRemoteControlCmd err = json.Unmarshal([]byte(raw), &ec) if err != nil { diff --git a/tools/cmd/at/template.go b/tools/cmd/at/template.go index 5b63775ec..92fcc3417 100644 --- a/tools/cmd/at/template.go +++ b/tools/cmd/at/template.go @@ -7,6 +7,8 @@ package at import ( + "time" + "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -26,21 +28,15 @@ type CMD_NAME_json_type struct { var CMD_NAME_json CMD_NAME_json_type -func create_payload_CMD_NAME(args []string) (payload map[string]interface{}, err error) { +func create_payload_CMD_NAME(io_data *rc_io_data, args []string) (err error) { return } func create_rc_CMD_NAME(args []string) (*utils.RemoteControlCmd, error) { - var err error - payload, err := create_payload_CMD_NAME(args) - if err != nil { - return nil, err - } rc := utils.RemoteControlCmd{ Cmd: "CLI_NAME", Version: ProtocolVersion, NoResponse: NO_RESPONSE_BASE, - Payload: payload, } return &rc, nil } @@ -61,7 +57,21 @@ func run_CMD_NAME(cmd *cobra.Command, args []string) (err error) { if err == nil { timeout = rt } - err = send_rc_command(cmd, rc, timeout, STRING_RESPONSE_IS_ERROR) + io_data := rc_io_data{ + cmd: cmd, + rc: rc, + timeout: time.Duration(timeout * float64(time.Second)), + string_response_is_err: STRING_RESPONSE_IS_ERROR, + next_block: func(rc *utils.RemoteControlCmd, serializer serializer_func) ([]byte, error) { + return make([]byte, 0), nil + }, + } + err = create_payload_CMD_NAME(&io_data, args) + if err != nil { + return err + } + + err = send_rc_command(&io_data) return } diff --git a/tools/cmd/at/tty_io.go b/tools/cmd/at/tty_io.go new file mode 100644 index 000000000..f2d80d1e5 --- /dev/null +++ b/tools/cmd/at/tty_io.go @@ -0,0 +1,7 @@ +// License: GPLv3 Copyright: 2022, Kovid Goyal, + +package at + +func do_tty_io(io_data *rc_io_data) (serialized_response []byte, err error) { + return +} diff --git a/tools/utils/types.go b/tools/utils/types.go index 904a00eaf..b41ff7782 100644 --- a/tools/utils/types.go +++ b/tools/utils/types.go @@ -3,13 +3,13 @@ package utils type RemoteControlCmd struct { - Cmd string `json:"cmd"` - Version [3]int `json:"version"` - NoResponse bool `json:"no_response,omitempty"` - Payload map[string]interface{} `json:"payload,omitempty"` - Timestamp int64 `json:"timestamp,omitempty"` - Password string `json:"password,omitempty"` - CancelAsync bool `json:"cancel_async,omitempty"` + Cmd string `json:"cmd"` + Version [3]int `json:"version"` + NoResponse bool `json:"no_response,omitempty"` + Payload *interface{} `json:"payload,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` + Password string `json:"password,omitempty"` + CancelAsync bool `json:"cancel_async,omitempty"` } type EncryptedRemoteControlCmd struct {