[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Cleaning up response classes
commit d3fe6a1ed751a60b395f80aa7b4663ab6f78d230
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Mon May 28 16:38:30 2012 -0700
Cleaning up response classes
General refactoring of the response classes.
---
stem/connection.py | 28 ++++++------
stem/response/__init__.py | 2 +-
stem/response/getinfo.py | 19 ++++----
stem/response/protocolinfo.py | 81 +++++++++++++++++------------------
test/integ/response/protocolinfo.py | 6 +-
test/unit/response/protocolinfo.py | 7 +++-
6 files changed, 72 insertions(+), 71 deletions(-)
diff --git a/stem/connection.py b/stem/connection.py
index 34c9c68..c48a7b5 100644
--- a/stem/connection.py
+++ b/stem/connection.py
@@ -82,21 +82,7 @@ import stem.version
import stem.util.enum
import stem.util.system
import stem.util.log as log
-
-# Methods by which a controller can authenticate to the control port. Tor gives
-# a list of all the authentication methods it will accept in response to
-# PROTOCOLINFO queries.
-#
-# NONE - No authentication required
-# PASSWORD - See tor's HashedControlPassword option. Controllers must provide
-# the password used to generate the hash.
-# COOKIE - See tor's CookieAuthentication option. Controllers need to supply
-# the contents of the cookie file.
-# UNKNOWN - Tor provided one or more authentication methods that we don't
-# recognize. This is probably from a new addition to the control
-# protocol.
-
-AuthMethod = stem.util.enum.Enum("NONE", "PASSWORD", "COOKIE", "UNKNOWN")
+from stem.response.protocolinfo import AuthMethod
class AuthenticationFailure(Exception):
"""
@@ -628,6 +614,13 @@ def get_protocolinfo(controller):
the tor process running on it. If the socket is already closed then it is
first reconnected.
+ According to the control spec the cookie_file is an absolute path. However,
+ this often is not the case (especially for the Tor Browser Bundle)...
+ https://trac.torproject.org/projects/tor/ticket/1101
+
+ If the path is relative then we'll make an attempt (which may not work) to
+ correct this.
+
Arguments:
controller (stem.socket.ControlSocket or stem.control.BaseController) -
tor controller connection
@@ -659,6 +652,11 @@ def get_protocolinfo(controller):
stem.response.convert("PROTOCOLINFO", protocolinfo_response)
+ # attempt to expand relative cookie paths
+
+ if protocolinfo_response.cookie_path:
+ stem.connection._expand_cookie_path(protocolinfo_response, stem.util.system.get_pid_by_name, "tor")
+
# attempt to expand relative cookie paths via the control port or socket file
if isinstance(controller, stem.socket.ControlSocket):
diff --git a/stem/response/__init__.py b/stem/response/__init__.py
index 92b9846..ab38747 100644
--- a/stem/response/__init__.py
+++ b/stem/response/__init__.py
@@ -1,7 +1,7 @@
"""
Parses replies from the control socket.
-converts - translates a ControlMessage into a particular response subclass
+convert - translates a ControlMessage into a particular response subclass
"""
__all__ = ["getinfo", "protocolinfo"]
diff --git a/stem/response/getinfo.py b/stem/response/getinfo.py
index dabbe12..620ca02 100644
--- a/stem/response/getinfo.py
+++ b/stem/response/getinfo.py
@@ -22,26 +22,25 @@ class GetInfoResponse(stem.socket.ControlMessage):
# 250 OK
self.entries = {}
+ remaining_lines = list(self)
- lines = list(self)
-
- if not self.is_ok() or not lines.pop() == "OK":
+ if not self.is_ok() or not remaining_lines.pop() == "OK":
raise stem.socket.ProtocolError("GETINFO response didn't have an OK status:\n%s" % self)
- for line in lines:
- if not "=" in line:
+ while remaining_lines:
+ try:
+ key, value = remaining_lines.pop(0).split("=", 1)
+ except ValueError:
raise stem.socket.ProtocolError("GETINFO replies should only contain parameter=value mappings:\n%s" % self)
- key, value = line.split("=", 1)
-
# if the value is a multiline value then it *must* be of the form
# '<key>=\n<value>'
if "\n" in value:
- if value.startswith("\n"):
- value = value[1:]
- else:
+ if not value.startswith("\n"):
raise stem.socket.ProtocolError("GETINFO response contained a multiline value that didn't start with a newline:\n%s" % self)
+
+ value = value[1:]
self.entries[key] = value
diff --git a/stem/response/protocolinfo.py b/stem/response/protocolinfo.py
index 97ecfb7..7ead680 100644
--- a/stem/response/protocolinfo.py
+++ b/stem/response/protocolinfo.py
@@ -1,19 +1,27 @@
-import stem.connection
import stem.socket
import stem.version
+import stem.util.enum
import stem.util.log as log
+# Methods by which a controller can authenticate to the control port. Tor gives
+# a list of all the authentication methods it will accept in response to
+# PROTOCOLINFO queries.
+#
+# NONE - No authentication required
+# PASSWORD - See tor's HashedControlPassword option. Controllers must provide
+# the password used to generate the hash.
+# COOKIE - See tor's CookieAuthentication option. Controllers need to supply
+# the contents of the cookie file.
+# UNKNOWN - Tor provided one or more authentication methods that we don't
+# recognize. This is probably from a new addition to the control
+# protocol.
+
+AuthMethod = stem.util.enum.Enum("NONE", "PASSWORD", "COOKIE", "UNKNOWN")
+
class ProtocolInfoResponse(stem.socket.ControlMessage):
"""
Version one PROTOCOLINFO query response.
- According to the control spec the cookie_file is an absolute path. However,
- this often is not the case (especially for the Tor Browser Bundle)...
- https://trac.torproject.org/projects/tor/ticket/1101
-
- If the path is relative then we'll make an attempt (which may not work) to
- correct this.
-
The protocol_version is the only mandatory data for a valid PROTOCOLINFO
response, so all other values are None if undefined or empty if a collection.
@@ -34,20 +42,22 @@ class ProtocolInfoResponse(stem.socket.ControlMessage):
self.protocol_version = None
self.tor_version = None
+ self.auth_methods = ()
+ self.unknown_auth_methods = ()
self.cookie_path = None
auth_methods, unknown_auth_methods = [], []
- lines = list(self)
+ remaining_lines = list(self)
- if not self.is_ok() or not lines.pop() == "OK":
- raise stem.socket.ProtocolError("GETINFO response didn't have an OK status:\n%s" % self)
+ if not self.is_ok() or not remaining_lines.pop() == "OK":
+ raise stem.socket.ProtocolError("PROTOCOLINFO response didn't have an OK status:\n%s" % self)
# sanity check that we're a PROTOCOLINFO response
- if not lines[0].startswith("PROTOCOLINFO"):
- msg = "Message is not a PROTOCOLINFO response (%s)" % self
- raise stem.socket.ProtocolError(msg)
+ if not remaining_lines[0].startswith("PROTOCOLINFO"):
+ raise stem.socket.ProtocolError("Message is not a PROTOCOLINFO response:\n%s" % self)
- for line in lines:
+ while remaining_lines:
+ line = remaining_lines.pop(0)
line_type = line.pop()
if line_type == "PROTOCOLINFO":
@@ -56,16 +66,12 @@ class ProtocolInfoResponse(stem.socket.ControlMessage):
# PIVERSION = 1*DIGIT
if line.is_empty():
- msg = "PROTOCOLINFO response's initial line is missing the protocol version: %s" % line
- raise stem.socket.ProtocolError(msg)
+ raise stem.socket.ProtocolError("PROTOCOLINFO response's initial line is missing the protocol version: %s" % line)
- piversion = line.pop()
-
- if not piversion.isdigit():
- msg = "PROTOCOLINFO response version is non-numeric: %s" % line
- raise stem.socket.ProtocolError(msg)
-
- self.protocol_version = int(piversion)
+ try:
+ self.protocol_version = int(line.pop())
+ except ValueError:
+ raise stem.socket.ProtocolError("PROTOCOLINFO response version is non-numeric: %s" % line)
# The piversion really should be "1" but, according to the spec, tor
# does not necessarily need to provide the PROTOCOLINFO version that we
@@ -83,49 +89,42 @@ class ProtocolInfoResponse(stem.socket.ControlMessage):
# parse AuthMethod mapping
if not line.is_next_mapping("METHODS"):
- msg = "PROTOCOLINFO response's AUTH line is missing its mandatory 'METHODS' mapping: %s" % line
- raise stem.socket.ProtocolError(msg)
+ raise stem.socket.ProtocolError("PROTOCOLINFO response's AUTH line is missing its mandatory 'METHODS' mapping: %s" % line)
for method in line.pop_mapping()[1].split(","):
if method == "NULL":
- auth_methods.append(stem.connection.AuthMethod.NONE)
+ auth_methods.append(AuthMethod.NONE)
elif method == "HASHEDPASSWORD":
- auth_methods.append(stem.connection.AuthMethod.PASSWORD)
+ auth_methods.append(AuthMethod.PASSWORD)
elif method == "COOKIE":
- auth_methods.append(stem.connection.AuthMethod.COOKIE)
+ auth_methods.append(AuthMethod.COOKIE)
else:
unknown_auth_methods.append(method)
- message_id = "stem.connection.unknown_auth_%s" % method
+ message_id = "stem.response.protocolinfo.unknown_auth_%s" % method
log.log_once(message_id, log.INFO, "PROTOCOLINFO response included a type of authentication that we don't recognize: %s" % method)
# our auth_methods should have a single AuthMethod.UNKNOWN entry if
# any unknown authentication methods exist
- if not stem.connection.AuthMethod.UNKNOWN in auth_methods:
- auth_methods.append(stem.connection.AuthMethod.UNKNOWN)
+ if not AuthMethod.UNKNOWN in auth_methods:
+ auth_methods.append(AuthMethod.UNKNOWN)
# parse optional COOKIEFILE mapping (quoted and can have escapes)
if line.is_next_mapping("COOKIEFILE", True, True):
self.cookie_path = line.pop_mapping(True, True)[1]
-
- # attempt to expand relative cookie paths
- stem.connection._expand_cookie_path(self, stem.util.system.get_pid_by_name, "tor")
elif line_type == "VERSION":
# Line format:
# VersionLine = "250-VERSION" SP "Tor=" TorVersion OptArguments CRLF
# TorVersion = QuotedString
if not line.is_next_mapping("Tor", True):
- msg = "PROTOCOLINFO response's VERSION line is missing its mandatory tor version mapping: %s" % line
- raise stem.socket.ProtocolError(msg)
-
- torversion = line.pop_mapping(True)[1]
+ raise stem.socket.ProtocolError("PROTOCOLINFO response's VERSION line is missing its mandatory tor version mapping: %s" % line)
try:
- self.tor_version = stem.version.Version(torversion)
+ self.tor_version = stem.version.Version(line.pop_mapping(True)[1])
except ValueError, exc:
raise stem.socket.ProtocolError(exc)
else:
- log.debug("unrecognized PROTOCOLINFO line type '%s', ignoring entry: %s" % (line_type, line))
+ log.debug("Unrecognized PROTOCOLINFO line type '%s', ignoring it: %s" % (line_type, line))
self.auth_methods = tuple(auth_methods)
self.unknown_auth_methods = tuple(unknown_auth_methods)
diff --git a/test/integ/response/protocolinfo.py b/test/integ/response/protocolinfo.py
index f5eb518..e8e8c12 100644
--- a/test/integ/response/protocolinfo.py
+++ b/test/integ/response/protocolinfo.py
@@ -119,7 +119,7 @@ class TestProtocolInfo(unittest.TestCase):
auth_methods, auth_cookie_path = [], None
if test.runner.Torrc.COOKIE in tor_options:
- auth_methods.append(stem.connection.AuthMethod.COOKIE)
+ auth_methods.append(stem.response.protocolinfo.AuthMethod.COOKIE)
chroot_path = runner.get_chroot()
auth_cookie_path = runner.get_auth_cookie_path()
@@ -127,10 +127,10 @@ class TestProtocolInfo(unittest.TestCase):
auth_cookie_path = auth_cookie_path[len(chroot_path):]
if test.runner.Torrc.PASSWORD in tor_options:
- auth_methods.append(stem.connection.AuthMethod.PASSWORD)
+ auth_methods.append(stem.response.protocolinfo.AuthMethod.PASSWORD)
if not auth_methods:
- auth_methods.append(stem.connection.AuthMethod.NONE)
+ auth_methods.append(stem.response.protocolinfo.AuthMethod.NONE)
self.assertEqual((), protocolinfo_response.unknown_auth_methods)
self.assertEqual(tuple(auth_methods), protocolinfo_response.auth_methods)
diff --git a/test/unit/response/protocolinfo.py b/test/unit/response/protocolinfo.py
index a17c58c..1d282ea 100644
--- a/test/unit/response/protocolinfo.py
+++ b/test/unit/response/protocolinfo.py
@@ -4,7 +4,6 @@ Unit tests for the stem.response.protocolinfo.ProtocolInfoResponse class.
import unittest
-from stem.connection import AuthMethod
import stem.socket
import stem.version
import stem.util.proc
@@ -12,6 +11,7 @@ import stem.util.system
import stem.response
import stem.response.protocolinfo
import test.mocking as mocking
+from stem.response.protocolinfo import AuthMethod
NO_AUTH = """250-PROTOCOLINFO 1
250-AUTH METHODS=NULL
@@ -148,6 +148,8 @@ class TestProtocolInfoResponse(unittest.TestCase):
succeeds and fails.
"""
+ # TODO: move into stem.connection unit tests?
+
# we need to mock both pid and cwd lookups since the general cookie
# expanion works by...
# - resolving the pid of the "tor" process
@@ -165,6 +167,9 @@ class TestProtocolInfoResponse(unittest.TestCase):
control_message = mocking.get_message(RELATIVE_COOKIE_PATH)
stem.response.convert("PROTOCOLINFO", control_message)
+
+ stem.connection._expand_cookie_path(control_message, stem.util.system.get_pid_by_name, "tor")
+
self.assertEquals("/tmp/foo/tor-browser_en-US/Data/control_auth_cookie", control_message.cookie_path)
# exercise cookie expansion where both calls fail (should work, just
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits