Beispiel #1
0
    def message(self):
        msg_root = MIMEMultipart('mixed')
        msg_root['Subject'] = forbid_multi_line_headers(
            'Subject', self.subject, 'utf-8')[1]
        msg_root['From'] = forbid_multi_line_headers('From', self.from_email,
                                                     'utf-8')[1]
        msg_root['To'] = forbid_multi_line_headers('To', ', '.join(self.to),
                                                   'utf-8')[1]
        msg_root['Date'] = formatdate(localtime=settings.EMAIL_USE_LOCALTIME)
        msg_root['Message-ID'] = make_msgid(domain=DNS_NAME)

        msg_content = MIMEMultipart('related')
        msg_root.attach(msg_content)

        msg_text = MIMEText(self.content, 'html', 'utf-8')
        msg_content.attach(msg_text)

        if self.images:
            for image in self.images:
                msg_content.attach(self.create_inline_image(*image))

        if self.attachments:
            for attachment in self.attachments:
                msg_root.attach(self.create_attachment(*attachment))

        self.patch_message(msg_root)
        return msg_root
Beispiel #2
0
    def test_long_course_display_name(self):
        """
        This test tests that courses with exorbitantly large display names
        can still send emails, since it appears that 320 appears to be the
        character length limit of from emails for Amazon SES.
        """
        test_email = {
            'action': 'Send email',
            'send_to': '["myself", "staff", "learners"]',
            'subject': 'test subject for self',
            'message': 'test message for self'
        }

        # make display_name that's longer than 320 characters when encoded
        # to ascii and escaped, but shorter than 320 unicode characters
        long_name = u"Финансовое программирование и политика, часть 1: макроэкономические счета и анализ"

        course = CourseFactory.create(
            display_name=long_name,
            org="IMF",
            number="FPP.1x",
            run="2016",
        )
        instructor = InstructorFactory(course_key=course.id)

        unexpected_from_addr = _get_source_address(
            course.id, course.display_name, course_language=None, truncate=False
        )
        __, encoded_unexpected_from_addr = forbid_multi_line_headers(
            "from", unexpected_from_addr, 'utf-8'
        )
        escaped_encoded_unexpected_from_addr = escape(encoded_unexpected_from_addr)

        # it's shorter than 320 characters when just encoded
        self.assertEqual(len(encoded_unexpected_from_addr), 318)
        # escaping it brings it over that limit
        self.assertEqual(len(escaped_encoded_unexpected_from_addr), 324)
        # when not escaped or encoded, it's well below 320 characters
        self.assertEqual(len(unexpected_from_addr), 137)

        self.login_as_user(instructor)
        send_mail_url = reverse('send_email', kwargs={'course_id': unicode(course.id)})
        response = self.client.post(send_mail_url, test_email)
        self.assertTrue(json.loads(response.content)['success'])

        self.assertEqual(len(mail.outbox), 1)
        from_email = mail.outbox[0].from_email

        expected_from_addr = (
            u'"{course_name}" Course Staff <{course_name}[email protected]>'
        ).format(course_name=course.id.course)

        self.assertEqual(
            from_email,
            expected_from_addr
        )
        self.assertEqual(len(from_email), 61)
Beispiel #3
0
    def test_long_course_display_name(self):
        """
        This test tests that courses with exorbitantly large display names
        can still send emails, since it appears that 320 appears to be the
        character length limit of from emails for Amazon SES.
        """
        test_email = {
            'action': 'Send email',
            'send_to': '["myself", "staff", "learners"]',
            'subject': 'test subject for self',
            'message': 'test message for self'
        }

        # make display_name that's longer than 320 characters when encoded
        # to ascii and escaped, but shorter than 320 unicode characters
        long_name = u"Финансовое программирование и политика, часть 1: макроэкономические счета и анализ"

        course = CourseFactory.create(
            display_name=long_name,
            org="IMF",
            number="FPP.1x",
            run="2016",
        )
        instructor = InstructorFactory(course_key=course.id)

        unexpected_from_addr = _get_source_address(
            course.id, course.display_name, course_language=None, truncate=False
        )
        __, encoded_unexpected_from_addr = forbid_multi_line_headers(
            "from", unexpected_from_addr, 'utf-8'
        )
        escaped_encoded_unexpected_from_addr = escape(encoded_unexpected_from_addr)

        # it's shorter than 320 characters when just encoded
        self.assertEqual(len(encoded_unexpected_from_addr), 318)
        # escaping it brings it over that limit
        self.assertEqual(len(escaped_encoded_unexpected_from_addr), 324)
        # when not escaped or encoded, it's well below 320 characters
        self.assertEqual(len(unexpected_from_addr), 137)

        self.login_as_user(instructor)
        send_mail_url = reverse('send_email', kwargs={'course_id': six.text_type(course.id)})
        response = self.client.post(send_mail_url, test_email)
        self.assertTrue(json.loads(response.content.decode('utf-8'))['success'])

        self.assertEqual(len(mail.outbox), 1)
        from_email = mail.outbox[0].from_email

        expected_from_addr = (
            u'"{course_name}" Course Staff <{course_name}[email protected]>'
        ).format(course_name=course.id.course)

        self.assertEqual(
            from_email,
            expected_from_addr
        )
        self.assertEqual(len(from_email), 61)
Beispiel #4
0
def _get_source_address(course_id, course_title, truncate=True):
    """
    Calculates an email address to be used as the 'from-address' for sent emails.

    Makes a unique from name and address for each course, e.g.

        "COURSE_TITLE" Course Staff <*****@*****.**>

    If, when decoded to ascii, this from_addr is longer than 320 characters,
    use the course_name rather than the course title, e.g.

        "course_name" Course Staff <*****@*****.**>

    The "truncate" kwarg is only used for tests.

    """
    course_title_no_quotes = re.sub(r'"', '', course_title)

    # For the email address, get the course.  Then make sure that it can be used
    # in an email address, by substituting a '_' anywhere a non-(ascii, period, or dash)
    # character appears.
    course_name = re.sub(r"[^\w.-]", '_', course_id.course)

    from_addr_format = u'"{course_title}" Course Staff <{course_name}-{from_email}>'

    def format_address(course_title_no_quotes):
        """
        Partial function for formatting the from_addr. Since
        `course_title_no_quotes` may be truncated to make sure the returned
        string has fewer than 320 characters, we define this function to make
        it easy to determine quickly what the max length is for
        `course_title_no_quotes`.
        """
        return from_addr_format.format(
            course_title=course_title_no_quotes,
            course_name=course_name,
            from_email=theming_helpers.get_value(
                'bulk_email_default_from_email',
                settings.BULK_EMAIL_DEFAULT_FROM_EMAIL
            )
        )

    from_addr = format_address(course_title_no_quotes)

    # If the encoded from_addr is longer than 320 characters, reformat,
    # but with the course name rather than course title.
    # Amazon SES's from address field appears to have a maximum length of 320.
    __, encoded_from_addr = forbid_multi_line_headers('from', from_addr, 'utf-8')

    # It seems that this value is also escaped when set out to amazon, judging
    # from our logs
    escaped_encoded_from_addr = escape(encoded_from_addr)
    if len(escaped_encoded_from_addr) >= 320 and truncate:
        from_addr = format_address(course_name)

    return from_addr
Beispiel #5
0
def _get_source_address(course_id, course_title, truncate=True):
    """
    Calculates an email address to be used as the 'from-address' for sent emails.

    Makes a unique from name and address for each course, e.g.

        "COURSE_TITLE" Course Staff <*****@*****.**>

    If, when decoded to ascii, this from_addr is longer than 320 characters,
    use the course_name rather than the course title, e.g.

        "course_name" Course Staff <*****@*****.**>

    The "truncate" kwarg is only used for tests.

    """
    course_title_no_quotes = re.sub(r'"', '', course_title)

    # For the email address, get the course.  Then make sure that it can be used
    # in an email address, by substituting a '_' anywhere a non-(ascii, period, or dash)
    # character appears.
    course_name = re.sub(r"[^\w.-]", '_', course_id.course)

    from_addr_format = u'"{course_title}" Course Staff <{course_name}-{from_email}>'

    def format_address(course_title_no_quotes):
        """
        Partial function for formatting the from_addr. Since
        `course_title_no_quotes` may be truncated to make sure the returned
        string has fewer than 320 characters, we define this function to make
        it easy to determine quickly what the max length is for
        `course_title_no_quotes`.
        """
        return from_addr_format.format(
            course_title=course_title_no_quotes,
            course_name=course_name,
            from_email=theming_helpers.get_value(
                'email_from_address',
                settings.BULK_EMAIL_DEFAULT_FROM_EMAIL
            )
        )

    from_addr = format_address(course_title_no_quotes)

    # If the encoded from_addr is longer than 320 characters, reformat,
    # but with the course name rather than course title.
    # Amazon SES's from address field appears to have a maximum length of 320.
    __, encoded_from_addr = forbid_multi_line_headers('from', from_addr, 'utf-8')

    # It seems that this value is also escaped when set out to amazon, judging
    # from our logs
    escaped_encoded_from_addr = escape(encoded_from_addr)
    if len(escaped_encoded_from_addr) >= 320 and truncate:
        from_addr = format_address(course_name)

    return from_addr
    def test_long_course_display_name(self):
        """
        This test tests that courses with exorbitantly large display names
        can still send emails, since it appears that 320 appears to be the
        character length limit of from emails for Amazon SES.
        """
        test_email = {
            'action': 'Send email',
            'send_to': 'myself',
            'subject': 'test subject for self',
            'message': 'test message for self'
        }

        # make display_name that's longer than 320 characters when encoded
        # to ascii, but shorter than 320 unicode characters
        long_name = u"é" * 200

        course = CourseFactory.create(display_name=long_name,
                                      number="bulk_email_course_name")
        instructor = InstructorFactory(course_key=course.id)

        unexpected_from_addr = _get_source_address(course.id,
                                                   course.display_name,
                                                   truncate=False)
        __, encoded_unexpected_from_addr = forbid_multi_line_headers(
            "from", unexpected_from_addr, 'utf-8')
        self.assertEqual(len(encoded_unexpected_from_addr), 748)
        self.assertEqual(len(unexpected_from_addr), 261)

        self.login_as_user(instructor)
        send_mail_url = reverse('send_email',
                                kwargs={'course_id': unicode(course.id)})
        response = self.client.post(send_mail_url, test_email)
        self.assertTrue(json.loads(response.content)['success'])

        self.assertEqual(len(mail.outbox), 1)
        from_email = mail.outbox[0].from_email

        expected_from_addr = (
            u'"{course_name}" Course Staff <{course_name}[email protected]>'
        ).format(course_name=course.id.course)

        self.assertEqual(from_email, expected_from_addr)
        self.assertEqual(len(from_email), 83)
Beispiel #7
0
def _get_source_address(course_id, course_title, course_language, truncate=True):
    """
    Calculates an email address to be used as the 'from-address' for sent emails.

    Makes a unique from name and address for each course, e.g.

        "COURSE_TITLE" Course Staff <*****@*****.**>

    If, when decoded to ascii, this from_addr is longer than 320 characters,
    use the course_name rather than the course title, e.g.

        "course_name" Course Staff <*****@*****.**>

    The "truncate" kwarg is only used for tests.

    """
    course_title_no_quotes = re.sub(r'"', '', course_title)

    # For the email address, get the course.  Then make sure that it can be used
    # in an email address, by substituting a '_' anywhere a non-(ascii, period, or dash)
    # character appears.
    course_name = re.sub(r"[^\w.-]", '_', course_id.course)

    # Use course.language if present
    language = course_language if course_language else settings.LANGUAGE_CODE
    with override_language(language):
        # RFC2821 requires the byte order of the email address to be the name then email
        #   e.g. "John Doe <*****@*****.**>"
        # Although the display will be flipped in RTL languages, the byte order is still the same.
        from_addr_format = '{name} {email}'.format(
            # Translators: Bulk email from address e.g. ("Physics 101" Course Staff)
            name=_('"{course_title}" Course Staff'),
            email='<{course_name}-{from_email}>',  # xss-lint: disable=python-wrap-html
        )

    def format_address(course_title_no_quotes):
        """
        Partial function for formatting the from_addr. Since
        `course_title_no_quotes` may be truncated to make sure the returned
        string has fewer than 320 characters, we define this function to make
        it easy to determine quickly what the max length is for
        `course_title_no_quotes`.
        """
        return from_addr_format.format(
            course_title=course_title_no_quotes,
            course_name=course_name,
            from_email=configuration_helpers.get_value(
                'email_from_address',
                settings.BULK_EMAIL_DEFAULT_FROM_EMAIL
            )
        )

    from_addr = format_address(course_title_no_quotes)

    # If the encoded from_addr is longer than 320 characters, reformat,
    # but with the course name rather than course title.
    # Amazon SES's from address field appears to have a maximum length of 320.
    __, encoded_from_addr = forbid_multi_line_headers('from', from_addr, 'utf-8')

    # It seems that this value is also escaped when set out to amazon, judging
    # from our logs
    escaped_encoded_from_addr = escape(encoded_from_addr)
    if len(escaped_encoded_from_addr) >= 320 and truncate:
        from_addr = format_address(course_name)

    return from_addr
Beispiel #8
0
def _get_source_address(course_id, course_title, course_language, truncate=True):
    """
    Calculates an email address to be used as the 'from-address' for sent emails.

    Makes a unique from name and address for each course, e.g.

        "COURSE_TITLE" Course Staff <*****@*****.**>

    If, when decoded to ascii, this from_addr is longer than 320 characters,
    use the course_name rather than the course title, e.g.

        "course_name" Course Staff <*****@*****.**>

    The "truncate" kwarg is only used for tests.

    """
    course_title_no_quotes = re.sub(r'"', '', course_title)

    # For the email address, get the course.  Then make sure that it can be used
    # in an email address, by substituting a '_' anywhere a non-(ascii, period, or dash)
    # character appears.
    course_name = re.sub(r"[^\w.-]", '_', course_id.course)

    # Use course.language if present
    language = course_language if course_language else settings.LANGUAGE_CODE
    with override_language(language):
        # RFC2821 requires the byte order of the email address to be the name then email
        #   e.g. "John Doe <*****@*****.**>"
        # Although the display will be flipped in RTL languages, the byte order is still the same.
        from_addr_format = u'{name} {email}'.format(
            # Translators: Bulk email from address e.g. ("Physics 101" Course Staff)
            name=_('"{course_title}" Course Staff'),
            email=u'<{course_name}-{from_email}>',
        )

    def format_address(course_title_no_quotes):
        """
        Partial function for formatting the from_addr. Since
        `course_title_no_quotes` may be truncated to make sure the returned
        string has fewer than 320 characters, we define this function to make
        it easy to determine quickly what the max length is for
        `course_title_no_quotes`.
        """
        return from_addr_format.format(
            course_title=course_title_no_quotes,
            course_name=course_name,
            from_email=configuration_helpers.get_value(
                'email_from_address',
                settings.BULK_EMAIL_DEFAULT_FROM_EMAIL
            )
        )

    from_addr = format_address(course_title_no_quotes)

    # If the encoded from_addr is longer than 320 characters, reformat,
    # but with the course name rather than course title.
    # Amazon SES's from address field appears to have a maximum length of 320.
    __, encoded_from_addr = forbid_multi_line_headers('from', from_addr, 'utf-8')

    # It seems that this value is also escaped when set out to amazon, judging
    # from our logs
    escaped_encoded_from_addr = escape(encoded_from_addr)
    if len(escaped_encoded_from_addr) >= 320 and truncate:
        from_addr = format_address(course_name)

    return from_addr