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
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)
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)
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)
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)