← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/refactor-notification into lp:launchpad

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/refactor-notification into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #786852 in Launchpad itself: "Package copies should be able to trigger notifications"
  https://bugs.launchpad.net/launchpad/+bug/786852

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/refactor-notification/+merge/61919

Building on the work started in https://code.launchpad.net/~stevenk/launchpad/announcements-copies/+merge/61516, this branch starts on disassociating package upload notifications with the PackageUpload class. The eventual plan is external call sites use the first function (def notification) shown in notification.py -- which is why it is there, and empty, and pass it a SourcePackageRelease, along with other information -- so that other places, most notably package copies can use the same infrastructure.
-- 
https://code.launchpad.net/~stevenk/launchpad/refactor-notification/+merge/61919
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/refactor-notification into lp:launchpad.
=== modified file 'lib/lp/archiveuploader/tests/nascentupload-announcements.txt'
--- lib/lp/archiveuploader/tests/nascentupload-announcements.txt	2011-01-26 22:19:55 +0000
+++ lib/lp/archiveuploader/tests/nascentupload-announcements.txt	2011-05-23 06:45:59 +0000
@@ -652,7 +652,7 @@
     ...
     INFO bar (1.0-6) breezy; urgency=low
     ...
-    INFO No announcement sent
+    INFO Announcing to hoary-announce@xxxxxxxxxxxxxxxx
     ...
     INFO You are receiving this email because you are the uploader, maintainer
     or
@@ -814,9 +814,6 @@
 
     >>> bar_src.logger = FakeLogger()
     >>> result = bar_src.do_accept()
-    DEBUG Building recipients list.
-    ...
-    DEBUG Sending rejection email.
     DEBUG Sent a mail:
     ...
     DEBUG Rejected:

=== modified file 'lib/lp/soyuz/adapters/notification.py'
--- lib/lp/soyuz/adapters/notification.py	2011-05-19 09:07:30 +0000
+++ lib/lp/soyuz/adapters/notification.py	2011-05-23 06:45:59 +0000
@@ -12,6 +12,8 @@
 
 from email.mime.multipart import MIMEMultipart
 from email.mime.text import MIMEText
+import os
+from zope.component import getUtility
 
 from canonical.config import config
 from canonical.launchpad.helpers import get_email_template
@@ -22,6 +24,8 @@
 from canonical.launchpad.webapp import canonical_url
 from lp.archivepublisher.utils import get_ppa_reference
 from lp.archiveuploader.changesfile import ChangesFile
+from lp.archiveuploader.utils import safe_fix_maintainer
+from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.pocket import (
     PackagePublishingPocket,
     pocketsuffix,
@@ -33,13 +37,81 @@
 from lp.soyuz.enums import PackageUploadStatus
 
 
-def notification(blamer, changesfile, archive, distroseries, pocket, action,
+def notification(blamer, spr, archive, distroseries, pocket, action,
                  actor=None, reason=None):
     pass
 
 
-def notify_spr_less(blamer, upload_path, changesfiles, reason):
-    pass
+def notify_spr_less(blamer, changes_file_path, changes, reason, logger=None):
+    ignored, filename = os.path.split(changes_file_path)
+    subject = '%s rejected' % filename
+    information = {
+        'SUMMARY': reason,
+        'CHANGESFILE': '',
+        'DATE': '',
+        'CHANGEDBY': '',
+        'MAINTAINER': '',
+        'SIGNER': '',
+        'ORIGIN': '',
+    }
+    template = get_email_template('upload-rejection.txt')
+    body = template % information
+    from_addr = format_address(
+        config.uploader.default_sender_name,
+        config.uploader.default_sender_address)
+    changedby = changes.get('Changed-By')
+    recipients = ''
+    if changedby is not None:
+        changedby_person = _emailToPerson(changedby)
+        if (
+            changedby_person is not None and
+            changedby_person.preferredemail is not None):
+                recipients = format_address(changedby_person.displayname,
+                    changedby_person.preferredemail.email)
+    if recipients == '':
+        recipients = "%s <%s>" % (
+            config.uploader.default_recipient_name,
+            config.uploader.default_recipient_address)
+    extra_headers = {'X-Katie': 'Launchpad actually'}
+    sendMessage(
+        subject, from_addr, recipients, extra_headers, body,
+        attach_changes=False, logger=logger)
+
+
+def get_template(archive, action):
+    """Return the appropriate e-mail template."""
+    template_name = 'upload-'
+    if action in ('new', 'accepted', 'announcement'):
+        template_name += action
+    elif action == 'unapproved':
+        template_name += 'accepted'
+    elif action == 'rejected':
+        template_name += 'rejection'
+    if archive.is_ppa:
+        template_name = 'ppa-%s' % template_name
+    template_name += '.txt'
+    return get_email_template(template_name)
+
+
+def get_status(action):
+    status = {
+        'new': 'New', 'unapproved': 'Waiting for approval',
+        'rejected': 'Rejected', 'accepted': 'Accepted',
+        'announcement': 'Accepted'}
+    return status[action]
+
+
+def calculate_subject(spr, archive, distroseries, pocket, action):
+    """Return the e-mail subject for the notification."""
+    suite = distroseries.name
+    if pocket != PackagePublishingPocket.RELEASE:
+        suite += pocketsuffix[pocket]
+    subject = '[%s/%s] %s %s (%s)' % (
+        distroseries.distribution.name, suite, spr.sourcepackagename.name,
+        spr.version, get_status(action))
+    if archive.is_ppa:
+        subject = '[%s] %s' % (get_ppa_reference(archive), subject)
+    return subject
 
 
 def notify(packageupload, announce_list=None, summary_text=None,
@@ -47,16 +119,13 @@
        allow_unsigned=None):
     """See `IPackageUpload`."""
 
-    packageupload.logger = logger
-
     # If this is a binary or mixed upload, we don't send *any* emails
     # provided it's not a rejection or a security upload:
-    if(packageupload.from_build and
-       packageupload.status != PackageUploadStatus.REJECTED and
-       packageupload.pocket != PackagePublishingPocket.SECURITY):
-        debug(
-            packageupload.logger,
-            "Not sending email; upload is from a build.")
+    if (
+        packageupload.from_build and
+        packageupload.status != PackageUploadStatus.REJECTED and
+        packageupload.pocket != PackagePublishingPocket.SECURITY):
+        debug(logger, "Not sending email; upload is from a build.")
         return
 
     # XXX julian 2007-05-11:
@@ -71,11 +140,21 @@
     changes, changes_lines = packageupload._getChangesDict(
         changes_file_object, allow_unsigned=allow_unsigned)
 
+    spr = packageupload.sourcepackagerelease
+
+    if spr == None:
+        # The sourcepackagerelease object that PackageUpload has fed us is
+        # fake.
+        notify_spr_less(
+            None, changes_file_object.name, changes, summary_text,
+            logger=logger)
+        return
+
     # "files" will contain a list of tuples of filename,component,section.
     # If files is empty, we don't need to send an email if this is not
     # a rejection.
     try:
-        files = _buildUploadedFilesList(packageupload)
+        files = _buildUploadedFilesList(spr, logger)
     except LanguagePackEncountered:
         # Don't send emails for language packs.
         return
@@ -83,17 +162,17 @@
     if not files and packageupload.status != PackageUploadStatus.REJECTED:
         return
 
-    summary = _buildSummary(packageupload, files)
+    summary = _buildSummary(spr, files)
     if summary_text:
         summary.append(summary_text)
     summarystring = "\n".join(summary)
 
-    recipients = _getRecipients(packageupload, changes)
+    recipients = _getRecipients(packageupload, changes, logger)
 
     # There can be no recipients if none of the emails are registered
     # in LP.
     if not recipients:
-        debug(packageupload.logger, "No recipients on email, not sending.")
+        debug(logger, "No recipients on email, not sending.")
         return
 
     # Make the content of the actual changes file available to the
@@ -107,156 +186,114 @@
     if packageupload.status == PackageUploadStatus.REJECTED:
         _sendRejectionNotification(
             packageupload, recipients, changes_lines, changes, summary_text,
-            dry_run, changesfile_content)
+            dry_run, changesfile_content, logger)
         return
 
     _sendSuccessNotification(
         packageupload, recipients, announce_list, changes_lines, changes,
-        summarystring, dry_run, changesfile_content)
+        summarystring, dry_run, changesfile_content, logger)
+
+
+def getFieldFromChanges(changes, field):
+    if changes.get(field):
+        return guess_encoding(changes.get(field))
+    else:
+        return ''
+
+
+def assembleBody(spr, archive, distroseries, summary, changes, action):
+    information = {
+        'STATUS': get_status(action),
+        'SUMMARY': summary,
+        'DATE': 'Date: %s' % changes['Date'],
+        'CHANGESFILE': ChangesFile.formatChangesComment(
+            getFieldFromChanges(changes, 'Changes')),
+        'DISTRO': distroseries.distribution.title,
+        'ANNOUNCE': 'No announcement sent',
+        'CHANGEDBY': '\nChanged-By: %s' % (
+            getFieldFromChanges(changes, 'Changed-By')),
+        'ORIGIN': '',
+        'SIGNER': '',
+        'SPR_URL': canonical_url(spr),
+        'USERS_ADDRESS': config.launchpad.users_address,
+        }
+    origin = getFieldFromChanges(changes, 'Origin')
+    if origin:
+        information['ORIGIN'] = '\nOrigin: %s' % origin
+    if action == 'unapproved':
+        information['SUMMARY'] += (
+            "\nThis upload awaits approval by a distro manager\n")
+    if distroseries.changeslist:
+        information['ANNOUNCE'] = "Announcing to %s" % (
+            distroseries.changeslist)
+    if spr.package_upload.signing_key is not None:
+        signer = spr.package_upload.signing_key.owner
+        signer_signature = '%s <%s>' % (
+            signer.displayname, signer.preferredemail.email)
+        if signer_signature != information['CHANGEDBY']:
+            information['SIGNER'] = '\nSigned-By: %s' % signer_signature
+    # Add maintainer if present and different from changed-by.
+    maintainer = getFieldFromChanges(changes, 'Maintainer')
+    if maintainer != information['CHANGEDBY']:
+        information['MAINTAINER'] = '\nMaintainer: %s' % maintainer
+    return get_template(archive, action) % information
+
+
+def sendMail(packageupload, summary_text, changes, recipients, dry_run,
+        action, changesfile_content=None, from_addr=None, bcc=None,
+        announce_list=None, logger=None):
+    spr = packageupload.sourcepackagerelease
+    archive = packageupload.archive
+    distroseries = packageupload.distroseries
+    pocket = packageupload.pocket
+    attach_changes = not archive.is_ppa
+
+    subject = calculate_subject(spr, archive, distroseries, pocket, action)
+    body = assembleBody(
+        spr, archive, distroseries, summary_text, changes, action)
+
+    _sendMail(
+        packageupload, archive, recipients, subject, body, dry_run,
+        changesfile_content=changesfile_content,
+        attach_changes=attach_changes, from_addr=from_addr, bcc=bcc,
+        logger=logger)
 
 
 def _sendSuccessNotification(
     packageupload, recipients, announce_list, changes_lines, changes,
-    summarystring, dry_run, changesfile_content):
+    summarystring, dry_run, changesfile_content, logger):
     """Send a success email."""
 
-    def do_sendmail(message, recipients=recipients, from_addr=None,
-                    bcc=None):
-        """Perform substitutions on a template and send the email."""
-        _handleCommonBodyContent(packageupload, message, changes)
-        body = message.template % message.__dict__
-
-        # Weed out duplicate name entries.
-        names = ', '.join(set(packageupload.displayname.split(', ')))
-
-        # Construct the suite name according to Launchpad/Soyuz
-        # convention.
-        pocket_suffix = pocketsuffix[packageupload.pocket]
-        if pocket_suffix:
-            suite = '%s%s' % (packageupload.distroseries.name, pocket_suffix)
-        else:
-            suite = packageupload.distroseries.name
-
-        subject = '[%s/%s] %s %s (%s)' % (
-            packageupload.distroseries.distribution.name, suite, names,
-            packageupload.displayversion, message.STATUS)
-
-        if packageupload.isPPA():
-            subject = "[PPA %s] %s" % (
-                get_ppa_reference(packageupload.archive), subject)
-            attach_changes = False
-        else:
-            attach_changes = True
-
-        _sendMail(
-            packageupload, recipients, subject, body, dry_run,
-            from_addr=from_addr, bcc=bcc,
-            changesfile_content=changesfile_content,
-            attach_changes=attach_changes)
-
-    class NewMessage:
-        """New message."""
-        template = get_email_template('upload-new.txt')
-
-        STATUS = "New"
-        SUMMARY = summarystring
-        CHANGESFILE = sanitize_string(
-            ChangesFile.formatChangesComment(changes['Changes']))
-        DISTRO = packageupload.distroseries.distribution.title
-        if announce_list:
-            ANNOUNCE = 'Announcing to %s' % announce_list
-        else:
-            ANNOUNCE = 'No announcement sent'
-
-    class UnapprovedMessage:
-        """Unapproved message."""
-        template = get_email_template('upload-accepted.txt')
-
-        STATUS = "Waiting for approval"
-        SUMMARY = summarystring + (
-                "\nThis upload awaits approval by a distro manager\n")
-        CHANGESFILE = sanitize_string(
-            ChangesFile.formatChangesComment(changes['Changes']))
-        DISTRO = packageupload.distroseries.distribution.title
-        if announce_list:
-            ANNOUNCE = 'Announcing to %s' % announce_list
-        else:
-            ANNOUNCE = 'No announcement sent'
-        CHANGEDBY = ''
-        ORIGIN = ''
-        SIGNER = ''
-        MAINTAINER = ''
-        SPR_URL = ''
-
-    class AcceptedMessage:
-        """Accepted message."""
-        template = get_email_template('upload-accepted.txt')
-
-        STATUS = "Accepted"
-        SUMMARY = summarystring
-        CHANGESFILE = sanitize_string(
-            ChangesFile.formatChangesComment(changes['Changes']))
-        DISTRO = packageupload.distroseries.distribution.title
-        if announce_list:
-            ANNOUNCE = 'Announcing to %s' % announce_list
-        else:
-            ANNOUNCE = 'No announcement sent'
-        CHANGEDBY = ''
-        ORIGIN = ''
-        SIGNER = ''
-        MAINTAINER = ''
-        SPR_URL = ''
-
-    class PPAAcceptedMessage:
-        """PPA accepted message."""
-        template = get_email_template('ppa-upload-accepted.txt')
-
-        STATUS = "Accepted"
-        SUMMARY = summarystring
-        CHANGESFILE = guess_encoding(
-            ChangesFile.formatChangesComment("".join(changes_lines)))
-
-    class AnnouncementMessage:
-        template = get_email_template('upload-announcement.txt')
-
-        STATUS = "Accepted"
-        SUMMARY = summarystring
-        CHANGESFILE = sanitize_string(
-            ChangesFile.formatChangesComment(changes['Changes']))
-        CHANGEDBY = ''
-        ORIGIN = ''
-        SIGNER = ''
-        MAINTAINER = ''
-        SPR_URL = ''
-
-    # The template is ready.  The remainder of this function deals with
-    # whether to send a 'new' message, an acceptance message and/or an
-    # announcement message.
-
     if packageupload.status == PackageUploadStatus.NEW:
         # This is an unknown upload.
-        do_sendmail(NewMessage)
+        sendMail(packageupload, summarystring, changes, recipients, dry_run,
+            'new', changesfile_content=changesfile_content, logger=logger)
         return
 
     # Unapproved uploads coming from an insecure policy only send
     # an acceptance message.
     if packageupload.status == PackageUploadStatus.UNAPPROVED:
         # Only send an acceptance message.
-        do_sendmail(UnapprovedMessage)
+        sendMail(packageupload, summarystring, changes, recipients, dry_run,
+            'unapproved', changesfile_content=changesfile_content,
+            logger=logger)
         return
 
     if packageupload.isPPA():
         # PPA uploads receive an acceptance message.
-        do_sendmail(PPAAcceptedMessage)
+        sendMail(packageupload, summarystring, changes, recipients, dry_run,
+            'accepted', changesfile_content=changesfile_content,
+            logger=logger)
         return
 
     # Auto-approved uploads to backports skips the announcement,
     # they are usually processed with the sync policy.
     if packageupload.pocket == PackagePublishingPocket.BACKPORTS:
-        debug(
-            packageupload.logger, "Skipping announcement, it is a BACKPORT.")
+        debug(logger, "Skipping announcement, it is a BACKPORT.")
 
-        do_sendmail(AcceptedMessage)
+        sendMail(packageupload, summarystring, changes, recipients, dry_run,
+            'accepted', changesfile_content=changesfile_content,
+            logger=logger)
         return
 
     # Auto-approved binary-only uploads to security skip the
@@ -264,14 +301,18 @@
     if (packageupload.pocket == PackagePublishingPocket.SECURITY
         and not packageupload.contains_source):
         # We only send announcements if there is any source in the upload.
-        debug(packageupload.logger,
+        debug(
+            logger,
             "Skipping announcement, it is a binary upload to SECURITY.")
-        do_sendmail(AcceptedMessage)
+        sendMail(packageupload, summarystring, changes, recipients, dry_run,
+            'accepted', changesfile_content=changesfile_content,
+            logger=logger)
         return
 
     # Fallback, all the rest coming from insecure, secure and sync
     # policies should send an acceptance and an announcement message.
-    do_sendmail(AcceptedMessage)
+    sendMail(packageupload, summarystring, changes, recipients, dry_run,
+        'accepted', changesfile_content=changesfile_content, logger=logger)
 
     # Don't send announcements for Debian auto sync uploads.
     if packageupload.isAutoSyncUpload(changed_by_email=changes['Changed-By']):
@@ -283,74 +324,33 @@
         else:
             from_addr = guess_encoding(changes['Changed-By'])
 
-        do_sendmail(
-            AnnouncementMessage,
-            recipients=[str(announce_list)],
+        sendMail(packageupload, summarystring, changes, [str(announce_list)],
+            dry_run, 'announcement', changesfile_content=changesfile_content,
             from_addr=from_addr,
-            bcc="%s_derivatives@xxxxxxxxxxxxxxxxxxxxxx" %
-                packageupload.displayname)
+            bcc="%s_derivatives@xxxxxxxxxxxxxxxxxxxxxx" % (
+                packageupload.displayname), logger=logger)
 
 
 def _sendRejectionNotification(
     packageupload, recipients, changes_lines, changes, summary_text, dry_run,
-    changesfile_content):
+    changesfile_content, logger):
     """Send a rejection email."""
 
-    class PPARejectedMessage:
-        """PPA rejected message."""
-        template = get_email_template('ppa-upload-rejection.txt')
-        SUMMARY = sanitize_string(summary_text)
-        CHANGESFILE = sanitize_string(
-            ChangesFile.formatChangesComment("".join(changes_lines)))
-        USERS_ADDRESS = config.launchpad.users_address
-
-    class RejectedMessage:
-        """Rejected message."""
-        template = get_email_template('upload-rejection.txt')
-        SUMMARY = sanitize_string(summary_text)
-        CHANGESFILE = sanitize_string(
-            ChangesFile.formatChangesComment(changes['Changes']))
-        CHANGEDBY = ''
-        ORIGIN = ''
-        SIGNER = ''
-        MAINTAINER = ''
-        SPR_URL = ''
-        USERS_ADDRESS = config.launchpad.users_address,
-
     default_recipient = "%s <%s>" % (
         config.uploader.default_recipient_name,
         config.uploader.default_recipient_address)
     if not recipients:
         recipients = [default_recipient]
 
-    debug(packageupload.logger, "Sending rejection email.")
-    if packageupload.isPPA():
-        message = PPARejectedMessage
-        attach_changes = False
-    else:
-        message = RejectedMessage
-        attach_changes = True
-
-    _handleCommonBodyContent(packageupload, message, changes)
-    if summary_text is None:
-        message.SUMMARY = 'Rejected by archive administrator.'
-
-    body = message.template % message.__dict__
-
-    subject = "%s rejected" % packageupload.changesfile.filename
-    if packageupload.isPPA():
-        subject = "[PPA %s] %s" % (
-            get_ppa_reference(packageupload.archive), subject)
-
-    _sendMail(
-        packageupload, recipients, subject, body, dry_run,
-        changesfile_content=changesfile_content,
-        attach_changes=attach_changes)
+    debug(logger, "Sending rejection email.")
+    sendMail(packageupload, summary_text, changes, recipients, dry_run,
+        'rejected', changesfile_content=changesfile_content, logger=logger)
 
 
 def _sendMail(
-    packageupload, to_addrs, subject, mail_text, dry_run, from_addr=None,
-    bcc=None, changesfile_content=None, attach_changes=False):
+    packageupload, archive, to_addrs, subject, mail_text, dry_run,
+    from_addr=None, bcc=None, changesfile_content=None,
+    attach_changes=False, logger=None):
     """Send an email to to_addrs with the given text and subject.
 
     :to_addrs: A list of email addresses to be used as recipients. Each
@@ -370,28 +370,18 @@
     """
     extra_headers = {'X-Katie': 'Launchpad actually'}
 
-    # XXX cprov 20071212: ideally we only need to check archive.purpose,
-    # however the current code in uploadprocessor.py (around line 259)
-    # temporarily transforms the primary-archive into a PPA one (w/o
-    # setting a proper owner) in order to allow processing of a upload
-    # to unknown PPA and subsequent rejection notification.
-
     # Include the 'X-Launchpad-PPA' header for PPA upload notfications
     # containing the PPA owner name.
-    if (
-        packageupload.archive.is_ppa and
-        packageupload.archive.owner is not None):
-        extra_headers['X-Launchpad-PPA'] = get_ppa_reference(
-            packageupload.archive)
+    if archive.is_ppa:
+        extra_headers['X-Launchpad-PPA'] = get_ppa_reference(archive)
 
     # Include a 'X-Launchpad-Component' header with the component and
     # the section of the source package uploaded in order to facilitate
     # filtering on the part of the email recipients.
-    if packageupload.sources:
-        spr = packageupload.my_source_package_release
-        xlp_component_header = 'component=%s, section=%s' % (
-            spr.component.name, spr.section.name)
-        extra_headers['X-Launchpad-Component'] = xlp_component_header
+    spr = packageupload.sourcepackagerelease
+    xlp_component_header = 'component=%s, section=%s' % (
+        spr.component.name, spr.section.name)
+    extra_headers['X-Launchpad-Component'] = xlp_component_header
 
     if from_addr is None:
         from_addr = format_address(
@@ -417,22 +407,31 @@
     else:
         from_addr.encode('ascii')
 
-    if dry_run and packageupload.logger is not None:
-        packageupload.logger.info("Would have sent a mail:")
-        packageupload.logger.info("  Subject: %s" % subject)
-        packageupload.logger.info("  Sender: %s" % from_addr)
-        packageupload.logger.info("  Recipients: %s" % recipients)
-        packageupload.logger.info("  Bcc: %s" % extra_headers['Bcc'])
-        packageupload.logger.info("  Body:")
+    sendMessage(
+        subject, from_addr, recipients, extra_headers, mail_text,
+        dry_run=dry_run, attach_changes=attach_changes,
+        changesfile_content=changesfile_content, logger=logger)
+
+
+def sendMessage(subject, from_addr, recipients, extra_headers, mail_text,
+                dry_run=None, attach_changes=None, changesfile_content=None,
+                logger=None):
+    if dry_run and logger is not None:
+        logger.info("Would have sent a mail:")
+        logger.info("  Subject: %s" % subject)
+        logger.info("  Sender: %s" % from_addr)
+        logger.info("  Recipients: %s" % recipients)
+        logger.info("  Bcc: %s" % extra_headers['Bcc'])
+        logger.info("  Body:")
         for line in mail_text.splitlines():
-            packageupload.logger.info(line)
+            logger.info(line)
     else:
-        debug(packageupload.logger, "Sent a mail:")
-        debug(packageupload.logger, "    Subject: %s" % subject)
-        debug(packageupload.logger, "    Recipients: %s" % recipients)
-        debug(packageupload.logger, "    Body:")
+        debug(logger, "Sent a mail:")
+        debug(logger, "    Subject: %s" % subject)
+        debug(logger, "    Recipients: %s" % recipients)
+        debug(logger, "    Body:")
         for line in mail_text.splitlines():
-            debug(packageupload.logger, line)
+            debug(logger, line)
 
         # Since we need to send the original changesfile as an
         # attachment the sendmail() method will be used as opposed to
@@ -447,13 +446,12 @@
             message.add_header(key, value)
 
         # Add the email body.
-        message.attach(MIMEText(
-           sanitize_string(mail_text).encode('utf-8'), 'plain', 'utf-8'))
+        message.attach(MIMEText(mail_text.encode('utf-8'), 'plain', 'utf-8'))
 
         if attach_changes:
             # Add the original changesfile as an attachment.
             if changesfile_content is not None:
-                changesfile_text = sanitize_string(changesfile_content)
+                changesfile_text = guess_encoding(changesfile_content)
             else:
                 changesfile_text = ("Sorry, changesfile not available.")
 
@@ -468,88 +466,24 @@
         sendmail(message)
 
 
-def _handleCommonBodyContent(packageupload, message, changes):
-    """Put together pieces of the body common to all emails.
-
-    Sets the date, changed-by, maintainer, signer and origin properties on
-    the message as appropriate.
-
-    :message: An object containing the various pieces of the notification
-        email.
-    :changes: A dictionary with the changes file content.
-    """
-    # Add the date field.
-    message.DATE = 'Date: %s' % changes['Date']
-
-    # Add the debian 'Changed-By:' field.
-    changed_by = changes.get('Changed-By')
-    if changed_by is not None:
-        changed_by = sanitize_string(changed_by)
-        message.CHANGEDBY = '\nChanged-By: %s' % changed_by
-
-    # Add maintainer if present and different from changed-by.
-    maintainer = changes.get('Maintainer')
-    if maintainer is not None:
-        maintainer = sanitize_string(maintainer)
-        if maintainer != changed_by:
-            message.MAINTAINER = '\nMaintainer: %s' % maintainer
-
-    # Add a 'Signed-By:' line if this is a signed upload and the
-    # signer/sponsor differs from the changed-by.
-    if packageupload.signing_key is not None:
-        # This is a signed upload.
-        signer = packageupload.signing_key.owner
-
-        signer_name = sanitize_string(signer.displayname)
-        signer_email = sanitize_string(signer.preferredemail.email)
-
-        signer_signature = '%s <%s>' % (signer_name, signer_email)
-
-        if changed_by != signer_signature:
-            message.SIGNER = '\nSigned-By: %s' % signer_signature
-
-    # Add the debian 'Origin:' field if present.
-    if changes.get('Origin') is not None:
-        message.ORIGIN = '\nOrigin: %s' % changes['Origin']
-
-    if packageupload.sources or packageupload.builds:
-        message.SPR_URL = canonical_url(
-            packageupload.my_source_package_release)
-
-
-def sanitize_string(s):
-    """Make sure string does not trigger 'ascii' codec errors.
-
-    Convert string to unicode if needed so that characters outside
-    the (7-bit) ASCII range do not cause errors like these:
-
-        'ascii' codec can't decode byte 0xc4 in position 21: ordinal
-        not in range(128)
-    """
-    if isinstance(s, unicode):
-        return s
-    else:
-        return guess_encoding(s)
-
-
 def debug(logger, msg):
     """Shorthand debug notation for publish() methods."""
     if logger is not None:
         logger.debug(msg)
 
 
-def _getRecipients(packageupload, changes):
+def _getRecipients(packageupload, changes, logger):
     """Return a list of recipients for notification emails."""
     candidate_recipients = []
-    debug(packageupload.logger, "Building recipients list.")
-    changer = packageupload._emailToPerson(changes['Changed-By'])
+    debug(logger, "Building recipients list.")
+    changer = _emailToPerson(changes['Changed-By'])
 
     if packageupload.signing_key:
         # This is a signed upload.
         signer = packageupload.signing_key.owner
         candidate_recipients.append(signer)
     else:
-        debug(packageupload.logger,
+        debug(logger,
             "Changes file is unsigned, adding changer as recipient")
         candidate_recipients.append(changer)
 
@@ -564,16 +498,16 @@
 
     # If this is not a PPA, we also consider maintainer and changed-by.
     if packageupload.signing_key and not packageupload.isPPA():
-        maintainer = packageupload._emailToPerson(changes['Maintainer'])
+        maintainer = _emailToPerson(changes['Maintainer'])
         if (maintainer and maintainer != signer and
                 maintainer.isUploader(
                     packageupload.distroseries.distribution)):
-            debug(packageupload.logger, "Adding maintainer to recipients")
+            debug(logger, "Adding maintainer to recipients")
             candidate_recipients.append(maintainer)
 
         if (changer and changer != signer and
                 changer.isUploader(packageupload.distroseries.distribution)):
-            debug(packageupload.logger, "Adding changed-by to recipients")
+            debug(logger, "Adding changed-by to recipients")
             candidate_recipients.append(changer)
 
     # Now filter list of recipients for persons only registered in
@@ -584,13 +518,13 @@
             continue
         recipient = format_address(person.displayname,
             person.preferredemail.email)
-        debug(packageupload.logger, "Adding recipient: '%s'" % recipient)
+        debug(logger, "Adding recipient: '%s'" % recipient)
         recipients.append(recipient)
 
     return recipients
 
 
-def _buildUploadedFilesList(packageupload):
+def _buildUploadedFilesList(spr, logger):
     """Return a list of tuples of (filename, component, section).
 
     Component and section are only set where the file is a source upload.
@@ -599,39 +533,24 @@
     No emails should be sent for language packs.
     """
     files = []
-    if packageupload.contains_source:
-        [source] = packageupload.sources
-        spr = source.sourcepackagerelease
-        # Bail out early if this is an upload for the translations
-        # section.
-        if spr.section.name == 'translations':
-            debug(packageupload.logger,
-                "Skipping acceptance and announcement, it is a "
-                "language-package upload.")
-            raise LanguagePackEncountered
-        for sprfile in spr.files:
-            files.append(
-                (sprfile.libraryfile.filename, spr.component.name,
-                 spr.section.name))
-
-    # Component and section don't get set for builds and custom, since
-    # this information is only used in the summary string for source
-    # uploads.
-    for build in packageupload.builds:
-        for bpr in build.build.binarypackages:
-            files.extend([
-                (bpf.libraryfile.filename, '', '') for bpf in bpr.files])
-
-    if packageupload.customfiles:
-        files.extend(
-            [(file.libraryfilealias.filename, '', '')
-            for file in packageupload.customfiles])
+    # Bail out early if this is an upload for the translations
+    # section.
+    if spr.section.name == 'translations':
+        debug(logger,
+            "Skipping acceptance and announcement, it is a "
+            "language-package upload.")
+        raise LanguagePackEncountered
+    for sprfile in spr.files:
+        files.append(
+            (sprfile.libraryfile.filename, spr.component.name,
+             spr.section.name))
 
     return files
 
 
-def _buildSummary(packageupload, files):
+def _buildSummary(spr, files):
     """Build a summary string based on the files present in the upload."""
+    packageupload = spr.package_upload
     summary = []
     for filename, component, section in files:
         if packageupload.status == PackageUploadStatus.NEW:
@@ -644,5 +563,14 @@
     return summary
 
 
+def _emailToPerson(fullemail):
+    """Return an IPerson given an RFC2047 email address."""
+    # The 2nd arg to s_f_m() doesn't matter as it won't fail since every-
+    # thing will have already parsed at this point.
+    (rfc822, rfc2047, name, email) = safe_fix_maintainer(
+        fullemail, "email")
+    return getUtility(IPersonSet).getByEmail(email)
+
+
 class LanguagePackEncountered(Exception):
     """Thrown when not wanting to email notifications for language packs."""