Esempio n. 1
0
 def __init__(self, fmt=None, datefmt="%Y-%m-%d %H:%M:%S"):
     if fmt is None:
         if config.isTestRunner():
             # Don't output timestamps in the test environment
             fmt = '%(levelname)-7s %(message)s'
         else:
             fmt = '%(asctime)s %(levelname)-7s %(message)s'
     logging.Formatter.__init__(self, fmt, datefmt)
     # Output should be UTC.
     self.converter = time.gmtime
Esempio n. 2
0
 def __init__(self, fmt=None, datefmt="%Y-%m-%d %H:%M:%S"):
     if fmt is None:
         if config.isTestRunner():
             # Don't output timestamps in the test environment
             fmt = '%(levelname)-7s %(message)s'
         else:
             fmt = '%(asctime)s %(levelname)-7s %(message)s'
     logging.Formatter.__init__(self, fmt, datefmt)
     # Output should be UTC.
     self.converter = time.gmtime
Esempio n. 3
0
def sendmail(message, to_addrs=None, bulk=True):
    """Send an email.message.Message

    If you just need to send dumb ASCII or Unicode, simple_sendmail
    will be easier for you. Sending attachments or multipart messages
    will need to use this method.

    From:, To: and Subject: headers should already be set.
    Message-Id:, Date:, and Reply-To: headers will be set if they are
    not already. Errors-To: and Return-Path: headers will always be set.
    The more we look valid, the less we look like spam.

    If to_addrs is None, the message will be sent to all the addresses
    specified in the To: and CC: headers.

    Uses zope.sendmail.interfaces.IMailer, so you can subscribe to
    IMailSentEvent or IMailErrorEvent to record status.

    This function looks at the `config` singleton for configuration as to
    where to send the mail; in particular for whether this code is running in
    zopeless mode, and for a `sendmail_to_stdout` attribute for testing.

    :param bulk: By default, a Precedence: bulk header is added to the
        message. Pass False to disable this.

    Returns the Message-Id
    """
    validate_message(message)
    if to_addrs is None:
        to_addrs = get_addresses_from_header(message['to'])
        if message['cc']:
            to_addrs = to_addrs + get_addresses_from_header(message['cc'])

    do_paranoid_envelope_to_validation(to_addrs)

    # Add a Message-Id: header if it isn't already there
    if 'message-id' not in message:
        message['Message-Id'] = get_msgid()

    # Add a Date: header if it isn't already there
    if 'date' not in message:
        message['Date'] = formatdate()

    # Add a Reply-To: header if it isn't already there
    if 'reply-to' not in message:
        message['Reply-To'] = message['from']

    # Add a Sender: header to show that we were the one sending the
    # email.
    if "Sender" not in message:
        message["Sender"] = config.canonical.bounce_address

    # Add an Errors-To: header for bounce handling
    del message['Errors-To']
    message['Errors-To'] = config.canonical.bounce_address

    # Add a Return-Path: header for bounce handling as well. Normally
    # this is added by the SMTP mailer using the From: header. But we
    # want it to be bounce_address instead.
    if 'return-path' not in message:
        message['Return-Path'] = config.canonical.bounce_address

    if bulk:
        # Add Precedence header to prevent automatic reply programs
        # (e.g. vacation) from trying to respond to our messages.
        del message['Precedence']
        message['Precedence'] = 'bulk'

    # Add an X-Generated-By header for easy whitelisting
    del message['X-Generated-By']
    message['X-Generated-By'] = 'Launchpad (canonical.com)'
    message.set_param('Revision', str(versioninfo.revision), 'X-Generated-By')
    message.set_param('Instance', config.name, 'X-Generated-By')

    # Add a shared secret header for pre-approval with Mailman. This approach
    # helps security, but still exposes us to a replay attack; we consider the
    # risk low.
    del message['X-Launchpad-Hash']
    hash = hashlib.sha1(config.mailman.shared_secret)
    hash.update(str(message['message-id']))
    message['X-Launchpad-Hash'] = hash.hexdigest()

    raw_message = message.as_string()
    message_detail = message['Subject']
    if not isinstance(message_detail, basestring):
        # Might be a Header object; can be squashed.
        message_detail = unicode(message_detail)
    if _immediate_mail_delivery:
        # Immediate email delivery is not unit tested, and won't be.
        # The immediate-specific stuff is pretty simple though so this
        # should be fine.
        # TODO: Store a timeline action for immediate mail.

        if config.isTestRunner():
            # when running in the testing environment, store emails
            TestMailer().send(config.canonical.bounce_address, to_addrs,
                              raw_message)
        elif getattr(config, 'sendmail_to_stdout', False):
            # For debugging, from process-one-mail, just print it.
            sys.stdout.write(raw_message)
        else:
            if config.immediate_mail.send_email:
                # Note that we simply throw away dud recipients. This is fine,
                # as it emulates the Z3 API which doesn't report this either
                # (because actual delivery is done later).
                smtp = SMTP(config.immediate_mail.smtp_host,
                            config.immediate_mail.smtp_port)

                # The "MAIL FROM" is set to the bounce address, to behave in a
                # way similar to mailing list software.
                smtp.sendmail(config.canonical.bounce_address, to_addrs,
                              raw_message)
                smtp.quit()
        # Strip the angle brackets to the return a Message-Id consistent with
        # raw_sendmail (which doesn't include them).
        return message['message-id'][1:-1]
    else:
        # The "MAIL FROM" is set to the bounce address, to behave in a way
        # similar to mailing list software.
        return raw_sendmail(config.canonical.bounce_address, to_addrs,
                            raw_message, message_detail)
Esempio n. 4
0
def execute_zcml_for_scripts(use_web_security=False):
    """Execute the zcml rooted at launchpad/script.zcml

    If use_web_security is True, the same security policy as the web
    application uses will be used. Otherwise everything protected by a
    permission is allowed, and everything else denied.
    """

    # When in testing mode, prevent some cases of erroneous layer usage.
    # But we don't want to import that module in production usage, thus
    # the conditional block.
    if 'lp.testing.layers' in sys.modules:
        from lp.testing.layers import (FunctionalLayer, BaseLayer,
                                       ZopelessLayer)
        assert not FunctionalLayer.isSetUp, \
                'Setting up Zopeless CA when Zopefull CA is already running'
        assert not BaseLayer.isSetUp or ZopelessLayer.isSetUp, """
                execute_zcml_for_scripts should not be called from tests.
                Instead, your test should use the Zopeless layer.
            """

    if config.isTestRunner():
        scriptzcmlfilename = 'script-testing.zcml'
    else:
        scriptzcmlfilename = 'script.zcml'

    scriptzcmlfilename = os.path.abspath(
        os.path.join(config.root, 'zcml', scriptzcmlfilename))

    from zope.configuration import xmlconfig

    # Hook up custom component architecture calls
    zope.site.hooks.setHooks()

    # Load server-independent site config
    context = ConfigurationMachine()
    xmlconfig.registerCommonDirectives(context)
    context = xmlconfig.file(scriptzcmlfilename, execute=True, context=context)

    if use_web_security:
        setSecurityPolicy(LaunchpadSecurityPolicy)
    else:
        setSecurityPolicy(LaunchpadPermissiveSecurityPolicy)

    # Register atexit handler to kill off mail delivery daemon threads, and
    # thus avoid spew at exit.  See:
    # http://mail.python.org/pipermail/python-list/2003-October/192044.html
    # http://mail.python.org/pipermail/python-dev/2003-September/038151.html
    # http://mail.python.org/pipermail/python-dev/2003-September/038153.html

    def kill_queue_processor_threads():
        for thread in threading.enumerate():
            if isinstance(thread, zope.sendmail.delivery.QueueProcessorThread):
                thread.stop()
                thread.join(30)
                if thread.isAlive():
                    raise RuntimeError(
                        "QueueProcessorThread did not shut down")

    atexit.register(kill_queue_processor_threads)

    # This is a convenient hack to set up a zope interaction, before we get
    # the proper API for having a principal / user running in scripts.
    setupInteractionByEmail(ANONYMOUS)
Esempio n. 5
0
def execute_zcml_for_scripts(use_web_security=False):
    """Execute the zcml rooted at launchpad/script.zcml

    If use_web_security is True, the same security policy as the web
    application uses will be used. Otherwise everything protected by a
    permission is allowed, and everything else denied.
    """

    # When in testing mode, prevent some cases of erroneous layer usage.
    # But we don't want to import that module in production usage, thus
    # the conditional block.
    if 'lp.testing.layers' in sys.modules:
        from lp.testing.layers import (
                FunctionalLayer, BaseLayer, ZopelessLayer)
        assert not FunctionalLayer.isSetUp, \
                'Setting up Zopeless CA when Zopefull CA is already running'
        assert not BaseLayer.isSetUp or ZopelessLayer.isSetUp, """
                execute_zcml_for_scripts should not be called from tests.
                Instead, your test should use the Zopeless layer.
            """

    if config.isTestRunner():
        scriptzcmlfilename = 'script-testing.zcml'
    else:
        scriptzcmlfilename = 'script.zcml'

    scriptzcmlfilename = os.path.abspath(
        os.path.join(config.root, 'zcml', scriptzcmlfilename))

    from zope.configuration import xmlconfig

    # Hook up custom component architecture calls
    zope.site.hooks.setHooks()

    # Load server-independent site config
    context = ConfigurationMachine()
    xmlconfig.registerCommonDirectives(context)
    context = xmlconfig.file(
        scriptzcmlfilename, execute=True, context=context)

    if use_web_security:
        setSecurityPolicy(LaunchpadSecurityPolicy)
    else:
        setSecurityPolicy(LaunchpadPermissiveSecurityPolicy)

    # Register atexit handler to kill off mail delivery daemon threads, and
    # thus avoid spew at exit.  See:
    # http://mail.python.org/pipermail/python-list/2003-October/192044.html
    # http://mail.python.org/pipermail/python-dev/2003-September/038151.html
    # http://mail.python.org/pipermail/python-dev/2003-September/038153.html

    def kill_queue_processor_threads():
        for thread in threading.enumerate():
            if isinstance(
                thread, zope.sendmail.delivery.QueueProcessorThread):
                thread.stop()
                thread.join(30)
                if thread.isAlive():
                    raise RuntimeError(
                        "QueueProcessorThread did not shut down")
    atexit.register(kill_queue_processor_threads)

    # This is a convenient hack to set up a zope interaction, before we get
    # the proper API for having a principal / user running in scripts.
    setupInteractionByEmail(ANONYMOUS)
Esempio n. 6
0
def sendmail(message, to_addrs=None, bulk=True):
    """Send an email.Message.Message

    If you just need to send dumb ASCII or Unicode, simple_sendmail
    will be easier for you. Sending attachments or multipart messages
    will need to use this method.

    From:, To: and Subject: headers should already be set.
    Message-Id:, Date:, and Reply-To: headers will be set if they are
    not already. Errors-To: and Return-Path: headers will always be set.
    The more we look valid, the less we look like spam.

    If to_addrs is None, the message will be sent to all the addresses
    specified in the To: and CC: headers.

    Uses zope.sendmail.interfaces.IMailer, so you can subscribe to
    IMailSentEvent or IMailErrorEvent to record status.

    This function looks at the `config` singleton for configuration as to
    where to send the mail; in particular for whether this code is running in
    zopeless mode, and for a `sendmail_to_stdout` attribute for testing.

    :param bulk: By default, a Precedence: bulk header is added to the
        message. Pass False to disable this.

    Returns the Message-Id
    """
    validate_message(message)
    if to_addrs is None:
        to_addrs = get_addresses_from_header(message['to'])
        if message['cc']:
            to_addrs = to_addrs + get_addresses_from_header(message['cc'])

    do_paranoid_envelope_to_validation(to_addrs)

    # Add a Message-Id: header if it isn't already there
    if 'message-id' not in message:
        message['Message-Id'] = get_msgid()

    # Add a Date: header if it isn't already there
    if 'date' not in message:
        message['Date'] = formatdate()

    # Add a Reply-To: header if it isn't already there
    if 'reply-to' not in message:
        message['Reply-To'] = message['from']

    # Add a Sender: header to show that we were the one sending the
    # email.
    if "Sender" not in message:
        message["Sender"] = config.canonical.bounce_address

    # Add an Errors-To: header for bounce handling
    del message['Errors-To']
    message['Errors-To'] = config.canonical.bounce_address

    # Add a Return-Path: header for bounce handling as well. Normally
    # this is added by the SMTP mailer using the From: header. But we
    # want it to be bounce_address instead.
    if 'return-path' not in message:
        message['Return-Path'] = config.canonical.bounce_address

    if bulk:
        # Add Precedence header to prevent automatic reply programs
        # (e.g. vacation) from trying to respond to our messages.
        del message['Precedence']
        message['Precedence'] = 'bulk'

    # Add an X-Generated-By header for easy whitelisting
    del message['X-Generated-By']
    message['X-Generated-By'] = 'Launchpad (canonical.com)'
    message.set_param('Revision', str(versioninfo.revno), 'X-Generated-By')
    message.set_param('Instance', config.name, 'X-Generated-By')

    # Add a shared secret header for pre-approval with Mailman. This approach
    # helps security, but still exposes us to a replay attack; we consider the
    # risk low.
    del message['X-Launchpad-Hash']
    hash = hashlib.sha1(config.mailman.shared_secret)
    hash.update(str(message['message-id']))
    message['X-Launchpad-Hash'] = hash.hexdigest()

    raw_message = message.as_string()
    message_detail = message['Subject']
    if not isinstance(message_detail, basestring):
        # Might be a Header object; can be squashed.
        message_detail = unicode(message_detail)
    if _immediate_mail_delivery:
        # Immediate email delivery is not unit tested, and won't be.
        # The immediate-specific stuff is pretty simple though so this
        # should be fine.
        # TODO: Store a timeline action for immediate mail.

        if config.isTestRunner():
            # when running in the testing environment, store emails
            TestMailer().send(
                config.canonical.bounce_address, to_addrs, raw_message)
        elif getattr(config, 'sendmail_to_stdout', False):
            # For debugging, from process-one-mail, just print it.
            sys.stdout.write(raw_message)
        else:
            if config.immediate_mail.send_email:
                # Note that we simply throw away dud recipients. This is fine,
                # as it emulates the Z3 API which doesn't report this either
                # (because actual delivery is done later).
                smtp = SMTP(
                    config.immediate_mail.smtp_host,
                    config.immediate_mail.smtp_port)

                # The "MAIL FROM" is set to the bounce address, to behave in a
                # way similar to mailing list software.
                smtp.sendmail(
                    config.canonical.bounce_address, to_addrs, raw_message)
                smtp.quit()
        # Strip the angle brackets to the return a Message-Id consistent with
        # raw_sendmail (which doesn't include them).
        return message['message-id'][1:-1]
    else:
        # The "MAIL FROM" is set to the bounce address, to behave in a way
        # similar to mailing list software.
        return raw_sendmail(
            config.canonical.bounce_address,
            to_addrs,
            raw_message,
            message_detail)