Example #1
0
def send_mail(subject, body, from_email, recipient_list, message_id=None):
    msg = SafeMIMEText(body)
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = ', '.join(recipient_list)
    msg['Date'] = formatdate()
    msg['Message-Id'] = '<%s>' % (message_id or create_message_id())

    server = smtplib.SMTP(settings.EMAIL_HOST, settings.EMAIL_PORT)
    try:
        server.sendmail(from_email, recipient_list, msg.as_string())
    finally:
        server.quit()
 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
Example #3
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
Example #4
0
def send_mass_mail(datatuple, fail_silently=False, auth_user=settings.EMAIL_HOST_USER, auth_password=settings.EMAIL_HOST_PASSWORD, extra_headers=None):
    """
    Given a datatuple of (subject, message, from_email, recipient_list), sends
    each message to each recipient list. Returns the number of e-mails sent.

    If from_email is None, the DEFAULT_FROM_EMAIL setting is used.
    If auth_user and auth_password are set, they're used to log in.
    """
    try:
        server = smtplib.SMTP(settings.EMAIL_HOST, settings.EMAIL_PORT)
        if auth_user and auth_password:
            server.login(auth_user, auth_password)
    except:
        if fail_silently:
            return
        raise
    num_sent = 0
    for subject, message, from_email, recipient_list in datatuple:
        if not recipient_list:
            continue
        from_email = from_email or settings.DEFAULT_FROM_EMAIL
        msg = SafeMIMEText(message, 'plain', settings.DEFAULT_CHARSET)
        msg['Subject'] = subject
        msg['From'] = from_email
        msg['To'] = ', '.join(recipient_list)
        msg['Date'] = rfc822.formatdate()
	msg['Message-ID'] = '<*****@*****.**>' % \
			(datetime.datetime.now().isoformat('T').replace(':', '.'),
                         os.getpid(), random.randrange(0, sys.maxint))
	if extra_headers:
            for h in extra_headers:
                msg[h] = extra_headers[h]
        try:
            server.sendmail(from_email, recipient_list, msg.as_string())
            num_sent += 1
        except:
            if not fail_silently:
                raise
    try:
        server.quit()
    except:
        if fail_silently:
            return
        raise
    return num_sent
Example #5
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"
Example #6
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
Example #7
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 = DEFAULT_ATTACHMENT_MIME_TYPE
         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
Example #8
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
Example #9
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)
Example #10
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
Example #11
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
Example #12
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
Example #13
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()
Example #14
0
def send_mass_mail(datatuple, extra={}, fail_silently=False, auth_user=settings.EMAIL_HOST_USER,
        auth_password=settings.EMAIL_HOST_PASSWORD, tls=getattr(settings, 'EMAIL_TLS', False),
        encoding=settings.DEFAULT_CHARSET):
    """Sends a message to each receipient in list.
    
    Given a datatuple of (subject, message, from_email, recipient_list), sends
    each message to each recipient list. Returns the number of e-mails sent.
    
    If from_email is None, the DEFAULT_FROM_EMAIL setting is used.
    If auth_user and auth_password are set, they're used to log in.
    Note that the message parameter can be either text or one of the
    SafeMIMExxx methods listed above.
    """
    try:
        SMTP = smtplib.SMTP
        if settings.EMAIL_DEBUG:
            SMTP = STMPMock
        server = SMTP(settings.EMAIL_HOST, settings.EMAIL_PORT)
        server.ehlo()
        server.esmtp_features["auth"] = "LOGIN PLAIN"
        if tls:
            server.starttls()
            server.ehlo()
        if auth_user and auth_password:
            server.login(auth_user, auth_password)
    except:
        if fail_silently:
            return
        raise
    num_sent = 0

    for subject, message, from_email, recipient_list, cc_list in datatuple:
        if not recipient_list:
            continue
        from_email = from_email or settings.DEFAULT_FROM_EMAIL
        #################################################
        msg = None
        if isinstance(message, SafeMIMEText) or isinstance(message, SafeMIMEMultipart):
            ## Change below is important!
            ## msg does not act as a proper dictionary... msg['key'] = value does not
            ## reset the value for msg['key'], but adds to it!
            msg = copy.deepcopy(message)
        else:
            msg = SafeMIMEText(message.encode(encoding), 'plain', encoding)
        #################################################
        # TODO: we should encode header fields that aren't pure ASCII, see:
        # http://maxischenko.in.ua/blog/entries/103/python-emails-i18n/
        msg['Subject'] = Header(subject, encoding)
        msg['From'] = from_email
        msg['To'] = ', '.join(recipient_list)
        msg['Date'] = rfc822.formatdate()
        if cc_list:
            msg['Cc'] = ', '.join(cc_list)
            recipient_list.extend(cc_list)
        if extra:
            for key in extra.keys():
                msg[key] = extra[key]
        try:
            server.sendmail(from_email, recipient_list, msg.as_string())
            num_sent += 1
        except:
            if not fail_silently:
                raise
    try:
        server.quit()
    except:
        if fail_silently:
            return
        raise
    return num_sent
Example #15
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
 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
Example #17
0
 def build_plain_message():
     msg = SafeMIMEText(self.body, self.content_subtype, encoding)
     msg = self._create_message(msg)
     return msg
Example #18
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