[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r23720: {arm} Renaming the torrc util to torConfig now that it has more fu (in arm/trunk/src: . interface util)
Author: atagar
Date: 2010-10-29 15:14:31 +0000 (Fri, 29 Oct 2010)
New Revision: 23720
Added:
arm/trunk/src/util/torConfig.py
Removed:
arm/trunk/src/util/torrc.py
Modified:
arm/trunk/src/interface/configFilePanel.py
arm/trunk/src/interface/controller.py
arm/trunk/src/starter.py
Log:
Renaming the torrc util to torConfig now that it has more functionality.
Modified: arm/trunk/src/interface/configFilePanel.py
===================================================================
--- arm/trunk/src/interface/configFilePanel.py 2010-10-29 13:00:09 UTC (rev 23719)
+++ arm/trunk/src/interface/configFilePanel.py 2010-10-29 15:14:31 UTC (rev 23720)
@@ -6,7 +6,7 @@
import curses
import threading
-from util import conf, panel, torrc, uiTools
+from util import conf, panel, torConfig, uiTools
DEFAULT_CONFIG = {"features.config.file.showScrollbars": True,
"features.config.file.maxLinesPerEntry": 8}
@@ -73,7 +73,7 @@
renderedContents, corrections, confLocation = None, {}, None
if self.configType == TORRC:
- loadedTorrc = torrc.getTorrc()
+ loadedTorrc = torConfig.getTorrc()
loadedTorrc.getLock().acquire()
confLocation = loadedTorrc.getConfigLocation()
@@ -145,10 +145,10 @@
if lineNumber in corrections:
lineIssue, lineIssueMsg = corrections[lineNumber]
- if lineIssue == torrc.VAL_DUPLICATE:
+ if lineIssue == torConfig.VAL_DUPLICATE:
lineComp["option"][1] = curses.A_BOLD | uiTools.getColor("blue")
lineComp["argument"][1] = curses.A_BOLD | uiTools.getColor("blue")
- elif lineIssue == torrc.VAL_MISMATCH:
+ elif lineIssue == torConfig.VAL_MISMATCH:
lineComp["argument"][1] = curses.A_BOLD | uiTools.getColor("red")
lineComp["correction"][0] = " (%s)" % lineIssueMsg
else:
Modified: arm/trunk/src/interface/controller.py
===================================================================
--- arm/trunk/src/interface/controller.py 2010-10-29 13:00:09 UTC (rev 23719)
+++ arm/trunk/src/interface/controller.py 2010-10-29 15:14:31 UTC (rev 23720)
@@ -23,7 +23,7 @@
import descriptorPopup
import fileDescriptorPopup
-from util import conf, log, connections, hostnames, panel, sysTools, torrc, torTools, uiTools
+from util import conf, log, connections, hostnames, panel, sysTools, torConfig, torTools, uiTools
import graphing.bandwidthStats
import graphing.connStats
import graphing.psStats
@@ -361,7 +361,7 @@
# confLocation = ""
# loads the torrc and provides warnings in case of validation errors
- loadedTorrc = torrc.getTorrc()
+ loadedTorrc = torConfig.getTorrc()
loadedTorrc.getLock().acquire()
try:
@@ -378,8 +378,8 @@
for lineNum in corrections:
problem = corrections[lineNum][0]
- if problem == torrc.VAL_DUPLICATE: irrelevantLines.append(lineNum)
- elif problem == torrc.VAL_MISMATCH: mismatchLines.append(lineNum)
+ if problem == torConfig.VAL_DUPLICATE: irrelevantLines.append(lineNum)
+ elif problem == torConfig.VAL_MISMATCH: mismatchLines.append(lineNum)
if irrelevantLines:
irrelevantLines.sort()
@@ -1412,7 +1412,7 @@
panel.CURSES_LOCK.release()
elif page == 2 and False and key == ord('r') or key == ord('R'):
# reloads torrc, providing a notice if successful or not
- loadedTorrc = torrc.getTorrc()
+ loadedTorrc = torConfig.getTorrc()
loadedTorrc.getLock().acquire()
try:
Modified: arm/trunk/src/starter.py
===================================================================
--- arm/trunk/src/starter.py 2010-10-29 13:00:09 UTC (rev 23719)
+++ arm/trunk/src/starter.py 2010-10-29 15:14:31 UTC (rev 23720)
@@ -20,7 +20,7 @@
import util.log
import util.panel
import util.sysTools
-import util.torrc
+import util.torConfig
import util.torTools
import util.uiTools
import TorCtl.TorCtl
@@ -150,7 +150,7 @@
config.update(DEFAULTS)
# loads user preferences for utilities
- for utilModule in (util.conf, util.connections, util.hostnames, util.log, util.panel, util.sysTools, util.torrc, util.torTools, util.uiTools):
+ for utilModule in (util.conf, util.connections, util.hostnames, util.log, util.panel, util.sysTools, util.torConfig, util.torTools, util.uiTools):
utilModule.loadConfig(config)
# overwrites undefined parameters with defaults
Copied: arm/trunk/src/util/torConfig.py (from rev 23717, arm/trunk/src/util/torrc.py)
===================================================================
--- arm/trunk/src/util/torConfig.py (rev 0)
+++ arm/trunk/src/util/torConfig.py 2010-10-29 15:14:31 UTC (rev 23720)
@@ -0,0 +1,455 @@
+"""
+Helper functions for working with tor's configuration file.
+"""
+
+import curses
+import threading
+
+from util import sysTools, torTools, uiTools
+
+CONFIG = {"features.torrc.validate": True,
+ "torrc.multiline": [],
+ "torrc.alias": {},
+ "torrc.label.size.b": [],
+ "torrc.label.size.kb": [],
+ "torrc.label.size.mb": [],
+ "torrc.label.size.gb": [],
+ "torrc.label.size.tb": [],
+ "torrc.label.time.sec": [],
+ "torrc.label.time.min": [],
+ "torrc.label.time.hour": [],
+ "torrc.label.time.day": [],
+ "torrc.label.time.week": []}
+
+# enums and values for numeric torrc entries
+UNRECOGNIZED, SIZE_VALUE, TIME_VALUE = range(1, 4)
+SIZE_MULT = {"b": 1, "kb": 1024, "mb": 1048576, "gb": 1073741824, "tb": 1099511627776}
+TIME_MULT = {"sec": 1, "min": 60, "hour": 3600, "day": 86400, "week": 604800}
+
+# enums for issues found during torrc validation:
+# VAL_DUPLICATE - entry is ignored due to being a duplicate
+# VAL_MISMATCH - the value doesn't match tor's current state
+VAL_DUPLICATE, VAL_MISMATCH = range(1, 3)
+
+# descriptions of tor's configuration options fetched from its man page
+CONFIG_DESCRIPTIONS_LOCK = threading.RLock()
+CONFIG_DESCRIPTIONS = {}
+
+TORRC = None # singleton torrc instance
+MAN_OPT_INDENT = 7 # indentation before options in the man page
+MAN_EX_INDENT = 15 # indentation used for man page examples
+
+def loadConfig(config):
+ CONFIG["torrc.multiline"] = config.get("torrc.multiline", [])
+ CONFIG["torrc.alias"] = config.get("torrc.alias", {})
+
+ # all the torrc.label.* values are comma separated lists
+ for configKey in CONFIG.keys():
+ if configKey.startswith("torrc.label."):
+ configValues = config.get(configKey, "").split(",")
+ if configValues: CONFIG[configKey] = [val.strip() for val in configValues]
+
+def getTorrc():
+ """
+ Singleton constructor for a Controller. Be aware that this starts as being
+ unloaded, needing the torrc contents to be loaded before being functional.
+ """
+
+ global TORRC
+ if TORRC == None: TORRC = Torrc()
+ return TORRC
+
+def loadOptionDescriptions():
+ """
+ Fetches and parses descriptions for tor's configuration options from its man
+ page. This can be a somewhat lengthy call, and raises an IOError if issues
+ occure.
+ """
+
+ CONFIG_DESCRIPTIONS_LOCK.acquire()
+ CONFIG_DESCRIPTIONS.clear()
+
+ raisedExc = None
+ try:
+ manCallResults = sysTools.call("man tor")
+
+ lastOption, lastArg = None, None
+ lastDescription = ""
+ for line in manCallResults:
+ strippedLine = line.strip()
+
+ # we have content, but an indent less than an option (ignore line)
+ if strippedLine and not line.startswith(" " * MAN_OPT_INDENT): continue
+
+ # line starts with an indent equivilant to a new config option
+ isOptIndent = line.startswith(" " * MAN_OPT_INDENT) and line[MAN_OPT_INDENT] != " "
+
+ if isOptIndent:
+ # Most lines with this indent that aren't config options won't have
+ # any description set at this point (not a perfect filter, but cuts
+ # down on the noise).
+ strippedDescription = lastDescription.strip()
+ if lastOption and strippedDescription:
+ CONFIG_DESCRIPTIONS[lastOption] = (lastArg, strippedDescription)
+ lastDescription = ""
+
+ # parses the option and argument
+ line = line.strip()
+ divIndex = line.find(" ")
+ if divIndex != -1:
+ lastOption, lastArg = line[:divIndex], line[divIndex + 1:]
+ else:
+ # Appends the text to the running description. Empty lines and lines
+ # starting with a specific indentation are used for formatting, for
+ # instance the ExitPolicy and TestingTorNetwork entries.
+ if lastDescription and lastDescription[-1] != "\n":
+ lastDescription += " "
+
+ if not strippedLine:
+ lastDescription += "\n\n"
+ elif line.startswith(" " * MAN_EX_INDENT):
+ lastDescription += " %s\n" % strippedLine
+ else: lastDescription += strippedLine
+
+ except IOError, exc:
+ raisedExc = exc
+
+ CONFIG_DESCRIPTIONS_LOCK.release()
+ if raisedExc: raise raisedExc
+
+def isConfigDescriptionAvailable(option):
+ """
+ Returns if a description for the given configuration option has been loaded
+ or not.
+
+ Arguments:
+ option - tor config option
+ """
+
+ return option in CONFIG_DESCRIPTIONS
+
+def getConfigDescription(option):
+ """
+ Provides a tuple with arguments and description for the given tor
+ configuration option, fetched from its man page. This provides None if no
+ such option has been loaded. If the man page is in the process of being
+ loaded then this call blocks until it finishes.
+
+ Arguments:
+ option - tor config option
+ """
+
+ CONFIG_DESCRIPTIONS_LOCK.acquire()
+
+ if option in CONFIG_DESCRIPTIONS:
+ returnVal = CONFIG_DESCRIPTIONS[option]
+ else: returnVal = None
+
+ CONFIG_DESCRIPTIONS_LOCK.release()
+ return returnVal
+
+def getConfigLocation():
+ """
+ Provides the location of the torrc, raising an IOError with the reason if the
+ path can't be determined.
+ """
+
+ conn = torTools.getConn()
+ configLocation = conn.getInfo("config-file")
+ if not configLocation: raise IOError("unable to query the torrc location")
+
+ # checks if this is a relative path, needing the tor pwd to be appended
+ if configLocation[0] != "/":
+ torPid = conn.getMyPid()
+ failureMsg = "querying tor's pwd failed because %s"
+ if not torPid: raise IOError(failureMsg % "we couldn't get the pid")
+
+ try:
+ # pwdx results are of the form:
+ # 3799: /home/atagar
+ # 5839: No such process
+ results = sysTools.call("pwdx %s" % torPid)
+ if not results:
+ raise IOError(failureMsg % "pwdx didn't return any results")
+ elif results[0].endswith("No such process"):
+ raise IOError(failureMsg % ("pwdx reported no process for pid " + torPid))
+ elif len(results) != 1 or results.count(" ") != 1:
+ raise IOError(failureMsg % "we got unexpected output from pwdx")
+ else:
+ pwdPath = results[0][results[0].find(" ") + 1:]
+ configLocation = "%s/%s" % (pwdPath, configLocation)
+ except IOError, exc:
+ raise IOError(failureMsg % ("the pwdx call failed: " + str(exc)))
+
+ return torTools.getPathPrefix() + configLocation
+
+def validate(contents = None):
+ """
+ Performs validation on the given torrc contents, providing back a mapping of
+ line numbers to tuples of the (issue, msg) found on them.
+
+ Arguments:
+ contents - torrc contents
+ """
+
+ conn = torTools.getConn()
+ contents = _stripComments(contents)
+ issuesFound, seenOptions = {}, []
+ for lineNumber in range(len(contents) - 1, -1, -1):
+ lineText = contents[lineNumber]
+ if not lineText: continue
+
+ lineComp = lineText.split(None, 1)
+ if len(lineComp) == 2: option, value = lineComp
+ else: option, value = lineText, ""
+
+ # most parameters are overwritten if defined multiple times
+ if option in seenOptions and not option in CONFIG["torrc.multiline"]:
+ issuesFound[lineNumber] = (VAL_DUPLICATE, "")
+ continue
+ else: seenOptions.append(option)
+
+ # replace aliases with their recognized representation
+ if option in CONFIG["torrc.alias"]:
+ option = CONFIG["torrc.alias"][option]
+
+ # tor appears to replace tabs with a space, for instance:
+ # "accept\t*:563" is read back as "accept *:563"
+ value = value.replace("\t", " ")
+
+ # parse value if it's a size or time, expanding the units
+ value, valueType = _parseConfValue(value)
+
+ # issues GETCONF to get the values tor's currently configured to use
+ torValues = conn.getOption(option, [], True)
+
+ # multiline entries can be comma separated values (for both tor and conf)
+ valueList = [value]
+ if option in CONFIG["torrc.multiline"]:
+ valueList = [val.strip() for val in value.split(",")]
+
+ fetchedValues, torValues = torValues, []
+ for fetchedValue in fetchedValues:
+ for fetchedEntry in fetchedValue.split(","):
+ fetchedEntry = fetchedEntry.strip()
+ if not fetchedEntry in torValues:
+ torValues.append(fetchedEntry)
+
+ for val in valueList:
+ # checks if both the argument and tor's value are empty
+ isBlankMatch = not val and not torValues
+
+ if not isBlankMatch and not val in torValues:
+ # converts corrections to reader friedly size values
+ displayValues = torValues
+ if valueType == SIZE_VALUE:
+ displayValues = [uiTools.getSizeLabel(int(val)) for val in torValues]
+ elif valueType == TIME_VALUE:
+ displayValues = [uiTools.getTimeLabel(int(val)) for val in torValues]
+
+ issuesFound[lineNumber] = (VAL_MISMATCH, ", ".join(displayValues))
+
+ return issuesFound
+
+def _parseConfValue(confArg):
+ """
+ Converts size or time values to their lowest units (bytes or seconds) which
+ is what GETCONF calls provide. The returned is a tuple of the value and unit
+ type.
+
+ Arguments:
+ confArg - torrc argument
+ """
+
+ if confArg.count(" ") == 1:
+ val, unit = confArg.lower().split(" ", 1)
+ if not val.isdigit(): return confArg, UNRECOGNIZED
+ mult, multType = _getUnitType(unit)
+
+ if mult != None:
+ return str(int(val) * mult), multType
+
+ return confArg, UNRECOGNIZED
+
+def _getUnitType(unit):
+ """
+ Provides the type and multiplier for an argument's unit. The multiplier is
+ None if the unit isn't recognized.
+
+ Arguments:
+ unit - string representation of a unit
+ """
+
+ for label in SIZE_MULT:
+ if unit in CONFIG["torrc.label.size." + label]:
+ return SIZE_MULT[label], SIZE_VALUE
+
+ for label in TIME_MULT:
+ if unit in CONFIG["torrc.label.time." + label]:
+ return TIME_MULT[label], TIME_VALUE
+
+ return None, UNRECOGNIZED
+
+def _stripComments(contents):
+ """
+ Removes comments and extra whitespace from the given torrc contents.
+
+ Arguments:
+ contents - torrc contents
+ """
+
+ strippedContents = []
+ for line in contents:
+ if line and "#" in line: line = line[:line.find("#")]
+ strippedContents.append(line.strip())
+ return strippedContents
+
+class Torrc():
+ """
+ Wrapper for the torrc. All getters provide None if the contents are unloaded.
+ """
+
+ def __init__(self):
+ self.contents = None
+ self.configLocation = None
+ self.valsLock = threading.RLock()
+
+ # cached results for the current contents
+ self.displayableContents = None
+ self.strippedContents = None
+ self.corrections = None
+
+ def load(self):
+ """
+ Loads or reloads the torrc contents, raising an IOError if there's a
+ problem.
+ """
+
+ self.valsLock.acquire()
+
+ # clears contents and caches
+ self.contents, self.configLocation = None, None
+ self.displayableContents = None
+ self.strippedContents = None
+ self.corrections = None
+
+ try:
+ self.configLocation = getConfigLocation()
+ configFile = open(self.configLocation, "r")
+ self.contents = configFile.readlines()
+ configFile.close()
+ except IOError, exc:
+ self.valsLock.release()
+ raise exc
+
+ self.valsLock.release()
+
+ def isLoaded(self):
+ """
+ Provides true if there's loaded contents, false otherwise.
+ """
+
+ return self.contents != None
+
+ def getConfigLocation(self):
+ """
+ Provides the location of the loaded configuration contents. This may be
+ available, even if the torrc failed to be loaded.
+ """
+
+ return self.configLocation
+
+ def getContents(self):
+ """
+ Provides the contents of the configuration file.
+ """
+
+ self.valsLock.acquire()
+ returnVal = list(self.contents) if self.contents else None
+ self.valsLock.relese()
+ return returnVal
+
+ def getDisplayContents(self, strip = False):
+ """
+ Provides the contents of the configuration file, formatted in a rendering
+ frindly fashion:
+ - Tabs print as three spaces. Keeping them as tabs is problematic for
+ layouts since it's counted as a single character, but occupies several
+ cells.
+ - Strips control and unprintable characters.
+
+ Arguments:
+ strip - removes comments and extra whitespace if true
+ """
+
+ self.valsLock.acquire()
+
+ if not self.isLoaded(): returnVal = None
+ else:
+ if self.displayableContents == None:
+ # restricts contents to displayable characters
+ self.displayableContents = []
+
+ for lineNum in range(len(self.contents)):
+ lineText = self.contents[lineNum]
+ lineText = lineText.replace("\t", " ")
+ lineText = "".join([char for char in lineText if curses.ascii.isprint(char)])
+ self.displayableContents.append(lineText)
+
+ if strip:
+ if self.strippedContents == None:
+ self.strippedContents = _stripComments(self.displayableContents)
+
+ returnVal = list(self.strippedContents)
+ else: returnVal = list(self.displayableContents)
+
+ self.valsLock.release()
+ return returnVal
+
+ def getCorrections(self):
+ """
+ Performs validation on the loaded contents and provides back the
+ corrections. If validation is disabled then this won't provide any
+ results.
+ """
+
+ self.valsLock.acquire()
+
+ if not self.isLoaded(): returnVal = None
+ elif not CONFIG["features.torrc.validate"]: returnVal = {}
+ else:
+ if self.corrections == None:
+ self.corrections = validate(self.contents)
+
+ returnVal = dict(self.corrections)
+
+ self.valsLock.release()
+ return returnVal
+
+ def getLock(self):
+ """
+ Provides the lock governing concurrent access to the contents.
+ """
+
+ return self.valsLock
+
+def _testConfigDescriptions():
+ """
+ Tester for the loadOptionDescriptions function, fetching the man page
+ contents and dumping its parsed results.
+ """
+
+ loadOptionDescriptions()
+ sortedOptions = CONFIG_DESCRIPTIONS.keys()
+ sortedOptions.sort()
+
+ for i in range(len(sortedOptions)):
+ option = sortedOptions[i]
+ argument, description = getConfigDescription(option)
+ optLabel = "OPTION: \"%s\"" % option
+ argLabel = "ARGUMENT: \"%s\"" % argument
+
+ print " %-45s %s" % (optLabel, argLabel)
+ print "\"%s\"" % description
+ if i != len(sortedOptions) - 1: print "-" * 80
+
Deleted: arm/trunk/src/util/torrc.py
===================================================================
--- arm/trunk/src/util/torrc.py 2010-10-29 13:00:09 UTC (rev 23719)
+++ arm/trunk/src/util/torrc.py 2010-10-29 15:14:31 UTC (rev 23720)
@@ -1,455 +0,0 @@
-"""
-Helper functions for working with tor's configuration file.
-"""
-
-import curses
-import threading
-
-from util import sysTools, torTools, uiTools
-
-CONFIG = {"features.torrc.validate": True,
- "torrc.multiline": [],
- "torrc.alias": {},
- "torrc.label.size.b": [],
- "torrc.label.size.kb": [],
- "torrc.label.size.mb": [],
- "torrc.label.size.gb": [],
- "torrc.label.size.tb": [],
- "torrc.label.time.sec": [],
- "torrc.label.time.min": [],
- "torrc.label.time.hour": [],
- "torrc.label.time.day": [],
- "torrc.label.time.week": []}
-
-# enums and values for numeric torrc entries
-UNRECOGNIZED, SIZE_VALUE, TIME_VALUE = range(1, 4)
-SIZE_MULT = {"b": 1, "kb": 1024, "mb": 1048576, "gb": 1073741824, "tb": 1099511627776}
-TIME_MULT = {"sec": 1, "min": 60, "hour": 3600, "day": 86400, "week": 604800}
-
-# enums for issues found during torrc validation:
-# VAL_DUPLICATE - entry is ignored due to being a duplicate
-# VAL_MISMATCH - the value doesn't match tor's current state
-VAL_DUPLICATE, VAL_MISMATCH = range(1, 3)
-
-# descriptions of tor's configuration options fetched from its man page
-CONFIG_DESCRIPTIONS_LOCK = threading.RLock()
-CONFIG_DESCRIPTIONS = {}
-
-TORRC = None # singleton torrc instance
-MAN_OPT_INDENT = 7 # indentation before options in the man page
-MAN_EX_INDENT = 15 # indentation used for man page examples
-
-def loadConfig(config):
- CONFIG["torrc.multiline"] = config.get("torrc.multiline", [])
- CONFIG["torrc.alias"] = config.get("torrc.alias", {})
-
- # all the torrc.label.* values are comma separated lists
- for configKey in CONFIG.keys():
- if configKey.startswith("torrc.label."):
- configValues = config.get(configKey, "").split(",")
- if configValues: CONFIG[configKey] = [val.strip() for val in configValues]
-
-def getTorrc():
- """
- Singleton constructor for a Controller. Be aware that this starts as being
- unloaded, needing the torrc contents to be loaded before being functional.
- """
-
- global TORRC
- if TORRC == None: TORRC = Torrc()
- return TORRC
-
-def getConfigLocation():
- """
- Provides the location of the torrc, raising an IOError with the reason if the
- path can't be determined.
- """
-
- conn = torTools.getConn()
- configLocation = conn.getInfo("config-file")
- if not configLocation: raise IOError("unable to query the torrc location")
-
- # checks if this is a relative path, needing the tor pwd to be appended
- if configLocation[0] != "/":
- torPid = conn.getMyPid()
- failureMsg = "querying tor's pwd failed because %s"
- if not torPid: raise IOError(failureMsg % "we couldn't get the pid")
-
- try:
- # pwdx results are of the form:
- # 3799: /home/atagar
- # 5839: No such process
- results = sysTools.call("pwdx %s" % torPid)
- if not results:
- raise IOError(failureMsg % "pwdx didn't return any results")
- elif results[0].endswith("No such process"):
- raise IOError(failureMsg % ("pwdx reported no process for pid " + torPid))
- elif len(results) != 1 or results.count(" ") != 1:
- raise IOError(failureMsg % "we got unexpected output from pwdx")
- else:
- pwdPath = results[0][results[0].find(" ") + 1:]
- configLocation = "%s/%s" % (pwdPath, configLocation)
- except IOError, exc:
- raise IOError(failureMsg % ("the pwdx call failed: " + str(exc)))
-
- return torTools.getPathPrefix() + configLocation
-
-def validate(contents = None):
- """
- Performs validation on the given torrc contents, providing back a mapping of
- line numbers to tuples of the (issue, msg) found on them.
-
- Arguments:
- contents - torrc contents
- """
-
- conn = torTools.getConn()
- contents = _stripComments(contents)
- issuesFound, seenOptions = {}, []
- for lineNumber in range(len(contents) - 1, -1, -1):
- lineText = contents[lineNumber]
- if not lineText: continue
-
- lineComp = lineText.split(None, 1)
- if len(lineComp) == 2: option, value = lineComp
- else: option, value = lineText, ""
-
- # most parameters are overwritten if defined multiple times
- if option in seenOptions and not option in CONFIG["torrc.multiline"]:
- issuesFound[lineNumber] = (VAL_DUPLICATE, "")
- continue
- else: seenOptions.append(option)
-
- # replace aliases with their recognized representation
- if option in CONFIG["torrc.alias"]:
- option = CONFIG["torrc.alias"][option]
-
- # tor appears to replace tabs with a space, for instance:
- # "accept\t*:563" is read back as "accept *:563"
- value = value.replace("\t", " ")
-
- # parse value if it's a size or time, expanding the units
- value, valueType = _parseConfValue(value)
-
- # issues GETCONF to get the values tor's currently configured to use
- torValues = conn.getOption(option, [], True)
-
- # multiline entries can be comma separated values (for both tor and conf)
- valueList = [value]
- if option in CONFIG["torrc.multiline"]:
- valueList = [val.strip() for val in value.split(",")]
-
- fetchedValues, torValues = torValues, []
- for fetchedValue in fetchedValues:
- for fetchedEntry in fetchedValue.split(","):
- fetchedEntry = fetchedEntry.strip()
- if not fetchedEntry in torValues:
- torValues.append(fetchedEntry)
-
- for val in valueList:
- # checks if both the argument and tor's value are empty
- isBlankMatch = not val and not torValues
-
- if not isBlankMatch and not val in torValues:
- # converts corrections to reader friedly size values
- displayValues = torValues
- if valueType == SIZE_VALUE:
- displayValues = [uiTools.getSizeLabel(int(val)) for val in torValues]
- elif valueType == TIME_VALUE:
- displayValues = [uiTools.getTimeLabel(int(val)) for val in torValues]
-
- issuesFound[lineNumber] = (VAL_MISMATCH, ", ".join(displayValues))
-
- return issuesFound
-
-def loadOptionDescriptions():
- """
- Fetches and parses descriptions for tor's configuration options from its man
- page. This can be a somewhat lengthy call, and raises an IOError if issues
- occure.
- """
-
- CONFIG_DESCRIPTIONS_LOCK.acquire()
- CONFIG_DESCRIPTIONS.clear()
-
- raisedExc = None
- try:
- manCallResults = sysTools.call("man tor")
-
- lastOption, lastArg = None, None
- lastDescription = ""
- for line in manCallResults:
- strippedLine = line.strip()
-
- # we have content, but an indent less than an option (ignore line)
- if strippedLine and not line.startswith(" " * MAN_OPT_INDENT): continue
-
- # line starts with an indent equivilant to a new config option
- isOptIndent = line.startswith(" " * MAN_OPT_INDENT) and line[MAN_OPT_INDENT] != " "
-
- if isOptIndent:
- # Most lines with this indent that aren't config options won't have
- # any description set at this point (not a perfect filter, but cuts
- # down on the noise).
- strippedDescription = lastDescription.strip()
- if lastOption and strippedDescription:
- CONFIG_DESCRIPTIONS[lastOption] = (lastArg, strippedDescription)
- lastDescription = ""
-
- # parses the option and argument
- line = line.strip()
- divIndex = line.find(" ")
- if divIndex != -1:
- lastOption, lastArg = line[:divIndex], line[divIndex + 1:]
- else:
- # Appends the text to the running description. Empty lines and lines
- # starting with a specific indentation are used for formatting, for
- # instance the ExitPolicy and TestingTorNetwork entries.
- if lastDescription and lastDescription[-1] != "\n":
- lastDescription += " "
-
- if not strippedLine:
- lastDescription += "\n\n"
- elif line.startswith(" " * MAN_EX_INDENT):
- lastDescription += " %s\n" % strippedLine
- else: lastDescription += strippedLine
-
- except IOError, exc:
- raisedExc = exc
-
- CONFIG_DESCRIPTIONS_LOCK.release()
- if raisedExc: raise raisedExc
-
-def isConfigDescriptionAvailable(option):
- """
- Returns if a description for the given configuration option has been loaded
- or not.
-
- Arguments:
- option - tor config option
- """
-
- return option in CONFIG_DESCRIPTIONS
-
-def getConfigDescription(option):
- """
- Provides a tuple with arguments and description for the given tor
- configuration option, fetched from its man page. This provides None if no
- such option has been loaded. If the man page is in the process of being
- loaded then this call blocks until it finishes.
-
- Arguments:
- option - tor config option
- """
-
- CONFIG_DESCRIPTIONS_LOCK.acquire()
-
- if option in CONFIG_DESCRIPTIONS:
- returnVal = CONFIG_DESCRIPTIONS[option]
- else: returnVal = None
-
- CONFIG_DESCRIPTIONS_LOCK.release()
- return returnVal
-
-def _testConfigDescriptions():
- """
- Tester for the loadOptionDescriptions function, fetching the man page
- contents and dumping its parsed results.
- """
-
- loadOptionDescriptions()
- sortedOptions = CONFIG_DESCRIPTIONS.keys()
- sortedOptions.sort()
-
- for i in range(len(sortedOptions)):
- option = sortedOptions[i]
- argument, description = getConfigDescription(option)
- optLabel = "OPTION: \"%s\"" % option
- argLabel = "ARGUMENT: \"%s\"" % argument
-
- print " %-45s %s" % (optLabel, argLabel)
- print "\"%s\"" % description
- if i != len(sortedOptions) - 1: print "-" * 80
-
-def _parseConfValue(confArg):
- """
- Converts size or time values to their lowest units (bytes or seconds) which
- is what GETCONF calls provide. The returned is a tuple of the value and unit
- type.
-
- Arguments:
- confArg - torrc argument
- """
-
- if confArg.count(" ") == 1:
- val, unit = confArg.lower().split(" ", 1)
- if not val.isdigit(): return confArg, UNRECOGNIZED
- mult, multType = _getUnitType(unit)
-
- if mult != None:
- return str(int(val) * mult), multType
-
- return confArg, UNRECOGNIZED
-
-def _getUnitType(unit):
- """
- Provides the type and multiplier for an argument's unit. The multiplier is
- None if the unit isn't recognized.
-
- Arguments:
- unit - string representation of a unit
- """
-
- for label in SIZE_MULT:
- if unit in CONFIG["torrc.label.size." + label]:
- return SIZE_MULT[label], SIZE_VALUE
-
- for label in TIME_MULT:
- if unit in CONFIG["torrc.label.time." + label]:
- return TIME_MULT[label], TIME_VALUE
-
- return None, UNRECOGNIZED
-
-def _stripComments(contents):
- """
- Removes comments and extra whitespace from the given torrc contents.
-
- Arguments:
- contents - torrc contents
- """
-
- strippedContents = []
- for line in contents:
- if line and "#" in line: line = line[:line.find("#")]
- strippedContents.append(line.strip())
- return strippedContents
-
-class Torrc():
- """
- Wrapper for the torrc. All getters provide None if the contents are unloaded.
- """
-
- def __init__(self):
- self.contents = None
- self.configLocation = None
- self.valsLock = threading.RLock()
-
- # cached results for the current contents
- self.displayableContents = None
- self.strippedContents = None
- self.corrections = None
-
- def load(self):
- """
- Loads or reloads the torrc contents, raising an IOError if there's a
- problem.
- """
-
- self.valsLock.acquire()
-
- # clears contents and caches
- self.contents, self.configLocation = None, None
- self.displayableContents = None
- self.strippedContents = None
- self.corrections = None
-
- try:
- self.configLocation = getConfigLocation()
- configFile = open(self.configLocation, "r")
- self.contents = configFile.readlines()
- configFile.close()
- except IOError, exc:
- self.valsLock.release()
- raise exc
-
- self.valsLock.release()
-
- def isLoaded(self):
- """
- Provides true if there's loaded contents, false otherwise.
- """
-
- return self.contents != None
-
- def getConfigLocation(self):
- """
- Provides the location of the loaded configuration contents. This may be
- available, even if the torrc failed to be loaded.
- """
-
- return self.configLocation
-
- def getContents(self):
- """
- Provides the contents of the configuration file.
- """
-
- self.valsLock.acquire()
- returnVal = list(self.contents) if self.contents else None
- self.valsLock.relese()
- return returnVal
-
- def getDisplayContents(self, strip = False):
- """
- Provides the contents of the configuration file, formatted in a rendering
- frindly fashion:
- - Tabs print as three spaces. Keeping them as tabs is problematic for
- layouts since it's counted as a single character, but occupies several
- cells.
- - Strips control and unprintable characters.
-
- Arguments:
- strip - removes comments and extra whitespace if true
- """
-
- self.valsLock.acquire()
-
- if not self.isLoaded(): returnVal = None
- else:
- if self.displayableContents == None:
- # restricts contents to displayable characters
- self.displayableContents = []
-
- for lineNum in range(len(self.contents)):
- lineText = self.contents[lineNum]
- lineText = lineText.replace("\t", " ")
- lineText = "".join([char for char in lineText if curses.ascii.isprint(char)])
- self.displayableContents.append(lineText)
-
- if strip:
- if self.strippedContents == None:
- self.strippedContents = _stripComments(self.displayableContents)
-
- returnVal = list(self.strippedContents)
- else: returnVal = list(self.displayableContents)
-
- self.valsLock.release()
- return returnVal
-
- def getCorrections(self):
- """
- Performs validation on the loaded contents and provides back the
- corrections. If validation is disabled then this won't provide any
- results.
- """
-
- self.valsLock.acquire()
-
- if not self.isLoaded(): returnVal = None
- elif not CONFIG["features.torrc.validate"]: returnVal = {}
- else:
- if self.corrections == None:
- self.corrections = validate(self.contents)
-
- returnVal = dict(self.corrections)
-
- self.valsLock.release()
- return returnVal
-
- def getLock(self):
- """
- Provides the lock governing concurrent access to the contents.
- """
-
- return self.valsLock
-