Exemplo n.º 1
0
    def getMailTo(self):
        """Attempt to get the client's email address from an incoming email.

        :rtype: list
        :returns: A list containing the client's
            :func:`normalized <bridgedb.parse.addr.normalizeEmail>` email
            :api:`Address <twisted.mail.smtp.Address>`, if it originated from
            a domain that we accept and the address was well-formed. Otherwise,
            returns ``None``. Even though we're likely to respond to only one
            client at a time, the return value of this method must be a list
            in order to hook into the rest of
            :api:`twisted.mail.smtp.SMTPClient` correctly.
        """
        clients = []
        addrHeader = None
        try:
            fromAddr = email.utils.parseaddr(
                self.incoming.message.get("From"))[1]
        except (IndexError, TypeError, AttributeError):
            pass
        else:
            addrHeader = fromAddr

        if not addrHeader:
            logging.warn("No From header on incoming mail.")
            try:
                senderHeader = email.utils.parseaddr(
                    self.incoming.message.get("Sender"))[1]
            except (IndexError, TypeError, AttributeError):
                pass
            else:
                addrHeader = senderHeader
        if not addrHeader:
            logging.warn("No Sender header on incoming mail.")
            return clients

        client = None
        try:
            if addrHeader in self.incoming.context.whitelist.keys():
                logging.debug("Email address was whitelisted: %s." %
                              addrHeader)
                client = smtp.Address(addrHeader)
            else:
                normalized = addr.normalizeEmail(
                    addrHeader, self.incoming.context.domainMap,
                    self.incoming.context.domainRules)
                client = smtp.Address(normalized)
        except (addr.UnsupportedDomain) as error:
            logging.warn(error)
        except (addr.BadEmail, smtp.AddressError) as error:
            logging.warn(error)

        if client:
            clients.append(client)

        return clients
Exemplo n.º 2
0
    def testQuoteAddr(self):
        cases = [
            ['*****@*****.**', '<*****@*****.**>'],
            ['"User Name" <*****@*****.**>', '<*****@*****.**>'],
            [smtp.Address('someguy@someplace'), '<someguy@someplace>'],
            ['', '<>'],
            [smtp.Address(''), '<>'],
        ]

        for (c, e) in cases:
            self.assertEquals(smtp.quoteaddr(c), e)
Exemplo n.º 3
0
 def setUp(self):
     self.settings = SettingsMock()
     self.settings.load()
     self.factory = smtp2web.ESMTPFactory(self.settings)
     self.smtp = self.factory.buildProtocol(None)
     # Cheating here to bypass testing smtp.ESMTP
     self.delivery = self.smtp.deliveryFactory.getMessageDelivery()
     self.sender = smtp.Address("*****@*****.**")
     self.test_message = """From: Me <*****@*****.**>
Exemplo n.º 4
0
    def getMailFrom(self):
        """Find our address in the recipients list of the **incoming** message.

        :rtype: str
        :return: Our address from the recipients list. If we can't find it
            return our default ``EMAIL_FROM_ADDRESS`` from the config file.
        """
        logging.debug("Searching for our email address in 'To:' header...")

        ours = None

        try:
            ourAddress = smtp.Address(self.incoming.context.fromAddr)
            allRecipients = self.incoming.message.get_all("To")

            for address in allRecipients:
                recipient = smtp.Address(address)
                if not ourAddress.domain in recipient.domain:
                    logging.debug(("Not our domain (%s) or subdomain, skipping"
                                   " email address: %s") %
                                  (ourAddress.domain, str(recipient)))
                    continue
                # The recipient's username should at least start with ours,
                # but it still might be a '+' address.
                if not recipient.local.startswith(ourAddress.local):
                    logging.debug(("Username doesn't begin with ours, skipping"
                                   " email address: %s") % str(recipient))
                    continue
                # Only check the username before the first '+':
                beforePlus = recipient.local.split(b'+', 1)[0]
                if beforePlus == ourAddress.local:
                    ours = str(recipient)
            if not ours:
                raise addr.BadEmail(
                    'No email address accepted, please see log', allRecipients)

        except Exception as error:
            logging.error(("Couldn't find our email address in incoming email "
                           "headers: %r" % error))
            # Just return the email address that we're configured to use:
            ours = self.incoming.context.fromAddr

        logging.debug("Found our email address: %s." % ours)
        return ours
Exemplo n.º 5
0
    def __init__(self, domains, original):
        """
        @type domains: L{dict} mapping L{bytes} to L{IDomain} provider
        @param domains: A mapping of domain name to domain object.

        @type original: L{bytes}
        @param original: The original address being aliased.
        """
        self.domains = domains
        self.original = smtp.Address(original)
Exemplo n.º 6
0
    def test_validateFromAuthenticatedNonLocal(self):
        """
        Test that using a non-local address as the sender address after
        authenticating as a user is rejected.
        """
        avatar = self.login.accountByAddress(u'testuser', u'localhost')
        delivery = smtp.IMessageDeliveryFactory(avatar).getMessageDelivery()

        addr = smtp.Address('*****@*****.**')
        d = delivery.validateFrom(('home.example.net', '192.168.1.1'), addr)
        return self.assertFailure(d, smtp.SMTPBadSender)
Exemplo n.º 7
0
    def test_validateFromAuthenticatedDisallowedLocal(self):
        """
        Test that using a local address as the sender address after
        authenticating as a user who does /not/ own that address is rejected.
        """
        avatar = self.login.accountByAddress(u'testuser', u'localhost')
        delivery = smtp.IMessageDeliveryFactory(avatar).getMessageDelivery()

        addr = smtp.Address('admistrator@localhost')
        d = delivery.validateFrom(('home.example.net', '192.168.1.1'), addr)
        return self.assertFailure(d, smtp.SMTPBadSender)
Exemplo n.º 8
0
    def test_validateFromAuthenticatedLocal(self):
        """
        Test that using a local address as the sender address after
        authenticating as the user who owns that address is accepted.
        """
        avatar = self.login.accountByAddress(u'testuser', u'localhost')
        delivery = smtp.IMessageDeliveryFactory(avatar).getMessageDelivery()

        addr = smtp.Address('testuser@localhost')
        d = delivery.validateFrom(('home.example.net', '192.168.1.1'), addr)
        return d.addCallback(self.assertEquals, addr)
Exemplo n.º 9
0
    def test_validateFromUnauthenticatedNonLocal(self):
        """
        Test that using a non-local address as the sender address without
        authenticating first is accepted.
        """
        factory = mail.MailTransferAgent(store=self.store)
        installOn(factory, self.store)
        delivery = factory.getMessageDelivery()

        addr = smtp.Address('*****@*****.**')
        d = delivery.validateFrom(('home.example.net', '192.168.1.1'), addr)
        return d.addCallback(self.assertEquals, addr)
Exemplo n.º 10
0
    def __init__(self, alias, *args):
        """
        @type alias: L{Address}, L{User}, L{bytes} or object which can be
            converted into L{bytes}
        @param alias: The destination address.

        @type args: 2-L{tuple} of (0) L{dict} mapping L{bytes} to L{IDomain}
            provider, (1) L{bytes}
        @param args: Arguments for L{AliasBase.__init__}.
        """
        AliasBase.__init__(self, *args)
        self.alias = smtp.Address(alias)
Exemplo n.º 11
0
def loadAliasFile(domains, filename=None, fp=None):
    """Load a file containing email aliases.

    Lines in the file should be formatted like so::

        username: alias1,alias2,...,aliasN

    Aliases beginning with a | will be treated as programs, will be run, and
    the message will be written to their stdin.

    Aliases without a host part will be assumed to be addresses on localhost.

    If a username is specified multiple times, the aliases for each are joined
    together as if they had all been on one line.

    @type domains: C{dict} of implementor of C{IDomain}
    @param domains: The domains to which these aliases will belong.

    @type filename: C{str}
    @param filename: The filename from which to load aliases.

    @type fp: Any file-like object.
    @param fp: If specified, overrides C{filename}, and aliases are read from
    it.

    @rtype: C{dict}
    @return: A dictionary mapping usernames to C{AliasGroup} objects.
    """
    result = {}
    if fp is None:
        fp = file(filename)
    else:
        filename = getattr(fp, 'name', '<unknown>')
    i = 0
    prev = ''
    for line in fp:
        i += 1
        line = line.rstrip()
        if line.lstrip().startswith('#'):
            continue
        elif line.startswith(' ') or line.startswith('\t'):
            prev = prev + line
        else:
            if prev:
                handle(result, prev, filename, i)
            prev = line
    if prev:
        handle(result, prev, filename, i)
    for (u, a) in result.items():
        addr = smtp.Address(u)
        result[u] = AliasGroup(a, domains, u)
    return result
Exemplo n.º 12
0
 def test_validateFromUnauthenticatedLocal(self):
     """
     Test that using a local address as the sender address without
     authenticating as that user raises an exception to prevent the
     delivery.
     """
     factory = mail.MailTransferAgent(store=self.store)
     installOn(factory, self.store)
     delivery = factory.getMessageDelivery()
     d = delivery.validateFrom(
         ('home.example.net', '192.168.1.1'),
         smtp.Address('testuser@localhost'))
     return self.assertFailure(d, smtp.SMTPBadSender)
Exemplo n.º 13
0
    def test_authenticatedReceivedHeader(self):
        """
        Test that something at least minimally reasonable comes back from the
        receivedHeader method of L{AuthenticatedMessageDelivery}.
        """
        avatar = self.login.accountByAddress(u'testuser', u'localhost')
        composer = IMessageSender(avatar)

        delivery = mail.AuthenticatedMessageDelivery(composer.store, composer)
        header = delivery.receivedHeader(
            ("example.com", "192.168.123.45"),
            smtp.Address("testuser@localhost"),
            [smtp.User("*****@*****.**", None, None, None),
             smtp.User("*****@*****.**", None, None, None)])

        self.failUnless(
            isinstance(header, str),
            "Got %r instead of a string" % (header,))
Exemplo n.º 14
0
    def test_validateToAuthenticatedNonLocal(self):
        """
        Test that using a non-local address as the recipient address after
        authenticating as anyone is accepted.
        """
        avatar = self.login.accountByAddress(u'testuser', u'localhost')
        delivery = smtp.IMessageDeliveryFactory(avatar).getMessageDelivery()

        addr = smtp.Address('testuser@localhost')
        d = delivery.validateFrom(('home.example.net', '192.168.1.1'), addr)
        def validatedFrom(ign):
            d = delivery.validateTo(
                smtp.User(
                    smtp.Address('administrator', 'example.com'),
                    None, None, None))
            return d
        d.addCallback(validatedFrom)
        return d
Exemplo n.º 15
0
    def test_validateToAuthenticatedNonExistentLocal(self):
        """
        Test that using as the recipient address a non-existent address which
        would exist locally if it existed at all is rejected.
        """
        avatar = self.login.accountByAddress(u'testuser', u'localhost')
        delivery = smtp.IMessageDeliveryFactory(avatar).getMessageDelivery()

        addr = smtp.Address('testuser@localhost')
        d = delivery.validateFrom(('home.example.net', '192.168.1.1'), addr)
        def validatedFrom(ign):
            d = delivery.validateTo(
                smtp.User(
                    smtp.Address('nonexistent', 'localhost'),
                    None, None, None))
            return self.assertFailure(d, smtp.SMTPBadRcpt)
        d.addCallback(validatedFrom)
        return d
Exemplo n.º 16
0
    def test_validateToUnauthenticatedLocal(self):
        """
        Test that using a local address as the recipient address without
        authenticating is accepted.
        """
        factory = mail.MailTransferAgent(store=self.store)
        installOn(factory, self.store)
        delivery = factory.getMessageDelivery()

        addr = smtp.Address('*****@*****.**')
        d = delivery.validateFrom(('home.example.net', '192.168.1.1'), addr)
        def validatedFrom(ign):
            d = delivery.validateTo(
                smtp.User(
                    smtp.Address('testuser', 'localhost'),
                    None, None, None))
            return d
        d.addCallback(validatedFrom)
        return d
Exemplo n.º 17
0
    def test_validateToUnauthenticatedNonExistentLocal(self):
        """
        Test that using as the recipient address a non-existent address which
        would exist locally if it existed at all is rejected.
        """
        factory = mail.MailTransferAgent(store=self.store)
        installOn(factory, self.store)
        delivery = factory.getMessageDelivery()

        addr = smtp.Address('*****@*****.**')
        d = delivery.validateFrom(('home.example.net', '192.168.1.1'), addr)
        def validatedFrom(ign):
            d = delivery.validateTo(
                smtp.User(
                    smtp.Address('nonexistent', 'localhost'),
                    None, None, None))
            return self.assertFailure(d, smtp.SMTPBadRcpt)
        d.addCallback(validatedFrom)
        return d
Exemplo n.º 18
0
    def validateTo(self, user):
        """Validate the SMTP ``RCPT TO:`` address for the incoming connection.

        The local username and domain name to which this SMTP message is
        addressed, after being stripped of any ``'+'`` aliases, **must** be
        identical to those in the email address set our
        ``EMAIL_SMTP_FROM_ADDR`` configuration file option.

        :type user: :api:`twisted.mail.smtp.User`
        :param user: Information about the user this SMTP message was
            addressed to.
        :raises: A :api:`twisted.mail.smtp.SMTPBadRcpt` if any of the above
            conditions weren't met.
        :rtype: callable
        :returns: A parameterless function which returns an instance of
            :class:`SMTPMessage`.
        """
        logging.debug("Validating SMTP 'RCPT TO:' email address...")

        recipient = user.dest
        ourAddress = smtp.Address(self.context.smtpFromAddr)

        if not ((ourAddress.domain in recipient.domain) or
                (recipient.domain == b"bridgedb")):
            logging.debug(("Not our domain (%s) or subdomain, skipping"
                           " SMTP 'RCPT TO' address: %s")
                          % (ourAddress.domain.decode('utf-8'), str(recipient)))
            raise smtp.SMTPBadRcpt(str(recipient))
        # The recipient's username should at least start with ours,
        # but it still might be a '+' address.
        if not recipient.local.startswith(ourAddress.local):
            logging.debug(("Username doesn't begin with ours, skipping"
                           " SMTP 'RCPT TO' address: %s") % str(recipient))
            raise smtp.SMTPBadRcpt(str(recipient))
        # Ignore everything after the first '+', if there is one.
        beforePlus = recipient.local.split(b'+', 1)[0]
        if beforePlus != ourAddress.local:
            raise smtp.SMTPBadRcpt(str(recipient))

        return lambda: SMTPMessage(self.context, self.fromCanonicalSMTP)
Exemplo n.º 19
0
    def test_authenticatedMultipleRecipientsOneRecord(self):
        """
        Test that only one sent message object is created even if a messages is
        destined for multiple recipients.
        """
        self.installStubSender(u'testuser', u'localhost')
        account = self.login.accountByAddress(u'testuser', u'localhost')
        factory = smtp.IMessageDeliveryFactory(account)
        delivery = factory.getMessageDelivery()

        d = delivery.validateFrom(
            ('home.example.net', '192.168.1.1'),
            smtp.Address('testuser@localhost'))
        def validatedFrom(ign):
            return gatherResults([
                    delivery.validateTo(
                        smtp.User(
                            smtp.Address(addr), None, None, None))
                    for addr in self.MULTI_RECIPIENT_ADDRESSES])
        d.addCallback(validatedFrom)
        d.addCallback(self.deliverMessageAndVerify, u'testuser', u'localhost')
        return d
Exemplo n.º 20
0
 def validatedFrom(ign):
     d = delivery.validateTo(
         smtp.User(
             smtp.Address('testuser', 'localhost'),
             None, None, None))
     return d
Exemplo n.º 21
0
def loadAliasFile(domains, filename=None, fp=None):
    """
    Load a file containing email aliases.

    Lines in the file should be formatted like so::

         username: alias1, alias2, ..., aliasN

    Aliases beginning with a C{|} will be treated as programs, will be run, and
    the message will be written to their stdin.

    Aliases beginning with a C{:} will be treated as a file containing
    additional aliases for the username.

    Aliases beginning with a C{/} will be treated as the full pathname to a file
    to which the message will be appended.

    Aliases without a host part will be assumed to be addresses on localhost.

    If a username is specified multiple times, the aliases for each are joined
    together as if they had all been on one line.

    Lines beginning with a space or a tab are continuations of the previous
    line.

    Lines beginning with a C{#} are comments.

    @type domains: L{dict} mapping L{bytes} to L{IDomain} provider
    @param domains: A mapping of domain name to domain object.

    @type filename: L{bytes} or L{NoneType <types.NoneType>}
    @param filename: The full or relative path to a file from which to load
        aliases. If omitted, the C{fp} parameter must be specified.

    @type fp: file-like object or L{NoneType <types.NoneType>}
    @param fp: The file from which to load aliases. If specified,
        the C{filename} parameter is ignored.

    @rtype: L{dict} mapping L{bytes} to L{AliasGroup}
    @return: A mapping from username to group of aliases.
    """
    result = {}
    if fp is None:
        fp = file(filename)
    else:
        filename = getattr(fp, 'name', '<unknown>')
    i = 0
    prev = ''
    for line in fp:
        i += 1
        line = line.rstrip()
        if line.lstrip().startswith('#'):
            continue
        elif line.startswith(' ') or line.startswith('\t'):
            prev = prev + line
        else:
            if prev:
                handle(result, prev, filename, i)
            prev = line
    if prev:
        handle(result, prev, filename, i)
    for (u, a) in result.items():
        addr = smtp.Address(u)
        result[u] = AliasGroup(a, domains, u)
    return result
Exemplo n.º 22
0
 def validatedFrom(ign):
     d = delivery.validateTo(
         smtp.User(
             smtp.Address('administrator', 'localhost'),
             None, None, None))
     return d
Exemplo n.º 23
0
 def validatedFrom(ign):
     d = delivery.validateTo(
         smtp.User(
             smtp.Address('*****@*****.**'),
             None, None, None))
     return self.assertFailure(d, smtp.SMTPBadRcpt)
Exemplo n.º 24
0
 def validatedFrom(ign):
     d = delivery.validateTo(
         smtp.User(
             smtp.Address('administrator', 'example.com'),
             None, None, None))
     return d
Exemplo n.º 25
0
 def validatedFrom(ign):
     d = delivery.validateTo(
         smtp.User(
             smtp.Address('nonexistent', 'localhost'),
             None, None, None))
     return self.assertFailure(d, smtp.SMTPBadRcpt)
Exemplo n.º 26
0
 def __init__(self, domains, original):
     self.domains = domains
     self.original = smtp.Address(original)
Exemplo n.º 27
0
 def validatedFrom(ign):
     return gatherResults([
             delivery.validateTo(
                 smtp.User(
                     smtp.Address(addr), None, None, None))
             for addr in self.MULTI_RECIPIENT_ADDRESSES])
Exemplo n.º 28
0
 def __init__(self, alias, *args):
     AliasBase.__init__(self, *args)
     self.alias = smtp.Address(alias)