Example #1
0
    def test_sendmail_with_catch_all(self):
        recipients = ['*****@*****.**',
                      'Recipient-2 <*****@*****.**>',
                      ('Recipient-3', '*****@*****.**')]
        ccs = ['*****@*****.**',
               'CC-2 <*****@*****.**>',
               ('CC-3', '*****@*****.**')]
        bccs = ['*****@*****.**',
                'BCC-2 <*****@*****.**>',
                ('BCC-3', '*****@*****.**')]

        with self.mail_config(catch_all='*****@*****.**'):
            with self.mail.connect() as conn:
                with mock.patch.object(conn, 'host') as host:
                    msg = Message(subject="testing",
                                  sender="*****@*****.**",
                                  recipients=recipients,
                                  cc=ccs, bcc=bccs,
                                  body=u"Öö")

                    conn.send(msg)

                    args, kwargs = host.sendmail.call_args

                    for mail in recipients + ccs + bccs:
                        send_to = args[1]
                        expected = sanitize_address(mail, catch_all='*****@*****.**')
                        not_expected = sanitize_address(mail)
                        self.assertIn(expected, send_to)
                        self.assertNotIn(not_expected, send_to)
Example #2
0
    def send(self, message, envelope_from=None):
        """Verifies and sends message.

        :param message: Message instance.
        :param envelope_from: Email address to be used in MAIL FROM command.
        """
        assert message.recipients, "No recipients have been added"

        assert message.sender, (
                "The message does not specify a sender and a default sender "
                "has not been configured")

        if message.has_bad_headers():
            raise BadHeaderError

        if message.date is None:
            message.date = time.time()

        if self.host:
            self.host.sendmail(sanitize_address(envelope_from or message.sender),
                               message.send_to,
                               message.as_string(),
                               message.mail_options,
                               message.rcpt_options)

        email_dispatched.send(message, app=current_app._get_current_object())

        self.num_emails += 1

        if self.num_emails == self.mail.max_emails:
            self.num_emails = 0
            if self.host:
                self.host.quit()
                self.host = self.configure_host()
Example #3
0
    def send(self, job, parent=None):
        # TODO(dcramer): we should send a clipping of a relevant job log
        recipients = self.get_recipients(job)
        if not recipients:
            return

        event = try_create(Event, where={
            'type': EventType.email,
            'item_id': job.build_id,
            'data': {
                'recipients': recipients,
            }
        })
        if not event:
            # We've already sent out notifications for this build
            return

        context = self.get_context(job, parent)

        msg = Message(context['title'], recipients=recipients, extra_headers={
            'Reply-To': ', '.join(sanitize_address(r) for r in recipients),
        })
        msg.body = render_template('listeners/mail/notification.txt', **context)
        msg.html = Markup(toronado.from_string(
            render_template('listeners/mail/notification.html', **context)
        ))

        mail.send(msg)
Example #4
0
    def get_msg(self, builds):
        # type: (List[Build]) -> Message
        context = build_context_lib.get_collection_context(
            builds)  # type: CollectionContext
        if context.result == Result.passed:
            return None
        max_shown = current_app.config.get('MAX_SHOWN_ITEMS_PER_BUILD_MAIL', 3)
        context_dict = context._asdict()
        context_dict.update({
            'MAX_SHOWN_ITEMS_PER_BUILD':
            max_shown,
            'showing_failing_tests_count':
            sum([
                min(b['failing_tests_count'], max_shown)
                for b in context.builds
            ])
        })
        recipients = self.get_collection_recipients(context)

        msg = Message(context.title,
                      recipients=recipients,
                      extra_headers={
                          'Reply-To':
                          ', '.join(sanitize_address(r) for r in recipients),
                      })
        msg.body = render_template('listeners/mail/notification.txt',
                                   **context_dict)
        msg.html = Markup(
            toronado.from_string(
                render_template('listeners/mail/notification.html',
                                **context_dict)))

        return msg
Example #5
0
    def send(self, job, parent=None):
        # TODO(dcramer): we should send a clipping of a relevant job log
        recipients = self.get_recipients(job)
        if not recipients:
            return

        event = try_create(Event,
                           where={
                               'type': EventType.email,
                               'item_id': job.build_id,
                               'data': {
                                   'recipients': recipients,
                               }
                           })
        if not event:
            # We've already sent out notifications for this build
            return

        context = self.get_context(job, parent)

        msg = Message(context['title'],
                      recipients=recipients,
                      extra_headers={
                          'Reply-To':
                          ', '.join(sanitize_address(r) for r in recipients),
                      })
        msg.body = render_template('listeners/mail/notification.txt',
                                   **context)
        msg.html = Markup(
            toronado.from_string(
                render_template('listeners/mail/notification.html',
                                **context)))

        mail.send(msg)
Example #6
0
    def send(self, message, envelope_from=None):
        """Verifies and sends message.

        :param message: Message instance.
        :param envelope_from: Email address to be used in MAIL FROM command.
        """
        assert message.recipients, "No recipients have been added"

        assert message.sender, (
            "The message does not specify a sender and a default sender "
            "has not been configured")

        if message.has_bad_headers():
            raise BadHeaderError

        if message.date is None:
            message.date = time.time()

        if self.host:
            self.host.sendmail(
                sanitize_address(envelope_from
                                 or message.sender), message.send_to,
                message.as_string(), message.mail_options,
                message.rcpt_options)

        email_dispatched.send(message, app=current_app._get_current_object())

        self.num_emails += 1

        if self.num_emails == self.mail.max_emails:
            self.num_emails = 0
            if self.host:
                self.host.quit()
                self.host = self.configure_host()
Example #7
0
 def test_sanitize_addresse_with_catch_all(self):
     values = (
         '*****@*****.**',
         ('Recipient', '*****@*****.**'),
         'Recipient <*****@*****.**>',
     )
     for value in values:
         self.assertIn(' <*****@*****.**>',
                       sanitize_address(value, catch_all='*****@*****.**'))
Example #8
0
    def test_reply_to(self):
        msg = Message(subject="testing",
                      recipients=["*****@*****.**"],
                      sender="spammer <*****@*****.**>",
                      reply_to="somebody <*****@*****.**>",
                      body="testing")
        response = msg.as_string()

        h = Header("Reply-To: %s" % sanitize_address('somebody <*****@*****.**>'))
        self.assertIn(h.encode(), str(response))
Example #9
0
    def test_reply_to(self):
        msg = Message(subject="testing",
                      recipients=["*****@*****.**"],
                      sender="spammer <*****@*****.**>",
                      reply_to="somebody <*****@*****.**>",
                      body="testing")
        response = msg.as_string()

        h = Header("Reply-To: %s" % sanitize_address('somebody <*****@*****.**>'))
        self.assertIn(h.encode(), str(response))
Example #10
0
def send_notification(job, recipients):
    # TODO(dcramer): we should send a clipping of a relevant job log
    test_failures = TestGroup.query.filter(
        TestGroup.job_id == job.id,
        TestGroup.result == Result.failed,
        TestGroup.num_leaves == 0,
    ).order_by(TestGroup.name.asc())
    num_test_failures = test_failures.count()
    test_failures = test_failures[:25]

    build = job.build

    # TODO(dcramer): we should probably find a better way to do logs
    primary_log = LogSource.query.filter(
        LogSource.job_id == job.id,
    ).order_by(LogSource.date_created.asc()).first()
    if primary_log:
        log_clipping = get_log_clipping(
            primary_log, max_size=5000, max_lines=25)

    subject = u"Build {result} - {project} #{number} ({target})".format(
        number='{0}.{1}'.format(job.build.number, job.number),
        result=unicode(job.result),
        target=build.target or build.source.revision_sha or 'Unknown',
        project=job.project.name,
    )

    for testgroup in test_failures:
        testgroup.uri = build_uri('/testgroups/{0}/'.format(testgroup.id.hex))

    job.uri = build_uri('/jobs/{0}/'.format(job.id.hex))
    build.uri = build_uri('/builds/{0}/'.format(build.id.hex))

    context = {
        'job': job,
        'build': job.build,
        'total_test_failures': num_test_failures,
        'test_failures': test_failures,
    }

    if primary_log:
        context['build_log'] = {
            'text': log_clipping,
            'name': primary_log.name,
            'link': '{0}logs/{1}/'.format(job.uri, primary_log.id.hex),
        }

    msg = Message(subject, recipients=recipients, extra_headers={
        'Reply-To': ', '.join(sanitize_address(r) for r in recipients),
    })
    msg.body = render_template('listeners/mail/notification.txt', **context)
    msg.html = render_template('listeners/mail/notification.html', **context)

    mail.send(msg)
Example #11
0
    def get_msg(self, context):
        recipients = self.get_collection_recipients(context)

        msg = Message(context['title'], recipients=recipients, extra_headers={
            'Reply-To': ', '.join(sanitize_address(r) for r in recipients),
        })
        msg.body = render_template('listeners/mail/notification.txt', **context)
        msg.html = Markup(toronado.from_string(
            render_template('listeners/mail/notification.html', **context)
        ))

        return msg
Example #12
0
    def test_unicode_headers(self):
        msg = Message(subject="subject",
                      sender=u'ÄÜÖ → ✓ <*****@*****.**>',
                      recipients=[u"Ä <*****@*****.**>", u"Ü <*****@*****.**>"],
                      cc=[u"Ö <*****@*****.**>"])

        response = msg.as_string()
        a1 = sanitize_address(u"Ä <*****@*****.**>")
        a2 = sanitize_address(u"Ü <*****@*****.**>")
        h1_a = Header("To: %s, %s" % (a1, a2))
        h1_b = Header("To: %s, %s" % (a2, a1))
        h2 = Header("From: %s" % sanitize_address(u"ÄÜÖ → ✓ <*****@*****.**>"))
        h3 = Header("Cc: %s" % sanitize_address(u"Ö <*****@*****.**>"))

        # Ugly, but there's no guaranteed order of the recipieints in the header
        try:
            self.assertIn(h1_a.encode(), response)
        except AssertionError:
            self.assertIn(h1_b.encode(), response)

        self.assertIn(h2.encode(), response)
        self.assertIn(h3.encode(), response)
Example #13
0
    def test_send_message_with_catch_all(self):
        recipients = [
            '*****@*****.**', 'Recipient-2 <*****@*****.**>',
            ('Recipient-3', '*****@*****.**')
        ]
        ccs = [
            '*****@*****.**', 'CC-2 <*****@*****.**>', ('CC-3', '*****@*****.**')
        ]

        # Not tested because BCCs are not serialized into headers
        # See: https://github.com/mattupstate/flask-mail/pull/29
        bccs = [
            '*****@*****.**', 'BCC-2 <*****@*****.**>',
            ('BCC-3', '*****@*****.**')
        ]
        sender = '*****@*****.**'
        reply_to = '*****@*****.**'
        catch_all = '*****@*****.**'

        with self.mail_config(catch_all=catch_all):
            with self.mail.record_messages() as outbox:
                self.mail.send_message(subject='testing',
                                       sender=sender,
                                       reply_to=reply_to,
                                       recipients=recipients,
                                       cc=ccs,
                                       bcc=bccs,
                                       body='test')
                self.assertEqual(len(outbox), 1)
                msg = outbox[0]
                response = msg.as_string()

        for value in recipients + ccs:
            expected = sanitize_address(value, catch_all=catch_all)
            self.assertIn(expected, response)

        for value in (sender, reply_to):
            not_expected = sanitize_address(value, catch_all=catch_all)
            self.assertNotIn(not_expected, response)
Example #14
0
    def test_unicode_headers(self):
        msg = Message(subject="subject",
                      sender=u'ÄÜÖ → ✓ <*****@*****.**>',
                      recipients=[u"Ä <*****@*****.**>", u"Ü <*****@*****.**>"],
                      cc=[u"Ö <*****@*****.**>"])

        response = msg.as_string()
        a1 = sanitize_address(u"Ä <*****@*****.**>")
        a2 = sanitize_address(u"Ü <*****@*****.**>")
        h1_a = Header("To: %s, %s" % (a1, a2))
        h1_b = Header("To: %s, %s" % (a2, a1))
        h2 = Header("From: %s" % sanitize_address(u"ÄÜÖ → ✓ <*****@*****.**>"))
        h3 = Header("Cc: %s" % sanitize_address(u"Ö <*****@*****.**>"))

        # Ugly, but there's no guaranteed order of the recipieints in the header
        try:
            self.assertIn(h1_a.encode(), response)
        except AssertionError:
            self.assertIn(h1_b.encode(), response)

        self.assertIn(h2.encode(), response)
        self.assertIn(h3.encode(), response)
Example #15
0
    def send(self, job, parent=None):
        # TODO(dcramer): we should send a clipping of a relevant job log
        recipients = self.get_recipients(job)
        if not recipients:
            return

        context = self.get_context(job, parent)

        msg = Message(context['title'], recipients=recipients, extra_headers={
            'Reply-To': ', '.join(sanitize_address(r) for r in recipients),
        })
        msg.body = render_template('listeners/mail/notification.txt', **context)
        msg.html = Markup(Pynliner().from_string(
            render_template('listeners/mail/notification.html', **context)
        ).run())

        mail.send(msg)
Example #16
0
    def get_msg(self, builds):
        context = build_context_lib.get_collection_context(builds)
        if context['result'] == Result.passed:
            return None
        max_shown = current_app.config.get('MAX_SHOWN_ITEMS_PER_BUILD_MAIL', 3)
        context.update({
            'MAX_SHOWN_ITEMS_PER_BUILD': max_shown,
            'showing_failing_tests_count':
                sum([min(b['failing_tests_count'], max_shown) for b in context['builds']])
        })
        recipients = self.get_collection_recipients(context)

        msg = Message(context['title'], recipients=recipients, extra_headers={
            'Reply-To': ', '.join(sanitize_address(r) for r in recipients),
        })
        msg.body = render_template('listeners/mail/notification.txt', **context)
        msg.html = Markup(toronado.from_string(
            render_template('listeners/mail/notification.html', **context)
        ))

        return msg
Example #17
0
def build_message(repository: Repository,
                  reason: DeactivationReason) -> Message:
    recipients = [
        e for e, in db.session.query(User.email).filter(
            User.id.in_(
                db.session.query(RepositoryAccess.user_id).filter(
                    RepositoryAccess.repository_id == repository.id,
                    RepositoryAccess.permission == Permission.admin,
                )))
    ]
    if not recipients:
        current_app.logger.warn(
            "email.deactivated-repository.no-recipients repository_id=%s",
            repository.id)
        return

    context = {
        "title": "Repository Disabled",
        "settings_url": absolute_url("/settings/github/repos"),
        "repo": {
            "owner_name": repository.owner_name,
            "name": repository.name,
            "full_name": repository.get_full_name,
        },
        "reason": reason,
    }

    msg = Message(
        "Repository Disabled - {}/{}".format(repository.owner_name,
                                             repository.name),
        recipients=recipients,
        extra_headers={
            "Reply-To": ", ".join(sanitize_address(r) for r in recipients)
        },
    )
    msg.body = render_template("emails/deactivated-repository.txt", **context)
    msg.html = inline_css(
        render_template("emails/deactivated-repository.html", **context))

    return msg
Example #18
0
    def send(self, job, parent=None):
        # TODO(dcramer): we should send a clipping of a relevant job log
        recipients = filter_recipients(self.get_recipients(job))
        if not recipients:
            return

        event = try_create(
            Event, where={"type": EventType.email, "item_id": job.build_id, "data": {"recipients": recipients}}
        )
        if not event:
            # We've already sent out notifications for this build
            return

        context = self.get_context(job, parent)

        msg = Message(
            context["title"],
            recipients=recipients,
            extra_headers={"Reply-To": ", ".join(sanitize_address(r) for r in recipients)},
        )
        msg.body = render_template("listeners/mail/notification.txt", **context)
        msg.html = Markup(toronado.from_string(render_template("listeners/mail/notification.html", **context)))

        mail.send(msg)
Example #19
0
def build_message(build: Build, force=False) -> Message:
    author = Author.query.join(
        Source,
        Source.author_id == Author.id,
    ).filter(Source.id == build.source_id, ).first()
    if not author:
        current_app.logger.info('mail.missing-author',
                                extra={
                                    'build_id': build.id,
                                })
        return

    users = find_linked_accounts(build)
    if not users and not force:
        current_app.logger.info('mail.no-linked-accounts',
                                extra={
                                    'build_id': build.id,
                                })
        return
    elif not users:
        users = [auth.get_current_user()]

    # filter it down to the users that have notifications enabled
    user_options = dict(
        db.session.query(ItemOption.item_id, ItemOption.value).filter(
            ItemOption.item_id.in_([u.id for u in users]),
            ItemOption.name == 'mail.notify_author',
        ))
    users = [u for u in users if user_options.get(u.id, '1') == '1']
    if not users:
        current_app.logger.info('mail.no-enabed-accounts',
                                extra={
                                    'build_id': build.id,
                                })
        return

    source = Source.query.get(build.source_id)
    assert source

    repo = Repository.query.get(build.repository_id)
    assert repo

    revision = Revision.query.filter(
        Revision.sha == source.revision_sha,
        Revision.repository_id == build.repository_id,
    ).first()
    assert revision

    job_list = sorted(Job.query.filter(Job.build_id == build.id),
                      key=lambda x: [x.result != Result.failed, x.number])
    job_ids = [j.id for j in job_list]

    recipients = [u.email for u in users]

    subject = 'Build {} - {}/{} #{}'.format(
        str(build.result).title(),
        repo.owner_name,
        repo.name,
        build.number,
    )

    if job_ids:
        failing_tests_query = TestCase.query.options(
            undefer('message')).filter(
                TestCase.job_id.in_(job_ids),
                TestCase.result == Result.failed,
            )

        failing_tests_count = failing_tests_query.count()
        failing_tests = failing_tests_query.limit(10)

        style_violations_query = StyleViolation.query.filter(
            StyleViolation.job_id.in_(job_ids), )
        style_violations_count = style_violations_query.count()
        style_violations = style_violations_query.limit(10)
    else:
        failing_tests = ()
        failing_tests_count = 0
        style_violations = ()
        style_violations_count = 0

    context = {
        'title':
        subject,
        'uri':
        '{proto}://{domain}/{repo}/builds/{build_no}'.format(
            proto='https' if current_app.config['SSL'] else 'http',
            domain=current_app.config['DOMAIN'],
            repo=repo.get_full_name(),
            build_no=build.number,
        ),
        'build': {
            'number': build.number,
            'result': {
                'id': str(build.result),
                'name': str(build.result).title(),
            },
            'label': build.label,
        },
        'repo': {
            'owner_name': repo.owner_name,
            'name': repo.name,
            'full_name': repo.get_full_name,
        },
        'author': {
            'name': author.name,
            'email': author.email,
        },
        'revision': {
            'sha': revision.sha,
            'short_sha': revision.sha[:7],
            'message': revision.message,
        },
        'job_list': [{
            'number': job.number,
            'result': {
                'id': str(job.result),
                'name': str(job.result).title(),
            },
            'url': job.url,
            'label': job.label,
        } for job in job_list],
        'job_failure_count':
        sum((1 for job in job_list if job.result == Result.failed)),
        'date_created':
        build.date_created,
        'failing_tests': [{
            'name': test.name
        } for test in failing_tests],
        'failing_tests_count':
        failing_tests_count,
        'style_violations': [{
            'message': violation.message,
            'filename': violation.filename
        } for violation in style_violations],
        'style_violations_count':
        style_violations_count,
    }

    msg = Message(subject,
                  recipients=recipients,
                  extra_headers={
                      'Reply-To':
                      ', '.join(sanitize_address(r) for r in recipients),
                  })
    msg.body = render_template('notifications/email.txt', **context)
    msg.html = inline_css(
        render_template('notifications/email.html', **context))

    return msg
Example #20
0
def build_message(build: Build, force=False) -> Message:
    author = Author.query.join(Source, Source.author_id == Author.id).filter(
        Source.id == build.source_id
    ).first()
    if not author:
        current_app.logger.info("mail.missing-author", extra={"build_id": build.id})
        return

    emails = find_linked_emails(build)
    if not emails and not force:
        current_app.logger.info("mail.no-linked-accounts", extra={"build_id": build.id})
        return

    elif not emails:
        current_user = auth.get_current_user()
        emails = [[current_user.id, current_user.email]]

    # filter it down to the users that have notifications enabled
    user_options = dict(
        db.session.query(ItemOption.item_id, ItemOption.value).filter(
            ItemOption.item_id.in_([uid for uid, _ in emails]),
            ItemOption.name == "mail.notify_author",
        )
    )
    emails = [r for r in emails if user_options.get(r[0], "1") == "1"]
    if not emails:
        current_app.logger.info("mail.no-enabed-accounts", extra={"build_id": build.id})
        return

    source = Source.query.get(build.source_id)
    assert source

    repo = Repository.query.get(build.repository_id)
    assert repo

    revision = Revision.query.filter(
        Revision.sha == source.revision_sha,
        Revision.repository_id == build.repository_id,
    ).first()
    assert revision

    job_list = sorted(
        Job.query.filter(Job.build_id == build.id),
        key=lambda x: [x.result != Result.failed, x.number],
    )
    job_ids = [j.id for j in job_list]

    recipients = [r[1] for r in emails]

    subject = "Build {} - {}/{} #{}".format(
        str(build.result).title(), repo.owner_name, repo.name, build.number
    )

    if job_ids:
        failing_tests_query = TestCase.query.options(undefer("message")).filter(
            TestCase.job_id.in_(job_ids), TestCase.result == Result.failed
        )

        failing_tests_count = failing_tests_query.count()
        failing_tests = failing_tests_query.limit(10)

        style_violations_query = StyleViolation.query.filter(
            StyleViolation.job_id.in_(job_ids)
        ).order_by(
            (StyleViolation.severity == Severity.error).desc(),
            StyleViolation.filename.asc(),
            StyleViolation.lineno.asc(),
            StyleViolation.colno.asc(),
        )
        style_violations_count = style_violations_query.count()
        style_violations = style_violations_query.limit(10)
    else:
        failing_tests = ()
        failing_tests_count = 0
        style_violations = ()
        style_violations_count = 0

    context = {
        "title": subject,
        "uri": "{proto}://{domain}/{repo}/builds/{build_no}".format(
            proto="https" if current_app.config["SSL"] else "http",
            domain=current_app.config["DOMAIN"],
            repo=repo.get_full_name(),
            build_no=build.number,
        ),
        "build": {
            "number": build.number,
            "result": {"id": str(build.result), "name": str(build.result).title()},
            "label": build.label,
        },
        "repo": {
            "owner_name": repo.owner_name,
            "name": repo.name,
            "full_name": repo.get_full_name,
        },
        "author": {"name": author.name, "email": author.email},
        "revision": {
            "sha": revision.sha,
            "short_sha": revision.sha[:7],
            "message": revision.message,
        },
        "job_list": [
            {
                "number": job.number,
                "result": {"id": str(job.result), "name": str(job.result).title()},
                "url": job.url,
                "label": job.label,
            }
            for job in job_list
        ],
        "job_failure_count": sum(
            (1 for job in job_list if job.result == Result.failed)
        ),
        "date_created": build.date_created,
        "failing_tests": [{"name": test.name} for test in failing_tests],
        "failing_tests_count": failing_tests_count,
        "style_violations": [
            {"message": violation.message, "filename": violation.filename}
            for violation in style_violations
        ],
        "style_violations_count": style_violations_count,
    }

    msg = Message(
        subject,
        recipients=recipients,
        extra_headers={"Reply-To": ", ".join(sanitize_address(r) for r in recipients)},
    )
    msg.body = render_template("notifications/email.txt", **context)
    msg.html = inline_css(render_template("notifications/email.html", **context))

    return msg
    def _message(self):
        """Creates email as 'multipart/related' instead of 'multipart/mixed'"""
        ascii_attachments = current_app.extensions['mail'].ascii_attachments
        encoding = self.charset or 'utf-8'

        attachments = self.attachments or []

        if len(attachments) == 0 and not self.html:
            # No html content and zero attachments means plain text
            msg = self._mimetext(self.body)
        elif len(attachments) > 0 and not self.html:
            # No html and at least one attachment means multipart
            msg = MIMEMultipart()
            msg.attach(self._mimetext(self.body))
        else:
            # Anything else
            msg = MIMEMultipart(
                'related')  # This fixes embedded images in the html body
            alternative = MIMEMultipart('alternative')
            alternative.attach(self._mimetext(self.body, 'plain'))
            alternative.attach(self._mimetext(self.html, 'html'))
            msg.attach(alternative)

        if self.subject:
            msg['Subject'] = sanitize_subject(force_text(self.subject),
                                              encoding)

        msg['From'] = sanitize_address(self.sender, encoding)
        msg['To'] = ', '.join(
            list(set(sanitize_addresses(self.recipients, encoding))))

        msg['Date'] = formatdate(self.date, localtime=True)
        # see RFC 5322 section 3.6.4.
        msg['Message-ID'] = self.msgId

        if self.cc:
            msg['Cc'] = ', '.join(
                list(set(sanitize_addresses(self.cc, encoding))))

        if self.reply_to:
            msg['Reply-To'] = sanitize_address(self.reply_to, encoding)

        if self.extra_headers:
            for k, v in self.extra_headers.items():
                msg[k] = v

        SPACES = re.compile(r'[\s]+', re.UNICODE)
        for attachment in attachments:
            f = MIMEBase(*attachment.content_type.split('/'))
            f.set_payload(attachment.data)
            encode_base64(f)

            filename = attachment.filename
            if filename and ascii_attachments:
                # force filename to ascii
                filename = unicodedata.normalize('NFKD', filename)
                filename = filename.encode('ascii', 'ignore').decode('ascii')
                filename = SPACES.sub(u' ', filename).strip()

            try:
                filename and filename.encode('ascii')
            except UnicodeEncodeError:
                filename = ('UTF8', '', filename)

            f.add_header('Content-Disposition',
                         attachment.disposition,
                         filename=filename)

            for key, value in attachment.headers:
                f.add_header(key, value)

            msg.attach(f)
        if message_policy:
            msg.policy = message_policy

        return msg
Example #22
0
    def as_string(self):
        """Creates the email"""

        attachments = self.attachments or []

        if len(attachments) == 0 and not self.html:
            # No html content and zero attachments means plain text
            msg = self._mimetext(self.body)
        elif len(attachments) > 0 and not self.html:
            # No html and at least one attachment means multipart
            msg = MIMEMultipart()
            msg.attach(self._mimetext(self.body))
        else:
            # Anything else
            msg = MIMEMultipart()
            alternative = MIMEMultipart('alternative')
            alternative.attach(self._mimetext(self.body, 'plain'))
            alternative.attach(self._mimetext(self.html, 'html'))
            msg.attach(alternative)

        msg['Subject'] = self.subject
        msg['From'] = sanitize_address(self.sender)
        msg['To'] = ', '.join(list(set(sanitize_addresses(self.recipients))))

        msg['Date'] = formatdate(self.date, localtime=True)
        # see RFC 5322 section 3.6.4.
        msg['Message-ID'] = self.msgId

        if self.cc:
            msg['Cc'] = ', '.join(list(set(sanitize_addresses(self.cc))))

        if self.reply_to:
            msg['Reply-To'] = sanitize_address(self.reply_to)

        if self.extra_headers:
            for k, v in self.extra_headers.items():
                msg[k] = v

        for attachment in attachments:
            f = MIMEBase(*attachment.content_type.split('/'))
            f.set_payload(attachment.data)
            encode_base64(f)

            try:
                attachment.filename and attachment.filename.encode('ascii')
            except UnicodeEncodeError:
                filename = attachment.filename
                if not PY3:
                    filename = filename.encode('utf8')
                f.add_header('Content-Disposition', attachment.disposition,
                            filename=('UTF8', '', filename))
            else:
                f.add_header('Content-Disposition', '%s;filename=%s' %
                             (attachment.disposition, attachment.filename))

            for key, value in attachment.headers:
                f.add_header(key, value)

            msg.attach(f)

        return msg.as_string()
Example #23
0
    def as_string(self):
        """Creates the email"""

        attachments = self.attachments or []

        if len(attachments) == 0 and not self.html:
            # No html content and zero attachments means plain text
            msg = self._mimetext(self.body)
        elif len(attachments) > 0 and not self.html:
            # No html and at least one attachment means multipart
            msg = MIMEMultipart()
            msg.attach(self._mimetext(self.body))
        else:
            # Anything else
            msg = MIMEMultipart()
            alternative = MIMEMultipart('alternative')
            alternative.attach(self._mimetext(self.body, 'plain'))
            alternative.attach(self._mimetext(self.html, 'html'))
            msg.attach(alternative)

        msg['Subject'] = self.subject
        msg['From'] = sanitize_address(self.sender)
        msg['To'] = ', '.join(list(set(sanitize_addresses(self.recipients))))

        msg['Date'] = formatdate(self.date, localtime=True)
        # see RFC 5322 section 3.6.4.
        msg['Message-ID'] = self.msgId

        if self.cc:
            msg['Cc'] = ', '.join(list(set(sanitize_addresses(self.cc))))

        if self.reply_to:
            msg['Reply-To'] = sanitize_address(self.reply_to)

        if self.extra_headers:
            for k, v in self.extra_headers.items():
                msg[k] = v

        for attachment in attachments:
            f = MIMEBase(*attachment.content_type.split('/'))
            f.set_payload(attachment.data)
            encode_base64(f)

            try:
                attachment.filename and attachment.filename.encode('ascii')
            except UnicodeEncodeError:
                filename = attachment.filename
                if not PY3:
                    filename = filename.encode('utf8')
                f.add_header('Content-Disposition',
                             attachment.disposition,
                             filename=('UTF8', '', filename))
            else:
                f.add_header(
                    'Content-Disposition', '%s;filename=%s' %
                    (attachment.disposition, attachment.filename))

            for key, value in attachment.headers:
                f.add_header(key, value)

            msg.attach(f)

        return msg.as_string()
Example #24
0
def sanitize_addresses(addresses):
    return map(lambda e: sanitize_address(e), addresses)
Example #25
0
def sanitize_addresses(addresses):
    return map(lambda e: sanitize_address(e), addresses)