def inputcooker_store_queue(self, char): """Put the cooked data in the input queue (no locking needed)""" if type(char) in [type(()), type([]), type(b"")]: for v in char: self.cookedq.put(chr_py3(v)) else: self.cookedq.put(chr_py3(char))
def _readline_insert(self, char, echo, insptr, line): """Deal properly with inserted chars in a line.""" if not self._readline_do_echo(echo): return # Write out the remainder of the line self.write(char + b''.join([chr_py3(i) for i in line[insptr:]])) # Cursor Left to the current insert point char_count = len(line) - insptr self.write(self.CODES['CSRLEFT'] * char_count)
def process(self, line): """Step through the line and process each character""" self.raw = self.raw + line try: if chr_py3(line[-1]) != self.eol_char: # Should always be here, but add it just in case. line = line + self.eol_char except IndexError: # Thrown if line == '' line = self.eol_char for char in line: if chr_py3(char) == self.escape_char: # Always handle escaped characters. self.last_process_char = self.process_char self.process_char = self.process_escape continue self.process_char(chr_py3(char)) if not self.complete: # Ask for more. self.process( self.handler.readline(prompt=self.handler.CONTINUE_PROMPT, echo=True))
def process_delimiter(self, char): """Process chars while not in a part""" char = chr_py3(char) if char in self.whitespace: return if char in self.quote_chars: # Store the quote type (' or ") and switch to quote processing. self.inquote = char self.process_char = self.process_quote return if char == self.eol_char: self.complete = True return # Switch to processing a part. self.process_char = self.process_part self.process_char(char)
def _inputcooker_getc(self, block=True): """Get one character from the raw queue. Optionally blocking. Raise EOFError on end of stream. SHOULD ONLY BE CALLED FROM THE INPUT COOKER.""" if self.rawq: ret = self.rawq[0] self.rawq = self.rawq[1:] return chr_py3(ret) if not block: if not self.inputcooker_socket_ready(): return b'' ret = self.sock.recv(20) self.eof = not ret self.rawq = self.rawq + ret if self.eof: raise EOFError return self._inputcooker_getc(block)
def process_part(self, char): """Process chars while in a part""" char = chr_py3(char) if char in self.whitespace or char == self.eol_char: # End of the part. self.parts.append(b''.join(self.part)) self.part = [] # Switch back to processing a delimiter. self.process_char = self.process_delimiter if char == self.eol_char: self.complete = True return if char in self.quote_chars: # Store the quote type (' or ") and switch to quote processing. self.inquote = char self.process_char = self.process_quote return self.part.append(char)
def inputcooker(self): """Input Cooker - Transfer from raw queue to cooked queue. Set self.eof when connection is closed. Don't block unless in the midst of an IAC sequence. """ try: while True: c = self._inputcooker_getc() if not self.iacseq: if c == IAC: self.iacseq += c continue elif c == chr_py3(13) and not self.sb: c2 = self._inputcooker_getc(block=False) if c2 == theNULL or c2 == b'': c = chr_py3(10) elif c2 == chr_py3(10): c = c2 else: self._inputcooker_ungetc(c2) c = chr_py3(10) elif c in [x[0] for x in list(self.ESCSEQ.keys())]: 'Looks like the begining of a key sequence' codes = c for keyseq in list(self.ESCSEQ.keys()): if len(keyseq) == 0: continue while codes == keyseq[:len(codes)] and len( codes) <= keyseq: if codes == keyseq: c = self.ESCSEQ[keyseq] break codes = codes + self._inputcooker_getc() if codes == keyseq: break self._inputcooker_ungetc(codes[1:]) codes = codes[0] self._inputcooker_store(c) elif len(self.iacseq) == 1: 'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]' if c in (DO, DONT, WILL, WONT): self.iacseq += c continue self.iacseq = b'' if c == IAC: self._inputcooker_store(c) else: if c == SB: # SB ... SE start. self.sb = 1 self.sbdataq = b'' elif c == SE: # SB ... SE end. self.sb = 0 # Callback is supposed to look into # the sbdataq self.options_handler(self.sock, c, NOOPT) elif len(self.iacseq) == 2: cmd = self.iacseq[1] self.iacseq = b'' if cmd in (DO, DONT, WILL, WONT): self.options_handler(self.sock, cmd, c) except (EOFError, socket.error): pass
def write(self, text): """Send a packet to the socket. This function cooks output.""" text = str_to_bytes(text) # eliminate any unicode or other snigglets text = text.replace(IAC, IAC + IAC) text = text.replace(chr_py3(10), chr_py3(13) + chr_py3(10)) self.writecooked(text)
def writemessage(self, text): """Write out an asynchr_py3onous message, then reconstruct the prompt and entered text.""" log.debug('writing message %r', text) self.write(chr_py3(10) + str_to_bytes(text) + chr_py3(10)) self.write(self._current_prompt + b''.join([chr_py3(i) for i in self._current_line]))
def writeline(self, text): """Send a packet with line ending.""" text = str_to_bytes(text) if isinstance(text, str) else text log.debug('writing line %r' % text) self.write(text + chr_py3(10))
def readline(self, echo=None, prompt='', use_history=True): """Return a line of text, including the terminating LF If echo is true always echo, if echo is false never echo If echo is None follow the negotiated setting. prompt is the current prompt to write (and rewrite if needed) use_history controls if this current line uses (and adds to) the command history. """ line = [] insptr = 0 ansi = 0 histptr = len(self.history) prompt = str_to_bytes(prompt) if isinstance(prompt, str) else prompt if self.DOECHO: self.write(prompt) self._current_prompt = prompt else: self._current_prompt = b'' self._current_line = b'' while True: c = self.getc(block=True) c = self.ansi_to_curses(c) if c == theNULL: continue elif c == curses.KEY_LEFT: if insptr > 0: insptr = insptr - 1 self._readline_echo(self.CODES['CSRLEFT'], echo) else: self._readline_echo(BELL, echo) continue elif c == curses.KEY_RIGHT: if insptr < len(line): insptr = insptr + 1 self._readline_echo(self.CODES['CSRRIGHT'], echo) else: self._readline_echo(BELL, echo) continue elif c == curses.KEY_UP or c == curses.KEY_DOWN: if not use_history: self._readline_echo(BELL, echo) continue if c == curses.KEY_UP: if histptr > 0: histptr = histptr - 1 else: self._readline_echo(BELL, echo) continue elif c == curses.KEY_DOWN: if histptr < len(self.history): histptr = histptr + 1 else: self._readline_echo(BELL, echo) continue line = [] if histptr < len(self.history): line.extend(self.history[histptr]) for char in range(insptr): self._readline_echo(self.CODES['CSRLEFT'], echo) self._readline_echo(self.CODES['DEOL'], echo) self._readline_echo(b''.join([chr_py3(i) for i in line]), echo) insptr = len(line) continue elif c == chr_py3(3): self._readline_echo( b'\n' + str_to_bytes(curses.ascii.unctrl(ord(c))) + b' ABORT\n', echo) return b'' elif c == chr_py3(4): if len(line) > 0: self._readline_echo( b'\n' + str_to_bytes(curses.ascii.unctrl(ord(c))) + b' ABORT (QUIT)\n', echo) return b'' self._readline_echo( b'\n' + str_to_bytes(curses.ascii.unctrl(ord(c))) + b' QUIT\n', echo) return b'QUIT' elif c == chr_py3(10): self._readline_echo(c, echo) result = b''.join([chr_py3(i) for i in line]) if use_history: self.history.append(result) if echo is False: if prompt: self.write(chr_py3(10)) log.debug('readline: %s(hidden text)', bytes_to_str(prompt)) else: log.debug('readline: %s%r', bytes_to_str(prompt), bytes_to_str(result)) return result elif c == curses.KEY_BACKSPACE or c == chr_py3( 127) or c == chr_py3(8): if insptr > 0: self._readline_echo( self.CODES['CSRLEFT'] + self.CODES['DEL'], echo) insptr = insptr - 1 del line[insptr] else: self._readline_echo(BELL, echo) continue elif c == curses.KEY_DC: if insptr < len(line): self._readline_echo(self.CODES['DEL'], echo) del line[insptr] else: self._readline_echo(BELL, echo) continue else: if ord(c) < 32: c = curses.ascii.unctrl(ord(c)) if len(line) > insptr: self._readline_insert(c, echo, insptr, line) else: self._readline_echo(c, echo) line[insptr:insptr] = c insptr = insptr + len(c) if self._readline_do_echo(echo): self._current_line = line
Function.__doc__ should be long help Function.aliases may be a list of alternative spellings """ import socket import socketserver import sys import traceback import curses from curses import ascii, has_key from telnetsrv.utils import chr_py3, str_to_bytes, bytes_to_str import logging log = logging.getLogger(__name__) BELL = chr_py3(7) ESC = chr_py3(27) ANSI_START_SEQ = b'[' ANSI_KEY_TO_CURSES = { b'A': curses.KEY_UP, b'B': curses.KEY_DOWN, b'C': curses.KEY_RIGHT, b'D': curses.KEY_LEFT, } IAC = chr_py3(255) # "Interpret As Command" DONT = chr_py3(254) DO = chr_py3(253) WONT = chr_py3(252) WILL = chr_py3(251) theNULL = chr_py3(0)