[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[bridgedb/develop] Move bridgedb.Dist.EmailBasedDistributor → bridgedb.email.distributor.
commit 55f36f523dae951e2c0e57cdfdc84e3a78ba7cbe
Author: Isis Lovecruft <isis@xxxxxxxxxxxxxx>
Date: Tue Apr 21 21:41:20 2015 +0000
Move bridgedb.Dist.EmailBasedDistributor â?? bridgedb.email.distributor.
---
doc/sphinx/source/bridgedb.Dist.rst | 8 -
doc/sphinx/source/bridgedb.email.rst | 1 +
doc/sphinx/source/bridgedb.rst | 1 -
doc/sphinx/source/conf.py | 2 +-
lib/bridgedb/Dist.py | 186 ----------------------
lib/bridgedb/Main.py | 9 +-
lib/bridgedb/email/autoresponder.py | 8 +-
lib/bridgedb/email/distributor.py | 209 +++++++++++++++++++++++++
lib/bridgedb/email/request.py | 6 +-
lib/bridgedb/email/server.py | 4 +-
lib/bridgedb/email/templates.py | 2 +-
lib/bridgedb/parse/addr.py | 7 +-
lib/bridgedb/persistent.py | 4 +-
lib/bridgedb/test/email_helpers.py | 14 +-
lib/bridgedb/test/legacy_Tests.py | 43 +----
lib/bridgedb/test/test_email_autoresponder.py | 2 +-
lib/bridgedb/test/test_email_distributor.py | 93 +++++++++++
lib/bridgedb/test/test_email_server.py | 6 +-
18 files changed, 340 insertions(+), 265 deletions(-)
diff --git a/doc/sphinx/source/bridgedb.Dist.rst b/doc/sphinx/source/bridgedb.Dist.rst
deleted file mode 100644
index 995a7ac..0000000
--- a/doc/sphinx/source/bridgedb.Dist.rst
+++ /dev/null
@@ -1,8 +0,0 @@
-bridgedb.Dist
-----------------
-
-.. automodule:: bridgedb.Dist
- :members:
- :undoc-members:
- :private-members:
- :show-inheritance:
diff --git a/doc/sphinx/source/bridgedb.email.rst b/doc/sphinx/source/bridgedb.email.rst
index ba9b4a9..020b614 100644
--- a/doc/sphinx/source/bridgedb.email.rst
+++ b/doc/sphinx/source/bridgedb.email.rst
@@ -8,6 +8,7 @@ bridgedb.email
.. automodule:: bridgedb.email.__init__
.. automodule:: bridgedb.email.autoresponder
+.. automodule:: bridgedb.email.distributor
.. automodule:: bridgedb.email.dkim
.. automodule:: bridgedb.email.request
.. automodule:: bridgedb.email.server
diff --git a/doc/sphinx/source/bridgedb.rst b/doc/sphinx/source/bridgedb.rst
index 7b1fef7..207313c 100644
--- a/doc/sphinx/source/bridgedb.rst
+++ b/doc/sphinx/source/bridgedb.rst
@@ -13,7 +13,6 @@ BridgeDB Package and Module Documentation
bridgedb.captcha
bridgedb.configure
bridgedb.crypto
- bridgedb.Dist
bridgedb.email
bridgedb.filters
bridgedb.geo
diff --git a/doc/sphinx/source/conf.py b/doc/sphinx/source/conf.py
index 5038abb..cab614f 100644
--- a/doc/sphinx/source/conf.py
+++ b/doc/sphinx/source/conf.py
@@ -33,9 +33,9 @@ import bridgedb.captcha
import bridgedb.Bridges
import bridgedb.Bucket
import bridgedb.crypto
-import bridgedb.Dist
import bridgedb.email
import bridgedb.email.autoresponder
+import bridgedb.email.distributor
import bridgedb.email.dkim
import bridgedb.email.request
import bridgedb.email.server
diff --git a/lib/bridgedb/Dist.py b/lib/bridgedb/Dist.py
deleted file mode 100644
index 1be705f..0000000
--- a/lib/bridgedb/Dist.py
+++ /dev/null
@@ -1,186 +0,0 @@
-# -*- coding: utf-8 ; test-case-name: bridgedb.test.test_Dist -*-
-#
-# This file is part of BridgeDB, a Tor bridge distribution system.
-#
-# :authors: Nick Mathewson
-# Isis Lovecruft 0xA3ADB67A2CDB8B35 <isis@xxxxxxxxxxxxxx>
-# Matthew Finkel 0x017DD169EA793BE2 <sysrqb@xxxxxxxxxxxxxx>
-# :copyright: (c) 2013-2015, Isis Lovecruft
-# (c) 2013-2015, Matthew Finkel
-# (c) 2007-2015, The Tor Project, Inc.
-# :license: 3-Clause BSD, see LICENSE for licensing information
-
-"""This module has functions to decide which bridges to hand out to whom."""
-
-import logging
-import time
-
-import bridgedb.Storage
-
-from bridgedb.Bridges import BridgeRing
-from bridgedb.Bridges import FilteredBridgeSplitter
-from bridgedb.crypto import getHMAC
-from bridgedb.crypto import getHMACFunc
-from bridgedb.distribute import Distributor
-from bridgedb.filters import byFilters
-from bridgedb.filters import byIPv4
-from bridgedb.filters import byIPv6
-from bridgedb.filters import bySubring
-from bridgedb.parse import addr
-
-
-MAX_EMAIL_RATE = 3*3600
-
-class IgnoreEmail(addr.BadEmail):
- """Raised when we get requests from this address after rate warning."""
-
-class TooSoonEmail(addr.BadEmail):
- """Raised when we got a request from this address too recently."""
-
-class EmailRequestedHelp(Exception):
- """Raised when a client has emailed requesting help."""
-
-class EmailRequestedKey(Exception):
- """Raised when an incoming email requested a copy of our GnuPG keys."""
-
-
-class EmailBasedDistributor(Distributor):
- """Object that hands out bridges based on the email address of an incoming
- request and the current time period.
-
- :type hashring: :class:`~bridgedb.Bridges.BridgeRing`
- :ivar hashring: A hashring to hold all the bridges we hand out.
- """
-
- def __init__(self, key, domainmap, domainrules,
- answerParameters=None, whitelist=None):
- """Create a bridge distributor which uses email.
-
- :type emailHmac: callable
- :param emailHmac: An hmac function used to order email addresses
- within a ring. See :func:`~bridgedb.crypto.getHMACFunc`.
- :param dict domainmap: A map from lowercase domains that we support
- mail from to their canonical forms. See `EMAIL_DOMAIN_MAP` option
- in `bridgedb.conf`.
- :param domainrules: DOCDOC
- :param answerParameters: DOCDOC
- :type whitelist: dict or ``None``
- :param whitelist: A dictionary that maps whitelisted email addresses
- to GnuPG fingerprints.
- """
- super(EmailBasedDistributor, self).__init__(key)
-
- key1 = getHMAC(key, "Map-Addresses-To-Ring")
- self.emailHmac = getHMACFunc(key1, hex=False)
-
- key2 = getHMAC(key, "Order-Bridges-In-Ring")
- # XXXX clear the store when the period rolls over!
- self.domainmap = domainmap
- self.domainrules = domainrules
- self.whitelist = whitelist or dict()
- self.answerParameters = answerParameters
-
- #XXX cache options not implemented
- self.hashring = FilteredBridgeSplitter(key2, max_cached_rings=5)
- self.name = "Email"
-
- def bridgesPerResponse(self, hashring=None):
- return super(EmailBasedDistributor, self).bridgesPerResponse(hashring)
-
- def insert(self, bridge):
- """Assign a bridge to this distributor."""
- self.hashring.insert(bridge)
-
- def getBridges(self, bridgeRequest, interval):
- """Return a list of bridges to give to a user.
-
- :type bridgeRequest: :class:`~bridgedb.email.request.EmailBridgeRequest`
- :param bridgeRequest: A :class:`~bridgedb.bridgerequest.BridgeRequestBase`
- with the :data:`~bridgedb.bridgerequest.BridgeRequestBase.client`
- attribute set to a string containing the client's full, canonicalized
- email address.
- :param interval: The time period when we got this request. This can be
- any string, so long as it changes with every period.
- """
- # All checks on the email address, such as checks for whitelisting and
- # canonicalization of domain name, are done in
- # :meth:`bridgedb.email.autoresponder.getMailTo` and
- # :meth:`bridgedb.email.autoresponder.SMTPAutoresponder.runChecks`.
- if (not bridgeRequest.client) or (bridgeRequest.client == 'default'):
- raise addr.BadEmail(
- ("%s distributor can't get bridges for invalid email email "
- " address: %s") % (self.name, bridgeRequest.client))
-
- logging.info("Attempting to get bridges for %s..." % bridgeRequest.client)
-
- now = time.time()
-
- with bridgedb.Storage.getDB() as db:
- wasWarned = db.getWarnedEmail(bridgeRequest.client)
- lastSaw = db.getEmailTime(bridgeRequest.client)
- if lastSaw is not None:
- if bridgeRequest.client in self.whitelist.keys():
- logging.info(("Whitelisted email address %s was last seen "
- "%d seconds ago.")
- % (bridgeRequest.client, now - lastSaw))
- elif (lastSaw + MAX_EMAIL_RATE) >= now:
- wait = (lastSaw + MAX_EMAIL_RATE) - now
- logging.info("Client %s must wait another %d seconds."
- % (bridgeRequest.client, wait))
- if wasWarned:
- raise IgnoreEmail("Client was warned.",
- bridgeRequest.client)
- else:
- logging.info("Sending duplicate request warning.")
- db.setWarnedEmail(bridgeRequest.client, True, now)
- db.commit()
- raise TooSoonEmail("Must wait %d seconds" % wait,
- bridgeRequest.client)
- # warning period is over
- elif wasWarned:
- db.setWarnedEmail(bridgeRequest.client, False)
-
- pos = self.emailHmac("<%s>%s" % (interval, bridgeRequest.client))
-
- ring = None
- ruleset = frozenset(bridgeRequest.filters)
- if ruleset in self.hashring.filterRings.keys():
- logging.debug("Cache hit %s" % ruleset)
- _, ring = self.hashring.filterRings[ruleset]
- else:
- # cache miss, add new ring
- logging.debug("Cache miss %s" % ruleset)
-
- # add new ring
- key1 = getHMAC(self.key, "Order-Bridges-In-Ring")
- ring = BridgeRing(key1, self.answerParameters)
- self.hashring.addRing(ring, ruleset, byFilters(ruleset),
- populate_from=self.hashring.bridges)
-
- returnNum = self.bridgesPerResponse(ring)
- result = ring.getBridges(pos, returnNum)
-
- db.setEmailTime(bridgeRequest.client, now)
- db.commit()
-
- return result
-
- def cleanDatabase(self):
- with bridgedb.Storage.getDB() as db:
- try:
- db.cleanEmailedBridges(time.time() - MAX_EMAIL_RATE)
- db.cleanWarnedEmails(time.time() - MAX_EMAIL_RATE)
- except:
- db.rollback()
- raise
- else:
- db.commit()
-
- def prepopulateRings(self):
- # populate all rings (for dumping assignments and testing)
- for filterFn in [byIPv4, byIPv6]:
- ruleset = frozenset([filterFn])
- key1 = getHMAC(self.key, "Order-Bridges-In-Ring")
- ring = BridgeRing(key1, self.answerParameters)
- self.hashring.addRing(ring, ruleset, byFilters([filterFn]),
- populate_from=self.hashring.bridges)
diff --git a/lib/bridgedb/Main.py b/lib/bridgedb/Main.py
index 3a15145..b281d21 100644
--- a/lib/bridgedb/Main.py
+++ b/lib/bridgedb/Main.py
@@ -30,13 +30,13 @@ from bridgedb.bridges import ServerDescriptorDigestMismatch
from bridgedb.bridges import ServerDescriptorWithoutNetworkstatus
from bridgedb.bridges import Bridge
from bridgedb.configure import loadConfig
+from bridgedb.email.distributor import EmailDistributor
from bridgedb.https.distributor import HTTPSDistributor
from bridgedb.parse import descriptors
import bridgedb.Storage
from bridgedb import Bridges
-from bridgedb import Dist
from bridgedb.Stability import updateBridgeHistory
@@ -197,7 +197,7 @@ def createBridgeRings(cfg, proxyList, key):
:rtype: tuple
:returns: A BridgeSplitter hashring, an
:class:`~bridgedb.https.distributor.HTTPSDistributor` or None, and an
- EmailBasedDistributor or None.
+ :class:`~bridgedb.email.distributor.EmailDistributor` or None.
"""
# Create a BridgeSplitter to assign the bridges to the different
# distributors.
@@ -222,7 +222,7 @@ def createBridgeRings(cfg, proxyList, key):
# As appropriate, create an email-based distributor.
if cfg.EMAIL_DIST and cfg.EMAIL_SHARE:
logging.debug("Setting up Email Distributor...")
- emailDistributor = Dist.EmailBasedDistributor(
+ emailDistributor = EmailDistributor(
crypto.getHMAC(key, "Email-Dist-Key"),
cfg.EMAIL_DOMAIN_MAP.copy(),
cfg.EMAIL_DOMAIN_RULES.copy(),
@@ -333,7 +333,8 @@ def run(options, reactor=reactor):
known open proxies.
:ivar ipDistributor: A
:class:`~bridgedb.https.distributor.HTTPSDistributor`.
- :ivar emailDistributor: A :class:`Dist.EmailBasedDistributor`.
+ :ivar emailDistributor: A
+ :class:`~bridgedb.email.distributor.EmailDistributor`.
:ivar dict tasks: A dictionary of ``{name: task}``, where name is a
string to associate with the ``task``, and ``task`` is some
scheduled event, repetitive or otherwise, for the :class:`reactor
diff --git a/lib/bridgedb/email/autoresponder.py b/lib/bridgedb/email/autoresponder.py
index b98717f..ad63bfd 100644
--- a/lib/bridgedb/email/autoresponder.py
+++ b/lib/bridgedb/email/autoresponder.py
@@ -47,13 +47,13 @@ from twisted.python import failure
from bridgedb import safelog
from bridgedb.crypto import NEW_BUFFER_INTERFACE
-from bridgedb.Dist import EmailRequestedHelp
-from bridgedb.Dist import EmailRequestedKey
-from bridgedb.Dist import TooSoonEmail
-from bridgedb.Dist import IgnoreEmail
from bridgedb.email import dkim
from bridgedb.email import request
from bridgedb.email import templates
+from bridgedb.email.distributor import EmailRequestedHelp
+from bridgedb.email.distributor import EmailRequestedKey
+from bridgedb.email.distributor import TooSoonEmail
+from bridgedb.email.distributor import IgnoreEmail
from bridgedb.parse import addr
from bridgedb.parse.addr import canonicalizeEmailDomain
from bridgedb.util import levenshteinDistance
diff --git a/lib/bridgedb/email/distributor.py b/lib/bridgedb/email/distributor.py
new file mode 100644
index 0000000..b73c082
--- /dev/null
+++ b/lib/bridgedb/email/distributor.py
@@ -0,0 +1,209 @@
+# -*- coding: utf-8 ; test-case-name: bridgedb.test.test_email_distributor -*-
+#
+# This file is part of BridgeDB, a Tor bridge distribution system.
+#
+# :authors: Nick Mathewson
+# Isis Lovecruft 0xA3ADB67A2CDB8B35 <isis@xxxxxxxxxxxxxx>
+# Matthew Finkel 0x017DD169EA793BE2 <sysrqb@xxxxxxxxxxxxxx>
+# :copyright: (c) 2013-2015, Isis Lovecruft
+# (c) 2013-2015, Matthew Finkel
+# (c) 2007-2015, The Tor Project, Inc.
+# :license: see LICENSE for licensing information
+
+"""A :class:`~bridgedb.distribute.Distributor` which hands out
+:class:`bridges <bridgedb.bridges.Bridge>` to clients via an email interface.
+"""
+
+import logging
+import time
+
+import bridgedb.Storage
+
+from bridgedb.Bridges import BridgeRing
+from bridgedb.Bridges import FilteredBridgeSplitter
+from bridgedb.crypto import getHMAC
+from bridgedb.crypto import getHMACFunc
+from bridgedb.distribute import Distributor
+from bridgedb.filters import byFilters
+from bridgedb.filters import byIPv4
+from bridgedb.filters import byIPv6
+from bridgedb.filters import bySubring
+from bridgedb.parse import addr
+
+
+#: The minimum amount of time (in seconds) which must pass before a client who
+#: has previously been given an email response must wait before being eligible
+#: to receive another response.
+MAX_EMAIL_RATE = 3 * 3600
+
+
+class IgnoreEmail(addr.BadEmail):
+ """Raised when we get requests from this address after rate warning."""
+
+
+class TooSoonEmail(addr.BadEmail):
+ """Raised when we got a request from this address too recently."""
+
+
+class EmailRequestedHelp(Exception):
+ """Raised when a client has emailed requesting help."""
+
+
+class EmailRequestedKey(Exception):
+ """Raised when an incoming email requested a copy of our GnuPG keys."""
+
+
+class EmailDistributor(Distributor):
+ """Object that hands out bridges based on the email address of an incoming
+ request and the current time period.
+
+ :type hashring: :class:`~bridgedb.Bridges.BridgeRing`
+ :ivar hashring: A hashring to hold all the bridges we hand out.
+ """
+
+ #: The minimum amount of time (in seconds) which must pass before a client
+ #: who has previously been given an email response must wait before being
+ #: eligible to receive another response.
+ emailRateMax = MAX_EMAIL_RATE
+
+ def __init__(self, key, domainmap, domainrules,
+ answerParameters=None, whitelist=None):
+ """Create a bridge distributor which uses email.
+
+ :type emailHmac: callable
+ :param emailHmac: An hmac function used to order email addresses
+ within a ring. See :func:`~bridgedb.crypto.getHMACFunc`.
+ :param dict domainmap: A map from lowercase domains that we support
+ mail from to their canonical forms. See `EMAIL_DOMAIN_MAP` option
+ in `bridgedb.conf`.
+ :param domainrules: DOCDOC
+ :param answerParameters: DOCDOC
+ :type whitelist: dict or ``None``
+ :param whitelist: A dictionary that maps whitelisted email addresses
+ to GnuPG fingerprints.
+ """
+ super(EmailDistributor, self).__init__(key)
+
+ self.domainmap = domainmap
+ self.domainrules = domainrules
+ self.whitelist = whitelist or dict()
+ self.answerParameters = answerParameters
+
+ key1 = getHMAC(key, "Map-Addresses-To-Ring")
+ key2 = getHMAC(key, "Order-Bridges-In-Ring")
+
+ self.emailHmac = getHMACFunc(key1, hex=False)
+ #XXX cache options not implemented
+ self.hashring = FilteredBridgeSplitter(key2, max_cached_rings=5)
+
+ self.name = "Email"
+
+ def bridgesPerResponse(self, hashring=None):
+ return super(EmailDistributor, self).bridgesPerResponse(hashring)
+
+ def getBridges(self, bridgeRequest, interval):
+ """Return a list of bridges to give to a user.
+
+ .. hint:: All checks on the email address (which should be stored in
+ the ``bridgeRequest.client`` attribute), such as checks for
+ whitelisting and canonicalization of domain name, are done in
+ :meth:`bridgedb.email.autoresponder.getMailTo` and
+ :meth:`bridgedb.email.autoresponder.SMTPAutoresponder.runChecks`.
+
+ :type bridgeRequest:
+ :class:`~bridgedb.email.request.EmailBridgeRequest`
+ :param bridgeRequest: A
+ :class:`~bridgedb.bridgerequest.BridgeRequestBase` with the
+ :data:`~bridgedb.bridgerequest.BridgeRequestBase.client` attribute
+ set to a string containing the client's full, canonicalized email
+ address.
+ :param interval: The time period when we got this request. This can be
+ any string, so long as it changes with every period.
+ """
+ if (not bridgeRequest.client) or (bridgeRequest.client == 'default'):
+ raise addr.BadEmail(
+ ("%s distributor can't get bridges for invalid email address: "
+ "%s") % (self.name, bridgeRequest.client), bridgeRequest.client)
+
+ logging.info("Attempting to get bridges for %s..." % bridgeRequest.client)
+
+ now = time.time()
+
+ with bridgedb.Storage.getDB() as db:
+ wasWarned = db.getWarnedEmail(bridgeRequest.client)
+ lastSaw = db.getEmailTime(bridgeRequest.client)
+ if lastSaw is not None:
+ if bridgeRequest.client in self.whitelist:
+ logging.info(
+ "Whitelisted address %s was last seen %d seconds ago."
+ % (bridgeRequest.client, now - lastSaw))
+ elif (lastSaw + self.emailRateMax) >= now:
+ wait = (lastSaw + self.emailRateMax) - now
+ logging.info("Client %s must wait another %d seconds."
+ % (bridgeRequest.client, wait))
+ if wasWarned:
+ raise IgnoreEmail(
+ "Client %s was warned." % bridgeRequest.client,
+ bridgeRequest.client)
+ else:
+ logging.info("Sending duplicate request warning.")
+ db.setWarnedEmail(bridgeRequest.client, True, now)
+ db.commit()
+ raise TooSoonEmail("Must wait %d seconds" % wait,
+ bridgeRequest.client)
+ # warning period is over
+ elif wasWarned:
+ db.setWarnedEmail(bridgeRequest.client, False)
+
+ pos = self.emailHmac("<%s>%s" % (interval, bridgeRequest.client))
+
+ ring = None
+ filtres = frozenset(bridgeRequest.filters)
+ if filtres in self.hashring.filterRings:
+ logging.debug("Cache hit %s" % filtres)
+ _, ring = self.hashring.filterRings[filtres]
+ else:
+ logging.debug("Cache miss %s" % filtres)
+ key = getHMAC(self.key, "Order-Bridges-In-Ring")
+ ring = BridgeRing(key, self.answerParameters)
+ self.hashring.addRing(ring, filtres, byFilters(filtres),
+ populate_from=self.hashring.bridges)
+
+ returnNum = self.bridgesPerResponse(ring)
+ result = ring.getBridges(pos, returnNum)
+
+ db.setEmailTime(bridgeRequest.client, now)
+ db.commit()
+
+ return result
+
+ def cleanDatabase(self):
+ """Clear all emailed response and warning times from the database."""
+ logging.info(("Cleaning all response and warning times for the %s "
+ "distributor from the database...") % self.name)
+ with bridgedb.Storage.getDB() as db:
+ try:
+ db.cleanEmailedBridges(time.time() - self.emailRateMax)
+ db.cleanWarnedEmails(time.time() - self.emailRateMax)
+ except:
+ db.rollback()
+ raise
+ else:
+ db.commit()
+
+ def prepopulateRings(self):
+ """Prepopulate this distributor's hashrings and subhashrings with
+ bridges.
+ """
+ logging.info("Prepopulating %s distributor hashrings..." % self.name)
+
+ for filterFn in [byIPv4, byIPv6]:
+ ruleset = frozenset([filterFn])
+ key = getHMAC(self.key, "Order-Bridges-In-Ring")
+ ring = BridgeRing(key, self.answerParameters)
+ self.hashring.addRing(ring, ruleset, byFilters([filterFn]),
+ populate_from=self.hashring.bridges)
+
+ # Since prepopulateRings is called every half hour when the bridge
+ # descriptors are re-parsed, we should clean the database then.
+ self.cleanDatabase()
diff --git a/lib/bridgedb/email/request.py b/lib/bridgedb/email/request.py
index 32446ac..50fd32c 100644
--- a/lib/bridgedb/email/request.py
+++ b/lib/bridgedb/email/request.py
@@ -40,8 +40,8 @@ import logging
import re
from bridgedb import bridgerequest
-from bridgedb.Dist import EmailRequestedHelp
-from bridgedb.Dist import EmailRequestedKey
+from bridgedb.email.distributor import EmailRequestedHelp
+from bridgedb.email.distributor import EmailRequestedKey
#: A regular expression for matching the Pluggable Transport method TYPE in
@@ -105,7 +105,7 @@ class EmailBridgeRequest(bridgerequest.BridgeRequestBase):
def __init__(self):
"""Process a new bridge request received through the
- :class:`~bridgedb.Dist.EmailBasedDistributor`.
+ :class:`~bridgedb.email.distributor.EmailDistributor`.
"""
super(EmailBridgeRequest, self).__init__()
self._wantsKey = False
diff --git a/lib/bridgedb/email/server.py b/lib/bridgedb/email/server.py
index 8034d35..736c3f6 100644
--- a/lib/bridgedb/email/server.py
+++ b/lib/bridgedb/email/server.py
@@ -103,7 +103,7 @@ class MailServerContext(object):
"""Create a context for storing configs for email bridge distribution.
:type config: :class:`bridgedb.persistent.Conf`
- :type distributor: :class:`bridgedb.Dist.EmailBasedDistributor`
+ :type distributor: :class:`~bridgedb.email.distributor.EmailDistributor`
:param distributor: The distributor will handle getting the correct
bridges (or none) for a client for us.
:type schedule: :class:`bridgedb.schedule.ScheduledInterval`
@@ -466,7 +466,7 @@ def addServer(config, distributor):
:type config: :class:`bridgedb.configure.Conf`
:param config: A configuration object.
- :type distributor: :class:`bridgedb.Dist.EmailBasedDistributor`
+ :type distributor: :class:`bridgedb.email.distributor.EmailDistributor`
:param dist: A distributor which will handle database interactions, and
will decide which bridges to give to who and when.
"""
diff --git a/lib/bridgedb/email/templates.py b/lib/bridgedb/email/templates.py
index 4db4e3c..6998716 100644
--- a/lib/bridgedb/email/templates.py
+++ b/lib/bridgedb/email/templates.py
@@ -30,7 +30,7 @@ import os
from datetime import datetime
from bridgedb import strings
-from bridgedb.Dist import MAX_EMAIL_RATE
+from bridgedb.email.distributor import MAX_EMAIL_RATE
def addCommands(template):
diff --git a/lib/bridgedb/parse/addr.py b/lib/bridgedb/parse/addr.py
index b3ad680..cd512d1 100644
--- a/lib/bridgedb/parse/addr.py
+++ b/lib/bridgedb/parse/addr.py
@@ -217,7 +217,7 @@ def canonicalizeEmailDomain(domain, domainmap):
:param str domain: The domain portion of an email address to validate. It
will be checked that it is one of the domains allowed to email
requests for bridges to the
- :class:`~bridgedb.Dist.EmailBasedDistributor`.
+ :class:`~bridgedb.email.distributor.EmailDistributor`.
:param dict domainmap: A map of permitted alternate domains (in lowercase)
to their canonical domain names (in lowercase). This can be configured
with the ``EMAIL_DOMAIN_MAP`` option in ``bridgedb.conf``, for
@@ -441,8 +441,9 @@ def normalizeEmail(emailaddr, domainmap, domainrules, ignorePlus=True):
The email address, **emailaddr**, will be parsed and validated, and then
checked that it originated from one of the domains allowed to email
- requests for bridges to the :class:`~bridgedb.Dist.EmailBasedDistributor`
- via the :func:`canonicaliseEmailDomain` function.
+ requests for bridges to the
+ :class:`~bridgedb.email.distributor.EmailDistributor` via the
+ :func:`canonicaliseEmailDomain` function.
:param str emailaddr: An email address to normalise.
:param dict domainmap: A map of permitted alternate domains (in lowercase)
diff --git a/lib/bridgedb/persistent.py b/lib/bridgedb/persistent.py
index 0853c1e..22673dd 100644
--- a/lib/bridgedb/persistent.py
+++ b/lib/bridgedb/persistent.py
@@ -24,8 +24,8 @@ from twisted.python.reflect import safe_repr
from twisted.spread import jelly
from bridgedb import Bridges
-from bridgedb import Dist
from bridgedb import filters
+from bridgedb.email import distributor as emailDistributor
from bridgedb.https import distributor as httpsDistributor
from bridgedb.configure import Conf
#from bridgedb.proxy import ProxySet
@@ -35,7 +35,7 @@ _state = None
#: Types and classes which are allowed to be jellied:
_security = jelly.SecurityOptions()
#_security.allowInstancesOf(ProxySet)
-_security.allowModules(filters, Bridges, Dist, httpsDistributor)
+_security.allowModules(filters, Bridges, emailDistributor, httpsDistributor)
class MissingState(Exception):
diff --git a/lib/bridgedb/test/email_helpers.py b/lib/bridgedb/test/email_helpers.py
index 20aee57..14c86f4 100644
--- a/lib/bridgedb/test/email_helpers.py
+++ b/lib/bridgedb/test/email_helpers.py
@@ -13,9 +13,9 @@
import io
-from bridgedb.Dist import IgnoreEmail
-from bridgedb.Dist import TooSoonEmail
from bridgedb.persistent import Conf
+from bridgedb.email.distributor import IgnoreEmail
+from bridgedb.email.distributor import TooSoonEmail
from bridgedb.email.server import MailServerContext
from bridgedb.schedule import Unscheduled
from bridgedb.test import util
@@ -120,8 +120,8 @@ def _createMailServerContext(config=None, distributor=None):
class DummyEmailDistributor(object):
- """A mocked :class:`bridgedb.Dist.EmailBasedDistributor` which is used to
- test :class:`bridgedb.EmailServer`.
+ """A mocked :class:`bridgedb.email.distributor.EmailDistributor` which is used
+ to test :class:`bridgedb.EmailServer`.
"""
_bridgesPerResponseMin = 3
@@ -144,9 +144,9 @@ class DummyEmailDistributor(object):
class DummyEmailDistributorWithState(DummyEmailDistributor):
- """A mocked :class:`bridgedb.Dist.EmailBasedDistributor` which raises
- :exc:`bridgedb.Dist.TooSoonEmail` on the second email and
- :exc:`bridgedb.Dist.IgnoreEmail` on the third.
+ """A mocked :class:`bridgedb.email.distributor.EmailDistributor` which raises
+ :exc:`bridgedb.email.distributor.TooSoonEmail` on the second email and
+ :exc:`bridgedb.email.distributor.IgnoreEmail` on the third.
Note that the state tracking is done in a really dumb way. For example, we
currently don't consider requests for help text or GnuPG keys to be a
diff --git a/lib/bridgedb/test/legacy_Tests.py b/lib/bridgedb/test/legacy_Tests.py
index c0a9ff0..40f7ae4 100644
--- a/lib/bridgedb/test/legacy_Tests.py
+++ b/lib/bridgedb/test/legacy_Tests.py
@@ -19,7 +19,6 @@ from datetime import datetime
import bridgedb.Bridges
import bridgedb.Main
-import bridgedb.Dist
import bridgedb.schedule
import bridgedb.Storage
import re
@@ -27,6 +26,9 @@ import ipaddr
from bridgedb.Stability import BridgeHistory
+from bridgedb.email.distributor import EmailDistributor
+from bridgedb.email.distributor import IgnoreEmail
+from bridgedb.email.distributor import TooSoonEmail
from bridgedb.parse import addr
from bridgedb.test.util import bracketIPv6
from bridgedb.test.util import randomIP
@@ -94,42 +96,6 @@ def fakeBridge6(orport=8080, running=True, stable=True, or_addresses=False,
return b
-class EmailBridgeDistTests(unittest.TestCase):
- def setUp(self):
- self.fd, self.fname = tempfile.mkstemp()
- self.db = bridgedb.Storage.Database(self.fname)
- bridgedb.Storage.setDB(self.db)
- self.cur = self.db._conn.cursor()
-
- def tearDown(self):
- self.db.close()
- os.close(self.fd)
- os.unlink(self.fname)
-
- def testEmailRateLimit(self):
- db = self.db
- EMAIL_DOMAIN_MAP = {'example.com':'example.com'}
- d = bridgedb.Dist.EmailBasedDistributor(
- "Foo",
- {'example.com': 'example.com',
- 'dkim.example.com': 'dkim.example.com'},
- {'example.com': [], 'dkim.example.com': ['dkim']})
- for _ in xrange(256):
- d.insert(fakeBridge())
- d.getBridges('abc@xxxxxxxxxxx', 1)
- self.assertRaises(bridgedb.Dist.TooSoonEmail,
- d.getBridges, 'abc@xxxxxxxxxxx', 1)
- self.assertRaises(bridgedb.Dist.IgnoreEmail,
- d.getBridges, 'abc@xxxxxxxxxxx', 1)
-
- def testUnsupportedDomain(self):
- db = self.db
- self.assertRaises(bridgedb.parse.addr.UnsupportedDomain,
- bridgedb.parse.addr.normalizeEmail,
- 'bad@xxxxxxxxx',
- {'example.com':'example.com'},
- {'example.com':[]})
-
class SQLStorageTests(unittest.TestCase):
def setUp(self):
@@ -358,8 +324,7 @@ class BridgeStabilityTests(unittest.TestCase):
def testSuite():
suite = unittest.TestSuite()
loader = unittest.TestLoader()
-
- for klass in [SQLStorageTests, EmailBridgeDistTests, BridgeStabilityTests]:
+ for klass in [SQLStorageTests, BridgeStabilityTests]:
suite.addTest(loader.loadTestsFromTestCase(klass))
return suite
diff --git a/lib/bridgedb/test/test_email_autoresponder.py b/lib/bridgedb/test/test_email_autoresponder.py
index 2895802..98302ca 100644
--- a/lib/bridgedb/test/test_email_autoresponder.py
+++ b/lib/bridgedb/test/test_email_autoresponder.py
@@ -25,7 +25,7 @@ from twisted.test import proto_helpers
from bridgedb.email import autoresponder
from bridgedb.email.server import SMTPMessage
-from bridgedb.Dist import TooSoonEmail
+from bridgedb.email.distributor import TooSoonEmail
from bridgedb.test.email_helpers import _createConfig
from bridgedb.test.email_helpers import _createMailServerContext
from bridgedb.test.email_helpers import DummyEmailDistributorWithState
diff --git a/lib/bridgedb/test/test_email_distributor.py b/lib/bridgedb/test/test_email_distributor.py
new file mode 100644
index 0000000..96eb306
--- /dev/null
+++ b/lib/bridgedb/test/test_email_distributor.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of BridgeDB, a Tor bridge distribution system.
+#
+# :authors: Isis Lovecruft 0xA3ADB67A2CDB8B35 <isis@xxxxxxxxxxxxxx>
+# :copyright: (c) 2013-2015 Isis Lovecruft
+# (c) 2007-2015, The Tor Project, Inc.
+# :license: see included LICENSE for information
+
+"""Tests for :mod:`bridgedb.email.distributor`."""
+
+from __future__ import print_function
+
+import logging
+import tempfile
+import os
+
+from twisted.trial import unittest
+
+import bridgedb.Storage
+
+from bridgedb.bridges import Bridge
+from bridgedb.email.distributor import EmailDistributor
+from bridgedb.email.distributor import IgnoreEmail
+from bridgedb.email.distributor import TooSoonEmail
+from bridgedb.email.request import EmailBridgeRequest
+from bridgedb.parse.addr import UnsupportedDomain
+from bridgedb.parse.addr import normalizeEmail
+from bridgedb.test.util import generateFakeBridges
+
+logging.disable(50)
+
+
+BRIDGES = generateFakeBridges()
+
+
+class EmailDistributorTests(unittest.TestCase):
+ """Tests for :class:`bridgedb.email.distributor.EmailDistributor`."""
+
+ def setUp(self):
+ self.fd, self.fname = tempfile.mkstemp()
+ bridgedb.Storage.initializeDBLock()
+ self.db = bridgedb.Storage.openDatabase(self.fname)
+ bridgedb.Storage.setDBFilename(self.fname)
+ self.cur = self.db.cursor()
+ self.db.close()
+
+ self.bridges = BRIDGES
+ self.key = 'aQpeOFIj8q20s98awfoiq23rpOIjFaqpEWFoij1X'
+ self.domainmap = {
+ 'example.com': 'example.com',
+ 'dkim.example.com': 'dkim.example.com',
+ }
+ self.domainrules = {
+ 'example.com': ['ignore_dots'],
+ 'dkim.example.com': ['dkim', 'ignore_dots']
+ }
+
+ def tearDown(self):
+ self.db.close()
+ os.close(self.fd)
+ os.unlink(self.fname)
+
+ def makeClientRequest(self, clientEmailAddress):
+ bridgeRequest = EmailBridgeRequest()
+ bridgeRequest.client = clientEmailAddress
+ bridgeRequest.isValid(True)
+ bridgeRequest.generateFilters()
+ return bridgeRequest
+
+ def test_EmailDistributor_rate_limit(self):
+ """A client's first email should return bridges. The second should
+ return a warning, and the third should receive no response.
+ """
+ dist = EmailDistributor(self.key, self.domainmap, self.domainrules)
+ [dist.hashring.insert(bridge) for bridge in self.bridges]
+
+ bridgeRequest = self.makeClientRequest('abc@xxxxxxxxxxx')
+
+ # The first request should get a response with bridges
+ bridges = dist.getBridges(bridgeRequest, 1)
+ self.assertGreater(len(bridges), 0)
+ [self.assertIsInstance(b, Bridge) for b in bridges]
+ self.assertEqual(len(bridges), 3)
+
+ # The second gets a warning, and the third is ignored
+ self.assertRaises(TooSoonEmail, dist.getBridges, bridgeRequest, 1)
+ self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest, 1)
+
+ def test_EmailDistributor_unsupported_domain(self):
+ """An unsupported domain should raise an UnsupportedDomain exception."""
+ self.assertRaises(UnsupportedDomain, normalizeEmail,
+ 'bad@xxxxxxxxx', self.domainmap, self.domainrules)
diff --git a/lib/bridgedb/test/test_email_server.py b/lib/bridgedb/test/test_email_server.py
index 561c14c..9c4fabb 100644
--- a/lib/bridgedb/test/test_email_server.py
+++ b/lib/bridgedb/test/test_email_server.py
@@ -31,9 +31,9 @@ from twisted.trial import unittest
from zope.interface import implementedBy
-from bridgedb.Dist import EmailBasedDistributor
-from bridgedb.Dist import TooSoonEmail
from bridgedb.email import server
+from bridgedb.email.distributor import EmailDistributor
+from bridgedb.email.distributor import TooSoonEmail
from bridgedb.parse.addr import BadEmail
from bridgedb.schedule import Unscheduled
from bridgedb.test import util
@@ -504,7 +504,7 @@ class EmailServerServiceTests(SMTPTestCaseMixin, unittest.TestCase):
"""Unittests for :func:`bridgedb.email.server.addServer`."""
def setUp(self):
- """Create a server.MailServerContext and EmailBasedDistributor."""
+ """Create a MailServerContext and EmailDistributor."""
self.config = _createConfig()
self.context = _createMailServerContext(self.config)
self.smtpFromAddr = self.context.smtpFromAddr # 'bridges@localhost'
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits