Index: Doc/lib/libtelnetlib.tex =================================================================== --- Doc/lib/libtelnetlib.tex (revision 54211) +++ Doc/lib/libtelnetlib.tex (arbetskopia) @@ -37,6 +37,9 @@ raise \exception{EOFError} when the end of the connection is read, because they can return an empty string for other reasons. See the individual descriptions below. + +Option negotiation is not done within telnetlib as options are +situation sensitive and should be done at the application level. \end{classdesc} @@ -104,10 +107,21 @@ \end{methoddesc} \begin{methoddesc}{read_sb_data}{} -Return the data collected between a SB/SE pair (suboption begin/end). -The callback should access these data when it was invoked with a -\code{SE} command. This method never blocks. +Return the subsection data collected between a SB/SE pair (suboption +begin/end). \method{handle_option} should access these data when it is +invoked with a \code{SE} command. This method never blocks. +A caller of this method must be aware of the following: + +\begin{itemize} +\item Any subsection data currently buffered is deleted at the + reception of a SB. +\item This method will only return anything meaningful after reception + of a SE. +\item The buffered subsection data is deleted after this method have + been called. +\end{itemize} + \versionadded{2.3} \end{methoddesc} @@ -178,11 +192,15 @@ results are indeterministic, and may depend on the I/O timing. \end{methoddesc} -\begin{methoddesc}{set_option_negotiation_callback}{callback} -Each time a telnet option is read on the input flow, this -\var{callback} (if set) is called with the following parameters : -callback(telnet socket, command (DO/DONT/WILL/WONT), option). No other -action is done afterwards by telnetlib. +\begin{methoddesc}{handle_option}{command, option} +This method is called internally each time a telnet option is read on +the input flow. \var{option} will be \code{chr(0)} when there is no +option. No other action is done afterwards by telnetlib. + +By default, all option negotiation requests are refused. Override +this method to support specific options. + +\versionadded{3.0} \end{methoddesc} @@ -213,3 +231,36 @@ print tn.read_all() \end{verbatim} + +An example of subclassing Telnet to handle specific options: + +\begin{verbatim} +from telnetlib import Telnet, IAC, DO, DONT, WILL, WONT, SE + +SGA = chr(3) # suppress go-ahead option, RFC 858 +TTYPE = chr(24) # terminal type option, RFC 1091 + +# Used for TTYPE +SEND = chr(1) +IS = chr(0) + +class TelnetWithOptions(Telnet): + def handle_option(self, cmd, opt): + if cmd == DO and opt == TTYPE: + self.msg('IAC DO TTYPE') + self.msg('send IAC WILL TTYPE') + self.sock.sendall(IAC + WILL + TTYPE) + elif cmd == WILL and opt == SGA: + self.msg('IAC WILL SGA') + self.msg('send IAC DO SGA') + self.sock.sendall(IAC + DO + SGA) + elif cmd == SE: + parms = self.read_sb_data() + self.msg('IAC SB %d %s IAC SE', ord(opt), `parms`) + if opt == TTYPE and parms[0] == SEND: + self.msg('send IAC SB TTYPE IS vt100 IAC SE') + self.sock.sendall(IAC + SB + TTYPE + IS + 'vt100' + IAC + SE) + else: + # Forward everything else to the base class + Telnet.handle_option(self, cmd, opt) +\end{verbatim} Index: Lib/telnetlib.py =================================================================== --- Lib/telnetlib.py (revision 54211) +++ Lib/telnetlib.py (arbetskopia) @@ -25,8 +25,10 @@ "connection closed" (since the socket also appears ready for reading when it is closed). +Option negotiation is not done within telnetlib as options are +situation sensitive and should be done at the application level. + To do: -- option negotiation - timeout should be intrinsic to the connection object instead of an option on one of the read calls only @@ -48,12 +50,13 @@ # Telnet protocol characters (don't change) IAC = chr(255) # "Interpret As Command" -DONT = chr(254) -DO = chr(253) -WONT = chr(252) -WILL = chr(251) +DONT = chr(254) # "Please dont do ..." +DO = chr(253) # "Please do ..." +WONT = chr(252) # "No, we will not do ..." +WILL = chr(251) # "Yes, we will do ..." theNULL = chr(0) +EORi = chr(239) # End of Record indication SE = chr(240) # Subnegotiation End NOP = chr(241) # No Operation DM = chr(242) # Data Mark @@ -175,13 +178,14 @@ read_sb_data() Reads available data between SB ... SE sequence. Don't block. - set_option_negotiation_callback(callback) - Each time a telnet option is read on the input flow, this callback - (if set) is called with the following parameters : - callback(telnet socket, command, option) - option will be chr(0) when there is no option. - No other action is done afterwards by telnetlib. + handle_option(command, option) + This method is called internally each time a telnet option is + read on the input flow. Option will be chr(0) when there is no + option. No other action is done afterwards by telnetlib. + By default, all option negotiation requests are refused. + Override this method to support specific options. + """ def __init__(self, host=None, port=0): @@ -203,7 +207,6 @@ self.iacseq = '' # Buffer for IAC sequence. self.sb = 0 # flag for SB and SE sequence. self.sbdataq = '' - self.option_callback = None if host is not None: self.open(host, port) @@ -419,10 +422,6 @@ self.sbdataq = '' return buf - def set_option_negotiation_callback(self, callback): - """Provide a callback function called after each receipt of a telnet option.""" - self.option_callback = callback - def process_rawq(self): """Transfer from raw queue to cooked queue. @@ -461,33 +460,16 @@ self.sb = 0 self.sbdataq = self.sbdataq + buf[1] buf[1] = '' - if self.option_callback: - # Callback is supposed to look into - # the sbdataq - self.option_callback(self.sock, c, NOOPT) + opt = self.sbdataq[0] + self.sbdataq = self.sbdataq[1:] + self.handle_option(c, opt) else: - # We can't offer automatic processing of - # suboptions. Alas, we should not get any - # unless we did a WILL/DO before. - self.msg('IAC %d not recognized' % ord(c)) + self.handle_option(c, NOOPT) elif len(self.iacseq) == 2: cmd = self.iacseq[1] self.iacseq = '' opt = c - if cmd in (DO, DONT): - self.msg('IAC %s %d', - cmd == DO and 'DO' or 'DONT', ord(opt)) - if self.option_callback: - self.option_callback(self.sock, cmd, opt) - else: - self.sock.sendall(IAC + WONT + opt) - elif cmd in (WILL, WONT): - self.msg('IAC %s %d', - cmd == WILL and 'WILL' or 'WONT', ord(opt)) - if self.option_callback: - self.option_callback(self.sock, cmd, opt) - else: - self.sock.sendall(IAC + DONT + opt) + self.handle_option(cmd, opt) except EOFError: # raised by self.rawq_getchar() self.iacseq = '' # Reset on EOF self.sb = 0 @@ -530,6 +512,29 @@ self.eof = (not buf) self.rawq = self.rawq + buf + def handle_option(self, command, option): + if command in (DO, DONT): + self.msg('IAC %s %d', + command == DO and 'DO' or 'DONT', ord(option)) + self.msg('send IAC WONT %d', ord(option)) + self.sock.sendall(IAC + WONT + option) + elif command in (WILL, WONT): + self.msg('IAC %s %d', + command == WILL and 'WILL' or 'WONT', ord(option)) + self.msg('send IAC DONT %d', ord(option)) + self.sock.sendall(IAC + DONT + option) + elif command == SB: + # Should never see SB, we only get called after SE + # arrives. + pass + elif command == SE: + parms = self.read_sb_data() + self.msg('IAC SB %d %r IAC SE', ord(option), parms) + elif command == NOP: + self.msg('IAC NOP') + else: + self.msg('IAC %d not recognized', ord(command)) + def sock_avail(self): """Test whether data is available on the socket.""" return select.select([self], [], [], 0) == ([self], [], [])