xtermjs / xterm.js

A terminal for the web
https://xtermjs.org/
MIT License
17.73k stars 1.64k forks source link

ZModem support? #279

Closed mqliang closed 8 years ago

mqliang commented 8 years ago

support ZModem so that we could receive/send files using lrzsz? Does this sounds interesting?

parisk commented 8 years ago

Could you please get into some more details on how this would work on xterm.js?

mqliang commented 8 years ago

I use xterm.js to connect to a bash(a full-feature bash) and let user interact with the bash. I'd like support user to upload/download file through web terminal by using rz/sz.

mqliang commented 8 years ago

If I connect to a machine' shell with SecureCRT/Xshell, I can upload/download files with rz/sz command. I'd like use zterm.js to connect to a machine's shell, and upload/download files with rz/sz command

jerch commented 8 years ago

Zmodem is a protocol feature of is the underlying serial line abstraction of the PTY system. This is already usable with tools like zssh in xterm.js. xterm.js itself is just forwarding the master IO of the PTY to the browser. In reality you are operating on the system where the xterm.js server part is hosted.

This is quite different to a terminal emulator operating on the same machine like SecureCRT does.

So the question is - From which machine to which machine do you want to move files through the terminal? (Remember your local machine has no terminal channel to the server part of xterm.js).

mqliang commented 8 years ago
+-----------------------------------+
|       local machine               |
|   +----------------------------+  |
|   |       browser              |  |
|   |   +-------------------+    |  |               +---------------------------+          +------------------------+
|   |   |                   |    |  |               |   proxy                   |          |                        |
|   |   |   web terminal    >----------------------->   server part of xterm.js >----------| romote machine terminal|
|   |   |                   |    |  |               +---------------------------+          +------------------------+
|   |   |                   |    |  |
|   |   +-------------------+    |  |
|   +----------------------------+  |
+-----------------------------------+

I'd like to move files from local machine to remote machine. The remote machine can not be accessed by local machine directly, since it has no external ip.

Remember your local machine has no terminal channel to the server part of xterm.js

Is there any workaround for this?

jerch commented 8 years ago

Well you can use zssh (with zmodem support), sftp or scp from the proxy.

Unless someone implements it there is no way to up/download files directly from the webterminal in your browser to the remote machine. One of the modem protocols could be used that way (there is a XModem JS implementation), but since the browser has very limited filesystem access I question the usefulness. The hard part would be to provide a download interaction with the browser for big files (since the data is landing directly in the browser through the websocket, JS would have to hold all data until it can be saved.)

Another approach would be to enhance the server part with rz/sz functionality and map that transparently to the browser. This would circumvent the FS limitations of the browser but would need to hook another parser into the terminal stream to catch the 'rz\r' initialisation of the modem protocols.

FGasper commented 7 years ago

I’ve been working on a ZMODEM implementation in JavaScript and have it transferring files in both directions via xterm.js.

The browser does indeed have to buffer the entire file when it receives. :( That’s really the only downside, though, and it’s not bad unless you’re sending very large files. Uploads can be done via FileReader, and downloads happen via the “download” attribute of <a> elements.

I’m still testing and documenting it, but is ZMODEM integration a feature that would interest xterm.js?

jerch commented 7 years ago

@FGasper Nice :) Sounds promising, if you implemented it with node's stream API it could also be useful for big files as a server side extension, that proxies those files to the browser endpoint via download link.

FGasper commented 7 years ago

The library itself is platform-agnostic, so it should work anywhere. (Knock on wood.)

parisk commented 7 years ago

@FGasper sounds neat 😄!

We can definitely consider a zmodem addon, if it works in the browser.

What are usually the use cases for using zmodem?

FGasper commented 7 years ago

I use it to do file transfers within a terminal session to/from my workstation. It allows you not to have to scp/sftp or what not.

Similar to this for iTerm2: https://github.com/mmastrac/iterm2-zmodem

jerch commented 7 years ago

@parisk The X/Y/Z-MODEM protocols allow to "abuse" a terminal connection for direct file transfer from and to the other side. This is very useful if you want to orchestrate a server only through a single terminal session. For POSIX systems there are the rz/sz tools to accomplish this.

Well for xterm.js it is a perfect addon, it will raise the usefulness of xterm.js for server admins in restricted environments. :+1:

FGasper commented 7 years ago

NB: XMODEM and YMODEM both have the drawback of not “prompting” the other side that there’s a file transfer ready. (It’s unfortunate because they’re much simpler!)

ZMODEM sends what is essentially a “magic string” that can be watched for; then you prompt the user, “ZMODEM detected; proceed with file transfer?” At that point the implementation provides means either to provide files to transfer or to accept/skip files as the sender offers them.

jerch commented 7 years ago

For xterm.js it would be neat to just drag'n drop files into the terminal - lets say a file gets saved at the current terminal shell path. But thats very tricky to accomplish unless xterm.js can control the remote shell itself or at least grab the current working directory at any point. Same goes for backwards - if a ls listing could be spotted as referring to remote files, drag'n drop out of the terminal would be neat. Well just dreaming :wink:

FGasper commented 7 years ago

I don’t see why drag/drop rz wouldn’t be doable once the Zmodem session is started. It could be implemented multiple ways, but maybe:

1) Type rz into your console. 2) Drag/drop files in. 3) Start the transfer.

FGasper commented 7 years ago

Question: How set is xterm.js still on IE support?

Tyriar commented 7 years ago

No more IE support in v3 due to https://github.com/sourcelair/xterm.js/pull/938, developers should be off it anyway.

FGasper commented 7 years ago

I’d like to show you folks a demo.

I’ve been testing against my own terminal server but would like to use whatever you guys have as default.

image

^^ Am I missing something here? npm install errors out for me …

felipe@Macintosh-4 18:00:56 ~/code/p5-Net-WebSocket/demo
> sudo npm install
npm ERR! install Couldn't read dependencies
npm ERR! Darwin 16.7.0
npm ERR! argv "/opt/local/bin/node" "/opt/local/bin/npm" "install"
npm ERR! node v8.3.0
npm ERR! npm  v2.15.12
npm ERR! path /Users/felipe/package.json
npm ERR! code ENOPACKAGEJSON
npm ERR! errno -2
npm ERR! syscall open

npm ERR! package.json ENOENT: no such file or directory, open '/Users/felipe/package.json'
npm ERR! package.json This is most likely not a problem with npm itself.
npm ERR! package.json npm can't find a package.json file in your current directory.

npm ERR! Please include the following file with any support request:
npm ERR!     /Users/felipe/code/p5-Net-WebSocket/demo/npm-debug.log
jerch commented 7 years ago

Maybe you forgot git clone ...? And why with sudo?

Tyriar commented 7 years ago

@FGasper

npm ERR! package.json ENOENT: no such file or directory, open '/Users/felipe/package.json'

You're running npm install in your home directory, not the repo directory.

FGasper commented 7 years ago

Is there a way to get node-pty to output binary rather than strings? ZMODEM is a binary protocol, but the demo seems to think everything is UTF-8. In particular, something seems to be converting 0x8a to UTF-8 \ufffd

jerch commented 7 years ago

This might be a bit more involving - for a quick fix you can try to disable the encoders of node-pty or set the string type to 'binary', but this might have unwanted impacts on the websocket transfer to xterm.js. As far as I can tell the whole chain node-pty <---> websocket <---> xterm.js relies on UTF-8 bytes that get decoded to JS strings on the fly. Might need a greater patch to get binary data flowing.

FGasper commented 7 years ago

Where is that binary string type setting? (I don’t see such in node-pty?)

As best I can tell, the data is already corrupted by the time it hits app.js, so the change in behavior would have to be in node-pty.

ZMODEM does spell out an encoding that escapes all high-bit characters down to 7-bit, but it was apparently never actually implemented … Forsberg probably figured it would never be needed as all the lines became 8-bit safe, heh. :)

jerch commented 7 years ago

You can try with {encoding: 'binary'} in ctor options or .setEncoding('binary') on the fly.

FGasper commented 7 years ago

It looks like the most recent node-pty allows setting or not setting UTF8 mode …

  if (info[8]->ToBoolean()->Value()) {
#if defined(IUTF8)
    term->c_iflag |= IUTF8;
#endif
}

https://github.com/Tyriar/node-pty/blob/master/src/unix/pty.cc

FGasper commented 7 years ago

Hm … the node-pty that xterm.js pulls in (0.4.1) is pretty old … maybe the most recent version would include that flag.

Calling .setEncoding('binary') doesn’t work … and neither does passing in encoding:"binary" to pty.spawn().

jerch commented 7 years ago

IUTF8 of termios does a different thing - it enables correct handling of multi byte UTF8 chars in the pty device (for line width and erase). Hmm maybe the hard way - try to remove the encoder:

delete ptyObj._socket._readableState.decoder;
delete ptyObj._socket._readableState.encoding;

This should give you buffer objects instead of strings (most likely to break upwards to xterm.js).

FGasper commented 7 years ago

OK, I got it by upgrading to node-pty 0.6.4 and setting encoding to null. (Didn’t need the _readableState stuff.) It does still send the first line of the shell session as text, but everything after is binary, so cool.

jerch commented 7 years ago

It does still send the first line of the shell session as text...

Even if you apply it to the constructor as {encoding: null}?

FGasper commented 7 years ago

Yes, even with {encoding: null} the first line is sent as a text frame.

FGasper commented 7 years ago

I’ve got this into what I think is a reasonable state to try it out.

1) Set up https://github.com/FGasper/xterm.js.git as a remote. 2) Check out that repo’s zmodem branch. 3) git submodule init; git submodule update 4) npm install (See below.) 5) npm start, then load localhost:3000 in the browser. (Chrome is what I’ve tested.) 6) ssh to a machine that has lrzsz installed. 7) Type rz, and send one or more files from your workstation to the remote. 8) sz <filename1> <filename2> … will send files in a batch to your workstation.

(The UI is minimal by design; assumedly a “real” deployment would polish it more.)

Concerning step 4: When I tested just now on my workstation I had to fix a permissions issue with the node-gyp package.

jerch commented 7 years ago

Nice, works like a charm (tested with text files in Firefox).

Just a few remarks:

FGasper commented 7 years ago

@jerch

1) Yes, it’s possible to trigger the init sequence by accident. That’s what the “Start ZMODEM session” prompt is for: the user can still back out if needs be.

2) Should be fixed now.

3) The application receives progress events in sync with the browser’s FileReader API. Chrome seems to give content with progress; however, Firefox doesn’t actually give the file content in those events. If you send yourself a suitably large file in Chrome you’ll see something like:

image

4) The weird characters at the start of a session are the printable parts of the ZMODEM receive-init sequence: ** + ASCII CAN + B01 + 10 hex characters + CR + 0x8a + XON. I agree that it’s ugly that they go to the screen; however, it’s pretty standard in ZMODEM-savvy terminals that I’ve used. I suppose it could send enough BS characters to the terminal to delete those characters? Or maybe just filter them out if they do arrive together (as I’d think they generally will). (UPDATE: “Problem” with sending BS characters is that it’ll only happen after the user accepts ZMODEM; the characters have to show on the terminal first because the user hasn’t yet confirmed that ZMODEM is what’s desired.)

There’s also a need to add a control to cancel an in-progress transfer. I’m still looking at the best way to handle that.

jerch commented 7 years ago

There’s also a need to add a control to cancel an in-progress transfer. I’m still looking at the best way to handle that.

Maybe this from the specs helps?

A ZFIN, ZABORT, or TIMEOUT terminates the session; a ZSKIP terminates the processing of
this file.
FGasper commented 7 years ago

Potentially. The spec isn’t always the most helpful thing; for example, it mentions an Attn sequence that is sent after a ZSINIT. The spec seems to suggest that that’s how to get a sender to pause a data send, but apparently nothing actually uses Attn. Likewise with the ESC8 option: it’s not actually implemented in lrzsz, and since that’s the de facto reference implementation, ESC8 is unusable—which is a shame because it would nicely work around that problem with the termios IEXTEN flag.

I’m in a bit of a time crunch on other projects right now but hope to return to this next week.

mofux commented 7 years ago

If I may add my two cents regarding the shape of this for making it an xterm plugin: I would propose to not add an UI at all, but to dispatch things through an event, similar to node streams. Something like

term.on('transfer', (transfer) => {

  // accept or reject the transfer
  transfer.accept();

  // listen for progress
  transfer.on('progress', ...);

  // data chunks arrive
  transfer.on('data', ...);

  // transfer has finished
  transfer.once('end', ...);

  // cancel transfer
  transfer.abort();

});

This way a consumer can build its own UI on top of the plugin.

FGasper commented 7 years ago

@mofux That’s how I would want this to work as well. The UI components that I’ve put into the demo are meant just to demonstrate the controls.

tsl0922 commented 7 years ago

@FGasper Good job 👍

I'm going to add ZModem support for ttyd when your api is ready for use (https://github.com/tsl0922/ttyd/issues/37), thanks for your work.

FGasper commented 7 years ago

https://www.npmjs.com/package/zmodem.js

I’ve made an ALPHA release of zmodem.js. From here I’ll look at the plugin interface for xterm.js, but anyone who wants to look at zmodem.js, please feel free to do so and let me know how it works for you.

FGasper commented 7 years ago

The ZMODEM addon is now merged, FYI.

zhengtulymGh commented 6 years ago

win7 system

$ npm run start-zmodem

> xterm@3.4.0 start-zmodem E:\test\xterm\xterm.js
> node demo/zmodem/app

App listening to http://127.0.0.1:3100

why can't open File Explorer? default

alanhe421 commented 2 years ago

win7 system

$ npm run start-zmodem

> xterm@3.4.0 start-zmodem E:\test\xterm\xterm.js
> node demo/zmodem/app

App listening to http://127.0.0.1:3100

why can't open File Explorer? default

The code is written very clearly, you can change it yourself.

just like this.

function _handle_send_session(zsession) {
  var file_el = document.getElementById('zm_files');
  file_el.click(); // simulate click event.
...
}

I forked demo https://github.com/alanhg/xterm.js

zhengtulymGh commented 2 years ago

这是来自QQ邮箱的假期自动回复邮件。   您好,来件已收到,我会尽快回复您。。。。