[tor-commits] [arm/release] Relay setup wizard role selection

atagar at torproject.org atagar at torproject.org
Sun Jul 17 06:08:27 UTC 2011


commit 5eb2155266cf4d50af23a9c0cd06d5ab05db9553
Author: Damian Johnson <atagar at torproject.org>
Date:   Tue Jun 21 20:14:09 2011 -0700

    Relay setup wizard role selection
    
    This provides the first prompt of the relay setup wizard which has options for
    the general role Tor will have. Options include:
    - Internal Realy
    - Exit Relay
    - Bridge
    - Client
    - Cancel
    
    Selection is currenty a no-op. Next thing to do are the sub-dialogs these
    options go to...
---
 README                  |    1 +
 src/cli/__init__.py     |    2 +-
 src/cli/controller.py   |    7 +++-
 src/cli/menu/actions.py |    2 +
 src/cli/menu/menu.py    |    1 +
 src/cli/wizard.py       |   98 +++++++++++++++++++++++++++++++++++++++++++++++
 src/settings.cfg        |   15 +++++++
 src/util/uiTools.py     |   11 +++++
 8 files changed, 135 insertions(+), 2 deletions(-)

diff --git a/README b/README
index 44fabf1..835b02b 100644
--- a/README
+++ b/README
@@ -187,6 +187,7 @@ Layout:
       headerPanel.py         - top of all pages, providing general information
       descriptorPopup.py     - displays connection descriptor data
       popups.py              - toolkit providing display popups
+      wizard.py              - provides the relay setup wizard
       
       logPanel.py            - (page 1) displays tor, arm, and torctl events
       configPanel.py         - (page 3) editor panel for the tor configuration
diff --git a/src/cli/__init__.py b/src/cli/__init__.py
index 1564f68..435883d 100644
--- a/src/cli/__init__.py
+++ b/src/cli/__init__.py
@@ -2,5 +2,5 @@
 Panels, popups, and handlers comprising the arm user interface.
 """
 
-__all__ = ["configPanel", "controller", "descriptorPopup", "headerPanel", "logPanel", "popups", "torrcPanel"]
+__all__ = ["configPanel", "controller", "descriptorPopup", "headerPanel", "logPanel", "popups", "torrcPanel", "wizard"]
 
diff --git a/src/cli/controller.py b/src/cli/controller.py
index e20e023..e771a26 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -8,6 +8,7 @@ import curses
 import threading
 
 import cli.menu.menu
+import cli.wizard
 import cli.popups
 import cli.headerPanel
 import cli.logPanel
@@ -455,6 +456,7 @@ def startTorMonitor(startTime):
   
   cli.graphing.graphPanel.loadConfig(config)
   cli.connections.connEntry.loadConfig(config)
+  cli.wizard.loadConfig(config)
   
   # attempts to fetch the tor pid, warning if unsuccessful (this is needed for
   # checking its resource usage, among other things)
@@ -536,7 +538,8 @@ def drawTorMonitor(stdscr, startTime):
   # main draw loop
   overrideKey = None     # uses this rather than waiting on user input
   isUnresponsive = False # flag for heartbeat responsiveness check
-
+  if not torTools.getConn().isAlive(): overrideKey = ord('w') # shows wizard
+  
   while not control.isDone():
     displayPanels = control.getDisplayPanels()
     isUnresponsive = heartbeatCheck(isUnresponsive)
@@ -593,6 +596,8 @@ def drawTorMonitor(stdscr, startTime):
           log.log(log.ERR, "Error detected when reloading tor: %s" % sysTools.getFileErrorMsg(exc))
     elif key == ord('h') or key == ord('H'):
       overrideKey = cli.popups.showHelpPopup()
+    elif key == ord('w') or key == ord('W'):
+      cli.wizard.showWizard()
     else:
       for panelImpl in displayPanels:
         isKeystrokeConsumed = panelImpl.handleKey(key)
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index c571bd8..3c90b10 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -5,6 +5,7 @@ Generates the menu for arm, binding options with their related actions.
 import functools
 
 import cli.popups
+import cli.wizard
 import cli.controller
 import cli.menu.item
 import cli.graphing.graphPanel
@@ -53,6 +54,7 @@ def makeActionsMenu():
   actionsMenu = cli.menu.item.Submenu("Actions")
   actionsMenu.add(cli.menu.item.MenuItem("Close Menu", None))
   actionsMenu.add(cli.menu.item.MenuItem("New Identity", headerPanel.sendNewnym))
+  actionsMenu.add(cli.menu.item.MenuItem("Setup Wizard", cli.wizard.showWizard))
   
   if control.isPaused(): label, arg = "Unpause", False
   else: label, arg = "Pause", True
diff --git a/src/cli/menu/menu.py b/src/cli/menu/menu.py
index 1dbbbd0..8302551 100644
--- a/src/cli/menu/menu.py
+++ b/src/cli/menu/menu.py
@@ -109,6 +109,7 @@ def showMenu():
       
       popup.win.refresh()
       
+      curses.cbreak()
       key = control.getScreen().getch()
       cursor.handleKey(key)
       
diff --git a/src/cli/wizard.py b/src/cli/wizard.py
new file mode 100644
index 0000000..2b7cdf7
--- /dev/null
+++ b/src/cli/wizard.py
@@ -0,0 +1,98 @@
+"""
+Provides user prompts for setting up a new relay. This autogenerates a torrc
+that's used by arm to start its tor instance.
+"""
+
+import curses
+
+import cli.popups
+import cli.controller
+
+from util import enum, uiTools
+
+# basic configuration types we can run as
+RunType = enum.Enum("RELAY", "EXIT", "BRIDGE", "CLIENT")
+
+# other options provided in the prompts
+CANCEL, BACK = "Cancel", "Back"
+
+CONFIG = {"wizard.role.message": "",
+          "wizard.role.option.label": {},
+          "wizard.role.option.description": {}}
+
+def loadConfig(config):
+  config.update(CONFIG)
+
+def showWizard():
+  myRole = promptRunType()
+
+def promptRunType():
+  """
+  Provides a prompt for selecting the general role we'd like Tor to run with.
+  This returns a RunType enumeration for the selection, or None if the dialog
+  was canceled.
+  """
+  
+  popup, _, _ = cli.popups.init(23, 58)
+  if not popup: return
+  control = cli.controller.getController()
+  key, selection = 0, 0
+  
+  # constructs (enum, label, [description lines]) tuples for our options
+  options = []
+  
+  for runType in RunType.values() + [CANCEL]:
+    label = CONFIG["wizard.role.option.label"].get(runType, "")
+    descRemainder = CONFIG["wizard.role.option.description"].get(runType, "")
+    descLines = []
+    
+    while descRemainder:
+      descLine, descRemainder = uiTools.cropStr(descRemainder, 54, None, endType = None, getRemainder = True)
+      descLines.append(descLine.strip())
+    
+    options.append((runType, label, descLines))
+  
+  try:
+    popup.win.box()
+    curses.cbreak()
+    format = uiTools.getColor("green")
+    y, msgRemainder = 1, CONFIG["wizard.role.message"]
+    
+    # provides the welcoming message
+    while msgRemainder:
+      msg, msgRemainder = uiTools.cropStr(msgRemainder, 54, None, endType = None, getRemainder = True)
+      popup.addstr(y, 2, msg.strip(), format | curses.A_BOLD)
+      y += 1
+    
+    while not uiTools.isSelectionKey(key):
+      offset = 0
+      
+      for i in range(len(options)):
+        _, label, lines = options[i]
+        optionFormat = format | curses.A_STANDOUT if i == selection else format
+        
+        # Curses has a weird bug where there's a one-pixel alignment
+        # difference between bold and regular text, so it looks better
+        # to render the whitespace here as not being bold.
+        
+        offset += 1
+        popup.addstr(y + offset, 2, label, optionFormat | curses.A_BOLD)
+        popup.addstr(y + offset, 2 + len(label), " " * (54 - len(label)), optionFormat)
+        offset += 1
+        
+        for line in lines:
+          popup.addstr(y + offset, 2, uiTools.padStr(line, 54), optionFormat)
+          offset += 1
+      
+      popup.win.refresh()
+      key = control.getScreen().getch()
+      
+      if key == curses.KEY_UP: selection = max(0, selection - 1)
+      elif key == curses.KEY_DOWN: selection = min(len(options) - 1, selection + 1)
+      elif key == 27: selection, key = -1, curses.KEY_ENTER # esc - cancel
+  finally:
+    cli.popups.finalize()
+  
+  selectedOption = options[selection][0]
+  return None if selectedOption == CANCEL else selectedOption
+
diff --git a/src/settings.cfg b/src/settings.cfg
index bced9a4..2ad2784 100644
--- a/src/settings.cfg
+++ b/src/settings.cfg
@@ -340,6 +340,21 @@ msg.ARM_DEBUG GETINFO traffic/written
 msg.ARM_DEBUG GETCONF
 msg.ARM_DEBUG Unable to query process resource usage from ps
 
+# descriptions used in the relay setup wizard
+wizard.role.message Welcome to the Tor network! This will step you through the configuration process for being a part of it. To start with, what role would you like to have?
+
+wizard.role.option.label Relay => Internal Relay
+wizard.role.option.label Exit => Exit Relay
+wizard.role.option.label Bridge => Bridge
+wizard.role.option.label Client => Client
+wizard.role.option.label Cancel => Cancel
+
+wizard.role.option.description Relay => Provides interconnections with other Tor relays. This is a safe and easy of making the network better.
+wizard.role.option.description Exit => Connects between Tor an the outside Internet. This is a vital role, but can lead to abuse complaints.
+wizard.role.option.description Bridge => Non-public relay specifically for helping censored users.
+wizard.role.option.description Client => Use the network without contributing to it.
+wizard.role.option.description Cancel => Close without starting Tor.
+
 # some config options are fetched via special values
 torrc.map HiddenServiceDir => HiddenServiceOptions
 torrc.map HiddenServicePort => HiddenServiceOptions
diff --git a/src/util/uiTools.py b/src/util/uiTools.py
index 40a3087..52885de 100644
--- a/src/util/uiTools.py
+++ b/src/util/uiTools.py
@@ -282,6 +282,17 @@ def cropStr(msg, size, minWordLen = 4, minCrop = 0, endType = Ending.ELLIPSE, ge
   if getRemainder: return (returnMsg, remainder)
   else: return returnMsg
 
+def padStr(msg, size):
+  """
+  Provides the string padded with whitespace to the given length.
+  
+  Arguments:
+    msg  - string to be padded
+    size - length to be padded to
+  """
+  
+  return ("%%-%is" % size) % msg
+
 def camelCase(label, divider = "_", joiner = " "):
   """
   Converts the given string to camel case, ie:





More information about the tor-commits mailing list