[tor-commits] [nyx/master] Nyx tor interpreter now uses Stem's tor interpretor methods

atagar at torproject.org atagar at torproject.org
Sun Jul 31 23:32:40 UTC 2016


commit 89e9ff7f8af12a3f9b5c4d6ad553825ecf8d281a
Author: Sambuddha Basu <sambuddhabasu1 at gmail.com>
Date:   Sat Jun 25 00:36:06 2016 -0700

    Nyx tor interpreter now uses Stem's tor interpretor methods
---
 nyx/panel/interpreter.py |  56 ++++++---
 nyx/tor_interpreter.py   | 310 -----------------------------------------------
 2 files changed, 39 insertions(+), 327 deletions(-)

diff --git a/nyx/panel/interpreter.py b/nyx/panel/interpreter.py
index 0c5d8c6..85efb02 100644
--- a/nyx/panel/interpreter.py
+++ b/nyx/panel/interpreter.py
@@ -5,14 +5,20 @@ information, tab completion, and other usability features.
 
 import nyx.controller
 import nyx.curses
+import re
 
-from nyx.curses import GREEN, CYAN, BOLD, HIGHLIGHT
+from nyx.curses import BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, BOLD, HIGHLIGHT, NORMAL
 from nyx import panel, tor_interpreter
 
+import stem.connection
+import stem.interpreter.commands
 
-USAGE_INFO = "to use this panel press enter"
-PROMPT = ">>> "
+
+USAGE_INFO = 'to use this panel press enter'
+PROMPT = '>>> '
 PROMPT_LINE = [[(PROMPT, GREEN, BOLD), (USAGE_INFO, CYAN, BOLD)]]
+ANSI_RE = re.compile("\\x1b\[([0-9;]*)m")
+ATTRS = {"0": NORMAL, "1": BOLD, "30": BLACK, "31": RED, "32": GREEN, "33": YELLOW, "34": BLUE, "35": MAGENTA, "36": CYAN}
 
 class InterpreterPanel(panel.Panel):
   """
@@ -24,7 +30,12 @@ class InterpreterPanel(panel.Panel):
     panel.Panel.__init__(self, 'interpreter')
 
     self._is_input_mode = False
-    self.interpreter = tor_interpreter.ControlInterpreter()
+    self.controller = stem.connection.connect(
+      control_port = ('127.0.0.1', 'default'),
+      control_socket = '/var/run/tor/control',
+      password_prompt = True,
+    )
+    self.interpreter = stem.interpreter.commands.ControlInterpretor(self.controller)
 
   def key_handlers(self):
     def _execute_command():
@@ -37,15 +48,21 @@ class InterpreterPanel(panel.Panel):
 
         if not user_input:
           is_done = True
-        else:
-          try:
-            input_entry, output_entry = self.interpreter.handle_query(user_input)
-            input_entry.insert(0, (PROMPT, GREEN, BOLD))
-            PROMPT_LINE.insert(len(PROMPT_LINE) - 1, input_entry)
-            for line in output_entry:
-              PROMPT_LINE.insert(len(PROMPT_LINE) - 1, line)
-          except tor_interpreter.InterpreterClosed:
-            is_done = True
+
+        response = self.interpreter.run_command(user_input)
+        color = None
+        if response:
+          PROMPT_LINE.insert(len(PROMPT_LINE) - 1, [(PROMPT, GREEN, BOLD), (user_input)])
+          attrs = []
+          for line in response.split('\n'):
+            new_attrs = []
+            ansi_re = ANSI_RE.findall(line)
+            if line.find("\x1b[") == 0 and ansi_re:
+              for attr in ansi_re[0].split(';'):
+                new_attrs.append(ATTRS[attr])
+              attrs = new_attrs
+            line = ANSI_RE.sub('', line)
+            PROMPT_LINE.insert(len(PROMPT_LINE) - 1, [(line, ) + tuple(attrs)])
 
         if is_done:
           self._is_input_mode = False
@@ -56,7 +73,7 @@ class InterpreterPanel(panel.Panel):
     )
 
   def draw(self, width, height):
-    usage_msg = " (enter \"/help\" for usage or a blank line to stop)" if self._is_input_mode else ""
+    usage_msg = ' (enter \"/help\" for usage or a blank line to stop)' if self._is_input_mode else ""
     self.addstr(0, 0, 'Control Interpreter%s:' % usage_msg, HIGHLIGHT)
 
     x_offset = 0
@@ -64,12 +81,17 @@ class InterpreterPanel(panel.Panel):
     for entry in PROMPT_LINE:
       cursor = x_offset
 
-      msg, color, attr = None, None, None
       for line in entry:
-        if len(line) == 2:
+        if len(line) == 1:
+          self.addstr(draw_line, cursor, line[0])
+        elif len(line) == 2:
           self.addstr(draw_line, cursor, line[0], line[1])
         elif len(line) == 3:
           self.addstr(draw_line, cursor, line[0], line[1], line[2])
-        cursor += len(line[0])
+        self.addstr(draw_line, cursor, line)
+        try:
+          cursor += len(line[0])
+        except:
+          pass
 
       draw_line += 1
diff --git a/nyx/tor_interpreter.py b/nyx/tor_interpreter.py
deleted file mode 100644
index 7b59a36..0000000
--- a/nyx/tor_interpreter.py
+++ /dev/null
@@ -1,310 +0,0 @@
-from nyx.curses import GREEN, CYAN, RED, MAGENTA, BLUE, BOLD, HIGHLIGHT
-from nyx import tor_controller
-
-
-# initial location /write will save to when no path is specified
-DEFAULT_WRITE_PATH = "/tmp/torInterpretor_output"
-
-MULTILINE_UNIMPLEMENTED_NOTICE = "Multi-line control options like this are not yet implemented."
-
-GENERAL_HELP = """Interpretor commands include:
-  /help   - provides information for interpretor and tor commands/config options
-  /info   - general information for a relay
-  /find   - searches backlog for lines with the given regex
-  /events - prints events that we've received
-  /write  - saves backlog to a given location
-  /quit   - shuts down the interpretor
-
-Tor commands include:
-  GETINFO - queries information from tor
-  GETCONF, SETCONF, RESETCONF - show or edit a configuration option
-  SIGNAL - issues control signal to the process (for resetting, stopping, etc)
-  SETEVENTS - configures the events tor will notify us of
-
-  USEFEATURE - enables custom behavior for the controller
-  SAVECONF - writes tor's current configuration to our torrc
-  LOADCONF - loads the given input like it was part of our torrc
-  MAPADDRESS - replaces requests for one address with another
-  POSTDESCRIPTOR - adds a relay descriptor to our cache
-  EXTENDCIRCUIT - create or extend a tor circuit
-  SETCIRCUITPURPOSE - configures the purpose associated with a circuit
-  CLOSECIRCUIT - closes the given circuit
-  ATTACHSTREAM - associates an application's stream with a tor circuit
-  REDIRECTSTREAM - sets a stream's destination
-  CLOSESTREAM - closes the given stream
-  RESOLVE - issues an asynchronous dns or rdns request over tor
-  TAKEOWNERSHIP - instructs tor to quit when this control connection is closed
-  PROTOCOLINFO - queries version and controller authentication information
-  QUIT - disconnect the control connection
-
-For more information use '/help [OPTION]'."""
-
-HELP_HELP = """Provides usage information for the given interpretor, tor command, or tor
-configuration option.
-
-Example:
-  /help info        # provides a description of the '/info' option
-  /help GETINFO     # usage information for tor's GETINFO controller option
-  /help ExitPolicy  # description of tor's ExitPolicy configuration option"""
-
-HELP_WRITE = """Writes the interpretor's backlog to the given path. If no location is
-specified then this saves to the last path specified (initially '%s').""" % DEFAULT_WRITE_PATH
-
-HELP_EVENTS = """Provides events that we've received belonging to the given event types. If
-no types are specified then this provides all the messages that we've
-received."""
-
-HELP_INFO = """Provides general information for a relay that's currently in the consensus.
-If no relay is specified then this provides information on ourselves."""
-
-HELP_FIND = """Searches the backlog for lines matching a given regular expression pattern.
-Results are deduplicated and the matching portions bolded."""
-
-HELP_QUIT = """Terminates the interpretor."""
-
-HELP_GETINFO = """Queries the tor process for information. Options are...
-"""
-
-HELP_GETCONF = """Provides the current value for a given configuration value. Options include...
-"""
-
-HELP_SETCONF = """Sets the given configuration parameters. Values can be quoted or non-quoted
-strings, and reverts the option to 0 or NULL if not provided.
-
-Examples:
-  * Sets a contact address and resets our family to NULL
-    SETCONF MyFamily ContactInfo=foo at bar.com
-
-  * Sets an exit policy that only includes port 80/443
-    SETCONF ExitPolicy=\"accept *:80, accept *:443, reject *:*\""""
-
-HELP_RESETCONF = """Reverts the given configuration options to their default values. If a value
-is provided then this behaves in the same way as SETCONF.
-
-Examples:
-  * Returns both of our accounting parameters to their defaults
-    RESETCONF AccountingMax AccountingStart
-
-  * Uses the default exit policy and sets our nickname to be 'Goomba'
-    RESETCONF ExitPolicy Nickname=Goomba"""
-
-HELP_SIGNAL = """Issues a signal that tells the tor process to reload its torrc, dump its
-stats, halt, etc.
-"""
-
-SIGNAL_DESCRIPTIONS = (
-  ("RELOAD / HUP", "reload our torrc"),
-  ("SHUTDOWN / INT", "gracefully shut down, waiting 30 seconds if we're a relay"),
-  ("DUMP / USR1", "logs information about open connections and circuits"),
-  ("DEBUG / USR2", "makes us log at the DEBUG runlevel"),
-  ("HALT / TERM", "immediately shut down"),
-  ("CLEARDNSCACHE", "clears any cached DNS results"),
-  ("NEWNYM", "clears the DNS cache and uses new circuits for future connections")
-)
-
-HELP_SETEVENTS = """Sets the events that we will receive. This turns off any events that aren't
-listed so sending 'SETEVENTS' without any values will turn off all event reporting.
-
-For Tor versions between 0.1.1.9 and 0.2.2.1 adding 'EXTENDED' causes some
-events to give us additional information. After version 0.2.2.1 this is
-always on.
-
-Events include...
-"""
-
-HELP_USEFEATURE = """Customizes the behavior of the control port. Options include...
-"""
-
-HELP_SAVECONF = """Writes Tor's current configuration to its torrc."""
-
-HELP_LOADCONF = """Reads the given text like it belonged to our torrc.
-
-Example:
-  +LOADCONF
-  # sets our exit policy to just accept ports 80 and 443
-  ExitPolicy accept *:80
-  ExitPolicy accept *:443
-  ExitPolicy reject *:*
-  ."""
-
-HELP_MAPADDRESS = """Replaces future requests for one address with another.
-
-Example:
-  MAPADDRESS 0.0.0.0=torproject.org 1.2.3.4=tor.freehaven.net"""
-
-HELP_POSTDESCRIPTOR = """Simulates getting a new relay descriptor."""
-
-HELP_EXTENDCIRCUIT = """Extends the given circuit or create a new one if the CircuitID is zero. The
-PATH is a comma separated list of fingerprints. If it isn't set then this
-uses Tor's normal path selection."""
-
-HELP_SETCIRCUITPURPOSE = """Sets the purpose attribute for a circuit."""
-
-HELP_CLOSECIRCUIT = """Closes the given circuit. If "IfUnused" is included then this only closes
-the circuit if it isn't currently being used."""
-
-HELP_ATTACHSTREAM = """Attaches a stream with the given built circuit (tor picks one on its own if
-CircuitID is zero). If HopNum is given then this hop is used to exit the
-circuit, otherwise the last relay is used."""
-
-HELP_REDIRECTSTREAM = """Sets the destination for a given stream. This can only be done after a
-stream is created but before it's attached to a circuit."""
-
-HELP_CLOSESTREAM = """Closes the given stream, the reason being an integer matching a reason as
-per section 6.3 of the tor-spec."""
-
-HELP_RESOLVE = """Performs IPv4 DNS resolution over tor, doing a reverse lookup instead if
-"mode=reverse" is included. This request is processed in the background and
-results in a ADDRMAP event with the response."""
-
-HELP_TAKEOWNERSHIP = """Instructs Tor to gracefully shut down when this control connection is closed."""
-
-HELP_PROTOCOLINFO = """Provides bootstrapping information that a controller might need when first
-starting, like Tor's version and controller authentication. This can be done
-before authenticating to the control port."""
-
-HELP_OPTIONS = {
-  "HELP": ("/help [OPTION]", HELP_HELP),
-  "WRITE": ("/write [PATH]", HELP_WRITE),
-  "EVENTS": ("/events [types]", HELP_EVENTS),
-  "INFO": ("/info [relay fingerprint, nickname, or IP address]", HELP_INFO),
-  "FIND": ("/find PATTERN", HELP_FIND),
-  "QUIT": ("/quit", HELP_QUIT),
-  "GETINFO": ("GETINFO OPTION", HELP_GETINFO),
-  "GETCONF": ("GETCONF OPTION", HELP_GETCONF),
-  "SETCONF": ("SETCONF PARAM[=VALUE]", HELP_SETCONF),
-  "RESETCONF": ("RESETCONF PARAM[=VALUE]", HELP_RESETCONF),
-  "SIGNAL": ("SIGNAL SIG", HELP_SIGNAL),
-  "SETEVENTS": ("SETEVENTS [EXTENDED] [EVENTS]", HELP_SETEVENTS),
-  "USEFEATURE": ("USEFEATURE OPTION", HELP_USEFEATURE),
-  "SAVECONF": ("SAVECONF", HELP_SAVECONF),
-  "LOADCONF": ("LOADCONF...", HELP_LOADCONF),
-  "MAPADDRESS": ("MAPADDRESS SOURCE_ADDR=DESTINATION_ADDR", HELP_MAPADDRESS),
-  "POSTDESCRIPTOR": ("POSTDESCRIPTOR [purpose=general/controller/bridge] [cache=yes/no]...", HELP_POSTDESCRIPTOR),
-  "EXTENDCIRCUIT": ("EXTENDCIRCUIT CircuitID [PATH] [purpose=general/controller]", HELP_EXTENDCIRCUIT),
-  "SETCIRCUITPURPOSE": ("SETCIRCUITPURPOSE CircuitID purpose=general/controller", HELP_SETCIRCUITPURPOSE),
-  "CLOSECIRCUIT": ("CLOSECIRCUIT CircuitID [IfUnused]", HELP_CLOSECIRCUIT),
-  "ATTACHSTREAM": ("ATTACHSTREAM StreamID CircuitID [HOP=HopNum]", HELP_ATTACHSTREAM),
-  "REDIRECTSTREAM": ("REDIRECTSTREAM StreamID Address [Port]", HELP_REDIRECTSTREAM),
-  "CLOSESTREAM": ("CLOSESTREAM StreamID Reason [Flag]", HELP_CLOSESTREAM),
-  "RESOLVE": ("RESOLVE [mode=reverse] address", HELP_RESOLVE),
-  "TAKEOWNERSHIP": ("TAKEOWNERSHIP", HELP_TAKEOWNERSHIP),
-  "PROTOCOLINFO": ("PROTOCOLINFO [ProtocolVersion]", HELP_PROTOCOLINFO),
-}
-
-class ControlInterpreter:
-  """
-  Interpretor that handles queries to the control port, providing usability
-  imporvements like irc style help optoins. This tracks input and responses.
-  """
-
-  def do_help(self, arg, output_entry):
-    """
-    Performs the '/help' operation, giving usage information for the given
-    argument or a general summary if there wasn't one.
-    """
-
-    arg = arg.upper()
-
-    # If there's multiple arguments then just take the first. This is
-    # particularly likely if they're trying to query a full command (for
-    # instance "/help GETINFO version")
-    arg = arg.split(" ")[0]
-
-    # strip slash if someone enters an interpretor command (ex. "/help /help")
-    if arg.startswith("/"): arg = arg[1:]
-
-    if arg:
-      if arg in HELP_OPTIONS:
-        # Provides information for the tor or interpretor argument. This bolds
-        # the usage information and indents the description after it.
-        usage, description = HELP_OPTIONS[arg]
-
-        output_entry.append([(usage, BLUE, BOLD)])
-
-        for line in description.split("\n"):
-          output_entry.append([("  " + line, BLUE, )])
-
-        if arg == "SIGNAL":
-          # lists descriptions for all of the signals
-          for signal, description in SIGNAL_DESCRIPTIONS:
-            output_entry.append([("%-15s" % signal, BLUE, BOLD), (" - %s" % description, BLUE, )])
-        elif arg == "SETEVENTS":
-          # lists all of the event types
-          event_options = tor_controller().get_info("events/names")
-          if event_options:
-            event_entries = event_options.split()
-
-            # displays four columns of 20 characters
-            for i in range(0, len(event_entries), 4):
-              line_entries = event_entries[i : i+4]
-
-              line_content = ""
-              for entry in line_entries:
-                line_content += "%-20s" % entry
-
-              output_entry.append([(line_content, BLUE, )])
-        elif arg == "USEFEATURE":
-          # lists the feature options
-          feature_options = tor_controller().get_info("features/names")
-          if feature_options:
-            output_entry.append([(feature_options, BLUE, )])
-        elif arg in ("LOADCONF", "POSTDESCRIPTOR"):
-          # gives a warning that this option isn't yet implemented
-          output_entry.append([(MULTILINE_UNIMPLEMENTED_NOTICE, RED, BOLD)])
-      else:
-        output_entry.append([("No help information available for '%s'..." % arg, RED, BOLD)])
-    else:
-      # provides the GENERAL_HELP with everything bolded except descriptions
-      for line in GENERAL_HELP.split("\n"):
-        cmd_start = line.find(" - ")
-        if cmd_start != -1:
-          output_entry.append([(line[:cmd_start], BLUE, BOLD), (line[cmd_start:], BLUE, )])
-        else:
-          output_entry.append([(line, BLUE, BOLD)])
-
-  def handle_query(self, user_input):
-    """
-    Processes the given input. Requests starting with a '/' are special
-    commands to the interpretor, and anything else is sent to the control port.
-    This returns an input/output tuple, each entry being a list of lines, each
-    line having a list of (msg, format) tuples for the content to be displayed.
-    This raises a InterpretorClosed if the interpretor should be shut down.
-
-    Arguments:
-      user_input - user input to be processed
-    """
-
-    user_input = user_input.strip()
-
-    input_entry, output_entry = [], []
-
-    if " " in user_input: cmd, arg = user_input.split(" ", 1)
-    else: cmd, arg = user_input, ""
-
-    if cmd.startswith("/"):
-      input_entry.append((user_input, MAGENTA, BOLD))
-      if cmd == "/quit": raise InterpreterClosed()
-      elif cmd == "/help": self.do_help(arg, output_entry)
-      else:
-        output_entry.append([("Not yet implemented...", RED, BOLD)])
-    else:
-      cmd = cmd.upper()
-      input_entry.append((cmd + " ", GREEN, BOLD))
-      if arg:
-        input_entry.append((arg, CYAN, BOLD))
-
-      if cmd == "GETINFO":
-        resp = tor_controller().get_info(arg)
-        for line in resp.split('\n'):
-          output_entry.append([(line, CYAN,)])
-    
-    return input_entry, output_entry
-
-
-class InterpreterClosed(Exception):
-  """
-  Exception raised when the interpreter should be shut down.
-  """
-
-  pass





More information about the tor-commits mailing list