예제 #1
0
class Connection(wx.SplitterWindow):
    def __init__(self, mainwindow):
        wx.SplitterWindow.__init__(self,
                                   mainwindow.tabs,
                                   style=wx.SP_LIVE_UPDATE)
        self.world = None

        # these two are set with dns_com_awns_serverinfo but hypothetically
        # -could- come from the saved world also
        # EDIT: and/or from MSSP, which should also be mashed into the world
        self.home_url = ''
        self.help_url = ''

        self.decompressor = zlib.decompressobj()

        # bin for Telnet IAC commands to stash any status info (on/off, etc)
        self.features = set()
        self.feature_init_callback = {
            'MCP': self.mcp_init_callback,
            'MSSP': self.mssp_init_callback,
        }

        # re-queue stuff for re-processing (ie if we turn on compression)
        self.filter_queue = b''

        self.reader = self.writer = None

        self.input_pane = InputPane(self, self)
        self.output_pane = OutputPane(self, self)
        self.status_bar = StatusBar(mainwindow, self)
        self.mainwindow = mainwindow

        self.SplitHorizontally(self.output_pane, self.input_pane)
        self.SetMinimumPaneSize(self.input_pane.font_size()[1] * 2)

        self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.saveSplitterSize)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.output_pane.Bind(EVT_ROW_COL_CHANGED, self.on_row_col_changed)

        mainwindow.Bind(EVT_PREFS_CHANGED, self.doPrefsChanged)

    def doPrefsChanged(self, evt):
        self.input_pane.restyle_thyself()
        self.output_pane.restyle_thyself()
        evt.Skip()

    def saveSplitterSize(self, evt):
        size = self.GetSize()
        prefs.set('input_height', size.GetHeight() - evt.GetSashPosition())
        evt.Skip()

    def OnSize(self, evt):
        size = self.GetSize()
        input_height = int(prefs.get('input_height')) or 25
        self.SetSashPosition(size.GetHeight() - input_height, True)
        self.output_pane.ScrollIfAppropriate()
        evt.Skip()

    def on_row_col_changed(self, evt):
        # This is sorta icky but math is hard
        filters.telnetiac.handle_naws(self)

    def ActivateFeature(self, feature, on=True):
        if on:
            self.features.add(feature)
            if self.feature_init_callback.get(feature):
                self.feature_init_callback[feature]()
        else:
            self.features.discard(feature)
        self.UpdateStatus()

    def SetTitle(self, text):
        tabindex = self.mainwindow.tabs.GetPageIndex(self)
        self.mainwindow.tabs.SetPageText(tabindex, text)

    def UpdateStatus(self):
        self.status_bar.LayoutWidgets()

    def UpdateIcon(self, icon, message):
        icon = self.status_bar.feature_icons.get(icon)
        if icon: icon.SetToolTip(message)

    def ShowMessage(self, message):
        self.status_bar.AddStatus(message)

    def IsCurrentConnection(self):
        return self.mainwindow.currentConnection() == self

    def Close(self):
        if self.is_connected():
            self.output_pane.display("=== wxpymoo: Connection closed. ===\n")

        if self.writer: self.writer.close()
        self.filter_queue = b''
        self.features.clear()
        self.connect_time = self.reader = self.writer = None

    # TODO - we need to cram charset into worlds more deterministically
    def charset(self):
        return self.world.get('charset', 'latin1') if self.world else 'latin1'

    # connection.connect ([host], [port])
    #
    # existing connections will remember their host and port if not supplied here,
    # for ease of reconnect etc.
    def connect(self, world):
        self.world = world
        self.connect_time = None

        StartCoroutine(self._connect, self)

    async def _connect(self):
        world = self.world
        host = world.get('host')
        port = int(world.get('port'))
        conntype = world.get('conntype')

        try:
            wx.BeginBusyCursor()
            self.reader, self.writer = await asyncio.wait_for(
                asyncio.open_connection(host, port, ssl=(conntype == "SSL")),
                timeout=15)
        except Exception as inst:
            self.Close()
            message = "Connection error: " + str(inst)
            if inst.__class__ == asyncio.TimeoutError:
                message = "Connection to " + host + ":" + str(
                    port) + " timed out."
            else:
                print("DEBUG: Connection Exception " + str(inst.__class__) +
                      " " + str(inst))
            wx.MessageDialog(self,
                             message,
                             "Error",
                             style=wx.OK | wx.ICON_ERROR).ShowModal()
            return
        finally:
            wx.EndBusyCursor()

        if conntype == "SSL":
            self.ActivateFeature('SSL')

        self.connect_time = time.time()

        prefs.set('last_world', world.get('name'))

        self.mcp = MCPCore(self)

        if self.world.get('auto_login'):
            login_script = self.world.get('login_script')
            if login_script:
                login_script = re.sub('%u', self.world.get('username', ''),
                                      login_script)
                login_script = re.sub('%p', self.world.get('password', ''),
                                      login_script)
            self.output(login_script + "\n")

        while True:
            data = await self.reader.read(65535)
            if not data: break

            if self.filter_queue:
                data = self.filter_queue + data
                self.filter_queue = b''

            # Do the initial telnet filtering here, while we're still in 'bytes' mode
            data = filters.telnetiac.process_line(self, data)
            if not data: continue

            data = data.decode(self.charset())

            self.output_pane.display(data)

    def output(self, stuff):
        if isinstance(stuff, str):
            stuff = bytes(stuff, self.charset())
        if self.writer:
            self.writer.write(stuff)
        else:
            print(f"Tried to write stuff to closed writer: {stuff}")

    def reconnect(self):
        if self.writer: self.Close()
        self.connect(self.world)

    def is_connected(self):
        return True if self.writer else False

    ### feature init callbacks
    def mcp_init_callback(self):
        self.debug_mcp = DebugMCP(self.mainwindow, self)

    def mssp_init_callback(self):
        self.mssp_info = MSSPInfo(self)