Exemplo n.º 1
0
 def _create_attachment(self, filename, content, mimetype=None):
     """
     Converts the filename, content, mimetype triple into a MIME attachment
     object. Use self.encoding when handling text attachments.
     """
     if mimetype is None:
         mimetype, _ = mimetypes.guess_type(filename)
         if mimetype is None:
             mimetype = constants.MIME_TYPE_EXCEL
     basetype, subtype = mimetype.split('/', 1)
     if basetype == 'text':
         encoding = self.encoding or settings.DEFAULT_CHARSET
         attachment = SafeMIMEText(
             smart_str(content, settings.DEFAULT_CHARSET), subtype,
             encoding)
     else:
         # Encode non-text attachments with base64.
         attachment = MIMEBase(basetype, subtype)
         attachment.set_payload(content)
         encoders.encode_base64(attachment)
     if filename:
         try:
             filename = filename.encode('ascii')
         except UnicodeEncodeError:
             filename = Header(filename, 'utf-8').encode()
         attachment.add_header('Content-Disposition',
                               'attachment',
                               filename=filename)
     return attachment
Exemplo n.º 2
0
 def build_version_attachment():
     version_attachment = SafeMIMEText('Version: 1\n',
                                       self.content_subtype, encoding)
     del version_attachment['Content-Type']
     version_attachment.add_header('Content-Type',
                                   'application/pgp-encrypted')
     version_attachment.add_header('Content-Description',
                                   'PGP/MIME Versions Identification')
     return version_attachment
Exemplo n.º 3
0
 def test_parse_raw_mime_8bit_utf8(self):
     # In come cases, the message below ends up with 'Content-Transfer-Encoding: 8bit',
     # so needs to be parsed as bytes, not text (see https://bugs.python.org/issue18271).
     # Message.as_string() returns str (text), not bytes.
     # (This might be a Django bug; plain old MIMEText avoids the problem by using
     # 'Content-Transfer-Encoding: base64', which parses fine as text or bytes.)
     # Either way, AnymailInboundMessage should try to sidestep the whole issue.
     raw = SafeMIMEText("Unicode ✓", "plain", "utf-8").as_string()
     msg = AnymailInboundMessage.parse_raw_mime(raw)
     self.assertEqual(msg.text, "Unicode ✓")  # *not* "Unicode \\u2713"
Exemplo n.º 4
0
 def build_gpg_attachment():
     gpg_attachment = SafeMIMEText(encrypted_msg, self.content_subtype,
                                   encoding)
     del gpg_attachment['Content-Type']
     gpg_attachment.add_header('Content-Type',
                               'application/octet-stream',
                               name=self.gpg_attachment_filename)
     gpg_attachment.add_header('Content-Disposition',
                               'inline',
                               filename=self.gpg_attachment_filename)
     gpg_attachment.add_header('Content-Description',
                               'OpenPGP encrypted message')
     return gpg_attachment
Exemplo n.º 5
0
def create_email(template_name: str, subject: str, context: dict,
                 **kwargs) -> EmailMessage:
    """
    Attempts to create an email that only has a single language, which is
    pulled from the context using get_language.

    If it cannot send the email in the requested language, it will call
    create_multilingual_email to send an email in all the languages that are
    available.

    :param template_name: The template that should be used for this email
    :param subject: The subject of this email
    :param context: The context for the template
    :param kwargs: Any other data that should be passed to the EmailMessage
        constructor
    :return: an EmailMessage
    """
    _ensure_setup()
    lang = get_language()
    kwargs['headers'] = kwargs['headers'] if 'headers' in kwargs else {}

    kwargs['headers'] = {
        "X-Mailer": get_mailer_name(),
        "Content-Language": lang,
        **kwargs['headers']
    }

    langs = templates[template_name]
    if lang in langs:
        tpls = langs[lang]

        msg = MultiTemplatableEmailMessage(subject=_(subject),
                                           body=False,
                                           **kwargs)

        for template in tpls:
            msg.attach_alternative(
                SafeMIMEText(_text=get_template(template['file']).render({
                    'locale':
                    template['locale'],
                    **context,
                }),
                             _subtype=template['subtype'],
                             _charset=msg.encoding
                             or settings.DEFAULT_CHARSET), 'unknown/unknown')

        return msg

    return create_multilingual_mail(template_name, subject, context, **kwargs)
Exemplo n.º 6
0
    def message(self):
        from ..utils import replace_cid_and_change_headers

        to = anyjson.loads(self.to)
        cc = anyjson.loads(self.cc)
        bcc = anyjson.loads(self.bcc)

        html, text, inline_headers = replace_cid_and_change_headers(
            self.body, self.original_message_id)

        email_message = SafeMIMEMultipart('related')
        email_message['Subject'] = self.subject
        email_message['From'] = self.send_from.to_header()

        if to:
            email_message['To'] = ','.join(list(to))
        if cc:
            email_message['cc'] = ','.join(list(cc))
        if bcc:
            email_message['bcc'] = ','.join(list(bcc))

        email_message_alternative = SafeMIMEMultipart('alternative')
        email_message.attach(email_message_alternative)

        email_message_text = SafeMIMEText(text, 'plain', 'utf-8')
        email_message_alternative.attach(email_message_text)

        email_message_html = SafeMIMEText(html, 'html', 'utf-8')
        email_message_alternative.attach(email_message_html)

        try:
            add_attachments_to_email(self, email_message, inline_headers)
        except IOError:
            return False

        return email_message
Exemplo n.º 7
0
    def get_base_message(self, message):
        payload = message.get_payload()

        if isinstance(message, SafeMIMEMultipart):
            # If this is a multipart message, we encrypt all its parts.
            # We create a new SafeMIMEMultipart instance, the original message contains all
            # headers (From, To, ...) which we shouldn't sign/encrypt.
            subtype = message.get_content_subtype()
            base = SafeMIMEMultipart(_subtype=subtype, _subparts=payload)
        else:
            # If it is a non-multipart message (-> plain-text email), we just encrypt the payload
            base = SafeMIMEText(payload)

            # TODO: Is it possible to influence the main content type of the message? If yes, we
            #       need to copy it here.

        del base['MIME-Version']
        return base
Exemplo n.º 8
0
    def message(self):
        encoding = self.encoding or settings.DEFAULT_CHARSET
        msg = SafeMIMEText(smart_str(self.body, encoding),
                           self.content_subtype, encoding)
        msg = self._create_message(msg)
        msg['Subject'] = self.subject
        msg['From'] = self.extra_headers.get('From', self.from_email)
        msg['To'] = self.extra_headers.get("To", ', '.join(self.to))
        if self.cc:
            msg['Cc'] = ', '.join(self.cc)

        # Email header names are case-insensitive (RFC 2045), so we have to
        # accommodate that when doing comparisons.
        header_names = [key.lower() for key in self.extra_headers]
        if 'date' not in header_names:
            msg['Date'] = formatdate()
        if 'message-id' not in header_names:
            msg['Message-ID'] = make_msgid()
        for name, value in self.extra_headers.items():
            if name.lower() in ('from',
                                'to'):  # From and To are already handled
                continue
            msg[name] = value
        return msg
Exemplo n.º 9
0
def send_credit_notifications(username, course_key):
    """Sends email notification to user on different phases during credit
    course e.g., credit eligibility, credit payment etc.
    """
    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        log.error(u'No user with %s exist', username)
        return

    course = modulestore().get_course(course_key, depth=0)
    course_display_name = course.display_name
    tracking_context = tracker.get_tracker().resolve_context()
    tracking_id = str(tracking_context.get('user_id'))
    client_id = str(tracking_context.get('client_id'))
    events = '&t=event&ec=email&ea=open'
    tracking_pixel = 'https://www.google-analytics.com/collect?v=1&tid' + tracking_id + '&cid' + client_id + events
    dashboard_link = _email_url_parser('dashboard')
    credit_course_link = _email_url_parser('courses', '?type=credit')

    # get attached branded logo
    logo_image = cache.get('credit.email.attached-logo')
    if logo_image is None:
        branded_logo = {
            'title': 'Logo',
            'path': settings.NOTIFICATION_EMAIL_EDX_LOGO,
            'cid': str(uuid.uuid4())
        }
        logo_image_id = branded_logo['cid']
        logo_image = attach_image(branded_logo, 'Header Logo')
        if logo_image:
            cache.set('credit.email.attached-logo', logo_image,
                      settings.CREDIT_NOTIFICATION_CACHE_TIMEOUT)
    else:
        # strip enclosing angle brackets from 'logo_image' cache 'Content-ID'
        logo_image_id = logo_image.get('Content-ID', '')[1:-1]

    providers_names = get_credit_provider_attribute_values(
        course_key, 'display_name')
    providers_string = make_providers_strings(providers_names)
    context = {
        'full_name':
        user.get_full_name(),
        'platform_name':
        configuration_helpers.get_value('PLATFORM_NAME',
                                        settings.PLATFORM_NAME),
        'course_name':
        course_display_name,
        'branded_logo':
        logo_image_id,
        'dashboard_link':
        dashboard_link,
        'credit_course_link':
        credit_course_link,
        'tracking_pixel':
        tracking_pixel,
        'providers':
        providers_string,
    }

    # create the root email message
    notification_msg = MIMEMultipart('related')
    # add 'alternative' part to root email message to encapsulate the plain and
    # HTML versions, so message agents can decide which they want to display.
    msg_alternative = MIMEMultipart('alternative')
    notification_msg.attach(msg_alternative)
    # render the credit notification templates
    subject = _(u'Course Credit Eligibility')

    if providers_string:
        subject = _(u'You are eligible for credit from {providers_string}'
                    ).format(providers_string=providers_string)

    # add alternative plain text message
    email_body_plain = render_to_string(
        'credit_notifications/credit_eligibility_email.txt', context)
    msg_alternative.attach(
        SafeMIMEText(email_body_plain, _subtype='plain', _charset='utf-8'))

    # add alternative html message
    email_body_content = cache.get('credit.email.css-email-body')
    if email_body_content is None:
        html_file_path = file_path_finder(
            'templates/credit_notifications/credit_eligibility_email.html')
        if html_file_path:
            with open(html_file_path, 'r') as cur_file:
                cur_text = cur_file.read()
                # use html parser to unescape html characters which are changed
                # by the 'pynliner' while adding inline css to html content
                html_parser = six.moves.html_parser.HTMLParser()
                email_body_content = html_parser.unescape(
                    with_inline_css(cur_text))
                # cache the email body content before rendering it since the
                # email context will change for each user e.g., 'full_name'
                cache.set('credit.email.css-email-body', email_body_content,
                          settings.CREDIT_NOTIFICATION_CACHE_TIMEOUT)
        else:
            email_body_content = ''

    email_body = Template(email_body_content).render(context)
    msg_alternative.attach(
        SafeMIMEText(email_body, _subtype='html', _charset='utf-8'))

    # attach logo image
    if logo_image:
        notification_msg.attach(logo_image)

    # add email addresses of sender and receiver
    from_address = configuration_helpers.get_value('email_from_address',
                                                   settings.DEFAULT_FROM_EMAIL)
    to_address = user.email

    # send the root email message
    msg = EmailMessage(subject, '', from_address, [to_address])
    msg.attach(notification_msg)
    msg.send()
Exemplo n.º 10
0
    def message(self):
        from ..utils import get_attachment_filename_from_url, replace_cid_and_change_headers

        to = anyjson.loads(self.to)
        cc = anyjson.loads(self.cc)
        bcc = anyjson.loads(self.bcc)

        if self.send_from.from_name:
            # Add account name to From header if one is available
            from_email = '"%s" <%s>' % (Header(
                u'%s' % self.send_from.from_name,
                'utf-8'), self.send_from.email_address)
        else:
            # Otherwise only add the email address
            from_email = self.send_from.email_address

        html, text, inline_headers = replace_cid_and_change_headers(
            self.body, self.original_message_id)

        email_message = SafeMIMEMultipart('related')
        email_message['Subject'] = self.subject
        email_message['From'] = from_email

        if to:
            email_message['To'] = ','.join(list(to))
        if cc:
            email_message['cc'] = ','.join(list(cc))
        if bcc:
            email_message['bcc'] = ','.join(list(bcc))

        email_message_alternative = SafeMIMEMultipart('alternative')
        email_message.attach(email_message_alternative)

        email_message_text = SafeMIMEText(text, 'plain', 'utf-8')
        email_message_alternative.attach(email_message_text)

        email_message_html = SafeMIMEText(html, 'html', 'utf-8')
        email_message_alternative.attach(email_message_html)

        for attachment in self.attachments.all():
            if attachment.inline:
                continue

            try:
                storage_file = default_storage._open(
                    attachment.attachment.name)
            except IOError:
                logger.exception('Couldn\'t get attachment, not sending %s' %
                                 self.id)
                return False

            filename = get_attachment_filename_from_url(
                attachment.attachment.name)

            storage_file.open()
            content = storage_file.read()
            storage_file.close()

            content_type, encoding = mimetypes.guess_type(filename)
            if content_type is None or encoding is not None:
                content_type = 'application/octet-stream'
            main_type, sub_type = content_type.split('/', 1)

            if main_type == 'text':
                msg = MIMEText(content, _subtype=sub_type)
            elif main_type == 'image':
                msg = MIMEImage(content, _subtype=sub_type)
            elif main_type == 'audio':
                msg = MIMEAudio(content, _subtype=sub_type)
            else:
                msg = MIMEBase(main_type, sub_type)
                msg.set_payload(content)
                Encoders.encode_base64(msg)

            msg.add_header('Content-Disposition',
                           'attachment',
                           filename=os.path.basename(filename))

            email_message.attach(msg)

        # Add the inline attachments to email message header
        for inline_header in inline_headers:
            main_type, sub_type = inline_header['content-type'].split('/', 1)
            if main_type == 'image':
                msg = MIMEImage(inline_header['content'],
                                _subtype=sub_type,
                                name=os.path.basename(
                                    inline_header['content-filename']))
                msg.add_header('Content-Disposition',
                               inline_header['content-disposition'],
                               filename=os.path.basename(
                                   inline_header['content-filename']))
                msg.add_header('Content-ID', inline_header['content-id'])

                email_message.attach(msg)

        return email_message
Exemplo n.º 11
0
 def build_plain_message():
     msg = SafeMIMEText(self.body, self.content_subtype, encoding)
     msg = self._create_message(msg)
     return msg
Exemplo n.º 12
0
def create_multilingual_mail(template_name: str, subject: str, context: dict,
                             **kwargs) -> EmailMessage:
    """
    Creates an instance of EmailMessage. If multiple languages exist for
    the given template name, it will create an RFC8255_ compatible email, and if
    only one language exists, it will simply send an email in that language,
    without bothering with any multilingual crap.

    As of implementing this method, very few to no email clients support
    RFC8255_ and so it is recommended to either use create_mail with a
    preference language or use this method and only create one set of language
    templates.

    :param template_name: The template that should be used for this email
    :param subject: The subject of this email
    :param context: The context for the template
    :param kwargs: Any other data that should be passed to the EmailMessage
        constructor
    :return: an EmailMessage

    .. _RFC8255: https://tools.ietf.org/html/rfc8255
    """
    _ensure_setup()
    kwargs['headers'] = kwargs['headers'] if 'headers' in kwargs else {}

    kwargs['headers'] = {"X-Mailer": get_mailer_name(), **kwargs['headers']}

    langs = templates[template_name]

    if len(langs.items()) == 1:
        lang, tpls = list(langs.items())[0]

        with language(lang):
            kwargs['headers'] = {"Content-Language": lang, **kwargs['headers']}

            msg = MultiTemplatableEmailMessage(subject=subject,
                                               body=False,
                                               **kwargs)

            for template in tpls:
                msg.attach_alternative(
                    SafeMIMEText(_text=get_template(template['file']).render({
                        'locale':
                        template['locale'],
                        **context,
                    }),
                                 _subtype=template['subtype'],
                                 _charset=msg.encoding
                                 or settings.DEFAULT_CHARSET),
                    'unknown/unknown')

        return msg

    msg = MultilingualEmailMessage(subject=subject, **kwargs)

    for lang, tpls in langs.items():
        with language(lang):
            lang_alt = MIMEMultipart(_subtype='alternative')
            lang_alt.add_header("Subject", _(subject))

            for template in tpls:
                lang_alt.attach(
                    SafeMIMEText(get_template(template['file']).render({
                        'locale':
                        template['locale'],
                        **context,
                    }),
                                 _subtype=template['subtype']))

            msg.add_language(lang, lang_alt)

    info = MIMEMultipart(_subtype='alternative')
    info.attach(
        SafeMIMEText(get_template(
            "steambird/../templates/mail/multilingual_header.html").render({}),
                     _subtype="html",
                     _charset="utf-8"))
    info.attach(
        SafeMIMEText(get_template(
            "steambird/../templates/mail/multilingual_header.plain").render(
                {}),
                     _subtype="plain",
                     _charset="utf-8"))

    info.add_header("Content-Disposition", "inline")

    msg.attach(info)

    return msg