from random import choice, randrange
from socket import AF_INET, SO_REUSEADDR, SOCK_STREAM, SOL_SOCKET, socket
from stat import S_IMODE, ST_MODE
+from telnetlib import DO, DONT, ECHO, EOR, IAC, WILL, WONT
from time import asctime, sleep
class Element:
def send(self, output, eol="$(eol)"):
"""Send arbitrary text to a connected user."""
- # only when there is actual output
- #if output:
-
# start with a newline, append the message, then end
# with the optional eol string passed to this function
# and the ansi escape to return to normal text
output = replace_macros(self, output)
# wrap the text at 80 characters
- # TODO: prompt user for preferred wrap width
output = wrap_ansi_text(output, 80)
# drop the formatted output into the output queue
self.output_queue.append(output)
- # try to send the last item in the queue, remove it and
- # flag that menu display is not needed
+ # try to send the last item in the queue and remove it
try:
self.connection.send(self.output_queue[0])
- self.output_queue.remove(self.output_queue[0])
- self.menu_seen = False
+ del self.output_queue[0]
# but if we can't, that's okay too
except:
self.show_menu()
# disconnect users with the appropriate state
- if self.state == "disconnecting":
- self.quit()
+ if self.state == "disconnecting": self.quit()
# the user is unique and not flagged to disconnect
else:
# tack this on to any previous partial
self.partial_input += input_data
+ # reply to and remove any IAC negotiation codes
+ self.negotiate_telnet_options()
+
# separate multiple input lines
new_input_lines = self.partial_input.split("\n")
# iterate over the remaining lines
for line in new_input_lines:
+ # remove a trailing carriage return
+ if line.endswith("\r"): line = line.rstrip("\r")
+
+ # log non-printable characters remaining
+ removed = filter(lambda x: (x < " " or x > "~"), line)
+ if removed:
+ logline = "Non-printable characters from "
+ if self.account and self.account.get("name"): logline += self.account.get("name") + ": "
+ else: logline += "unknown user: "
+ logline += repr(removed)
+ log(logline)
+
# filter out non-printables
- line = filter(lambda x: x>=' ' and x<='~', line)
+ line = filter(lambda x: " " <= x <= "~", line)
# strip off extra whitespace
line = line.strip()
# put on the end of the queue
self.input_queue.append(line)
+ def negotiate_telnet_options(self):
+ """Reply to/remove partial_input telnet negotiation options."""
+
+ # start at the begining of the input
+ position = 0
+
+ # make a local copy to play with
+ text = self.partial_input
+
+ # as long as we haven't checked it all
+ while position < len(text):
+
+ # jump to the first IAC you find
+ position = text.find(IAC, position)
+
+ # if there wasn't an IAC in the input, skip to the end
+ if position < 0: position = len(text)
+
+ # replace a double (literal) IAC and move on
+ elif len(text) > position+1 and text[position+1] == IAC:
+ text = text.replace(IAC+IAC, IAC)
+ position += 1
+
+ # this must be an option negotiation
+ elif len(text) > position+2 and text[position+1] in (DO, DONT, WILL, WONT):
+
+ # if we turned echo off, ignore the confirmation
+ if not self.echoing and text[position+1:position+3] == DO+ECHO: pass
+
+ # we don't want to allow anything else
+ elif text[position+1] in (DO, WILL): self.send(IAC+WONT+text[position+2])
+
+ # strip the negotiation from the input
+ text = text.replace(text[position:position+3], "")
+
+ # otherwise, strip out a two-byte IAC command
+ else: text = text.replace(text[position:position+2], "")
+
+ # replace the input with our cleaned-up text
+ self.partial_input = text
+
def can_run(self, command):
"""Check if the user can run this command object."""
# display a message indicating if echo is off
message += get_echo_message(state)
+ # tack on IAC EOR to indicate the prompt will not be followed by CRLF
+ message += IAC+EOR
+
# return the assembly of various strings defined above
return message
return universe.categories["menu"][state].getboolean("echo", True)
def get_echo_sequence(state, echoing):
- """Build the appropriate IAC ECHO sequence as needed."""
+ """Build the appropriate IAC WILL/WONT ECHO sequence as needed."""
# if the user has echo on and the menu specifies it should be turned
# off, send: iac + will + echo + null
- if echoing and not menu_echo_on(state): return chr(255) + chr(251) + chr(1) + chr(0)
+ if echoing and not menu_echo_on(state): return IAC+WILL+ECHO
# if echo is not set to off in the menu and the user curently has echo
# off, send: iac + wont + echo + null
- elif not echoing and menu_echo_on(state): return chr(255) + chr(252) + chr(1) + chr(0)
+ elif not echoing and menu_echo_on(state): return IAC+WONT+ECHO
# default is not to send an echo control sequence at all
else: return ""