Skip to content

Commit

Permalink
implement trzsz server ( trz / tsz )
Browse files Browse the repository at this point in the history
  • Loading branch information
lonnywong committed Jun 5, 2022
1 parent 2eb718b commit 7dececd
Show file tree
Hide file tree
Showing 11 changed files with 537 additions and 79 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ trzsz.exe ssh x.x.x.x

### on Server

*The `Go` version is under development. Please use the `Python` version instead. GitHub: https://github.com/trzsz/trzsz*

Similar to lrzsz ( rz / sz ), command `trz` to upload files, command `tsz /path/to/file` to download files.

For more information, see the website of trzsz: [https://trzsz.github.io](https://trzsz.github.io/).
Expand Down Expand Up @@ -81,8 +79,9 @@ DefaultDownloadPath = /Users/username/Downloads/
* `Git Bash` should have winpty installed, no need to install it manually.
* Add `winpty` before `trzsz`, e.g.: `winpty trzsz ssh x.x.x.x`.

* `/usr/bin/ssh` in [MSYS2](https://www.msys2.org/) or [Cygwin](https://www.cygwin.com/) is not supported yet.
* Use the [OpenSSH](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse) instead, e.g.: `winpty trzsz /c/Windows/System32/OpenSSH/ssh.exe x.x.x.x`.
* `/usr/bin/ssh` in [MSYS2](https://www.msys2.org/) and [Cygwin](https://www.cygwin.com/) is not supported yet, use the [OpenSSH](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse) instead.
* in `MSYS2`, e.g.: `winpty trzsz /c/Windows/System32/OpenSSH/ssh.exe x.x.x.x`.
* in `Cygwin`, e.g.: `trzsz "C:\Windows\System32\OpenSSH\ssh.exe" x.x.x.x`.


## Screenshot
Expand Down
3 changes: 3 additions & 0 deletions trzsz/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ func (b *TrzszBuffer) readLineOnWindows(timeout <-chan time.Time) ([]byte, error
} else {
b.nextIdx += len(buf)
}
if bytes.IndexByte(buf, '\x03') >= 0 { // `ctrl + c` to interrupt
return nil, newTrzszError("Interrupted")
}
for _, c := range buf {
if skipVT100 {
if isVT100End(c) {
Expand Down
115 changes: 112 additions & 3 deletions trzsz/comm.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,24 @@ import (
"encoding/base64"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"os/signal"
"path/filepath"
"regexp"
"runtime"
"runtime/debug"
"strconv"
"strings"
"syscall"
)

var is_windows bool = (runtime.GOOS == "windows")
var isWindows bool = (runtime.GOOS == "windows")

func IsWindows() bool {
return is_windows
return isWindows
}

type PtyIO interface {
Expand All @@ -64,6 +68,15 @@ type BufferSize struct {
Size int64
}

type Args struct {
Quiet bool `arg:"-q" help:"quiet (hide progress bar)"`
Overwrite bool `arg:"-y" help:"yes, overwrite existing file(s)"`
Binary bool `arg:"-b" help:"binary transfer mode, faster for binary files"`
Escape bool `arg:"-e" help:"escape all known control characters"`
Bufsize BufferSize `arg:"-B" placeholder:"N" default:"10M" help:"max buffer chunk size (1K<=N<=1G). (default: 10M)"`
Timeout int `arg:"-t" placeholder:"N" default:"100" help:"timeout ( N seconds ) for each buffer chunk.\nN <= 0 means never timeout. (default: 100)"`
}

var sizeRegexp = regexp.MustCompile("(?i)^(\\d+)(b|k|m|g|kb|mb|gb)?$")

func (b *BufferSize) UnmarshalText(buf []byte) error {
Expand Down Expand Up @@ -158,7 +171,7 @@ func (e *TrzszError) Error() string {
}

func (e *TrzszError) isTraceBack() bool {
if e.errType == "fail" {
if e.errType == "fail" || e.errType == "EXIT" {
return false
}
return e.trace
Expand Down Expand Up @@ -221,3 +234,99 @@ func getNewName(path, name string) (string, error) {
}
return "", newTrzszError("Fail to assign new file name")
}

type TmuxMode int

const (
NoTmux = iota
TmuxNormalMode
TmuxControlMode
)

func checkTmux() (TmuxMode, *os.File, int, error) {
if _, tmux := os.LookupEnv("TMUX"); !tmux {
return NoTmux, os.Stdout, -1, nil
}

cmd := exec.Command("tmux", "display-message", "-p", "#{client_tty}:#{client_control_mode}:#{pane_width}")
cmd.Stdin = os.Stdin
out, err := cmd.Output()
if err != nil {
return 0, nil, -1, err
}

output := strings.TrimSpace(string(out))
tokens := strings.Split(output, ":")
if len(tokens) != 3 {
return 0, nil, -1, fmt.Errorf("tmux unexpect output: %s", output)
}
tmuxTty, controlMode, paneWidth := tokens[0], tokens[1], tokens[2]

if controlMode == "1" || tmuxTty[0] != '/' {
return TmuxControlMode, os.Stdout, -1, nil
}
if _, err := os.Stat(tmuxTty); errors.Is(err, os.ErrNotExist) {
return TmuxControlMode, os.Stdout, -1, nil
}

tmuxStdout, err := os.OpenFile(tmuxTty, os.O_WRONLY, 0)
if err != nil {
return 0, nil, -1, err
}
tmuxPaneWidth := -1
if len(paneWidth) > 0 {
tmuxPaneWidth, err = strconv.Atoi(paneWidth)
if err != nil {
return 0, nil, -1, err
}
}
return TmuxNormalMode, tmuxStdout, tmuxPaneWidth, nil
}

func getTerminalColumns() int {
cmd := exec.Command("stty", "size")
cmd.Stdin = os.Stdin
out, err := cmd.Output()
if err != nil {
return 0
}
output := strings.TrimSpace(string(out))
tokens := strings.Split(output, " ")
if len(tokens) != 2 {
return 0
}
cols, _ := strconv.Atoi(tokens[1])
return cols
}

func reverseString(s string) string {
rns := []rune(s)
for i, j := 0, len(rns)-1; i < j; i, j = i+1, j-1 {
rns[i], rns[j] = rns[j], rns[i]
}
return string(rns)
}

func wrapStdinInput(transfer *TrzszTransfer) {
const bufSize = 10240
buffer := make([]byte, bufSize)
for {
n, err := os.Stdin.Read(buffer)
if err == io.EOF {
transfer.stopTransferringFiles()
} else {
buf := buffer[0:n]
transfer.addReceivedData(buf)
buffer = make([]byte, bufSize)
}
}
}

func handleServerSignal(transfer *TrzszTransfer) {
sigstop := make(chan os.Signal, 1)
signal.Notify(sigstop, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigstop
transfer.stopTransferringFiles()
}()
}
32 changes: 32 additions & 0 deletions trzsz/escape.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,43 @@ SOFTWARE.
package trzsz

import (
"bytes"
"fmt"
"strconv"

"golang.org/x/text/encoding/charmap"
)

type unicode string

func (s unicode) MarshalJSON() ([]byte, error) {
b := new(bytes.Buffer)
b.WriteByte('"')
for _, c := range s {
if c < 128 && strconv.IsPrint(c) {
b.WriteRune(c)
} else {
b.WriteString(fmt.Sprintf("\\u%04x", c))
}
}
b.WriteByte('"')
return b.Bytes(), nil
}

func getEscapeChars(escapeAll bool) [][]unicode {
escapeChars := [][]unicode{
{"\u00ee", "\u00ee\u00ee"},
{"\u007e", "\u00ee\u0031"},
}
if escapeAll {
const chars = unicode("\x02\x10\x1b\x1d\u009d")
for i, c := range chars {
escapeChars = append(escapeChars, []unicode{unicode(c), "\u00ee" + unicode(byte(i+0x41))})
}
}
return escapeChars
}

func escapeCharsToCodes(escapeChars []interface{}) ([][]byte, error) {
escapeCodes := make([][]byte, len(escapeChars))
encoder := charmap.ISO8859_1.NewEncoder()
Expand Down
2 changes: 1 addition & 1 deletion trzsz/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func (p *TextProgressBar) onDone(name string) {

func (p *TextProgressBar) showProgress() {
now := time.Now()
if p.lastUpdateTime != nil && now.Sub(*p.lastUpdateTime) < time.Duration(500)*time.Millisecond {
if p.lastUpdateTime != nil && now.Sub(*p.lastUpdateTime) < 500*time.Millisecond {
return
}
p.lastUpdateTime = &now
Expand Down
4 changes: 4 additions & 0 deletions trzsz/pty_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ func (t *TrzszPty) Wait() {
t.cmd.Wait()
}

func (t *TrzszPty) Terminate() {
t.cmd.Process.Signal(syscall.SIGTERM)
}

func (t *TrzszPty) ExitCode() int {
return t.cmd.ProcessState.ExitCode()
}
11 changes: 10 additions & 1 deletion trzsz/pty_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type TrzszPty struct {
outMode uint32
width int
height int
closed bool
exitCode *uint32
}

Expand Down Expand Up @@ -130,7 +131,7 @@ func Spawn(name string, args ...string) (*TrzszPty, error) {
return nil, err
}

return &TrzszPty{cpty, cpty, cpty, inMode, outMode, width, height, nil}, nil
return &TrzszPty{cpty, cpty, cpty, inMode, outMode, width, height, false, nil}, nil
}

func (t *TrzszPty) OnResize(cb func(int)) {
Expand All @@ -141,15 +142,23 @@ func (t *TrzszPty) GetColumns() (int, error) {
}

func (t *TrzszPty) Close() {
if t.closed {
return
}
t.cpty.Close()
resetVirtualTerminal(t.inMode, t.outMode)
t.closed = true
}

func (t *TrzszPty) Wait() {
code, _ := t.cpty.Wait(context.Background())
t.exitCode = &code
}

func (t *TrzszPty) Terminate() {
t.Close()
}

func (t *TrzszPty) ExitCode() int {
if t.exitCode == nil {
return 0
Expand Down
Loading

0 comments on commit 7dececd

Please sign in to comment.