module BBS::Telnet
The telnet line I/O of a BBS::Session: option negotiation, key and line input with telnet commands filtered out (Telnet::Parser), ANSI escape and mouse report decoding (Telnet::Keys, Telnet::Mouse), line editing (Telnet::LineInput), window size (NAWS) tracking, and the PETSCII mode for Commodore 64 callers. The including class keeps the socket in +@client+.
Constants
- DEFAULT_COLS
-
The terminal width assumed while the caller has not reported one.
- DEFAULT_ROWS
-
The terminal height assumed while the caller has not reported one.
- MAX_COLS
-
The widest terminal accepted from a NAWS report, in columns.
- MAX_ROWS
-
The tallest terminal accepted from a NAWS report, in rows.
- MAX_SUBNEGOTIATION
-
The most bytes of a subnegotiation kept; the rest is read and dropped.
- MIN_COLS
-
The narrowest terminal accepted from a NAWS report, in columns.
- MIN_ROWS
-
The shortest terminal accepted from a NAWS report, in rows.
- NAWS_SIZE
-
The bytes of a NAWS report: width and height, two bytes each.
- NEGOTIATION
-
The opening negotiation: the server will echo and suppress go-ahead, and asks the client to suppress go-ahead and report its window size.
Public Instance Methods
Source
# File lib/bbs/telnet.rb, line 101 def line = @client
Returns the caller’s socket itself, for a door that hands it on as it is (BBS::Gateway).
Source
# File lib/bbs/telnet.rb, line 63 def naws_reported? = @naws_reported == true
Returns true once the client has reported its window size (telnet NAWS). Real terminals do; port scanners and password-guessing bots usually do not.
Source
# File lib/bbs/telnet.rb, line 53 def negotiate @term_cols = DEFAULT_COLS @term_rows = DEFAULT_ROWS send_raw Ansi::CRLF send_raw NEGOTIATION end
Sets the terminal size to 80x24 until the client reports its own, and sends the opening NEGOTIATION.
A line end goes first: the UltimateTerm of the C64 Ultimate takes a line whose first byte is a telnet command (IAC) for ASCII with ANSI and translates every byte from ASCII to PETSCII, which turns CLR into nothing and swaps the case of letters both ways; any other first byte leaves it on PETSCII, printing the bytes as they come.
Source
# File lib/bbs/telnet.rb, line 86 def petscii! return if @petscii @mouse_enabled = false @term_cols = Petscii::COLS @term_rows = Petscii::ROWS send_raw Petscii.bytes("#{Petscii::LOWERCASE}#{Petscii::LOCK_CASE}#{Petscii::CLEAR}") @petscii = Petscii::Encoder.new end
Switches the line to PETSCII for a Commodore 64 (see BBS::Petscii): sets the size to 40x25, sends the codes for the lowercase character set (locked) and a clear screen, and turns mouse reporting off. From then on write encodes its output and readkey / readline decode PETSCII keys. Does nothing when the line is already in PETSCII mode.
Source
# File lib/bbs/telnet.rb, line 97 def petscii? = !@petscii.nil?
True when the line is in PETSCII mode (see petscii!).
Source
# File lib/bbs/telnet.rb, line 107 def resize!(cols, rows) unless @petscii @term_cols = cols.clamp(MIN_COLS, MAX_COLS) if cols.positive? @term_rows = rows.clamp(MIN_ROWS, MAX_ROWS) if rows.positive? end @naws_reported = true end
Sets the caller’s window size from a NAWS report: cols is clamped to MIN_COLS..MAX_COLS and rows to MIN_ROWS..MAX_ROWS, a zero value is ignored, and a PETSCII line keeps 40x25. Marks the size as reported (naws_reported?).
Source
# File lib/bbs/telnet.rb, line 69 def settle(timeout: 1.5) deadline = monotonic + timeout until naws_reported? left = deadline - monotonic raw = read_byte(left) if left.positive? break unless raw.is_a?(String) telnet_data(raw.ord) end self if naws_reported? end
Waits up to timeout seconds for the client’s answers to the negotiation, its window size above all, and drops whatever it types meanwhile: a bot’s “root” sent before any prompt goes nowhere. Returns self when the window size came, nil when it did not.
Source
# File lib/bbs/telnet.rb, line 119 def write(data) send_raw(@petscii ? @petscii.encode(data) : data) rescue IOError raise rescue StandardError nil end
Writes data (a String) to the caller, encoded to PETSCII on a PETSCII line. Returns nil when the write fails, except for a caller who stopped reading: when the write times out (IO::TimeoutError) the line is closed and IOError is raised, which ends the session.
Source
# File lib/bbs/telnet.rb, line 130 def write_raw(data) = send_raw(data)
Writes the bytes of data to the caller as they are, not encoded to PETSCII even on a PETSCII line: for binary protocols (BBS::Zmodem, BBS::Xmodem) and telnet commands. Fails as write does.