def test_determineBridgeRequestOptions_get_ipv6(self): """An valid request for 'get ipv6'.""" lines = ['', 'get ipv6'] reqvest = request.determineBridgeRequestOptions(lines) self.assertIs(reqvest.ipVersion, 6) self.assertEqual(reqvest.isValid(), True)
def test_determineBridgeRequestOptions_get_ipv6(self): """An valid request for 'get ipv6'.""" lines = ['', 'get ipv6'] reqvest = request.determineBridgeRequestOptions(lines) self.assertIs(reqvest.addressClass, ipaddr.IPv6Address) self.assertEqual(reqvest.isValid(), True)
def createResponseBody(lines, context, client, lang='en'): """Parse the **lines** from an incoming email request and determine how to respond. :param list lines: The list of lines from the original request sent by the client. :type context: :class:`bridgedb.email.server.MailServerContext` :param context: The context which contains settings for the email server. :type client: :api:`twisted.mail.smtp.Address` :param client: The client's email address which should be in the ``'To:'`` header of the response email. :param str lang: The 2-5 character locale code to use for translating the email. This is obtained from a client sending a email to a valid plus address which includes the translation desired, i.e. by sending an email to `[email protected] <mailto:[email protected]>`__, the client should receive a response in Farsi. :rtype: str :returns: ``None`` if we shouldn't respond to the client (i.e., if they have already received a rate-limiting warning email). Otherwise, returns a string containing the (optionally translated) body for the email response which we should send out. """ translator = translations.installTranslations(lang) bridges = None try: bridgeRequest = request.determineBridgeRequestOptions(lines) bridgeRequest.client = str(client) # The request was invalid, respond with a help email which explains # valid email commands: if not bridgeRequest.isValid(): raise EmailRequestedHelp("Email request from '%s' was invalid." % str(client)) # Otherwise they must have requested bridges: interval = context.schedule.intervalStart(time.time()) bridges = context.distributor.getBridges(bridgeRequest, interval) except EmailRequestedHelp as error: logging.info(error) return templates.buildWelcomeText(translator, client) except EmailRequestedKey as error: logging.info(error) return templates.buildKeyMessage(translator, client) except TooSoonEmail as error: logging.info("Got a mail too frequently: %s." % error) return templates.buildSpamWarning(translator, client) except (IgnoreEmail, addr.BadEmail) as error: logging.info(error) # Don't generate a response if their email address is unparsable or # invalid, or if we've already warned them about rate-limiting: return None else: answer = "(no bridges currently available)\r\n" if bridges: transport = bridgeRequest.justOnePTType() answer = "".join(" %s\r\n" % b.getBridgeLine( bridgeRequest, context.includeFingerprints) for b in bridges) return templates.buildAnswerMessage(translator, client, answer)
def test_determineBridgeRequestOptions_get_transport(self): """An invalid request for 'transport obfs3' (missing the 'get').""" lines = ['', 'transport obfs3'] reqvest = request.determineBridgeRequestOptions(lines) self.assertEqual(len(reqvest.transports), 1) self.assertEqual(reqvest.transports[0], 'obfs3') self.assertEqual(reqvest.isValid(), False)
def test_determineBridgeRequestOptions_multiline_invalid(self): """Requests without a 'get' anywhere should be considered invalid.""" lines = ['', 'transport obfs3', 'ipv6 vanilla bridges', 'give me your gpgs'] reqvest = request.determineBridgeRequestOptions(lines) # It's invalid because it didn't include a 'get' anywhere. self.assertEqual(reqvest.isValid(), False) self.assertFalse(reqvest.wantsKey()) # Though they did request IPv6, technically. self.assertIs(reqvest.ipVersion, 6) # And they did request a transport, technically. self.assertEqual(len(reqvest.transports), 1) self.assertEqual(reqvest.transports[0], 'obfs3')
def test_determineBridgeRequestOptions_multiline_invalid(self): """Requests without a 'get' anywhere should be considered invalid.""" lines = ['', 'transport obfs3', 'ipv6 vanilla bridges', 'give me your gpgs'] reqvest = request.determineBridgeRequestOptions(lines) # It's invalid because it didn't include a 'get' anywhere. self.assertEqual(reqvest.isValid(), False) self.assertFalse(reqvest.wantsKey()) # Though they did request IPv6, technically. self.assertIs(reqvest.addressClass, ipaddr.IPv6Address) # And they did request a transport, technically. self.assertEqual(len(reqvest.transports), 1) self.assertEqual(reqvest.transports[0], 'obfs3')
def test_determineBridgeRequestOptions_multiline_valid(self): """Though requests with a 'get' are considered valid.""" lines = ['', 'get transport obfs3', 'vanilla bridges', 'transport scramblesuit unblocked ca'] reqvest = request.determineBridgeRequestOptions(lines) # It's valid because it included a 'get'. self.assertEqual(reqvest.isValid(), True) self.assertFalse(reqvest.wantsKey()) # Though they didn't request IPv6, so it should default to IPv4. self.assertIs(reqvest.ipVersion, 4) # And they requested two transports. self.assertEqual(len(reqvest.transports), 2) self.assertEqual(reqvest.transports[0], 'obfs3') self.assertEqual(reqvest.transports[1], 'scramblesuit') # And they wanted this stuff to not be blocked in Canada. self.assertEqual(len(reqvest.notBlockedIn), 1) self.assertEqual(reqvest.notBlockedIn[0], 'ca')
def test_determineBridgeRequestOptions_multiline_valid(self): """Though requests with a 'get' are considered valid.""" lines = ['', 'get transport obfs3', 'vanilla bridges', 'transport scramblesuit unblocked ca'] reqvest = request.determineBridgeRequestOptions(lines) # It's valid because it included a 'get'. self.assertEqual(reqvest.isValid(), True) self.assertFalse(reqvest.wantsKey()) # Though they didn't request IPv6, so it should default to IPv4. self.assertIs(reqvest.addressClass, ipaddr.IPv4Address) # And they requested two transports. self.assertEqual(len(reqvest.transports), 2) self.assertEqual(reqvest.transports[0], 'obfs3') self.assertEqual(reqvest.transports[1], 'scramblesuit') # And they wanted this stuff to not be blocked in Canada. self.assertEqual(len(reqvest.notBlockedIn), 1) self.assertEqual(reqvest.notBlockedIn[0], 'ca')
def test_determineBridgeRequestOptions_multiline_valid_OMG_CAPSLOCK(self): """Though requests with a 'get' are considered valid, even if they appear to not know the difference between Capslock and Shift. """ lines = ['', 'get TRANSPORT obfs3', 'vanilla bridges', 'TRANSPORT SCRAMBLESUIT UNBLOCKED CA'] reqvest = request.determineBridgeRequestOptions(lines) # It's valid because it included a 'get'. self.assertEqual(reqvest.isValid(), True) self.assertFalse(reqvest.wantsKey()) # Though they didn't request IPv6, so it should default to IPv4. self.assertIs(reqvest.ipVersion, 4) # And they requested two transports. self.assertEqual(len(reqvest.transports), 2) self.assertEqual(reqvest.transports[0], 'obfs3') self.assertEqual(reqvest.transports[1], 'scramblesuit') # And they wanted this stuff to not be blocked in Canada. self.assertEqual(len(reqvest.notBlockedIn), 1) self.assertEqual(reqvest.notBlockedIn[0], 'ca')
def test_determineBridgeRequestOptions_multiline_valid_OMG_CAPSLOCK(self): """Though requests with a 'get' are considered valid, even if they appear to not know the difference between Capslock and Shift. """ lines = ['', 'get TRANSPORT obfs3', 'vanilla bridges', 'TRANSPORT SCRAMBLESUIT UNBLOCKED CA'] reqvest = request.determineBridgeRequestOptions(lines) # It's valid because it included a 'get'. self.assertEqual(reqvest.isValid(), True) self.assertFalse(reqvest.wantsKey()) # Though they didn't request IPv6, so it should default to IPv4. self.assertIs(reqvest.addressClass, ipaddr.IPv4Address) # And they requested two transports. self.assertEqual(len(reqvest.transports), 2) self.assertEqual(reqvest.transports[0], 'obfs3') self.assertEqual(reqvest.transports[1], 'scramblesuit') # And they wanted this stuff to not be blocked in Canada. self.assertEqual(len(reqvest.notBlockedIn), 1) self.assertEqual(reqvest.notBlockedIn[0], 'ca')
def createResponseBody(lines, context, clientAddress, lang='en'): """Parse the **lines** from an incoming email request and determine how to respond. :param list lines: The list of lines from the original request sent by the client. :type context: class:`MailContext` :param context: The context which contains settings for the email server. :type clientAddress: :api:`twisted.mail.smtp.Address` :param clientAddress: The client's email address which should be in the :header:`To:` header of the response email. :param str lang: The 2-5 character locale code to use for translating the email. This is obtained from a client sending a email to a valid plus address which includes the translation desired, i.e. by sending an email to ``[email protected]``, the client should receive a response in Farsi. :rtype: None or str :returns: None if we shouldn't respond to the client (i.e., if they have already received a rate-limiting warning email). Otherwise, returns a string containing the (optionally translated) body for the email response which we should send out. """ clientAddr = '@'.join([clientAddress.local, clientAddress.domain]) t = translations.installTranslations(lang) bridges = None try: bridgeRequest = request.determineBridgeRequestOptions(lines) # The request was invalid, respond with a help email which explains # valid email commands: if not bridgeRequest.isValid(): raise EmailRequestedHelp("Email request from %r was invalid." % clientAddr) # Otherwise they must have requested bridges: interval = context.schedule.getInterval(time.time()) bridges = context.distributor.getBridgesForEmail( clientAddr, interval, context.nBridges, countryCode=None, bridgeFilterRules=bridgeRequest.filters) except EmailRequestedHelp as error: logging.info(error) return templates.buildWelcomeText(t, clientAddress) except EmailRequestedKey as error: logging.info(error) return templates.buildKeyMessage(t, clientAddress) except TooSoonEmail as error: logging.info("Got a mail too frequently: %s." % error) return templates.buildSpamWarning(t, clientAddress) except (IgnoreEmail, BadEmail) as error: logging.info(error) # Don't generate a response if their email address is unparsable or # invalid, or if we've already warned them about rate-limiting: return None else: answer = "(no bridges currently available)\r\n" if bridges: transport = bridgeRequest.justOnePTType() answer = "".join(" %s\r\n" % b.getConfigLine( includeFingerprint=context.includeFingerprints, addressClass=bridgeRequest.addressClass, transport=transport, request=clientAddr) for b in bridges) return templates.buildAnswerMessage(t, clientAddress, answer)