Example #1
0
class EMail:
    """
	Wrapper on the email module. Email object represents emails to be sent to the client.
	Also provides a clean way to add binary `FileData` attachments
	Also sets all messages as multipart/alternative for cleaner reading in text-only clients
	"""
    def __init__(self,
                 sender='',
                 recipients=(),
                 subject='',
                 alternative=0,
                 reply_to=None,
                 cc=(),
                 email_account=None,
                 expose_recipients=None):
        from email import Charset
        Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')

        if isinstance(recipients, string_types):
            recipients = recipients.replace(';', ',').replace('\n', '')
            recipients = split_emails(recipients)

        # remove null
        recipients = filter(None, (strip(r) for r in recipients))

        self.sender = sender
        self.reply_to = reply_to or sender
        self.recipients = recipients
        self.subject = subject
        self.expose_recipients = expose_recipients

        self.msg_root = MIMEMultipart('mixed')
        self.msg_alternative = MIMEMultipart('alternative')
        self.msg_root.attach(self.msg_alternative)
        self.cc = cc or []
        self.html_set = False

        self.email_account = email_account or get_outgoing_email_account()

    def set_html(self,
                 message,
                 text_content=None,
                 footer=None,
                 print_html=None,
                 formatted=None,
                 inline_images=None,
                 header=None):
        """Attach message in the html portion of multipart/alternative"""
        if not formatted:
            formatted = get_formatted_html(self.subject,
                                           message,
                                           footer,
                                           print_html,
                                           email_account=self.email_account,
                                           header=header)

        # this is the first html part of a multi-part message,
        # convert to text well
        if not self.html_set:
            if text_content:
                self.set_text(expand_relative_urls(text_content))
            else:
                self.set_html_as_text(expand_relative_urls(formatted))

        self.set_part_html(formatted, inline_images)
        self.html_set = True

    def set_text(self, message):
        """
			Attach message in the text portion of multipart/alternative
		"""
        from email.mime.text import MIMEText
        part = MIMEText(message, 'plain', 'utf-8')
        self.msg_alternative.attach(part)

    def set_part_html(self, message, inline_images):
        from email.mime.text import MIMEText

        has_inline_images = re.search('''embed=['"].*?['"]''', message)

        if has_inline_images:
            # process inline images
            message, _inline_images = replace_filename_with_cid(message)

            # prepare parts
            msg_related = MIMEMultipart('related')

            html_part = MIMEText(message, 'html', 'utf-8')
            msg_related.attach(html_part)

            for image in _inline_images:
                self.add_attachment(image.get('filename'),
                                    image.get('filecontent'),
                                    content_id=image.get('content_id'),
                                    parent=msg_related,
                                    inline=True)

            self.msg_alternative.attach(msg_related)
        else:
            self.msg_alternative.attach(MIMEText(message, 'html', 'utf-8'))

    def set_html_as_text(self, html):
        """Set plain text from HTML"""
        self.set_text(to_markdown(html))

    def set_message(self,
                    message,
                    mime_type='text/html',
                    as_attachment=0,
                    filename='attachment.html'):
        """Append the message with MIME content to the root node (as attachment)"""
        from email.mime.text import MIMEText

        maintype, subtype = mime_type.split('/')
        part = MIMEText(message, _subtype=subtype)

        if as_attachment:
            part.add_header('Content-Disposition',
                            'attachment',
                            filename=filename)

        self.msg_root.attach(part)

    def attach_file(self, n):
        """attach a file from the `FileData` table"""
        from frappe.utils.file_manager import get_file
        res = get_file(n)
        if not res:
            return

        self.add_attachment(res[0], res[1])

    def add_attachment(self,
                       fname,
                       fcontent,
                       content_type=None,
                       parent=None,
                       content_id=None,
                       inline=False):
        """add attachment"""

        if not parent:
            parent = self.msg_root

        add_attachment(fname, fcontent, content_type, parent, content_id,
                       inline)

    def add_pdf_attachment(self, name, html, options=None):
        self.add_attachment(name, get_pdf(html, options),
                            'application/octet-stream')

    def validate(self):
        """validate the Email Addresses"""
        from frappe.utils import validate_email_add

        if not self.sender:
            self.sender = self.email_account.default_sender

        validate_email_add(strip(self.sender), True)
        self.reply_to = validate_email_add(
            strip(self.reply_to) or self.sender, True)

        self.replace_sender()

        self.recipients = [strip(r) for r in self.recipients]
        self.cc = [strip(r) for r in self.cc]

        for e in self.recipients + (self.cc or []):
            validate_email_add(e, True)

    def replace_sender(self):
        if cint(self.email_account.always_use_account_email_id_as_sender):
            self.set_header('X-Original-From', self.sender)
            sender_name, sender_email = parse_addr(self.sender)
            self.sender = email.utils.formataddr(
                (sender_name
                 or self.email_account.name, self.email_account.email_id))

    def set_message_id(self, message_id, is_notification=False):
        if message_id:
            self.msg_root["Message-Id"] = '<' + message_id + '>'
        else:
            self.msg_root["Message-Id"] = get_message_id()
            self.msg_root["isnotification"] = '<notification>'
        if is_notification:
            self.msg_root["isnotification"] = '<notification>'

    def set_in_reply_to(self, in_reply_to):
        """Used to send the Message-Id of a received email back as In-Reply-To"""
        self.msg_root["In-Reply-To"] = in_reply_to

    def make(self):
        """build into msg_root"""
        headers = {
            "Subject":
            strip(self.subject),
            "From":
            self.sender,
            "To":
            ', '.join(self.recipients)
            if self.expose_recipients == "header" else "<!--recipient-->",
            "Date":
            email.utils.formatdate(),
            "Reply-To":
            self.reply_to if self.reply_to else None,
            "CC":
            ', '.join(self.cc)
            if self.cc and self.expose_recipients == "header" else None,
            'X-Frappe-Site':
            get_url(),
        }

        # reset headers as values may be changed.
        for key, val in iteritems(headers):
            self.set_header(key, val)

        # call hook to enable apps to modify msg_root before sending
        for hook in frappe.get_hooks("make_email_body_message"):
            frappe.get_attr(hook)(self)

    def set_header(self, key, value):
        key = encode(key)
        value = encode(value)

        if self.msg_root.has_key(key):
            del self.msg_root[key]

        self.msg_root[key] = value

    def as_string(self):
        """validate, build message and convert to string"""
        self.validate()
        self.make()
        return self.msg_root.as_string()
Example #2
0
class EMail:
    """
	Wrapper on the email module. Email object represents emails to be sent to the client.
	Also provides a clean way to add binary `FileData` attachments
	Also sets all messages as multipart/alternative for cleaner reading in text-only clients
	"""
    def __init__(self,
                 sender='',
                 recipients=(),
                 subject='',
                 alternative=0,
                 reply_to=None,
                 cc=(),
                 email_account=None):
        from email.mime.multipart import MIMEMultipart
        from email import Charset
        Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')

        if isinstance(recipients, basestring):
            recipients = recipients.replace(';', ',').replace('\n', '')
            recipients = split_emails(recipients)

        # remove null
        recipients = filter(None, (strip(r) for r in recipients))

        self.sender = sender
        self.reply_to = reply_to or sender
        self.recipients = recipients
        self.subject = subject

        self.msg_root = MIMEMultipart('mixed')
        self.msg_multipart = MIMEMultipart('alternative')
        self.msg_root.attach(self.msg_multipart)
        self.cc = cc or []
        self.html_set = False

        self.email_account = email_account

    def set_html(self,
                 message,
                 text_content=None,
                 footer=None,
                 print_html=None,
                 formatted=None):
        """Attach message in the html portion of multipart/alternative"""
        if not formatted:
            formatted = get_formatted_html(self.subject,
                                           message,
                                           footer,
                                           print_html,
                                           email_account=self.email_account)

        # this is the first html part of a multi-part message,
        # convert to text well
        if not self.html_set:
            if text_content:
                self.set_text(expand_relative_urls(text_content))
            else:
                self.set_html_as_text(expand_relative_urls(formatted))

        self.set_part_html(formatted)
        self.html_set = True

    def set_text(self, message):
        """
			Attach message in the text portion of multipart/alternative
		"""
        from email.mime.text import MIMEText
        part = MIMEText(message, 'plain', 'utf-8')
        self.msg_multipart.attach(part)

    def set_part_html(self, message):
        from email.mime.text import MIMEText
        part = MIMEText(message, 'html', 'utf-8')
        self.msg_multipart.attach(part)

    def set_html_as_text(self, html):
        """return html2text"""
        self.set_text(to_markdown(html))

    def set_message(self,
                    message,
                    mime_type='text/html',
                    as_attachment=0,
                    filename='attachment.html'):
        """Append the message with MIME content to the root node (as attachment)"""
        from email.mime.text import MIMEText

        maintype, subtype = mime_type.split('/')
        part = MIMEText(message, _subtype=subtype)

        if as_attachment:
            part.add_header('Content-Disposition',
                            'attachment',
                            filename=filename)

        self.msg_root.attach(part)

    def attach_file(self, n):
        """attach a file from the `FileData` table"""
        from frappe.utils.file_manager import get_file
        res = get_file(n)
        if not res:
            return

        self.add_attachment(res[0], res[1])

    def add_attachment(self, fname, fcontent, content_type=None):
        """add attachment"""
        from email.mime.audio import MIMEAudio
        from email.mime.base import MIMEBase
        from email.mime.image import MIMEImage
        from email.mime.text import MIMEText

        import mimetypes
        if not content_type:
            content_type, encoding = mimetypes.guess_type(fname)

        if content_type is None:
            # No guess could be made, or the file is encoded (compressed), so
            # use a generic bag-of-bits type.
            content_type = 'application/octet-stream'

        maintype, subtype = content_type.split('/', 1)
        if maintype == 'text':
            # Note: we should handle calculating the charset
            if isinstance(fcontent, unicode):
                fcontent = fcontent.encode("utf-8")
            part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8")
        elif maintype == 'image':
            part = MIMEImage(fcontent, _subtype=subtype)
        elif maintype == 'audio':
            part = MIMEAudio(fcontent, _subtype=subtype)
        else:
            part = MIMEBase(maintype, subtype)
            part.set_payload(fcontent)
            # Encode the payload using Base64
            from email import encoders
            encoders.encode_base64(part)

        # Set the filename parameter
        if fname:
            part.add_header(b'Content-Disposition',
                            ("attachment; filename=\"%s\"" %
                             fname).encode('utf-8'))

        self.msg_root.attach(part)

    def add_pdf_attachment(self, name, html, options=None):
        self.add_attachment(name, get_pdf(html, options),
                            'application/octet-stream')

    def get_default_sender(self):
        return get_outgoing_email_account().default_sender

    def validate(self):
        """validate the email ids"""
        from frappe.utils import validate_email_add

        if not self.sender:
            self.sender = self.get_default_sender()

        validate_email_add(strip(self.sender), True)
        self.reply_to = validate_email_add(
            strip(self.reply_to) or self.sender, True)

        self.recipients = [strip(r) for r in self.recipients]
        self.cc = [strip(r) for r in self.cc]

        for e in self.recipients + (self.cc or []):
            validate_email_add(e, True)

    def set_message_id(self, message_id):
        self.msg_root["Message-Id"] = "<{0}@{1}>".format(
            message_id, frappe.local.site)

    def make(self):
        """build into msg_root"""
        headers = {
            "Subject": strip(self.subject).encode("utf-8"),
            "From": self.sender.encode("utf-8"),
            "To": ', '.join(self.recipients).encode("utf-8"),
            "Date": email.utils.formatdate(),
            "Reply-To":
            self.reply_to.encode("utf-8") if self.reply_to else None,
            "CC": ', '.join(self.cc).encode("utf-8") if self.cc else None,
            b'X-Frappe-Site': get_url().encode('utf-8'),
        }

        # reset headers as values may be changed.
        for key, val in headers.iteritems():
            if self.msg_root.has_key(key):
                del self.msg_root[key]
            self.msg_root[key] = val

        # call hook to enable apps to modify msg_root before sending
        for hook in frappe.get_hooks("make_email_body_message"):
            frappe.get_attr(hook)(self)

    def as_string(self):
        """validate, build message and convert to string"""
        self.validate()
        self.make()
        return self.msg_root.as_string()
Example #3
0
class EMail:
    """
	Wrapper on the email module. Email object represents emails to be sent to the client.
	Also provides a clean way to add binary `FileData` attachments
	Also sets all messages as multipart/alternative for cleaner reading in text-only clients
	"""

    def __init__(self, sender="", recipients=(), subject="", alternative=0, reply_to=None, cc=()):
        from email.mime.multipart import MIMEMultipart
        from email import Charset

        Charset.add_charset("utf-8", Charset.QP, Charset.QP, "utf-8")

        if isinstance(recipients, basestring):
            recipients = recipients.replace(";", ",").replace("\n", "")
            recipients = split_emails(recipients)

            # remove null
        recipients = filter(None, (strip(r) for r in recipients))

        self.sender = sender
        self.reply_to = reply_to or sender
        self.recipients = recipients
        self.subject = subject

        self.msg_root = MIMEMultipart("mixed")
        self.msg_multipart = MIMEMultipart("alternative")
        self.msg_root.attach(self.msg_multipart)
        self.cc = cc or []
        self.html_set = False

    def set_html(self, message, text_content=None, footer=None, print_html=None, formatted=None):
        """Attach message in the html portion of multipart/alternative"""
        if not formatted:
            formatted = get_formatted_html(self.subject, message, footer, print_html)

            # this is the first html part of a multi-part message,
            # convert to text well
        if not self.html_set:
            if text_content:
                self.set_text(expand_relative_urls(text_content))
            else:
                self.set_html_as_text(expand_relative_urls(formatted))

        self.set_part_html(formatted)
        self.html_set = True

    def set_text(self, message):
        """
			Attach message in the text portion of multipart/alternative
		"""
        from email.mime.text import MIMEText

        part = MIMEText(message, "plain", "utf-8")
        self.msg_multipart.attach(part)

    def set_part_html(self, message):
        from email.mime.text import MIMEText

        part = MIMEText(message, "html", "utf-8")
        self.msg_multipart.attach(part)

    def set_html_as_text(self, html):
        """return html2text"""
        self.set_text(to_markdown(html))

    def set_message(self, message, mime_type="text/html", as_attachment=0, filename="attachment.html"):
        """Append the message with MIME content to the root node (as attachment)"""
        from email.mime.text import MIMEText

        maintype, subtype = mime_type.split("/")
        part = MIMEText(message, _subtype=subtype)

        if as_attachment:
            part.add_header("Content-Disposition", "attachment", filename=filename)

        self.msg_root.attach(part)

    def attach_file(self, n):
        """attach a file from the `FileData` table"""
        from frappe.utils.file_manager import get_file

        res = get_file(n)
        if not res:
            return

        self.add_attachment(res[0], res[1])

    def add_attachment(self, fname, fcontent, content_type=None):
        """add attachment"""
        from email.mime.audio import MIMEAudio
        from email.mime.base import MIMEBase
        from email.mime.image import MIMEImage
        from email.mime.text import MIMEText

        import mimetypes

        if not content_type:
            content_type, encoding = mimetypes.guess_type(fname)

        if content_type is None:
            # No guess could be made, or the file is encoded (compressed), so
            # use a generic bag-of-bits type.
            content_type = "application/octet-stream"

        maintype, subtype = content_type.split("/", 1)
        if maintype == "text":
            # Note: we should handle calculating the charset
            if isinstance(fcontent, unicode):
                fcontent = fcontent.encode("utf-8")
            part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8")
        elif maintype == "image":
            part = MIMEImage(fcontent, _subtype=subtype)
        elif maintype == "audio":
            part = MIMEAudio(fcontent, _subtype=subtype)
        else:
            part = MIMEBase(maintype, subtype)
            part.set_payload(fcontent)
            # Encode the payload using Base64
            from email import encoders

            encoders.encode_base64(part)

            # Set the filename parameter
        if fname:
            part.add_header(b"Content-Disposition", ('attachment; filename="%s"' % fname).encode("utf-8"))

        self.msg_root.attach(part)

    def add_pdf_attachment(self, name, html, options=None):
        self.add_attachment(name, get_pdf(html, options), "application/octet-stream")

    def get_default_sender(self):
        return get_outgoing_email_account().default_sender

    def validate(self):
        """validate the email ids"""
        from frappe.utils import validate_email_add

        if not self.sender:
            self.sender = self.get_default_sender()

        validate_email_add(strip(self.sender), True)
        self.reply_to = validate_email_add(strip(self.reply_to) or self.sender, True)

        self.recipients = [strip(r) for r in self.recipients]
        self.cc = [strip(r) for r in self.cc]

        for e in self.recipients + (self.cc or []):
            validate_email_add(e, True)

    def set_message_id(self, message_id):
        self.msg_root["Message-Id"] = "<{0}@{1}>".format(message_id, frappe.local.site)

    def make(self):
        """build into msg_root"""
        headers = {
            "Subject": strip(self.subject).encode("utf-8"),
            "From": self.sender.encode("utf-8"),
            "To": ", ".join(self.recipients).encode("utf-8"),
            "Date": email.utils.formatdate(),
            "Reply-To": self.reply_to.encode("utf-8") if self.reply_to else None,
            "CC": ", ".join(self.cc).encode("utf-8") if self.cc else None,
            b"X-Frappe-Site": get_url().encode("utf-8"),
        }

        # reset headers as values may be changed.
        for key, val in headers.iteritems():
            if self.msg_root.has_key(key):
                del self.msg_root[key]
            self.msg_root[key] = val

            # call hook to enable apps to modify msg_root before sending
        for hook in frappe.get_hooks("make_email_body_message"):
            frappe.get_attr(hook)(self)

    def as_string(self):
        """validate, build message and convert to string"""
        self.validate()
        self.make()
        return self.msg_root.as_string()
Example #4
0
class EMail:
	"""
	Wrapper on the email module. Email object represents emails to be sent to the client.
	Also provides a clean way to add binary `FileData` attachments
	Also sets all messages as multipart/alternative for cleaner reading in text-only clients
	"""
	def __init__(self, sender='', recipients=(), subject='', alternative=0, reply_to=None, cc=(), email_account=None):
		from email.mime.multipart import MIMEMultipart
		from email import Charset
		Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')

		if isinstance(recipients, basestring):
			recipients = recipients.replace(';', ',').replace('\n', '')
			recipients = split_emails(recipients)

		# remove null
		recipients = filter(None, (strip(r) for r in recipients))

		self.sender = sender
		self.reply_to = reply_to or sender
		self.recipients = recipients
		self.subject = subject

		self.msg_root = MIMEMultipart('mixed')
		self.msg_multipart = MIMEMultipart('alternative')
		self.msg_root.attach(self.msg_multipart)
		self.cc = cc or []
		self.html_set = False

		self.email_account = email_account or get_outgoing_email_account()

	def set_html(self, message, text_content = None, footer=None, print_html=None, formatted=None):
		"""Attach message in the html portion of multipart/alternative"""
		if not formatted:
			formatted = get_formatted_html(self.subject, message, footer, print_html, email_account=self.email_account)

		# this is the first html part of a multi-part message,
		# convert to text well
		if not self.html_set:
			if text_content:
				self.set_text(expand_relative_urls(text_content))
			else:
				self.set_html_as_text(expand_relative_urls(formatted))

		self.set_part_html(formatted)
		self.html_set = True

	def set_text(self, message):
		"""
			Attach message in the text portion of multipart/alternative
		"""
		from email.mime.text import MIMEText
		part = MIMEText(message, 'plain', 'utf-8')
		self.msg_multipart.attach(part)

	def set_part_html(self, message):
		from email.mime.text import MIMEText
		part = MIMEText(message, 'html', 'utf-8')
		self.msg_multipart.attach(part)

	def set_html_as_text(self, html):
		"""return html2text"""
		self.set_text(to_markdown(html))

	def set_message(self, message, mime_type='text/html', as_attachment=0, filename='attachment.html'):
		"""Append the message with MIME content to the root node (as attachment)"""
		from email.mime.text import MIMEText

		maintype, subtype = mime_type.split('/')
		part = MIMEText(message, _subtype = subtype)

		if as_attachment:
			part.add_header('Content-Disposition', 'attachment', filename=filename)

		self.msg_root.attach(part)

	def attach_file(self, n):
		"""attach a file from the `FileData` table"""
		from frappe.utils.file_manager import get_file
		res = get_file(n)
		if not res:
			return

		self.add_attachment(res[0], res[1])

	def add_attachment(self, fname, fcontent, content_type=None):
		"""add attachment"""
		from email.mime.audio import MIMEAudio
		from email.mime.base import MIMEBase
		from email.mime.image import MIMEImage
		from email.mime.text import MIMEText

		import mimetypes
		if not content_type:
			content_type, encoding = mimetypes.guess_type(fname)

		if content_type is None:
			# No guess could be made, or the file is encoded (compressed), so
			# use a generic bag-of-bits type.
			content_type = 'application/octet-stream'

		maintype, subtype = content_type.split('/', 1)
		if maintype == 'text':
			# Note: we should handle calculating the charset
			if isinstance(fcontent, unicode):
				fcontent = fcontent.encode("utf-8")
			part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8")
		elif maintype == 'image':
			part = MIMEImage(fcontent, _subtype=subtype)
		elif maintype == 'audio':
			part = MIMEAudio(fcontent, _subtype=subtype)
		else:
			part = MIMEBase(maintype, subtype)
			part.set_payload(fcontent)
			# Encode the payload using Base64
			from email import encoders
			encoders.encode_base64(part)

		# Set the filename parameter
		if fname:
			part.add_header(b'Content-Disposition',
				("attachment; filename=\"%s\"" % fname).encode('utf-8'))

		self.msg_root.attach(part)

	def add_pdf_attachment(self, name, html, options=None):
		self.add_attachment(name, get_pdf(html, options), 'application/octet-stream')

	def validate(self):
		"""validate the email ids"""
		from frappe.utils import validate_email_add

		if not self.sender:
			self.sender = self.email_account.default_sender

		validate_email_add(strip(self.sender), True)
		self.reply_to = validate_email_add(strip(self.reply_to) or self.sender, True)

		self.replace_sender()

		self.recipients = [strip(r) for r in self.recipients]
		self.cc = [strip(r) for r in self.cc]

		for e in self.recipients + (self.cc or []):
			validate_email_add(e, True)

	def replace_sender(self):
		if cint(self.email_account.always_use_account_email_id_as_sender):
			self.set_header('X-Original-From', self.sender)

			sender_name, sender_email = email.utils.parseaddr(self.sender)
			self.sender = email.utils.formataddr((sender_name or self.email_account.name, self.email_account.email_id))

	def set_message_id(self, message_id):
		if message_id:
			self.msg_root["Message-Id"] = message_id
		else:
			self.msg_root["Message-Id"] = email.utils.make_msgid("{0}".format(frappe.local.site))
			self.msg_root["References"] = '<notification>'

	def set_in_reply_to(self, in_reply_to):
		"""Used to send the Message-Id of a received email back as In-Reply-To"""
		self.msg_root["In-Reply-To"] = in_reply_to

	def make(self):
		"""build into msg_root"""
		headers = {
			"Subject":        strip(self.subject),
			"From":           self.sender,
			"To":             ', '.join(self.recipients),
			"Date":           email.utils.formatdate(),
			"Reply-To":       self.reply_to if self.reply_to else None,
			"CC":             ', '.join(self.cc) if self.cc else None,
			'X-Frappe-Site':  get_url(),
		}

		# reset headers as values may be changed.
		for key, val in headers.iteritems():
			self.set_header(key, val)

		# call hook to enable apps to modify msg_root before sending
		for hook in frappe.get_hooks("make_email_body_message"):
			frappe.get_attr(hook)(self)

	def set_header(self, key, value):
		key = encode(key)
		value = encode(value)

		if self.msg_root.has_key(key):
			del self.msg_root[key]

		self.msg_root[key] = value

	def as_string(self):
		"""validate, build message and convert to string"""
		self.validate()
		self.make()
		return self.msg_root.as_string()
Example #5
0
class EMail:
    """
	Wrapper on the email module. Email object represents emails to be sent to the client.
	Also provides a clean way to add binary `FileData` attachments
	Also sets all messages as multipart/alternative for cleaner reading in text-only clients
	"""
    def __init__(self,
                 sender='',
                 recipients=(),
                 subject='',
                 alternative=0,
                 reply_to=None,
                 cc=(),
                 email_account=None,
                 expose_recipients=None):
        from email import Charset
        Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')

        if isinstance(recipients, basestring):
            recipients = recipients.replace(';', ',').replace('\n', '')
            recipients = split_emails(recipients)

        # remove null
        recipients = filter(None, (strip(r) for r in recipients))

        self.sender = sender
        self.reply_to = reply_to or sender
        self.recipients = recipients
        self.subject = subject
        self.expose_recipients = expose_recipients

        self.msg_root = MIMEMultipart('mixed')
        self.msg_multipart = MIMEMultipart('alternative')
        self.msg_root.attach(self.msg_multipart)
        self.cc = cc or []
        self.html_set = False

        self.email_account = email_account or get_outgoing_email_account()

    def set_html(self,
                 message,
                 text_content=None,
                 footer=None,
                 print_html=None,
                 formatted=None,
                 inline_images=None):
        """Attach message in the html portion of multipart/alternative"""
        if not formatted:
            formatted = get_formatted_html(self.subject,
                                           message,
                                           footer,
                                           print_html,
                                           email_account=self.email_account)

        # this is the first html part of a multi-part message,
        # convert to text well
        if not self.html_set:
            if text_content:
                self.set_text(expand_relative_urls(text_content))
            else:
                self.set_html_as_text(expand_relative_urls(formatted))

        self.set_part_html(formatted, inline_images)
        self.html_set = True

    def set_text(self, message):
        """
			Attach message in the text portion of multipart/alternative
		"""
        from email.mime.text import MIMEText
        part = MIMEText(message, 'plain', 'utf-8')
        self.msg_multipart.attach(part)

    def set_part_html(self, message, inline_images):
        from email.mime.text import MIMEText
        if inline_images:
            related = MIMEMultipart('related')

            for image in inline_images:
                # images in dict like {filename:'', filecontent:'raw'}
                content_id = random_string(10)

                # replace filename in message with CID
                message = re.sub(
                    '''src=['"]{0}['"]'''.format(image.get('filename')),
                    'src="cid:{0}"'.format(content_id), message)

                self.add_attachment(image.get('filename'),
                                    image.get('filecontent'),
                                    None,
                                    content_id=content_id,
                                    parent=related)

            html_part = MIMEText(message, 'html', 'utf-8')
            related.attach(html_part)

            self.msg_multipart.attach(related)
        else:
            self.msg_multipart.attach(MIMEText(message, 'html', 'utf-8'))

    def set_html_as_text(self, html):
        """return html2text"""
        self.set_text(to_markdown(html))

    def set_message(self,
                    message,
                    mime_type='text/html',
                    as_attachment=0,
                    filename='attachment.html'):
        """Append the message with MIME content to the root node (as attachment)"""
        from email.mime.text import MIMEText

        maintype, subtype = mime_type.split('/')
        part = MIMEText(message, _subtype=subtype)

        if as_attachment:
            part.add_header('Content-Disposition',
                            'attachment',
                            filename=filename)

        self.msg_root.attach(part)

    def attach_file(self, n):
        """attach a file from the `FileData` table"""
        from frappe.utils.file_manager import get_file
        res = get_file(n)
        if not res:
            return

        self.add_attachment(res[0], res[1])

    def add_attachment(self,
                       fname,
                       fcontent,
                       content_type=None,
                       parent=None,
                       content_id=None):
        """add attachment"""
        from email.mime.audio import MIMEAudio
        from email.mime.base import MIMEBase
        from email.mime.image import MIMEImage
        from email.mime.text import MIMEText

        import mimetypes
        if not content_type:
            content_type, encoding = mimetypes.guess_type(fname)

        if content_type is None:
            # No guess could be made, or the file is encoded (compressed), so
            # use a generic bag-of-bits type.
            content_type = 'application/octet-stream'

        maintype, subtype = content_type.split('/', 1)
        if maintype == 'text':
            # Note: we should handle calculating the charset
            if isinstance(fcontent, unicode):
                fcontent = fcontent.encode("utf-8")
            part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8")
        elif maintype == 'image':
            part = MIMEImage(fcontent, _subtype=subtype)
        elif maintype == 'audio':
            part = MIMEAudio(fcontent, _subtype=subtype)
        else:
            part = MIMEBase(maintype, subtype)
            part.set_payload(fcontent)
            # Encode the payload using Base64
            from email import encoders
            encoders.encode_base64(part)

        # Set the filename parameter
        if fname:
            part.add_header(b'Content-Disposition',
                            ("attachment; filename=\"%s\"" %
                             fname).encode('utf-8'))
        if content_id:
            part.add_header(b'Content-ID', '<{0}>'.format(content_id))

        if not parent:
            parent = self.msg_root

        parent.attach(part)

    def add_pdf_attachment(self, name, html, options=None):
        self.add_attachment(name, get_pdf(html, options),
                            'application/octet-stream')

    def validate(self):
        """validate the Email Addresses"""
        from frappe.utils import validate_email_add

        if not self.sender:
            self.sender = self.email_account.default_sender

        validate_email_add(strip(self.sender), True)
        self.reply_to = validate_email_add(
            strip(self.reply_to) or self.sender, True)

        self.replace_sender()

        self.recipients = [strip(r) for r in self.recipients]
        self.cc = [strip(r) for r in self.cc]

        for e in self.recipients + (self.cc or []):
            validate_email_add(e, True)

    def replace_sender(self):
        if cint(self.email_account.always_use_account_email_id_as_sender):
            self.set_header('X-Original-From', self.sender)
            sender_name, sender_email = parse_addr(self.sender)
            self.sender = email.utils.formataddr(
                (sender_name
                 or self.email_account.name, self.email_account.email_id))

    def set_message_id(self, message_id, is_notification=False):
        if message_id:
            self.msg_root["Message-Id"] = '<' + message_id + '>'
        else:
            self.msg_root["Message-Id"] = get_message_id()
            self.msg_root["isnotification"] = '<notification>'
        if is_notification:
            self.msg_root["isnotification"] = '<notification>'

    def set_in_reply_to(self, in_reply_to):
        """Used to send the Message-Id of a received email back as In-Reply-To"""
        self.msg_root["In-Reply-To"] = in_reply_to

    def make(self):
        """build into msg_root"""
        headers = {
            "Subject":
            strip(self.subject),
            "From":
            self.sender,
            "To":
            ', '.join(self.recipients)
            if self.expose_recipients == "header" else "<!--recipient-->",
            "Date":
            email.utils.formatdate(),
            "Reply-To":
            self.reply_to if self.reply_to else None,
            "CC":
            ', '.join(self.cc)
            if self.cc and self.expose_recipients == "header" else None,
            'X-Frappe-Site':
            get_url(),
        }

        # reset headers as values may be changed.
        for key, val in iteritems(headers):
            self.set_header(key, val)

        # call hook to enable apps to modify msg_root before sending
        for hook in frappe.get_hooks("make_email_body_message"):
            frappe.get_attr(hook)(self)

    def set_header(self, key, value):
        key = encode(key)
        value = encode(value)

        if self.msg_root.has_key(key):
            del self.msg_root[key]

        self.msg_root[key] = value

    def as_string(self):
        """validate, build message and convert to string"""
        self.validate()
        self.make()
        return self.msg_root.as_string()
Example #6
0
    def run(self):

        if len(self.args) == 0:
            raise UsageError("needs 1 argument")

        self.server = None
        self.dataDir = '.'
        self.count_ok = 0
        self.count_nok = 0

        if self.options.host is None:
            raise UsageError("--host must be specified")

##         for fn in  self.attach_files:
##             if not os.path.exists(fn):
##                 raise OperationFailed("File %s does not exist."%fn)

        files = []
        for pattern in self.args:
            files += glob.glob(pattern)

        self.count_todo = len(files)
        if self.count_todo == 0:
            self.notice("Nothing to do: no input files found.")
            return

        if self.options.recipient is None:
            recipients = []
        else:
            recipients = getaddresses([self.options.recipient])

        sender = self.options.sender
        subject = self.options.subject
        """

        if the first input file's name ends with .eml, then this
        becomes the outer message
        

        """
        if files[0].lower().endswith(".eml"):
            self.notice("Reading file %s...", files[0])
            first = email.message_from_file(codecs.open(
                files[0], "r", self.options.encoding),
                                            _class=MyMessage)
            if first.has_key('subject'):
                subject = first["subject"]
                del first["subject"]
                first["subject"] = Header(subject, first.get_charset())

            self.encodeaddrs(first, 'from')
            if sender is None: sender = first["from"]
            self.encodeaddrs(first, 'to', recipients)
            self.encodeaddrs(first, 'cc', recipients)
            self.encodeaddrs(first, 'bcc', recipients)
            del first['bcc']

            del files[0]
            self.count_todo -= 1
        else:
            first = None

        if len(files) == 0:
            outer = first
        else:
            # Create the enclosing (outer) message
            outer = MIMEMultipart()
            # outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
            if first is not None:
                first.add_header('Content-Disposition', 'inline')
                outer.attach(first)
                for hdr in ('to', 'cc'):
                    outer[hdr] = first[hdr]
            outer['subject'] = subject
            outer['from'] = sender
            self.notice("Attaching %d files...", self.count_todo)
            i = 1
            for filename in files:
                self.notice(u"%s (%d/%d)", filename, i, self.count_todo)
                part = self.file2msg(filename)
                # Set the filename parameter
                part.add_header('Content-Disposition',
                                'attachment',
                                filename=os.path.basename(filename))
                outer.attach(part)
                i += 1

        #for part in outer.walk():
        #if recipient is None: recipient=part["To"]
        #if bcc is None: bcc=part["Bcc"]

        if self.options.subject is not None:
            del outer['subject']
            outer['subject'] = self.options.subject
        #if self.options.sender is not None:
        #    del outer['from']
        #    outer['from'] = self.options.sender
        #del outer['to']
        #outer['to'] = recipient
        #if bcc is not None:
        #    outer['Bcc'] = bcc
        #    print "Bcc:", bcc

        #headers_i18n(outer)

        if len(recipients) == 0:
            for addr in open(opj(self.dataDir, "addrlist.txt")).xreadlines():
                addr = addr.strip()
                if len(addr) != 0 and addr[0] != "#":
                    recipients += getaddresses([addr])

        if not outer.has_key("Subject"):
            raise "Subject header is missing"
        if not outer.has_key("Date"):
            outer["Date"] = email.Utils.formatdate(None, True)

        for k, v in outer.items():
            print k, ":", unicode(v)
        #self.notice(str(outer.keys()))
        self.notice(_("Message size: %d bytes."), len(str(outer)))
        self.notice(_("Send this to %d recipients: %s"), len(recipients),
                    ", ".join([a[1] for a in recipients]))

        sender = parseaddr(unicode(sender))[1]

        # print "sender:", unicode(sender)

        # print outer.as_string(unixfrom=0)

        if not self.confirm("Okay?"):
            return

        self.connect()

        self.sendmsg(outer, sender, recipients)

        self.server.quit()

        self.notice(_("Sent to %d recipients."), self.count_ok)
        if self.count_nok != 0:
            self.notice(_("%d recipients refused."), self.count_nok)
Example #7
0
    def run(self):

        if len(self.args) == 0:
            raise UsageError("needs 1 argument")

        self.server = None
        self.dataDir = "."
        self.count_ok = 0
        self.count_nok = 0

        if self.options.host is None:
            raise UsageError("--host must be specified")

        ##         for fn in  self.attach_files:
        ##             if not os.path.exists(fn):
        ##                 raise OperationFailed("File %s does not exist."%fn)

        files = []
        for pattern in self.args:
            files += glob.glob(pattern)

        self.count_todo = len(files)
        if self.count_todo == 0:
            self.notice("Nothing to do: no input files found.")
            return

        if self.options.recipient is None:
            recipients = []
        else:
            recipients = getaddresses([self.options.recipient])

        sender = self.options.sender
        subject = self.options.subject

        """

        if the first input file's name ends with .eml, then this
        becomes the outer message
        

        """
        if files[0].lower().endswith(".eml"):
            self.notice("Reading file %s...", files[0])
            first = email.message_from_file(codecs.open(files[0], "r", self.options.encoding), _class=MyMessage)
            if first.has_key("subject"):
                subject = first["subject"]
                del first["subject"]
                first["subject"] = Header(subject, first.get_charset())

            self.encodeaddrs(first, "from")
            if sender is None:
                sender = first["from"]
            self.encodeaddrs(first, "to", recipients)
            self.encodeaddrs(first, "cc", recipients)
            self.encodeaddrs(first, "bcc", recipients)
            del first["bcc"]

            del files[0]
            self.count_todo -= 1
        else:
            first = None

        if len(files) == 0:
            outer = first
        else:
            # Create the enclosing (outer) message
            outer = MIMEMultipart()
            # outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
            if first is not None:
                first.add_header("Content-Disposition", "inline")
                outer.attach(first)
                for hdr in ("to", "cc"):
                    outer[hdr] = first[hdr]
            outer["subject"] = subject
            outer["from"] = sender
            self.notice("Attaching %d files...", self.count_todo)
            i = 1
            for filename in files:
                self.notice(u"%s (%d/%d)", filename, i, self.count_todo)
                part = self.file2msg(filename)
                # Set the filename parameter
                part.add_header("Content-Disposition", "attachment", filename=os.path.basename(filename))
                outer.attach(part)
                i += 1

        # for part in outer.walk():
        # if recipient is None: recipient=part["To"]
        # if bcc is None: bcc=part["Bcc"]

        if self.options.subject is not None:
            del outer["subject"]
            outer["subject"] = self.options.subject
        # if self.options.sender is not None:
        #    del outer['from']
        #    outer['from'] = self.options.sender
        # del outer['to']
        # outer['to'] = recipient
        # if bcc is not None:
        #    outer['Bcc'] = bcc
        #    print "Bcc:", bcc

        # headers_i18n(outer)

        if len(recipients) == 0:
            for addr in open(opj(self.dataDir, "addrlist.txt")).xreadlines():
                addr = addr.strip()
                if len(addr) != 0 and addr[0] != "#":
                    recipients += getaddresses([addr])

        if not outer.has_key("Subject"):
            raise "Subject header is missing"
        if not outer.has_key("Date"):
            outer["Date"] = email.Utils.formatdate(None, True)

        for k, v in outer.items():
            print k, ":", unicode(v)
        # self.notice(str(outer.keys()))
        self.notice(_("Message size: %d bytes."), len(str(outer)))
        self.notice(_("Send this to %d recipients: %s"), len(recipients), ", ".join([a[1] for a in recipients]))

        sender = parseaddr(unicode(sender))[1]

        # print "sender:", unicode(sender)

        # print outer.as_string(unixfrom=0)

        if not self.confirm("Okay?"):
            return

        self.connect()

        self.sendmsg(outer, sender, recipients)

        self.server.quit()

        self.notice(_("Sent to %d recipients."), self.count_ok)
        if self.count_nok != 0:
            self.notice(_("%d recipients refused."), self.count_nok)