Example #1
0
 def send_message(self):
      
     msg = MIMEMultipart('alternative')
     msg['From'] = self.fromEmail
     msg['To'] = self.toEmail
     msg['Subject'] = self.subject
     if self.filename:
         f = file(self.filename)
         attachment = MIMEText(f.read())
         attachment.add_header('Content-Disposition',
                               'attachment', 
                               filename=self.filename)
         msg.attach(attachment)
         print f
     if self.displayname:
         display_name = self.displayname + " <" + self.fromEmail + ">"
         msg.replace_header('From', display_name)
     if self.body:
         body = self.body
         content = MIMEText(body, 'plain')
         msg.attach(content)
     else:
         body = 'Whenever I\'m about to do something, I think, \'Would an idiot do that?\' And if they would, I do not do that thing.'
         content = MIMEText(body, 'plain')
         msg.attach(content)
     try: 
         print '[+] attempting to send message'
         s = smtplib.SMTP(self.server, self.port)
         s.sendmail(self.fromEmail, self.toEmail, msg.as_string())
         print '[$] successfully sent through {}:{}'.format(self.server,
                                                            self.port)
     except socket.error as e:
         print '[!] could not connect'
Example #2
0
def _mail_recipient(recipient_name, recipient_email,
                    sender_name, sender_url, subject,
                    body, body_html=None, headers=None):

    if not headers:
        headers = {}

    mail_from = config.get('smtp.mail_from')
    reply_to = config.get('smtp.reply_to')
    if body_html:
        # multipart
        msg = MIMEMultipart('alternative')
        part1 = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')
        part2 = MIMEText(body_html.encode('utf-8'), 'html', 'utf-8')
        msg.attach(part1)
        msg.attach(part2)
    else:
        # just plain text
        msg = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')
    for k, v in headers.items():
        if k in msg.keys():
            msg.replace_header(k, v)
        else:
            msg.add_header(k, v)
    subject = Header(subject.encode('utf-8'), 'utf-8')
    msg['Subject'] = subject
    msg['From'] = _("%s <%s>") % (sender_name, mail_from)
    recipient = u"%s <%s>" % (recipient_name, recipient_email)
    msg['To'] = Header(recipient, 'utf-8')
    msg['Date'] = utils.formatdate(time())
    msg['X-Mailer'] = "CKAN %s" % ckan.__version__
    if reply_to and reply_to != '':
        msg['Reply-to'] = reply_to
    _mail_payload(msg, mail_from, recipient_email)
Example #3
0
def send_email(TO, SUBJECT, body, FROM='*****@*****.**'):
    settings = get_current_registry().settings

    if 'smtp.host' in settings:
        sm = smtplib.SMTP(host=settings['smtp.host'])
    else:
        sm = smtplib.SMTP()
        sm.connect()
    if 'smtp.username' in settings:
        sm.login(settings['smtp.username'], settings['smtp.password'])

    msg = MIMEMultipart()
    msg['Subject'] = SUBJECT
    msg['From'] = FROM
    msg['To'] = TO
    msg.attach(MIMEText(body, 'html'))

    if 'debugging' in settings:
        print(msg.as_string())
        if 'debugging_send_email' in settings and settings['debugging_send_email'] == 'true':
            try:
                msg.replace_header('To', settings['debugging_send_email_to'])
                print("DEBUG: e-mail destination overidden to {}".format(msg['To']))
            except KeyError: pass
            send_to = msg['To'].split(', ')
            sm.sendmail(FROM, send_to, msg.as_string())
        else:
            print("Mail suppressed due to debug settings")
    else:
        send_to = msg['To'].split(', ')
        sm.sendmail(FROM, send_to, msg.as_string())
    sm.quit()
Example #4
0
def send_mail(MAP, server, send_from, send_to, subject, files=None, multiple=False):
	if not isinstance(send_to, list):
		logging.warning("Badly formed recipient list. Not sending any mail.")
		return

	# Root message
	msg = MIMEMultipart('related')
	msg['From'] = send_from
	msg['Date'] = formatdate(localtime=True)
	msg['Subject'] = subject

	# Now create a multipart message below the root message
	# and attach both plain text and HTML version of the message to it.
	msgAlternative = MIMEMultipart('alternative')
	msg.attach(msgAlternative)
	text = getMailText(MAP)
	html = getMailHTML(MAP)
	msgAlternative.attach(MIMEText(text, 'plain'))
	msgAlternative.attach(MIMEText(html, 'html'))

	# Finally attach the image to the root message... this loop is overkill
	# and unnecessary, but will do for our case !
	for f in files or []:
		with open(f, "rb") as fil:
			part = MIMEImage(
				fil.read(),
				"png"
			)
			#part['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(f)
			part.add_header('Content-ID', '<atImg>')
			msg.attach(part)

	try:
		smtp = smtplib.SMTP(server)
	except smtplib.SMTPConnectError:
		logging.warning("Unable to connect to SMTP server. Not sending any mail.")
		return

	send_to = list(set(send_to)) # Removes possible duplicates in to list
	try:
		refused = dict()
		if multiple: # send one message per recipient
			for dest in send_to:
				if 'To' in msg:
					msg.replace_header('To', dest)
				else:
					msg['To'] = dest
				d = smtp.sendmail(send_from, dest, msg.as_string())
				refused.update(d)
		else: # send only one message with everyone in To-List
			msg['To'] = COMMASPACE.join(send_to)
			refused = smtp.sendmail(send_from, send_to, msg.as_string())
	except smtplib.SMTPException as e:
		logging.warning(sys.stderr, "Error sending mail: %s." % (repr(e)))
	else:
		if refused:
			logging.warning(sys.stderr, "Some mails could not be delivered: %s.", str(refused))

	smtp.quit()
Example #5
0
class Email:
    def __init__(
        self,
        sender: str,
        sender_name: str = "me",
        recipients: Union[List[str], str] = "",
        configuration_set: str = None,
        subject: str = "",
        body_text: str = "",
    ):
        # This address must be verified with AWS SES.
        # Alternatively the DNS needs to be verified.
        self.sender = sender
        self.sender_name = sender_name
        recipients_ = []
        if recipients:
            if isinstance(recipients, list):
                recipients_ = recipients
            else:
                # Assuming 'str'.
                recipients_ = recipients.split(",")
        self.recipients = []
        for recipient in recipients_:
            self.recipients.append(recipient.strip())
        self.configuration_set = configuration_set
        self._subject = subject
        self._body_text = body_text

        self.msg = MIMEMultipart("alternative")
        self.msg["Subject"] = self._subject
        self.msg["From"] = email.utils.formataddr(
            (self.sender_name, self.sender))
        self.msg["To"] = ", ".join(self.recipients)
        if self.configuration_set:
            self.msg.add_header("X-SES-CONFIGURATION-SET",
                                self.configuration_set)

    @property
    def subject(self):
        return self._subject

    @subject.setter
    def subject(self, value):
        self._subject = str(value)
        self.msg.replace_header("Subject", self._subject)

    @property
    def body_text(self):
        return self._body_text

    @body_text.setter
    def body_text(self, value):
        self._body_text = str(value)

    def attach_body(self):
        # Record the MIME type of the plain text part - text/plain.
        text_part = MIMEText(self._body_text, "plain")

        self.msg.attach(text_part)
Example #6
0
class MimeMsg(MailMessage):
    """Plain text mail object."""
    def __init__(self, sender, receiver, subject, text, ttype, attachments):
        super(MimeMsg, self).__init__(sender, receiver, subject, text,
                                      attachments)
        self.text_type = ttype
        self.build()

    def build(self):
        if not self.attachments:
            if self.text_type == 'html':
                self.msg = MIMENonMultipart('text', 'html')
            else:
                self.msg = MIMENonMultipart('text', 'plain', charset='utf-8')
            self.msg.set_payload(self.text)
            self.msg['boundary'] = self.delimiter
        else:
            self.msg = MIMEMultipart(boundary=self.delimiter)
            if self.text_type == 'html':
                self.msg.attach(MIMEText(self.text, 'html'))
            else:
                self.msg.attach(MIMEText(self.text, _charset='utf-8'))
        self.msg['From'] = self.sender
        self.msg['To'] = self.receiver
        self.msg['Subject'] = self.subject
        self.msg['Date'] = "NULL"
        self.msg['X-Mailer'] = self.xmailer
        if self.attachments:
            for attachment, _name in self.attachments:
                to_attach = MIMEBase('application', "octet-stream")
                with open(attachment, "rb") as att:
                    to_attach.set_payload(att.read())
                Encoders.encode_base64(to_attach)
                _name = osp.basename(attachment) if not _name else _name
                to_attach.add_header('Content-Disposition',
                                     'attachment; filename="%s"' % _name)
                self.msg.attach(to_attach)

    def get_message(self, receiver=None, as_string=True):
        receiver = receiver if receiver is not None else self.receiver
        _time = mmutils.mail_format_time()
        self.msg.replace_header('To', receiver)
        self.msg.replace_header('Date', _time)
        if as_string:
            return self.msg.as_string()
        return self.msg

    def sign(self, file, detached):
        if detached:
            self.attachments.append((file, 'signature.sig'))
        else:
            super(MimeMsg, self).sign(file)
        self.build()
Example #7
0
    def sendmail(self):
        """
        Actually connect to the server, and push out the message.
        """
        msg = MIMEMultipart('alternative')
        msg['Subject'] = self.subject
        msg['From'] = self.settings['sender']
        msg['To'] = self.settings['sender']
        # Record the MIME types of both parts - text/plain and text/html.
        part1 = MIMEText(self.text, 'plain')
        part2 = MIMEText(self.html, 'html')

        # Attach parts into message container.
        # Do this in the Process, so it's local
        msg.attach(part1)
        msg.attach(part2)
    
        count = 0
        # Grab some emails from the stack
        while self.allemails.qsize() > 0:
            curemail = self.allemails.get()
            if count % 100 == 0:
                if count > 0:
                    conn.close()
                print("Establishing Connection to emailserver " + self.settings['smtpserver'])
                if self.settings['SSL'] == True:
                  conn = smtplib.SMTP_SSL(host=self.settings['smtpserver'],port=self.settings['port'])
                else:
                  conn = smtplib.SMTP(host=self.settings['smtpserver'],port=self.settings['port'])
                conn.set_debuglevel(False)
                conn.login(self.settings['username'], self.settings['password'])

            msg.replace_header("To", curemail)
            try:
                conn.sendmail(self.settings['sender'], curemail, msg.as_string())   
                self.sentmails.put(curemail)
            finally:
                count +=1
                print ("This thread has sent " + str(count))
        print("Thead is done. Thanks!")
        self.saveprogress(daemon=False)
        conn.close()        
Example #8
0
    def build(self, recipient, **kwargs):
        """Build this email and return the Email Instance.

        :param recipient: The recipient of the email.
        :param kwargs: Additional key/value arguments to be rendered.
        :return: The email instance.
        """

        # Create message container as multipart/alternative.
        msg = MIMEMultipart('alternative')
        msg['From'] = self.sender
        msg['To'] = recipient
        msg['Date'] = formatdate(localtime=True)

        # Render the subject template. Add length and \r\n sanity check
        # replacements. While we could fold the subject line, if our subject
        # lines are longer than 78 characters nobody's going to read them all.
        subject = self.subject.render(**kwargs)
        subject = re.sub(r'\r?\n', ' ', subject)

        if len(subject) > 78:
            subject = subject[0:75] + '...'
        msg['Subject'] = subject

        # Iterate and render over all additional headers.
        for key, value in six.iteritems(self.headers):
            try:
                msg.replace_header(key, value)
            except KeyError:
                msg.add_header(key, value)

        # Render and attach our templates.
        for type, template in six.iteritems(self.template_cache):
            body_part = template.render(**kwargs)
            msg.attach(MIMEText(body_part, type, "utf-8"))

        return msg
Example #9
0
    def build(self, recipient, **kwargs):
        """Build this email and return the Email Instance.

        :param recipient: The recipient of the email.
        :param kwargs: Additional key/value arguments to be rendered.
        :return: The email instance.
        """

        # Create message container as multipart/alternative.
        msg = MIMEMultipart('alternative')
        msg['From'] = self.sender
        msg['To'] = recipient
        msg['Date'] = formatdate(localtime=True)

        # Render the subject template. Add length and \r\n sanity check
        # replacements. While we could fold the subject line, if our subject
        # lines are longer than 78 characters nobody's going to read them all.
        subject = self.subject.render(**kwargs)
        subject = re.sub(r'\r?\n', ' ', subject)

        if len(subject) > 78:
            subject = subject[0:75] + '...'
        msg['Subject'] = subject

        # Iterate and render over all additional headers.
        for key, value in six.iteritems(self.headers):
            try:
                msg.replace_header(key, value)
            except KeyError:
                msg.add_header(key, value)

        # Render and attach our templates.
        for type, template in six.iteritems(self.template_cache):
            body_part = template.render(**kwargs)
            msg.attach(MIMEText(body_part, type, "utf-8"))

        return msg
Example #10
0
def call(application, title, users, body, action=False, post_text=False, pre_text=False, job=True, p_tag=True):
   try:
      manager = Manager()
      is_prod = __is_prod(manager)

      # arguments:
      # application - the name of the sending application. Example: "Support Exceptions"
      # title - the title that should be borne by the email. Example: "New comment on SE #44"
      # users - set of individual uids that will also get the notification. Example: set([ "*****@*****.**", "*****@*****.**" ])
      # body - what will go into an h3 tag as the main line of the email. Example: "mowens has left a new comment on <a href="tools.apps.cee.redhat.com/se/44">SE #44</a>."
      email_body = __build_content(body, pre_text, post_text, p_tag)

      email = {
          'from': reply_to,
          'subject': __get_subject(action, application, title),
          'body': email_body,
          'plaintext': __html_to_plaintext(email_body),
      }

      html_msg = __build_message(**email)
      plaintext_msg = __build_message(**email)

      html_msg.attach(MIMEText(email['body'], 'html'))
      plaintext_msg.attach(MIMEText(__html_to_plaintext(email['body']), 'plain'))

      emails = []
      to = []

      for uid in users:
         if __is_plaintext(uid):
            msg = plaintext_msg
         else:
            msg = html_msg

         user_email = __get_user_email(manager, uid)
         to.append(user_email)

         if is_prod:
            __set_msg_to(msg, user_email)

            if not job:
               __send_email(email['from'], user_email, msg.as_string())
            else:
               emails.append([email['from'], user_email, msg.as_string()])

      if is_prod and job:
         send_email.call(emails=emails)

      __send_dev_notifications(is_prod=is_prod, html_msg=html_msg, plaintext_msg=plaintext_msg, to=to, email=email, job=job)

   except Exception as e:
      FROM = reply_to

      msg = MIMEMultipart('alternative')
      msg['Subject'] = "%s Failed to Send Email" % app_title
      msg['From'] = reply_to
      msg['To'] = admins_email

      BODY = """
         <h3 style="padding-left: 1%%; padding-top: 1%%; padding-bottom: 1%%; color: black;"><i>%s</i></h3>
      """ % body

      if post_text != False:
         BODY += """
            <p style="padding-left: 1%%; padding-top: 1%%; padding-bottom: 1%%; color: black;"><i>%s</i></p>
         """ % post_text

      message = """
         <html>
            <head></head>
            <body>
               <p>Failed to send email to the following people '%s' with contents below.</p>
               %s
               <hr />
               <p>Below is the stack trace.</p>
               <p><pre>%s</pre></p>
            </body
         </html>
      """ % (', '.join(users), BODY, traceback.format_exc())
      part1 = MIMEText(message, 'html')
      msg.attach(part1)

      if not is_prod:
         msg.replace_header("Subject", "DEVEL ~ " + email['subject'])

      __send_email(FROM, admins_email, msg.as_string())
Example #11
0
context = ssl.create_default_context()

smtp, sender, port, pwd = itemgetter(
    'smtp', 'sender', 'port', 'password')(cred)

message["From"] = sender

with smtplib.SMTP_SSL(smtp, port, context=context) as server:
    server.login(sender, pwd)
    index_sent = df_email_valid.columns.get_loc('sent')

    for i, data in df_email_valid.iterrows():
        try:
            if "To" in message:
                message.replace_header("To", data['E-mail Address'])
            else:
                message["To"] = data['E-mail Address']
            server.sendmail(
                sender, data['E-mail Address'], message.as_string())
            df.loc[i, 'sent'] = 1
        except Exception as e:
            with open(sent_info, 'a') as f:
                f.write(
                    f'Emails send till index:{i-1}\n Last email was send to {df.loc[i-1]}\n')
            print(e)
            df.to_excel(os.path.join(BASE_DIR, 'sent.xlsx'))
        finally:
            df.to_excel(os.path.join(BASE_DIR, 'sent.xlsx'))

Example #12
0
    def send_raw(self, job, message, config=None):
        interval = message.get('interval')
        if interval is None:
            interval = timedelta()
        else:
            interval = timedelta(seconds=interval)

        sw_name = self.middleware.call_sync('system.info')['version'].split('-', 1)[0]

        channel = message.get('channel')
        if not channel:
            channel = sw_name.lower()
        if interval > timedelta():
            channelfile = '/tmp/.msg.%s' % (channel)
            last_update = datetime.now() - interval
            try:
                last_update = datetime.fromtimestamp(os.stat(channelfile).st_mtime)
            except OSError:
                pass
            timediff = datetime.now() - last_update
            if (timediff >= interval) or (timediff < timedelta()):
                # Make sure mtime is modified
                # We could use os.utime but this is simpler!
                with open(channelfile, 'w') as f:
                    f.write('!')
            else:
                raise CallError('This message was already sent in the given interval')

        if not config:
            config = self.middleware.call_sync('mail.config')
        verrors = self.__password_verify(config['pass'], 'mail-config.pass')
        if verrors:
            raise verrors
        to = message.get('to')
        if not to:
            to = [
                self.middleware.call_sync(
                    'user.query', [('username', '=', 'root')], {'get': True}
                )['email']
            ]
            if not to[0]:
                raise CallError('Email address for root is not configured')

        if message.get('attachments'):
            job.check_pipe("input")

            def read_json():
                f = job.pipes.input.r
                data = b''
                i = 0
                while True:
                    read = f.read(1048576)  # 1MiB
                    if read == b'':
                        break
                    data += read
                    i += 1
                    if i > 50:
                        raise ValueError('Attachments bigger than 50MB not allowed yet')
                if data == b'':
                    return None
                return json.loads(data)

            attachments = read_json()
        else:
            attachments = None

        if 'html' in message or attachments:
            msg = MIMEMultipart()
            msg.preamble = message['text']
            if 'html' in message:
                msg2 = MIMEMultipart('alternative')
                msg2.attach(MIMEText(message['text'], 'plain', _charset='utf-8'))
                msg2.attach(MIMEText(message['html'], 'html', _charset='utf-8'))
                msg.attach(msg2)
            if attachments:
                for attachment in attachments:
                    m = Message()
                    m.set_payload(attachment['content'])
                    for header in attachment.get('headers'):
                        m.add_header(header['name'], header['value'], **(header.get('params') or {}))
                    msg.attach(m)
        else:
            msg = MIMEText(message['text'], _charset='utf-8')

        msg['Subject'] = message['subject']

        msg['From'] = config['fromemail']
        msg['To'] = ', '.join(to)
        if message.get('cc'):
            msg['Cc'] = ', '.join(message.get('cc'))
        msg['Date'] = formatdate()

        local_hostname = socket.gethostname()

        msg['Message-ID'] = "<%s-%s.%s@%s>" % (sw_name.lower(), datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"), base64.urlsafe_b64encode(os.urandom(3)), local_hostname)

        extra_headers = message.get('extra_headers') or {}
        for key, val in list(extra_headers.items()):
            if key in msg:
                msg.replace_header(key, val)
            else:
                msg[key] = val

        syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_MAIL)
        try:
            server = self._get_smtp_server(config, message['timeout'], local_hostname=local_hostname)
            # NOTE: Don't do this.
            #
            # If smtplib.SMTP* tells you to run connect() first, it's because the
            # mailserver it tried connecting to via the outgoing server argument
            # was unreachable and it tried to connect to 'localhost' and barfed.
            # This is because FreeNAS doesn't run a full MTA.
            # else:
            #    server.connect()
            headers = '\n'.join([f'{k}: {v}' for k, v in msg._headers])
            syslog.syslog(f"sending mail to {', '.join(to)}\n{headers}")
            server.sendmail(config['fromemail'], to, msg.as_string())
            server.quit()
        except Exception as e:
            # Don't spam syslog with these messages. They should only end up in the
            # test-email pane.
            # We are only interested in ValueError, not subclasses.
            if e.__class__ is ValueError:
                raise CallError(str(e))
            syslog.syslog(f'Failed to send email to {", ".join(to)}: {str(e)}')
            if isinstance(e, smtplib.SMTPAuthenticationError):
                raise CallError(f'Authentication error ({e.smtp_code}): {e.smtp_error}', errno.EAUTH)
            self.logger.warn('Failed to send email: %s', str(e), exc_info=True)
            if message['queue']:
                with MailQueue() as mq:
                    mq.append(msg)
            raise CallError(f'Failed to send email: {e}')
        return True
Example #13
0
    def send(self):
        """
        Builds and sends out the message to all recipients.
        """

        # basic checks
        if not self.server or not self.sender or not self.to:
            raise emailError("Must specify server, sender, and recipient.")

        # put the current date / time into the replacement dict
        now = datetime.now()
        if 'date' not in self.replacement_dict:
            self.replacement_dict['date'] = '%i/%i/%s' % (now.month, now.day, now.strftime('%y'))
        if 'time' not in self.replacement_dict:
            self.replacement_dict['time'] = '%i:%0.2i %s' % (int(now.strftime('%I')), now.minute, 
                                                             now.strftime('%p'))

        # grab the template text and subject
        text = self.template_text or ''
        subject = self.subject or ''

        # prepare the template and subject
        if self.use_templating:
            if self.type == 'mako':
                from mako.template import Template
                text = Template(text).render_unicode(**self.replacement_dict)
            else:
                try:
                    text = text % self.replacement_dict
                except Exception:
                    pass
            try:
                subject = subject % self.replacement_dict
            except Exception:
                pass

        # start creating the message; using Header directly for potentially 
        # long headers since MIMEMultipart overrides its default space folding 
        # character with a hard tab, which Outlook 2003 and earlier (and 
        # possibly other email clients) doesn't unfold back into a space
        message = MIMEMultipart()
        if self.reply_to:
            message['Reply-To'] = Header(self.reply_to, header_name='Reply-To')
        message['From'] = Header(self.sender, header_name='From')
        message['To'] = ''
        if self.cc:
             message['Cc'] = Header(', '.join(self.cc), header_name='Cc')
        message['Subject'] = Header(subject.strip(), header_name='Subject')
        message['Date'] = email.utils.formatdate(localtime=True)
        message['Message-ID'] = email.utils.make_msgid()

        # add any custom headers
        for key, value in self.headers.iteritems():
            header = Header(value, header_name=key)
            if key in message:
                message.replace_header(key, header)
            else:
                message[key] = header
        
        # start off with a text/html
        text, charset = _encode_text(text)    
        
        if self.body_type == 'plain':
            body = MIMEText(text, _charset=charset)
        else:            
            body = MIMEText(text, _subtype='html', _charset=charset)
        
        if 'MIME-Version' in body:
            del body['MIME-Version']
        message.attach(body)
        
        # if there are attachment, add them
        for attachment in self.attachments:
            message.attach(attachment.get_mime_object())

        # now, send out each email
        for to in self.to:
            self._send_email(self.sender, to, self.cc, self.bcc, message)
Example #14
0
def send_mail(
    subject=None, text=None, interval=timedelta(), channel=None, to=None, extra_headers=None, attachments=None
):
    from freenasUI.account.models import bsdUsers
    from freenasUI.network.models import GlobalConfiguration
    from freenasUI.system.models import Email

    if not channel:
        channel = get_sw_name().lower()
    if interval > timedelta():
        channelfile = "/tmp/.msg.%s" % (channel)
        last_update = datetime.now() - interval
        try:
            last_update = datetime.fromtimestamp(os.stat(channelfile).st_mtime)
        except OSError:
            pass
        timediff = datetime.now() - last_update
        if (timediff >= interval) or (timediff < timedelta()):
            open(channelfile, "w").close()
        else:
            return True, "This message was already sent in the given interval"

    error = False
    errmsg = ""
    em = Email.objects.all().order_by("-id")[0]
    if not to:
        to = [bsdUsers.objects.get(bsdusr_username="******").bsdusr_email]
    if attachments:
        msg = MIMEMultipart()
        msg.preamble = text
        map(lambda attachment: msg.attach(attachment), attachments)
    else:
        msg = MIMEText(text, _charset="utf-8")
    if subject:
        msg["Subject"] = subject
    msg["From"] = em.em_fromemail
    msg["To"] = ", ".join(to)
    msg["Date"] = formatdate()

    if not extra_headers:
        extra_headers = {}
    for key, val in extra_headers.items():
        if key in msg:
            msg.replace_header(key, val)
        else:
            msg[key] = val
    msg = msg.as_string()

    try:
        gc = GlobalConfiguration.objects.order_by("-id")[0]
        local_hostname = "%s.%s" % (gc.gc_hostname, gc.gc_domain)
    except:
        local_hostname = None

    try:
        if not em.em_outgoingserver or not em.em_port:
            # See NOTE below.
            raise ValueError("you must provide an outgoing mailserver and mail" " server port when sending mail")
        if em.em_security == "ssl":
            server = smtplib.SMTP_SSL(em.em_outgoingserver, em.em_port, timeout=10, local_hostname=local_hostname)
        else:
            server = smtplib.SMTP(em.em_outgoingserver, em.em_port, timeout=10, local_hostname=local_hostname)
            if em.em_security == "tls":
                server.starttls()
        if em.em_smtp:
            server.login(em.em_user.encode("utf-8"), em.em_pass.encode("utf-8"))
        # NOTE: Don't do this.
        #
        # If smtplib.SMTP* tells you to run connect() first, it's because the
        # mailserver it tried connecting to via the outgoing server argument
        # was unreachable and it tried to connect to 'localhost' and barfed.
        # This is because FreeNAS doesn't run a full MTA.
        # else:
        #    server.connect()
        server.sendmail(em.em_fromemail, to, msg)
        server.quit()
    except ValueError, ve:
        # Don't spam syslog with these messages. They should only end up in the
        # test-email pane.
        errmsg = str(ve)
        error = True
Example #15
0
def send_mail(
    subject=None,
    text=None,
    interval=timedelta(),
    channel=None,
    to=None,
    extra_headers=None,
    attachments=None,
    timeout=300,
):
    from freenasUI.account.models import bsdUsers
    from freenasUI.network.models import GlobalConfiguration
    from freenasUI.system.models import Email
    if not channel:
        channel = get_sw_name().lower()
    if interval > timedelta():
        channelfile = '/tmp/.msg.%s' % (channel)
        last_update = datetime.now() - interval
        try:
            last_update = datetime.fromtimestamp(os.stat(channelfile).st_mtime)
        except OSError:
            pass
        timediff = datetime.now() - last_update
        if (timediff >= interval) or (timediff < timedelta()):
            open(channelfile, 'w').close()
        else:
            return True, 'This message was already sent in the given interval'

    error = False
    errmsg = ''
    em = Email.objects.all().order_by('-id')[0]
    if not to:
        to = [bsdUsers.objects.get(bsdusr_username='******').bsdusr_email]
    if attachments:
        msg = MIMEMultipart()
        msg.preamble = text
        map(lambda attachment: msg.attach(attachment), attachments)
    else:
        msg = MIMEText(text, _charset='utf-8')
    if subject:
        msg['Subject'] = subject

    msg['From'] = em.em_fromemail
    msg['To'] = ', '.join(to)
    msg['Date'] = formatdate()

    try:
        gc = GlobalConfiguration.objects.order_by('-id')[0]
        local_hostname = "%s.%s" % (gc.gc_hostname, gc.gc_domain)
    except:
        local_hostname = "%s.local" % get_sw_name()

    msg['Message-ID'] = "<%s-%s.%s@%s>" % (
        get_sw_name().lower(), datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"),
        base64.urlsafe_b64encode(os.urandom(3)), local_hostname)

    if not extra_headers:
        extra_headers = {}
    for key, val in extra_headers.items():
        if key in msg:
            msg.replace_header(key, val)
        else:
            msg[key] = val
    msg = msg.as_string()

    try:
        if not em.em_outgoingserver or not em.em_port:
            # See NOTE below.
            raise ValueError('you must provide an outgoing mailserver and mail'
                             ' server port when sending mail')
        if em.em_security == 'ssl':
            server = smtplib.SMTP_SSL(em.em_outgoingserver,
                                      em.em_port,
                                      timeout=timeout,
                                      local_hostname=local_hostname)
        else:
            server = smtplib.SMTP(em.em_outgoingserver,
                                  em.em_port,
                                  timeout=timeout,
                                  local_hostname=local_hostname)
            if em.em_security == 'tls':
                server.starttls()
        if em.em_smtp:
            server.login(em.em_user.encode('utf-8'),
                         em.em_pass.encode('utf-8'))
        # NOTE: Don't do this.
        #
        # If smtplib.SMTP* tells you to run connect() first, it's because the
        # mailserver it tried connecting to via the outgoing server argument
        # was unreachable and it tried to connect to 'localhost' and barfed.
        # This is because FreeNAS doesn't run a full MTA.
        #else:
        #    server.connect()
        server.sendmail(em.em_fromemail, to, msg)
        server.quit()
    except ValueError as ve:
        # Don't spam syslog with these messages. They should only end up in the
        # test-email pane.
        errmsg = str(ve)
        error = True
    except Exception as e:
        syslog.openlog(channel, syslog.LOG_PID, facility=syslog.LOG_MAIL)
        try:
            for line in traceback.format_exc().splitlines():
                syslog.syslog(syslog.LOG_ERR, line)
        finally:
            syslog.closelog()
        errmsg = str(e)
        error = True
    except smtplib.SMTPAuthenticationError as e:
        errmsg = "%d %s" % (e.smtp_code, e.smtp_error)
        error = True
    except:
        errmsg = "Unexpected error."
        error = True
    return error, errmsg
Example #16
0
class Mdn:
    """Class for handling AS2 MDNs. Includes functions for both
    parsing and building them.
    """
    def __init__(self, mdn_mode=None, digest_alg=None, mdn_url=None):
        self.message_id = None
        self.orig_message_id = None
        self.payload = None
        self.mdn_mode = mdn_mode
        self.digest_alg = digest_alg
        self.mdn_url = mdn_url

    @property
    def content(self):
        """Function returns the body of the mdn message as a byte string"""

        if self.payload is not None:
            message_bytes = mime_to_bytes(self.payload)
            boundary = b"--" + self.payload.get_boundary().encode("utf-8")
            temp = message_bytes.split(boundary)
            temp.pop(0)
            return boundary + boundary.join(temp)
        return ""

    @property
    def headers(self):
        """Return the headers in the payload as a dictionary."""
        if self.payload:
            return dict(self.payload.items())
        return {}

    @property
    def headers_str(self):
        """Return the headers in the payload as a string."""
        message_header = ""
        if self.payload:
            for k, v in self.headers.items():
                message_header += f"{k}: {v}\r\n"
        return message_header.encode("utf-8")

    def build(
        self,
        message,
        status,
        detailed_status=None,
        confirmation_text=MDN_CONFIRM_TEXT,
        failed_text=MDN_FAILED_TEXT,
    ):
        """Function builds and signs an AS2 MDN message.

        :param message: The received AS2 message for which this is an MDN.

        :param status: The status of processing of the received AS2 message.

        :param detailed_status: The optional detailed status of processing of the received AS2
        message. Used to give additional error info (default "None")

        :param confirmation_text: The confirmation message sent in the first part of the MDN.

        :param failed_text: The failure message sent in the first part of the failed MDN.
        """

        # Generate message id using UUID 1 as it uses both hostname and time
        self.message_id = email_utils.make_msgid().lstrip("<").rstrip(">")
        self.orig_message_id = message.message_id

        # Set up the message headers
        mdn_headers = {
            "AS2-Version": AS2_VERSION,
            "ediint-features": EDIINT_FEATURES,
            "Message-ID": f"<{self.message_id}>",
            "AS2-From": quote_as2name(message.headers.get("as2-to")),
            "AS2-To": quote_as2name(message.headers.get("as2-from")),
            "Date": email_utils.formatdate(localtime=True),
            "user-agent": "pyAS2 Open Source AS2 Software",
        }

        # Set the confirmation text message here
        # overwrite with organization specific message
        if message.receiver and message.receiver.mdn_confirm_text:
            confirmation_text = message.receiver.mdn_confirm_text

        # overwrite with partner specific message
        if message.sender and message.sender.mdn_confirm_text:
            confirmation_text = message.sender.mdn_confirm_text

        if status != "processed":
            confirmation_text = failed_text

        self.payload = MIMEMultipart("report",
                                     report_type="disposition-notification")

        # Create and attach the MDN Text Message
        mdn_text = email_message.Message()
        mdn_text.set_payload(f"{confirmation_text}\r\n")
        mdn_text.set_type("text/plain")
        del mdn_text["MIME-Version"]
        encoders.encode_7or8bit(mdn_text)
        self.payload.attach(mdn_text)

        # Create and attache the MDN Report Message
        mdn_base = email_message.Message()
        mdn_base.set_type("message/disposition-notification")
        mdn_report = "Reporting-UA: pyAS2 Open Source AS2 Software\r\n"
        mdn_report += f'Original-Recipient: rfc822; {message.headers.get("as2-to")}\r\n'
        mdn_report += f'Final-Recipient: rfc822; {message.headers.get("as2-to")}\r\n'
        mdn_report += f"Original-Message-ID: <{message.message_id}>\r\n"
        mdn_report += f"Disposition: automatic-action/MDN-sent-automatically; {status}"
        if detailed_status:
            mdn_report += f": {detailed_status}"
        mdn_report += "\r\n"
        if message.mic:
            mdn_report += f"Received-content-MIC: {message.mic.decode()}, {message.digest_alg}\r\n"
        mdn_base.set_payload(mdn_report)
        del mdn_base["MIME-Version"]
        encoders.encode_7or8bit(mdn_base)
        self.payload.attach(mdn_base)

        logger.debug(
            f"MDN report for message {message.message_id} created:\n{mime_to_bytes(mdn_base)}"
        )

        # Sign the MDN if it is requested by the sender
        if (message.headers.get("disposition-notification-options")
                and message.receiver and message.receiver.sign_key):
            self.digest_alg = (
                message.headers["disposition-notification-options"].split(
                    ";")[-1].split(",")[-1].strip().replace("-", ""))
            signed_mdn = MIMEMultipart("signed",
                                       protocol="application/pkcs7-signature")
            del signed_mdn["MIME-Version"]
            signed_mdn.attach(self.payload)

            # Create the signature mime message
            signature = email_message.Message()
            signature.set_type("application/pkcs7-signature")
            signature.set_param("name", "smime.p7s")
            signature.set_param("smime-type", "signed-data")
            signature.add_header("Content-Disposition",
                                 "attachment",
                                 filename="smime.p7s")
            del signature["MIME-Version"]

            signed_data = sign_message(canonicalize(self.payload),
                                       self.digest_alg,
                                       message.receiver.sign_key)
            signature.set_payload(signed_data)
            encoders.encode_base64(signature)

            signed_mdn.set_param("micalg", self.digest_alg)
            signed_mdn.attach(signature)

            self.payload = signed_mdn
            logger.debug(f"Signing the MDN for message {message.message_id}")

        # Update the headers of the final payload and set message boundary
        for k, v in mdn_headers.items():
            if self.payload.get(k):
                self.payload.replace_header(k, v)
            else:
                self.payload.add_header(k, v)
        self.payload.set_boundary(make_mime_boundary())
        logger.debug(f"MDN generated for message {message.message_id} with "
                     f"content:\n {mime_to_bytes(self.payload)}")

    def parse(self, raw_content, find_message_cb):
        """Function parses the RAW AS2 MDN, verifies it and extracts the
        processing status of the orginal AS2 message.

        :param raw_content:
            A byte string of the received HTTP headers followed by the body.

        :param find_message_cb:
            A callback the must returns the original Message Object. The
            original message-id and original recipient AS2 ID are passed
            as arguments to it.

        :returns:
            A two element tuple containing (status, detailed_status). The
            status is a string indicating the status of the transaction. The
            optional detailed_status gives additional information about the
            processing status.
        """

        status, detailed_status = None, None
        try:
            self.payload = parse_mime(raw_content)
            self.orig_message_id, orig_recipient = self.detect_mdn()

            # Call the find message callback which should return a Message instance
            orig_message = find_message_cb(self.orig_message_id,
                                           orig_recipient)

            # Extract the headers and save it
            mdn_headers = {}
            for k, v in self.payload.items():
                k = k.lower()
                if k == "message-id":
                    self.message_id = v.lstrip("<").rstrip(">")
                mdn_headers[k] = v

            if (orig_message.receiver.mdn_digest_alg
                    and self.payload.get_content_type() != "multipart/signed"):
                status = "failed/Failure"
                detailed_status = "Expected signed MDN but unsigned MDN returned"
                return status, detailed_status

            if self.payload.get_content_type() == "multipart/signed":
                logger.debug(
                    f"Verifying signed MDN: \n{mime_to_bytes(self.payload)}")
                message_boundary = (
                    "--" + self.payload.get_boundary()).encode("utf-8")

                # Extract the signature and the signed payload
                signature = None
                signature_types = [
                    "application/pkcs7-signature",
                    "application/x-pkcs7-signature",
                ]
                for part in self.payload.walk():
                    if part.get_content_type() in signature_types:
                        signature = part.get_payload(decode=True)
                    elif part.get_content_type() == "multipart/report":
                        self.payload = part

                # Verify the message, first using raw message and if it fails
                # then convert to canonical form and try again
                mic_content = extract_first_part(raw_content, message_boundary)
                verify_cert = orig_message.receiver.load_verify_cert()
                try:
                    self.digest_alg = verify_message(mic_content, signature,
                                                     verify_cert)
                except IntegrityError:
                    mic_content = canonicalize(self.payload)
                    self.digest_alg = verify_message(mic_content, signature,
                                                     verify_cert)

            for part in self.payload.walk():
                if part.get_content_type(
                ) == "message/disposition-notification":
                    logger.debug(
                        f"MDN report for message {orig_message.message_id}:\n{part.as_string()}"
                    )

                    mdn = part.get_payload()[-1]
                    mdn_status = mdn["Disposition"].split(
                        ";").pop().strip().split(":")
                    status = mdn_status[0]
                    if status == "processed":
                        # Compare the original mic with the received mic
                        mdn_mic = mdn.get("Received-Content-MIC",
                                          "").split(",")[0]
                        if (mdn_mic and orig_message.mic
                                and mdn_mic != orig_message.mic.decode()):
                            status = "processed/warning"
                            detailed_status = "Message Integrity check failed."
                    else:
                        detailed_status = " ".join(mdn_status[1:]).strip()
        except MDNNotFound:
            status = "failed/Failure"
            detailed_status = "mdn-not-found"
        except Exception as e:  # pylint: disable=W0703
            status = "failed/Failure"
            detailed_status = f"Failed to parse received MDN. {e}"
            logger.error(
                f"Failed to parse AS2 MDN\n: {traceback.format_exc()}")
        return status, detailed_status

    def detect_mdn(self):
        """Function checks if the received raw message is an AS2 MDN or not.

        :raises MDNNotFound: If the received payload is not an MDN then this
        exception is raised.

        :return:
            A two element tuple containing (message_id, message_recipient). The
            message_id is the original AS2 message id and the message_recipient
            is the original AS2 message recipient.
        """
        mdn_message = None
        if self.payload.get_content_type() == "multipart/report":
            mdn_message = self.payload
        elif self.payload.get_content_type() == "multipart/signed":
            for part in self.payload.walk():
                if part.get_content_type() == "multipart/report":
                    mdn_message = self.payload

        if not mdn_message:
            raise MDNNotFound("No MDN found in the received message")

        message_id, message_recipient = None, None
        for part in mdn_message.walk():
            if part.get_content_type() == "message/disposition-notification":
                mdn = part.get_payload()[0]
                message_id = mdn.get("Original-Message-ID").strip("<>")
                message_recipient = None
                if "Original-Recipient" in mdn:
                    message_recipient = mdn["Original-Recipient"].split(
                        ";")[1].strip()
                elif "Final-Recipient" in mdn:
                    message_recipient = mdn["Final-Recipient"].split(
                        ";")[1].strip()
        return message_id, message_recipient
Example #17
0
    def send(self, mailmessage, mail=None):

        if mail is None:
            mail = ConfigNode('mail', self.configstore).__getstate__()
        if not mail.get('server') or not mail.get('port'):
            raise RpcException(
                errno.EINVAL,
                'You must provide an outgoing server and port when sending mail',
            )

        to = mailmessage.get('to')
        attachments = mailmessage.get('attachments')
        subject = mailmessage.get('subject')
        extra_headers = mailmessage.get('extra_headers')

        if not to:
            to = self.dispatcher.call_sync(
                'users.query', [('username', '=', 'root')], {'single': True})
            if to and to.get('email'):
                to = [to['email']]

        if attachments:
            msg = MIMEMultipart()
            msg.preamble = mailmessage['message']
            list(map(lambda attachment: msg.attach(attachment), attachments))
        else:
            msg = MIMEText(mailmessage['message'], _charset='utf-8')
        if subject:
            msg['Subject'] = subject

        msg['From'] = mailmessage['from'] if mailmessage.get('from') else mail['from']
        msg['To'] = ', '.join(to)
        msg['Date'] = formatdate()

        local_hostname = socket.gethostname()
        version = self.dispatcher.call_sync('system.info.version').split('-')[0].lower()

        msg['Message-ID'] = "<{0}-{1}.{2}@{3}>".format(
            version,
            datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"),
            base64.urlsafe_b64encode(os.urandom(3)),
            local_hostname)

        if not extra_headers:
            extra_headers = {}
        for key, val in list(extra_headers.items()):
            if key in msg:
                msg.replace_header(key, val)
            else:
                msg[key] = val
        msg = msg.as_string()

        try:
            if mail['encryption'] == 'SSL':
                klass = smtplib.SMTP_SSL
            else:
                klass = smtplib.SMTP
            server = klass(mail['server'], mail['port'], timeout=300, local_hostname=local_hostname)
            if mail['encryption'] == 'TLS':
                server.starttls()

            if mail['auth']:
                server.login(mail['user'], mail['pass'])
            server.sendmail(mail['from'], to, msg)
            server.quit()
        except smtplib.SMTPAuthenticationError as e:
            raise RpcException(errno.EACCES, 'Authentication error: {0} {1}'.format(
                e.smtp_code, e.smtp_error))
        except Exception as e:
            logger.error('Failed to send email: {0}'.format(str(e)), exc_info=True)
            raise RpcException(errno.EFAULT, 'Email send error: {0}'.format(str(e)))
        except:
            raise RpcException(errno.EFAULT, 'Unexpected error')
Example #18
0
    def send(self, mailmessage, mail=None):

        if mail is None:
            mail = ConfigNode('mail', self.configstore).__getstate__()
        if not mail.get('server') or not mail.get('port'):
            raise RpcException(
                errno.EINVAL,
                'You must provide an outgoing server and port when sending mail',
            )

        to = mailmessage.get('to')
        attachments = mailmessage.get('attachments')
        subject = mailmessage.get('subject')
        extra_headers = mailmessage.get('extra_headers')

        if not to:
            to = self.dispatcher.call_sync('users.query',
                                           [('username', '=', 'root')],
                                           {'single': True})
            if to and to.get('email'):
                to = [to['email']]

        if attachments:
            msg = MIMEMultipart()
            msg.preamble = mailmessage['message']
            map(lambda attachment: msg.attach(attachment), attachments)
        else:
            msg = MIMEText(mailmessage['message'], _charset='utf-8')
        if subject:
            msg['Subject'] = subject

        msg['From'] = mailmessage['from'] if mailmessage.get(
            'from') else mail['from']
        msg['To'] = ', '.join(to)
        msg['Date'] = formatdate()

        local_hostname = socket.gethostname()
        version = self.dispatcher.call_sync('system.info.version').split(
            '-')[0].lower()

        msg['Message-ID'] = "<{0}-{1}.{2}@{3}>".format(
            version,
            datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"),
            base64.urlsafe_b64encode(os.urandom(3)), local_hostname)

        if not extra_headers:
            extra_headers = {}
        for key, val in extra_headers.items():
            if key in msg:
                msg.replace_header(key, val)
            else:
                msg[key] = val
        msg = msg.as_string()

        try:
            if mail['encryption'] == 'SSL':
                klass = smtplib.SMTP_SSL
            else:
                klass = smtplib.SMTP
            server = klass(mail['server'],
                           mail['port'],
                           timeout=300,
                           local_hostname=local_hostname)
            if mail['encryption'] == 'TLS':
                server.starttls()

            if mail['auth']:
                server.login(mail['user'], mail['pass'])
            server.sendmail(mail['from'], to, msg)
            server.quit()
        except smtplib.SMTPAuthenticationError as e:
            raise RpcException(
                errno.EACCES,
                'Authentication error: {0} {1}'.format(e.smtp_code,
                                                       e.smtp_error))
        except Exception as e:
            logger.error('Failed to send email: {0}'.format(str(e)),
                         exc_info=True)
            raise RpcException(errno.EFAULT,
                               'Email send error: {0}'.format(str(e)))
        except:
            raise RpcException(errno.EFAULT, 'Unexpected error')
Example #19
0
def send_simple_mail(sender, receiver, subject, msgtxt, attachments=None, usergenerated=False, cc=None, replyto=None, sendername=None, receivername=None, messageid=None, suppress_auto_replies=True, is_auto_reply=False, htmlbody=None, headers={}, staggertype=None, stagger=None):
    # attachment format, each is a tuple of (name, mimetype,contents)
    # content should be *binary* and not base64 encoded, since we need to
    # use the base64 routines from the email library to get a properly
    # formatted output message
    msg = MIMEMultipart()
    msg['Subject'] = subject
    msg['To'] = _encoded_email_header(receivername, receiver)
    msg['From'] = _encoded_email_header(sendername, sender)
    if cc:
        msg['Cc'] = cc
    if replyto:
        msg['Reply-To'] = replyto
    msg['Date'] = formatdate(localtime=True)
    if messageid:
        msg['Message-ID'] = messageid
    else:
        msg['Message-ID'] = make_msgid()
    if suppress_auto_replies:
        # Do our best to set some headers to indicate that auto-replies like out of office
        # messages should not be sent to this email.
        msg['X-Auto-Response-Suppress'] = 'All'

    # Is this email auto-generated or auto-replied?
    if is_auto_reply:
        msg['Auto-Submitted'] = 'auto-replied'
    elif not usergenerated:
        msg['Auto-Submitted'] = 'auto-generated'

    for h, v in headers.items():
        if h in msg:
            # Replace the existing header -- the one specified is supposedly overriding it
            msg.replace_header(h, v)
        else:
            msg.add_header(h, v)

    if htmlbody:
        mpart = MIMEMultipart("alternative")
        mpart.attach(MIMEText(msgtxt, _charset=_utf8_charset))
        mpart.attach(MIMEText(htmlbody, 'html', _charset=_utf8_charset))
        msg.attach(mpart)
    else:
        # Just a plaintext body, so append it directly
        msg.attach(MIMEText(msgtxt, _charset='utf-8'))

    if attachments:
        for a in attachments:
            main, sub = a['contenttype'].split('/')
            part = MIMENonMultipart(main, sub)
            part.set_payload(a['content'])
            part.add_header('Content-Disposition', a.get('disposition', 'attachment; filename="%s"' % a['filename']))
            if 'id' in a:
                part.add_header('Content-ID', a['id'])

            encoders.encode_base64(part)
            msg.attach(part)

    with transaction.atomic():
        if staggertype and stagger:
            # Don't send a second one too close after another one of this class.
            ls, created = LastSent.objects.get_or_create(type=staggertype, defaults={'lastsent': datetime.now()})

            sendat = ls.lastsent = ls.lastsent + stagger
            ls.save(update_fields=['lastsent'])
        else:
            sendat = datetime.now()

        # Just write it to the queue, so it will be transactionally rolled back
        QueuedMail(sender=sender, receiver=receiver, fullmsg=msg.as_string(), usergenerated=usergenerated, sendat=sendat).save()
        if cc:
            # Write a second copy for the cc, wihch will be delivered
            # directly to the recipient. (The sender doesn't parse the
            # message content to extract cc fields).
            QueuedMail(sender=sender, receiver=cc, fullmsg=msg.as_string(), usergenerated=usergenerated, sendat=sendat).save()
Example #20
0
def send_mail(to, subject, text, files=None, from_="", user="", password=""):
    """
    sends an e-mail (with attachement) using tu-ilmenau mail server
    """
    msg = MIMEMultipart()
    msg["From"] = from_
    msg["To"] = to
    msg["Subject"] = subject

    body = MIMEText(text, 'plain')
    msg.attach(body)

    for file_ in files or []:
        ctype, encoding = mimetypes.guess_type(file_)
        if ctype is None or encoding is not None:
            ctype = "application/octet-stream"

        maintype, subtype = ctype.split("/", 1)

        if maintype == "text":
            fp = open(file_)
            attachment = MIMEText(fp.read(), _subtype=subtype)
            fp.close()
        elif maintype == "image":
            fp = open(file_, "rb")
            attachment = MIMEImage(fp.read(), _subtype=subtype)
            fp.close()
        elif maintype == "audio":
            fp = open(file_, "rb")
            attachment = MIMEAudio(fp.read(), _subtype=subtype)
            fp.close()
        else:
            fp = open(file_, "rb")
            attachment = MIMEBase(maintype, subtype)
            attachment.set_payload(fp.read())
            fp.close()
            encoders.encode_base64(attachment)
        attachment.add_header("Content-Disposition",
                              "attachment",
                              filename=file_)
        msg.attach(attachment)

    failed = False
    check_login = False
    # try to send email
    while True:
        if check_login:
            check_login = False
            if from_ == "":
                from_, user, password = simple_gui.mail_login()
            else:
                if user == "unknown":
                    from_, user, password = simple_gui.mail_login()
                else:
                    message = "Der Server verweigert den Zugang.\n" \
                                + "Bitte überprüfen Sie ihre Daten."
                    mark1 = string.find(from_, "\"")
                    mark2 = string.rfind(from_, "\"")
                    mark3 = string.find(from_, "<")
                    mark4 = string.rfind(from_, ">")
                    name = from_[mark1 + 1:mark2]
                    mailadress = from_[mark3 + 1:mark4]
                    fields = [name, mailadress, user, password]
                    from_, user, password = simple_gui.mail_login(
                        message, fields)
            # check if cancel button was hit
            if user == "unknown":
                failed = True
                break

            # replace message
            msg.replace_header("From", from_)

        try:
            server = smtplib.SMTP()
            # TU Ilmenau: ausgehender Mail-Server (mit Authentifizierung):
            #     smail.tu-ilmenau.de, Port 25, Verbindungssicherheit STARTTLS
            # Quelle: http://www.tu-ilmenau.de/it-service/mitarbeiter/
            #                                  e-mail/konfigurationshinweise/
            server.connect("smail.tu-ilmenau.de", 25)
            server.starttls()
            server.login(user, password)
            server.sendmail(from_, to, msg.as_string())
            server.quit()
            break

        except smtplib.SMTPRecipientsRefused:
            # user data not correct
            # ask for data again
            check_login = True
            # start another while loop to try again

        except smtplib.SMTPAuthenticationError:
            # user/pw not correct
            # ask for data again
            password = ""  # reset password field
            check_login = True
            # start another while loop to try again

        except:
            failed = True
            raise

    return not failed
Example #21
0
 def send_mail_to_many_recipients(self, recipients: list,
                                  email_object: MIMEMultipart) -> None:
     for mail in recipients:
         email_object.replace_header("To", mail)
         self.send_mail_to_one_recipient(mail, email_object)
Example #22
0
  msg.set_payload(encdata)
  msg.add_header("Content-Transfer-Encoding", "quoted-printable")

def encode_base64(msg):
  """Extend encoders.encode_base64 to return CRLF at end of lines"""
  original_encode_base64(msg)
  msg.set_payload(msg.get_payload().replace("\n", "\r\n"))

outer = MIMEMultipart(subtype)
for key, value in param_list:
  outer.set_param(key, value)
if boundary is not None:
  outer.set_boundary(boundary)
if replace_header_list is not None:
  for key, value in replace_header_list:
    outer.replace_header(key, value)
if header_dict is not None:  # adds headers, does not replace or set
  for key, value in header_dict.items():
    outer.add_header(key, value)
if add_header_list is not None:
  for key, value in add_header_list:
    outer.add_header(key, value)
for attachment in attachment_list:
  mime_type = attachment.get("mime_type", "application/octet-stream")
  data = attachment.get("data", "")
  encoding = attachment.get("encode")
  if encoding not in ("base64", "quoted-printable", "7or8bit", "noop", None):
    raise ValueError("unknown attachment encoding %r" % encoding)
  main_type, sub_type = mime_type.split("/")
  if encoding is None:
    if main_type == "image":
Example #23
0
def send_mail(subject=None,
              text=None,
              interval=timedelta(),
              channel=None,
              to=None,
              extra_headers=None,
              attachments=None,
              ):
    from metanasUI.system.models import Email
    from metanasUI.account.models import bsdUsers
    if not channel:
        channel = get_sw_name().lower()
    if interval > timedelta():
        channelfile = '/tmp/.msg.%s' % (channel)
        last_update = datetime.now() - interval
        try:
            last_update = datetime.fromtimestamp(os.stat(channelfile).st_mtime)
        except OSError:
            pass
        timediff = datetime.now() - last_update
        if (timediff >= interval) or (timediff < timedelta()):
            open(channelfile, 'w').close()
        else:
            return

    error = False
    errmsg = ''
    em = Email.objects.all().order_by('-id')[0]
    if not to:
        to = [bsdUsers.objects.get(bsdusr_username='******').bsdusr_email]
    if attachments:
        msg = MIMEMultipart()
        msg.preamble = MIMEText(text, _charset='utf-8')
        map(lambda attachment: msg.attach(attachment), attachments)
    else:
        msg = MIMEText(text, _charset='utf-8')
    if subject:
        msg['Subject'] = subject
    msg['From'] = em.em_fromemail
    msg['To'] = ', '.join(to)
    msg['Date'] = formatdate()

    if not extra_headers:
        extra_headers = {}
    for key, val in extra_headers.items():
        if key in msg:
            msg.replace_header(key, val)
        else:
            msg[key] = val
    msg = msg.as_string()

    try:
        if not em.em_outgoingserver or not em.em_port:
            # See NOTE below.
            raise ValueError('you must provide an outgoing mailserver and mail'
                             ' server port when sending mail')
        if em.em_security == 'ssl':
            server = smtplib.SMTP_SSL(em.em_outgoingserver, em.em_port,
                                      timeout=10)
        else:
            server = smtplib.SMTP(em.em_outgoingserver, em.em_port,
                                  timeout=10)
            if em.em_security == 'tls':
                server.starttls()
        if em.em_smtp:
            server.login(em.em_user.encode('utf-8'),
                em.em_pass.encode('utf-8'))
        # NOTE: Don't do this.
        #
        # If smtplib.SMTP* tells you to run connect() first, it's because the
        # mailserver it tried connecting to via the outgoing server argument was
        # unreachable and it tried to connect to 'localhost' and barfed. This is
        # because MetaNAS doesn't run a full MTA.
        #else:
        #    server.connect()
        server.sendmail(em.em_fromemail, to, msg)
        server.quit()
    except ValueError, ve:
        # Don't spam syslog with these messages. They should only end up in the
        # test-email pane.
        errmsg = str(ve)
        error = True
Example #24
0
def signEmailFrom(msg, pubcert, privkey, sign_detached=False):
    """ Signs the provided email message object with the from address cert (.crt) & private key (.pem) from current dir.
    :type msg: email.message.Message
    :type pubcert: str
    :type privkey: str
    :type sign_detached: bool

    Default - Returns signed message object, in format
        Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"
        Content-Disposition: attachment; filename="smime.p7m"
        Content-Transfer-Encoding: base64
    See RFC5751 section 3.4
    The reason for choosing this format, rather than multipart/signed, is that it prevents the delivery service
    from applying open/link tracking or other manipulations on the body.

    sign_detached == true:, Returns signed message object, in format
        Content-Type: multipart/signed; protocol="application/x-pkcs7-signature";

    The reason for choosing this format is for transparency on mail clients that do not understand S/MIME.
    """
    assert isinstance(msg, email.message.Message)
    assert isinstance(pubcert, str)
    assert isinstance(privkey, str)
    assert isinstance(sign_detached, bool)
    if sign_detached:
        # Need to fix up the header order and formatting here
        rawMsg = msg.as_bytes()
        sgn = create_embedded_pkcs7_signature(rawMsg, pubcert, privkey,
                                              PKCS7_DETACHED)
        # Wrap message with multipart/signed header
        msg2 = MIMEMultipart()  # this makes a new boundary
        bound = msg2.get_boundary(
        )  # keep for later as we have to rewrite the header
        msg2.set_default_type('multipart/signed')
        copyHeaders(msg, msg2)
        del msg2['Content-Language']  # These don't apply to multipart/signed
        del msg2['Content-Transfer-Encoding']
        msg2.attach(msg)
        sgn_part = MIMEApplication(sgn,
                                   'x-pkcs7-signature; name="smime.p7s"',
                                   _encoder=email.encoders.encode_base64)
        sgn_part.add_header('Content-Disposition',
                            'attachment; filename="smime.p7s"')
        msg2.attach(sgn_part)
        # Fix up Content-Type headers, as default class methods don't allow passing in protocol etc.
        msg2.replace_header(
            'Content-Type',
            'multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha1"; boundary="{}"'
            .format(bound))
        return msg2

    else:
        rawMsg = msg.as_bytes()
        sgn = create_embedded_pkcs7_signature(rawMsg, pubcert, privkey,
                                              PKCS7_NOSIGS)
        msg.set_payload(base64.encodebytes(sgn))
        hdrList = {
            'Content-Type':
            'application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"',
            'Content-Transfer-Encoding': 'base64',
            'Content-Disposition': 'attachment; filename="smime.p7m"'
        }
        copyHeaders(hdrList, msg)
        return msg
Example #25
0
def send_mail(
    subject=None, text=None, interval=None, channel=None,
    to=None, extra_headers=None, attachments=None, timeout=300,
    queue=True,
):
    from freenasUI.account.models import bsdUsers
    from freenasUI.system.models import Email

    if interval is None:
        interval = timedelta()

    if not channel:
        channel = get_sw_name().lower()
    if interval > timedelta():
        channelfile = '/tmp/.msg.%s' % (channel)
        last_update = datetime.now() - interval
        try:
            last_update = datetime.fromtimestamp(os.stat(channelfile).st_mtime)
        except OSError:
            pass
        timediff = datetime.now() - last_update
        if (timediff >= interval) or (timediff < timedelta()):
            open(channelfile, 'w').close()
        else:
            return True, 'This message was already sent in the given interval'

    error = False
    errmsg = ''
    em = Email.objects.all().order_by('-id')[0]
    if not to:
        to = [bsdUsers.objects.get(bsdusr_username='******').bsdusr_email]
        if not to[0]:
            return False, 'Email address for root is not configured'
    if attachments:
        msg = MIMEMultipart()
        msg.preamble = text
        map(lambda attachment: msg.attach(attachment), attachments)
    else:
        msg = MIMEText(text, _charset='utf-8')
    if subject:
        msg['Subject'] = subject

    msg['From'] = em.em_fromemail
    msg['To'] = ', '.join(to)
    msg['Date'] = formatdate()

    local_hostname = _get_local_hostname()

    msg['Message-ID'] = "<%s-%s.%s@%s>" % (get_sw_name().lower(), datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"), base64.urlsafe_b64encode(os.urandom(3)), local_hostname)

    if not extra_headers:
        extra_headers = {}
    for key, val in extra_headers.items():
        if key in msg:
            msg.replace_header(key, val)
        else:
            msg[key] = val

    try:
        server = _get_smtp_server(timeout, local_hostname=local_hostname)
        # NOTE: Don't do this.
        #
        # If smtplib.SMTP* tells you to run connect() first, it's because the
        # mailserver it tried connecting to via the outgoing server argument
        # was unreachable and it tried to connect to 'localhost' and barfed.
        # This is because FreeNAS doesn't run a full MTA.
        # else:
        #    server.connect()
        server.sendmail(em.em_fromemail, to, msg.as_string())
        server.quit()
    except ValueError as ve:
        # Don't spam syslog with these messages. They should only end up in the
        # test-email pane.
        errmsg = str(ve)
        error = True
    except Exception as e:
        errmsg = str(e)
        log.warn('Failed to send email: %s', errmsg, exc_info=True)
        error = True
        if queue:
            with MailQueue() as mq:
                mq.append(msg)
    except smtplib.SMTPAuthenticationError as e:
        errmsg = "%d %s" % (e.smtp_code, e.smtp_error)
        error = True
    except:
        errmsg = "Unexpected error."
        error = True
    return error, errmsg
Example #26
0
def send_email(current_skyline_app, to, cc, subject, body, log=True):
    """
    Send a plain text email.

    :param current_skyline_app: the app calling the function
    :param to: the to address
    :param cc: the cc addresses
    :param subject: the subject
    :param body: the body
    :type current_skyline_app: str
    :type to: str
    :type cc: list
    :type body: str
    :type log: boolean
    :return: sent
    :rtype: boolean

    """

    function_str = 'functions.smtp.send_email'
    if log:
        current_skyline_app_logger = current_skyline_app + 'Log'
        current_logger = logging.getLogger(current_skyline_app_logger)
    else:
        current_logger = None

    sender = settings.SMTP_OPTS['sender']
    primary_recipient = to
    cc_recipients = False
    recipients = [to]
    current_logger.info('%s - will send to primary_recipient :: %s, cc :: %s' %
                        (function_str, str(primary_recipient), str(cc)))
    if len(cc) > 0:
        recipients = recipients + cc

    if recipients:
        for i_recipient in recipients:
            if not primary_recipient:
                primary_recipient = str(i_recipient)
            if primary_recipient != i_recipient:
                if not cc_recipients:
                    cc_recipients = str(i_recipient)
                else:
                    new_cc_recipients = '%s,%s' % (str(cc_recipients),
                                                   str(i_recipient))
                    cc_recipients = str(new_cc_recipients)
    current_logger.info(
        '%s - will send to primary_recipient :: %s, cc_recipients :: %s' %
        (function_str, str(primary_recipient), str(cc_recipients)))

    send_email_alert = True
    if primary_recipient:
        try:
            msg = MIMEMultipart('mixed')
            cs_ = charset.Charset('utf-8')
            cs_.header_encoding = charset.QP
            cs_.body_encoding = charset.QP
            msg.set_charset(cs_)

            msg['Subject'] = str(subject)
            msg['From'] = sender
            msg['To'] = primary_recipient
            if cc_recipients:
                msg['Cc'] = cc_recipients

            html_body = '<h3><font color="#dd3023">Sky</font><font color="#6698FF">line</font><font color="black"></font></h3><br>'
            html_body += '%s' % str(body)
            # msg.attach(MIMEText(str(body), 'text'))
            msg.attach(MIMEText(html_body, 'html'))

            msg.replace_header('content-transfer-encoding', 'quoted-printable')

            if settings.DOCKER_FAKE_EMAIL_ALERTS:
                current_logger.info(
                    'alert_smtp - DOCKER_FAKE_EMAIL_ALERTS is set to %s, not executing SMTP command'
                    % str(settings.DOCKER_FAKE_EMAIL_ALERTS))
                send_email_alert = False
        except Exception as e:
            current_logger.error(traceback.format_exc())
            current_logger.error(
                'error :: %s :: could not send email to primary_recipient :: %s, cc_recipients :: %s - %s'
                % (str(function_str), str(primary_recipient),
                   str(cc_recipients), e))
            send_email_alert = False

    if send_email_alert:

        # @added 20220203 - Feature #4416: settings - additional SMTP_OPTS
        smtp_server = None
        try:
            smtp_server = determine_smtp_server()
        except Exception as err:
            current_logger.error(traceback.format_exc())
            current_logger.error(
                'error :: %s :: determine_smtp_server failed' % err)
        if not smtp_server:
            current_logger.error('error :: no smtp_server mail cannot be sent')
            return

        try:
            # @modified 20220203 - Feature #4416: settings - additional SMTP_OPTS
            # s = SMTP('127.0.0.1')
            if not smtp_server['ssl']:
                s = SMTP(smtp_server['host'], smtp_server['port'])
            else:
                s = SMTP_SSL(smtp_server['host'], smtp_server['port'])
            if smtp_server['user']:
                s.login(smtp_server['user'], smtp_server['password'])

            if cc_recipients:
                s.sendmail(sender, [primary_recipient, cc_recipients],
                           msg.as_string())
            else:
                s.sendmail(sender, primary_recipient, msg.as_string())
            current_logger.info(
                '%s :: email sent - %s - primary_recipient :: %s, cc_recipients :: %s'
                % (str(function_str), subject, str(primary_recipient),
                   str(cc_recipients)))
        except Exception as e:
            current_logger.error(traceback.format_exc())
            current_logger.error(
                'error :: %s :: could not send email to primary_recipient :: %s, cc_recipients :: %s - %s'
                % (str(function_str), str(primary_recipient),
                   str(cc_recipients), e))
        s.quit()
    else:
        current_logger.info(
            'alert_smtp - send_email_alert was set to %s message was not sent to primary_recipient :: %s, cc_recipients :: %s'
            % (str(send_email_alert), str(primary_recipient),
               str(cc_recipients)))

    return
Example #27
0
    def send(self, job, message, config=None):
        """
        Sends mail using configured mail settings.

        If `attachments` is true, a list compromised of the following dict is required
        via HTTP upload:
          - headers(list)
            - name(str)
            - value(str)
            - params(dict)
          - content (str)

        [
         {
          "headers": [
           {
            "name": "Content-Transfer-Encoding",
            "value": "base64"
           },
           {
            "name": "Content-Type",
            "value": "application/octet-stream",
            "params": {
             "name": "test.txt"
            }
           }
          ],
          "content": "dGVzdAo="
         }
        ]
        """

        syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_MAIL)
        interval = message.get('interval')
        if interval is None:
            interval = timedelta()
        else:
            interval = timedelta(seconds=interval)

        sw_name = self.middleware.call_sync('system.info')['version'].split(
            '-', 1)[0]

        channel = message.get('channel')
        if not channel:
            channel = sw_name.lower()
        if interval > timedelta():
            channelfile = '/tmp/.msg.%s' % (channel)
            last_update = datetime.now() - interval
            try:
                last_update = datetime.fromtimestamp(
                    os.stat(channelfile).st_mtime)
            except OSError:
                pass
            timediff = datetime.now() - last_update
            if (timediff >= interval) or (timediff < timedelta()):
                # Make sure mtime is modified
                # We could use os.utime but this is simpler!
                with open(channelfile, 'w') as f:
                    f.write('!')
            else:
                raise CallError(
                    'This message was already sent in the given interval')

        if not config:
            config = self.middleware.call_sync('mail.config')
        to = message.get('to')
        if not to:
            to = [
                self.middleware.call_sync('user.query',
                                          [('username', '=', 'root')],
                                          {'get': True})['email']
            ]
            if not to[0]:
                raise CallError('Email address for root is not configured')

        def read_json():
            f = os.fdopen(job.read_fd, 'rb')
            data = b''
            i = 0
            while True:
                read = f.read(1048576)  # 1MiB
                if read == b'':
                    break
                data += read
                i += 1
                if i > 50:
                    raise ValueError(
                        'Attachments bigger than 50MB not allowed yet')
            if data == b'':
                return None
            return json.loads(data)

        attachments = read_json() if message.get('attachments') else None
        if attachments:
            msg = MIMEMultipart()
            msg.preamble = message['text']
            for attachment in attachments:
                m = Message()
                m.set_payload(attachment['content'])
                for header in attachment.get('headers'):
                    m.add_header(header['name'], header['value'],
                                 **(header.get('params') or {}))
                msg.attach(m)
        else:
            msg = MIMEText(message['text'], _charset='utf-8')

        subject = message.get('subject')
        if subject:
            msg['Subject'] = subject

        msg['From'] = config['fromemail']
        msg['To'] = ', '.join(to)
        if message.get('cc'):
            msg['Cc'] = ', '.join(message.get('cc'))
        msg['Date'] = formatdate()

        local_hostname = socket.gethostname()

        msg['Message-ID'] = "<%s-%s.%s@%s>" % (
            sw_name.lower(), datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"),
            base64.urlsafe_b64encode(os.urandom(3)), local_hostname)

        extra_headers = message.get('extra_headers') or {}
        for key, val in list(extra_headers.items()):
            if key in msg:
                msg.replace_header(key, val)
            else:
                msg[key] = val

        try:
            server = self._get_smtp_server(config,
                                           message['timeout'],
                                           local_hostname=local_hostname)
            # NOTE: Don't do this.
            #
            # If smtplib.SMTP* tells you to run connect() first, it's because the
            # mailserver it tried connecting to via the outgoing server argument
            # was unreachable and it tried to connect to 'localhost' and barfed.
            # This is because FreeNAS doesn't run a full MTA.
            # else:
            #    server.connect()
            syslog.syslog("sending mail to " + ','.join(to) +
                          msg.as_string()[0:140])
            server.sendmail(config['fromemail'], to, msg.as_string())
            server.quit()
        except ValueError as ve:
            # Don't spam syslog with these messages. They should only end up in the
            # test-email pane.
            raise CallError(str(ve))
        except smtplib.SMTPAuthenticationError as e:
            raise CallError(
                f'Authentication error ({e.smtp_code}): {e.smtp_error}',
                errno.EAUTH)
        except Exception as e:
            self.logger.warn('Failed to send email: %s', str(e), exc_info=True)
            if message['queue']:
                with MailQueue() as mq:
                    mq.append(msg)
            raise CallError(f'Failed to send email: {e}')
        return True
Example #28
0
def send_mail(
    subject=None,
    text=None,
    interval=timedelta(),
    channel=None,
    to=None,
    extra_headers=None,
    attachments=None,
):
    from freenasUI.system.models import Email
    from freenasUI.account.models import bsdUsers
    if not channel:
        channel = get_sw_name().lower()
    if interval > timedelta():
        channelfile = '/tmp/.msg.%s' % (channel)
        last_update = datetime.now() - interval
        try:
            last_update = datetime.fromtimestamp(os.stat(channelfile).st_mtime)
        except OSError:
            pass
        timediff = datetime.now() - last_update
        if (timediff >= interval) or (timediff < timedelta()):
            open(channelfile, 'w').close()
        else:
            return True, 'This message was already sent in the given interval'

    error = False
    errmsg = ''
    em = Email.objects.all().order_by('-id')[0]
    if not to:
        to = [bsdUsers.objects.get(bsdusr_username='******').bsdusr_email]
    if attachments:
        msg = MIMEMultipart()
        msg.preamble = MIMEText(text, _charset='utf-8')
        map(lambda attachment: msg.attach(attachment), attachments)
    else:
        msg = MIMEText(text, _charset='utf-8')
    if subject:
        msg['Subject'] = subject
    msg['From'] = em.em_fromemail
    msg['To'] = ', '.join(to)
    msg['Date'] = formatdate()

    if not extra_headers:
        extra_headers = {}
    for key, val in extra_headers.items():
        if key in msg:
            msg.replace_header(key, val)
        else:
            msg[key] = val
    msg = msg.as_string()

    try:
        if not em.em_outgoingserver or not em.em_port:
            # See NOTE below.
            raise ValueError('you must provide an outgoing mailserver and mail'
                             ' server port when sending mail')
        if em.em_security == 'ssl':
            server = smtplib.SMTP_SSL(em.em_outgoingserver,
                                      em.em_port,
                                      timeout=10)
        else:
            server = smtplib.SMTP(em.em_outgoingserver, em.em_port, timeout=10)
            if em.em_security == 'tls':
                server.starttls()
        if em.em_smtp:
            server.login(em.em_user.encode('utf-8'),
                         em.em_pass.encode('utf-8'))
        # NOTE: Don't do this.
        #
        # If smtplib.SMTP* tells you to run connect() first, it's because the
        # mailserver it tried connecting to via the outgoing server argument
        # was unreachable and it tried to connect to 'localhost' and barfed.
        # This is because FreeNAS doesn't run a full MTA.
        #else:
        #    server.connect()
        server.sendmail(em.em_fromemail, to, msg)
        server.quit()
    except ValueError, ve:
        # Don't spam syslog with these messages. They should only end up in the
        # test-email pane.
        errmsg = str(ve)
        error = True
Example #29
0
def _mail_recipient(recipient_name, recipient_email,
                    sender_name, sender_url, subject,
                    body, body_html=None, headers=None):

    if not headers:
        headers = {}

    mail_from = config.get('smtp.mail_from')
    reply_to = config.get('smtp.reply_to')
    if body_html:
        # multipart
        msg = MIMEMultipart('alternative')
        part1 = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')
        part2 = MIMEText(body_html.encode('utf-8'), 'html', 'utf-8')
        msg.attach(part1)
        msg.attach(part2)
    else:
        # just plain text
        msg = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')
    for k, v in headers.items():
        if k in msg.keys():
            msg.replace_header(k, v)
        else:
            msg.add_header(k, v)
    subject = Header(subject.encode('utf-8'), 'utf-8')
    msg['Subject'] = subject
    msg['From'] = _("%s <%s>") % (sender_name, mail_from)
    msg['To'] = u"%s <%s>" % (recipient_name, recipient_email)
    msg['Date'] = utils.formatdate(time())
    msg['X-Mailer'] = "CKAN %s" % ckan.__version__
    if reply_to and reply_to != '':
        msg['Reply-to'] = reply_to

    # Send the email using Python's smtplib.
    if 'smtp.test_server' in config:
        # If 'smtp.test_server' is configured we assume we're running tests,
        # and don't use the smtp.server, starttls, user, password etc. options.
        smtp_server = config['smtp.test_server']
        smtp_starttls = False
        smtp_user = None
        smtp_password = None
    else:
        smtp_server = config.get('smtp.server', 'localhost')
        smtp_starttls = ckan.common.asbool(
            config.get('smtp.starttls'))
        smtp_user = config.get('smtp.user')
        smtp_password = config.get('smtp.password')

    try:
        smtp_connection = smtplib.SMTP(smtp_server)
    except (socket.error, smtplib.SMTPConnectError) as e:
        log.exception(e)
        raise MailerException('SMTP server could not be connected to: "%s" %s'
                              % (smtp_server, e))

    try:
        # Identify ourselves and prompt the server for supported features.
        smtp_connection.ehlo()

        # If 'smtp.starttls' is on in CKAN config, try to put the SMTP
        # connection into TLS mode.
        if smtp_starttls:
            if smtp_connection.has_extn('STARTTLS'):
                smtp_connection.starttls()
                # Re-identify ourselves over TLS connection.
                smtp_connection.ehlo()
            else:
                raise MailerException("SMTP server does not support STARTTLS")

        # If 'smtp.user' is in CKAN config, try to login to SMTP server.
        if smtp_user:
            assert smtp_password, ("If smtp.user is configured then "
                                   "smtp.password must be configured as well.")
            smtp_connection.login(smtp_user, smtp_password)

        smtp_connection.sendmail(mail_from, [recipient_email], msg.as_string())
        log.info("Sent email to {0}".format(recipient_email))

    except smtplib.SMTPException as e:
        msg = '%r' % e
        log.exception(msg)
        raise MailerException(msg)
    finally:
        smtp_connection.quit()
Example #30
0
def send_solutions(con, selected, name=None, email=None):
    """
    Send out the selected corrected submissions.

    Args:
        selected (list of str): List of corrected submission file to be sent
            out.
        con (smtplib.SMTP): Active SMTP connection to the ETH mail server.
        name (str): Space-separate name and surname of the sender. This will be
            used in the email header as well as in the signature in the body of
            the email. Defaults to `None`, in which case the user is prompted
            for these.
        email (str): Sender's email address. Defaults to `None` in which case
            the user is prompted for it.
    """
    pairs = [PC.match(f).group(1, 0) for f in selected]
    addresses = [p[0] for p in pairs]
    sheet_number = PC.match(selected[0]).group(2)

    # Ask for confirmation of the submissions to be sent
    _show_confirmation_prompt(con, addresses)

    # Ask for the name to be included in the email signature
    if name is None:
        name = ""
        while name == "":
            name = input("Your first name: ")
        surname = ""
        while surname == "":
            surname = input("Your surname: ")
        name = f"{name} {surname}"

    # Send out the corrected submissions
    checked = False  # indicates whether the sender's email address was checked
    unsuccessful = addresses.copy()
    for address, sf in pairs:

        # Construct the base of the message
        msg = MIMEMultipart()
        msg["To"] = address
        msg["Date"] = formatdate(localtime=True)
        msg["Subject"] = f"Corrected submission {sheet_number}"

        # Attach the submission file
        with open(sf, "rb") as f:
            attachment = MIMEApplication(f.read(), Name=sf)
        attachment["Content-Disposition"] = f"attachment, {sf}"
        msg.attach(attachment)

        # Attach the text
        msg.attach(
            MIMEText(
                "Hi,\n\nThe correction of your submission for exercise sheet "
                f"{sheet_number} is attached.\n\nBest "
                f"regards,\n{name}"))

        # Fill out the sender's email address and send the message
        if not checked:
            while not checked:  # check sender's address with the first email
                if email is None:
                    source_address = input("Your ETH email address: ")
                else:
                    source_address = email
                addr = formataddr((str(Header(name)), source_address))
                if "From" in msg:
                    msg.replace_header("From", addr)
                else:
                    msg["From"] = addr

                try:
                    con.send_message(msg)
                    checked = True  # mark sender's address as checked
                    unsuccessful.remove(address)
                except (smtplib.SMTPSenderRefused, smtplib.SMTPDataError):
                    if email is None:
                        print("\nInvalid. Try again.")
                    else:
                        print("Invalid email in 'submissions' config file.")
                        con.quit()
                        sys.exit()
                except smtplib.SMTPServerDisconnected:
                    print("\nFailed. Try re-running the script.")
                    sys.exit()
                except smtplib.SMTPException:
                    unsuccessful.remove(address)
                    break

        else:
            msg["From"] = addr
            try:
                con.send_message(msg)
                unsuccessful.remove(address)
            except smtplib.SMTPException:
                unsuccessful.remove(address)
                break

    if not unsuccessful:
        print("\nAll correction submissions were sent out successfully!")
    else:
        print("\nFailed to send out corrected submissions to:\n")
        print("\n".join(unsuccessful))
Tk().withdraw() 
directory = askdirectory() # show an "Open" dialog box and return the path to the selected file

s = smtplib.SMTP('smtp.gmail.com:587')
s.starttls()
s.login(username,password)

# Loop through all the email addresses and send out their respective files
emailsfull = [line.rstrip('\n') for line in open(askopenfilename())] # emails and timestamps
emails = emailsfull[0::2]
for address in emails:
	for folder in os.listdir(directory):
		if address == folder:
			msg.set_payload(None)
			subdirectory = os.path.join(directory, folder)
			msg.replace_header('To', address)
			
			for filename in os.listdir(subdirectory):
				if filename.endswith('.pdf'):
					path = os.path.join(subdirectory, filename)
					fp = open(path, 'rb')
					msg.attach(MIMEText(body, 'plain'))

					pdf = MIMEBase('application', 'pdf')
					pdf.set_payload(fp.read())
					encoders.encode_base64(pdf)
					pdf.add_header('Content-Disposition', 'attachment', filename=filename)				
					msg.attach(pdf)
					
			s.sendmail(from_addr, msg['To'], msg.as_string())
			
Example #32
0
class Email():
    """A class to help with the making of emails."""
    def __init__(self, emailFrom):
        from email.mime.multipart import MIMEMultipart
        from email.mime.text import MIMEText
        from email.mime.base import MIMEBase
        from email import encoders
        self.encoders = encoders
        self.baseMime = MIMEBase
        self.MimeEmail = MIMEMultipart()
        self.textMime = MIMEText
        self.MimeEmail['From'] = emailFrom

    def add_attachment(self, attachment, filename):
        """Add any attachment. The filename for the email can be different than the local filename."""
        part = self.baseMime('application', 'octet-stream')
        part.set_payload((attachment).read())
        self.encoders.encode_base64(part)
        part.add_header('Content-Disposition',
                        "attachment; filename= %s" % filename)
        self.MimeEmail.attach(part)

    def set_subject(self, subject):
        """Set subject of email."""
        self.set_attr("Subject", subject)

    def set_to(self, to):
        """Set who the email is to."""
        self.set_attr("To", to)

    def _load_attachment(self, filepath):
        """Load attatchment for adding."""
        return open(filepath, "rb")

    def add_attachment_from_file(self, filepath):
        """straight add from filename and path to load file"""
        from os.path import basename
        self.add_attachment(self._load_attachment(filepath),
                            basename(filepath))

    def mime_behind(self):
        """returns the hidden MIMEMultipart"""
        return self.MimeEmail

    def as_string(self):
        """takes the hidden MIMEMultipart and returns it as string"""
        return self.MimeEmail.as_string()

    def set_body(self, body):
        """Adds body to email."""
        self.MimeEmail.attach(self.textMime(body, 'plain'))

    def get_attr(self, attribute):
        """gets any attribute from the MIMEMultipart"""
        return self.MimeEmail[attribute]

    def set_attr(self, attribute, value):
        """sets any attribute from the MIMEMultipart"""
        if attribute in self.MimeEmail:
            self.MimeEmail.replace_header(attribute, value)
        else:
            self.MimeEmail[attribute] = value

    def add_myself_to_email(self):
        """Finds this code on your computer and attatches this code to your email!"""
        self.add_attachment_from_file(__file__)
Example #33
0
    def send(self, mailmessage, mail=None):

        if mail is None:
            mail = ConfigNode("mail", self.configstore).__getstate__()
        if not mail.get("server") or not mail.get("port"):
            raise RpcException(errno.EINVAL, "You must provide an outgoing server and port when sending mail")

        to = mailmessage.get("to")
        attachments = mailmessage.get("attachments")
        subject = mailmessage.get("subject")
        extra_headers = mailmessage.get("extra_headers")

        if not to:
            to = self.dispatcher.call_sync("user.query", [("username", "=", "root")], {"single": True})
            if to and to.get("email"):
                to = [to["email"]]

        if attachments:
            msg = MIMEMultipart()
            msg.preamble = mailmessage["message"]
            list(map(lambda attachment: msg.attach(attachment), attachments))
        else:
            msg = MIMEText(mailmessage["message"], _charset="utf-8")
        if subject:
            msg["Subject"] = subject

        msg["From"] = mailmessage["from"] if mailmessage.get("from") else mail["from"]
        msg["To"] = ", ".join(to)
        msg["Date"] = formatdate()

        local_hostname = socket.gethostname()
        version = self.dispatcher.call_sync("system.info.version").split("-")[0].lower()

        msg["Message-ID"] = "<{0}-{1}.{2}@{3}>".format(
            version,
            datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"),
            base64.urlsafe_b64encode(os.urandom(3)),
            local_hostname,
        )

        if not extra_headers:
            extra_headers = {}
        for key, val in list(extra_headers.items()):
            if key in msg:
                msg.replace_header(key, val)
            else:
                msg[key] = val
        msg = msg.as_string()

        try:
            if mail["encryption"] == "SSL":
                klass = smtplib.SMTP_SSL
            else:
                klass = smtplib.SMTP
            server = klass(mail["server"], mail["port"], timeout=300, local_hostname=local_hostname)
            if mail["encryption"] == "TLS":
                server.starttls()

            if mail["auth"]:
                server.login(mail["user"], mail["pass"])
            server.sendmail(mail["from"], to, msg)
            server.quit()
        except smtplib.SMTPAuthenticationError as e:
            raise RpcException(errno.EACCES, "Authentication error: {0} {1}".format(e.smtp_code, e.smtp_error))
        except Exception as e:
            logger.error("Failed to send email: {0}".format(str(e)), exc_info=True)
            raise RpcException(errno.EFAULT, "Email send error: {0}".format(str(e)))
        except:
            raise RpcException(errno.EFAULT, "Unexpected error")
Example #34
0
def send_mail(keywords_map,
              server,
              send_from,
              send_to,
              subject,
              files=None,
              multiple=False):
    """
    Send the attractor of the day mail
    """
    if not isinstance(send_to, list):
        logging.warning("Badly formed recipient list. Not sending any mail.")
        return

    # Root message
    msg = MIMEMultipart("related")
    msg["From"] = send_from
    msg["Date"] = formatdate(localtime=True)
    msg["Subject"] = subject

    # Now create a multipart message below the root message
    # and attach both plain text and HTML version of the message to it.
    msg_alternative = MIMEMultipart("alternative")
    msg.attach(msg_alternative)
    text = generate_mail_text(keywords_map)
    html = generate_mail_html(keywords_map)
    msg_alternative.attach(MIMEText(text, "plain"))
    msg_alternative.attach(MIMEText(html, "html"))

    # Finally attach the image to the root message... this loop is overkill
    # and unnecessary, but will do for our case !
    for file_name in files or []:
        with open(file_name, "rb") as fil:
            part = MIMEImage(fil.read(), "png")
            # part['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(f)
            part.add_header("Content-ID", "<atImg>")
            msg.attach(part)

    try:
        smtp = smtplib.SMTP(server)
    except smtplib.SMTPConnectError:
        logging.warning(
            "Unable to connect to SMTP server. Not sending any mail.")
        return

    send_to = list(set(send_to))  # Removes possible duplicates in to list
    try:
        refused = dict()
        if multiple:  # send one message per recipient
            for dest in send_to:
                if "To" in msg:
                    msg.replace_header("To", dest)
                else:
                    msg["To"] = dest
                refused_recipient = smtp.sendmail(send_from, dest,
                                                  msg.as_string())
                refused.update(refused_recipient)
        else:  # send only one message with everyone in To-List
            msg["To"] = COMMASPACE.join(send_to)
            refused = smtp.sendmail(send_from, send_to, msg.as_string())
    except smtplib.SMTPException as exception:
        logging.warning(sys.stderr, "Error sending mail: %s.", repr(exception))
    else:
        if refused:
            logging.warning(sys.stderr,
                            "Some mails could not be delivered: %s.",
                            str(refused))

    smtp.quit()
Example #35
0
def send_mail(
    subject=None,
    text=None,
    interval=None,
    channel=None,
    to=None,
    extra_headers=None,
    attachments=None,
    timeout=300,
    queue=True,
):
    from freenasUI.account.models import bsdUsers
    from freenasUI.system.models import Email

    if interval is None:
        interval = timedelta()

    if not channel:
        channel = get_sw_name().lower()
    if interval > timedelta():
        channelfile = '/tmp/.msg.%s' % (channel)
        last_update = datetime.now() - interval
        try:
            last_update = datetime.fromtimestamp(os.stat(channelfile).st_mtime)
        except OSError:
            pass
        timediff = datetime.now() - last_update
        if (timediff >= interval) or (timediff < timedelta()):
            open(channelfile, 'w').close()
        else:
            return True, 'This message was already sent in the given interval'

    error = False
    errmsg = ''
    em = Email.objects.all().order_by('-id')[0]
    if not to:
        to = [bsdUsers.objects.get(bsdusr_username='******').bsdusr_email]
        if not to[0]:
            return False, 'Email address for root is not configured'
    if attachments:
        msg = MIMEMultipart()
        msg.preamble = text
        map(lambda attachment: msg.attach(attachment), attachments)
    else:
        msg = MIMEText(text, _charset='utf-8')
    if subject:
        msg['Subject'] = subject

    msg['From'] = em.em_fromemail
    msg['To'] = ', '.join(to)
    msg['Date'] = formatdate()

    local_hostname = _get_local_hostname()

    msg['Message-ID'] = "<%s-%s.%s@%s>" % (
        get_sw_name().lower(), datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"),
        base64.urlsafe_b64encode(os.urandom(3)), local_hostname)

    if not extra_headers:
        extra_headers = {}
    for key, val in extra_headers.items():
        if key in msg:
            msg.replace_header(key, val)
        else:
            msg[key] = val

    try:
        server = _get_smtp_server(timeout, local_hostname=local_hostname)
        # NOTE: Don't do this.
        #
        # If smtplib.SMTP* tells you to run connect() first, it's because the
        # mailserver it tried connecting to via the outgoing server argument
        # was unreachable and it tried to connect to 'localhost' and barfed.
        # This is because FreeNAS doesn't run a full MTA.
        # else:
        #    server.connect()
        server.sendmail(em.em_fromemail, to, msg.as_string())
        server.quit()
    except ValueError as ve:
        # Don't spam syslog with these messages. They should only end up in the
        # test-email pane.
        errmsg = str(ve)
        error = True
    except Exception as e:
        errmsg = str(e)
        log.warn('Failed to send email: %s', errmsg, exc_info=True)
        error = True
        if queue:
            with MailQueue() as mq:
                mq.append(msg)
    except smtplib.SMTPAuthenticationError as e:
        errmsg = "%d %s" % (e.smtp_code, e.smtp_error)
        error = True
    except:
        errmsg = "Unexpected error."
        error = True
    return error, errmsg
Example #36
0
class Mdn(object):
    """Class for handling AS2 MDNs. Includes functions for both
    parsing and building them.
    """

    def __init__(self, mdn_mode=None, digest_alg=None, mdn_url=None):
        self.message_id = None
        self.orig_message_id = None
        self.payload = None
        self.mdn_mode = mdn_mode
        self.digest_alg = digest_alg
        self.mdn_url = mdn_url

    @property
    def content(self):
        """Function returns the body of the mdn message as a byte string"""

        if self.payload:
            message_bytes = mime_to_bytes(
                self.payload, 0).replace(b'\n', b'\r\n')
            boundary = b'--' + self.payload.get_boundary().encode('utf-8')
            temp = message_bytes.split(boundary)
            temp.pop(0)
            return boundary + boundary.join(temp)
        else:
            return ''

    @property
    def headers(self):
        if self.payload:
            return dict(self.payload.items())
        else:
            return {}

    @property
    def headers_str(self):
        message_header = ''
        if self.payload:
            for k, v in self.headers.items():
                message_header += '{}: {}\r\n'.format(k, v)
        return message_header.encode('utf-8')

    def build(self, message, status, detailed_status=None):
        """Function builds and signs an AS2 MDN message.

        :param message: The received AS2 message for which this is an MDN.

        :param status: The status of processing of the received AS2 message.

        :param detailed_status:
            The optional detailed status of processing of the received AS2
            message. Used to give additional error info (default "None")

        """

        # Generate message id using UUID 1 as it uses both hostname and time
        self.message_id = email_utils.make_msgid().lstrip('<').rstrip('>')
        self.orig_message_id = message.message_id

        # Set up the message headers
        mdn_headers = {
            'AS2-Version': AS2_VERSION,
            'ediint-features': EDIINT_FEATURES,
            'Message-ID': '<{}>'.format(self.message_id),
            'AS2-From': quote_as2name(message.headers.get('as2-to')),
            'AS2-To': quote_as2name(message.headers.get('as2-from')),
            'Date': email_utils.formatdate(localtime=True),
            'user-agent': 'pyAS2 Open Source AS2 Software'
        }

        # Set the confirmation text message here
        confirmation_text = MDN_CONFIRM_TEXT

        # overwrite with organization specific message
        if message.receiver and message.receiver.mdn_confirm_text:
            confirmation_text = message.receiver.mdn_confirm_text

        # overwrite with partner specific message
        if message.sender and message.sender.mdn_confirm_text:
            confirmation_text = message.sender.mdn_confirm_text

        if status != 'processed':
            confirmation_text = MDN_FAILED_TEXT

        self.payload = MIMEMultipart(
            'report', report_type='disposition-notification')

        # Create and attach the MDN Text Message
        mdn_text = email_message.Message()
        mdn_text.set_payload('%s\n' % confirmation_text)
        mdn_text.set_type('text/plain')
        del mdn_text['MIME-Version']
        encoders.encode_7or8bit(mdn_text)
        self.payload.attach(mdn_text)

        # Create and attache the MDN Report Message
        mdn_base = email_message.Message()
        mdn_base.set_type('message/disposition-notification')
        mdn_report = 'Reporting-UA: pyAS2 Open Source AS2 Software\n'
        mdn_report += 'Original-Recipient: rfc822; {}\n'.format(
            message.headers.get('as2-to'))
        mdn_report += 'Final-Recipient: rfc822; {}\n'.format(
            message.headers.get('as2-to'))
        mdn_report += 'Original-Message-ID: <{}>\n'.format(message.message_id)
        mdn_report += 'Disposition: automatic-action/' \
                      'MDN-sent-automatically; {}'.format(status)
        if detailed_status:
            mdn_report += ': {}'.format(detailed_status)
        mdn_report += '\n'
        if message.mic:
            mdn_report += 'Received-content-MIC: {}, {}\n'.format(
                message.mic.decode(), message.digest_alg)
        mdn_base.set_payload(mdn_report)
        del mdn_base['MIME-Version']
        encoders.encode_7or8bit(mdn_base)
        self.payload.attach(mdn_base)

        # logger.debug('MDN for message %s created:\n%s' % (
        #     message.message_id, mdn_base.as_string()))

        # Sign the MDN if it is requested by the sender
        if message.headers.get('disposition-notification-options') and \
                message.receiver and message.receiver.sign_key:
            self.digest_alg = \
                message.headers['disposition-notification-options'].split(
                    ';')[-1].split(',')[-1].strip().replace('-', '')
            signed_mdn = MIMEMultipart(
                'signed', protocol="application/pkcs7-signature")
            del signed_mdn['MIME-Version']
            signed_mdn.attach(self.payload)

            # Create the signature mime message
            signature = email_message.Message()
            signature.set_type('application/pkcs7-signature')
            signature.set_param('name', 'smime.p7s')
            signature.set_param('smime-type', 'signed-data')
            signature.add_header(
                'Content-Disposition', 'attachment', filename='smime.p7s')
            del signature['MIME-Version']
            signature.set_payload(sign_message(
                canonicalize(self.payload),
                self.digest_alg,
                message.receiver.sign_key
            ))
            encoders.encode_base64(signature)
            # logger.debug(
            #     'Signature for MDN created:\n%s' % signature.as_string())
            signed_mdn.set_param('micalg', self.digest_alg)
            signed_mdn.attach(signature)

            self.payload = signed_mdn

        # Update the headers of the final payload and set message boundary
        for k, v in mdn_headers.items():
            if self.payload.get(k):
                self.payload.replace_header(k, v)
            else:
                self.payload.add_header(k, v)
        if self.payload.is_multipart():
            self.payload.set_boundary(make_mime_boundary())

    def parse(self, raw_content, find_message_cb):
        """Function parses the RAW AS2 MDN, verifies it and extracts the
        processing status of the orginal AS2 message.

        :param raw_content:
            A byte string of the received HTTP headers followed by the body.

        :param find_message_cb:
            A callback the must returns the original Message Object. The
            original message-id and original recipient AS2 ID are passed
            as arguments to it.

        :returns:
            A two element tuple containing (status, detailed_status). The
            status is a string indicating the status of the transaction. The
            optional detailed_status gives additional information about the
            processing status.
        """

        status, detailed_status = None, None
        self.payload = parse_mime(raw_content)
        self.orig_message_id, orig_recipient = self.detect_mdn()

        # Call the find message callback which should return a Message instance
        orig_message = find_message_cb(self.orig_message_id, orig_recipient)

        # Extract the headers and save it
        mdn_headers = {}
        for k, v in self.payload.items():
            k = k.lower()
            if k == 'message-id':
                self.message_id = v.lstrip('<').rstrip('>')
            mdn_headers[k] = v

        if orig_message.receiver.mdn_digest_alg \
                and self.payload.get_content_type() != 'multipart/signed':
            status = 'failed/Failure'
            detailed_status = 'Expected signed MDN but unsigned MDN returned'
            return status, detailed_status

        if self.payload.get_content_type() == 'multipart/signed':
            signature = None
            message_boundary = (
                    '--' + self.payload.get_boundary()).encode('utf-8')
            for part in self.payload.walk():
                if part.get_content_type() == 'application/pkcs7-signature':
                    signature = part.get_payload(decode=True)
                elif part.get_content_type() == 'multipart/report':
                    self.payload = part

            # Verify the message, first using raw message and if it fails
            # then convert to canonical form and try again
            mic_content = extract_first_part(raw_content, message_boundary)
            verify_cert = orig_message.receiver.load_verify_cert()
            try:
                self.digest_alg = verify_message(
                    mic_content, signature, verify_cert)
            except IntegrityError:
                mic_content = canonicalize(self.payload)
                self.digest_alg = verify_message(
                    mic_content, signature, verify_cert)

        for part in self.payload.walk():
            if part.get_content_type() == 'message/disposition-notification':
                # logger.debug('Found MDN report for message %s:\n%s' % (
                #     orig_message.message_id, part.as_string()))

                mdn = part.get_payload()[-1]
                mdn_status = mdn['Disposition'].split(
                    ';').pop().strip().split(':')
                status = mdn_status[0]
                if status == 'processed':
                    mdn_mic = mdn.get('Received-Content-MIC', '').split(',')[0]

                    # TODO: Check MIC for all cases
                    if mdn_mic and orig_message.mic \
                            and mdn_mic != orig_message.mic.decode():
                        status = 'processed/warning'
                        detailed_status = 'Message Integrity check failed.'
                else:
                    detailed_status = ' '.join(mdn_status[1:]).strip()

        return status, detailed_status

    def detect_mdn(self):
        """ Function checks if the received raw message is an AS2 MDN or not.

        :raises MDNNotFound: If the received payload is not an MDN then this
        exception is raised.

        :return:
            A two element tuple containing (message_id, message_recipient). The
            message_id is the original AS2 message id and the message_recipient
            is the original AS2 message recipient.
        """
        mdn_message = None
        if self.payload.get_content_type() == 'multipart/report':
            mdn_message = self.payload
        elif self.payload.get_content_type() == 'multipart/signed':
            for part in self.payload.walk():
                if part.get_content_type() == 'multipart/report':
                    mdn_message = self.payload

        if not mdn_message:
            raise MDNNotFound('No MDN found in the received message')

        message_id, message_recipient = None, None
        for part in mdn_message.walk():
            if part.get_content_type() == 'message/disposition-notification':
                mdn = part.get_payload()[0]
                message_id = mdn.get('Original-Message-ID').strip('<>')
                message_recipient = mdn.get(
                    'Original-Recipient').split(';')[1].strip()
        return message_id, message_recipient
Example #37
0
def send_mail(subject=None,
              text=None,
              interval=timedelta(),
              channel=None,
              to=None,
              extra_headers=None,
              attachments=None,
              timeout=300,
              ):
    from freenasUI.account.models import bsdUsers
    from freenasUI.network.models import GlobalConfiguration
    from freenasUI.system.models import Email
    if not channel:
        channel = get_sw_name().lower()
    if interval > timedelta():
        channelfile = '/tmp/.msg.%s' % (channel)
        last_update = datetime.now() - interval
        try:
            last_update = datetime.fromtimestamp(os.stat(channelfile).st_mtime)
        except OSError:
            pass
        timediff = datetime.now() - last_update
        if (timediff >= interval) or (timediff < timedelta()):
            open(channelfile, 'w').close()
        else:
            return True, 'This message was already sent in the given interval'

    error = False
    errmsg = ''
    em = Email.objects.all().order_by('-id')[0]
    if not to:
        to = [bsdUsers.objects.get(bsdusr_username='******').bsdusr_email]
    if attachments:
        msg = MIMEMultipart()
        msg.preamble = text
        map(lambda attachment: msg.attach(attachment), attachments)
    else:
        msg = MIMEText(text, _charset='utf-8')
    if subject:
        msg['Subject'] = subject

    msg['From'] = em.em_fromemail
    msg['To'] = ', '.join(to)
    msg['Date'] = formatdate()

    try:
        gc = GlobalConfiguration.objects.order_by('-id')[0]
        local_hostname = "%s.%s" % (gc.get_hostname(), gc.gc_domain)
    except:
        local_hostname = "%s.local" % get_sw_name()

    msg['Message-ID'] = "<%s-%s.%s@%s>" % (get_sw_name().lower(), datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"), base64.urlsafe_b64encode(os.urandom(3)), local_hostname)

    if not extra_headers:
        extra_headers = {}
    for key, val in extra_headers.items():
        if key in msg:
            msg.replace_header(key, val)
        else:
            msg[key] = val
    msg = msg.as_string()

    try:
        if not em.em_outgoingserver or not em.em_port:
            # See NOTE below.
            raise ValueError('you must provide an outgoing mailserver and mail'
                             ' server port when sending mail')
        if em.em_security == 'ssl':
            server = smtplib.SMTP_SSL(
                em.em_outgoingserver,
                em.em_port,
                timeout=timeout,
                local_hostname=local_hostname)
        else:
            server = smtplib.SMTP(
                em.em_outgoingserver,
                em.em_port,
                timeout=timeout,
                local_hostname=local_hostname)
            if em.em_security == 'tls':
                server.starttls()
        if em.em_smtp:
            server.login(
                em.em_user.encode('utf-8'),
                em.em_pass.encode('utf-8'))
        # NOTE: Don't do this.
        #
        # If smtplib.SMTP* tells you to run connect() first, it's because the
        # mailserver it tried connecting to via the outgoing server argument
        # was unreachable and it tried to connect to 'localhost' and barfed.
        # This is because FreeNAS doesn't run a full MTA.
        #else:
        #    server.connect()
        server.sendmail(em.em_fromemail, to, msg)
        server.quit()
    except ValueError as ve:
        # Don't spam syslog with these messages. They should only end up in the
        # test-email pane.
        errmsg = str(ve)
        error = True
    except Exception as e:
        syslog.openlog(channel, syslog.LOG_PID,
                       facility=syslog.LOG_MAIL)
        try:
            for line in traceback.format_exc().splitlines():
                syslog.syslog(syslog.LOG_ERR, line)
        finally:
            syslog.closelog()
        errmsg = str(e)
        error = True
    except smtplib.SMTPAuthenticationError as e:
        errmsg = "%d %s" % (e.smtp_code, e.smtp_error)
        error = True
    except:
        errmsg = "Unexpected error."
        error = True
    return error, errmsg
Example #38
0
PASSWORD = "******"
SENDER = "*****@*****.**"

MSG = MIMEMultipart()
MSG["From"] = Header(u'商艾华 <%s>' % SENDER)
MSG["To"] = Header("", "utf-8")
MSG["Subject"] = Header(SUBJECT, "utf-8")
MSG.attach(MIMEText(CONTENT, "html", "utf-8"))
ATTACHMENTS = os.listdir(".//attachments")
for attachment in ATTACHMENTS:
    basename = os.path.basename(attachment)
    att = MIMEApplication(open(".//attachments/" + attachment, 'rb').read())
    att.add_header('Content-Disposition', 'attachment', \
                    filename=('gbk', '', basename)) # 解决附件名中文乱码问题
    MSG.attach(att)

MSMTP = smtplib.SMTP(HOST, 25)
MSMTP.starttls()
MSMTP.login(ACCOUNT, PASSWORD)
for receiver in RECEIVERS:
    print("%s [%d/%d] Send to: %s" %
          (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
           RECEIVERS.index(receiver)+1, len(RECEIVERS), receiver[0]))
    MSG.replace_header("To", "<%s>" % receiver[0])
    try:
        MSMTP.sendmail(SENDER, receiver, MSG.as_string())
    except smtplib.SMTPException as exception:
        print("  Error: %s" % str(exception))
    # time.sleep(1)
raw_input("--------------------\nDone.\nPress Enter to continue...")
Example #39
0
class MimeMixin(MimeBaseMixin):
    def new_message(self):
        self.message = MIMEMultipart('alternative')

    def _add_text(self, text, subtype):
        self.message.attach(MIMEText(text, subtype))

    def add_attachment(self, file_path, filename=None):
        content_type, encoding = mimetypes.guess_type(file_path)

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

        with open(file_path, 'r' if main_type == 'text' else 'rb') as fp:
            content = fp.read()

        if main_type == 'text':
            part = MIMEText(content, _subtype=sub_type)
        elif main_type == 'image':
            part = MIMEImage(content, _subtype=sub_type)
        elif main_type == 'audio':
            part = MIMEAudio(content, _subtype=sub_type)
        else:
            part = MIMEApplication(content)

        part.add_header('Content-Disposition',
                        'attachment',
                        filename=filename or os.path.basename(file_path))
        self.message.attach(part)

    def add_image(self, img_path):
        with open(img_path, 'rb') as fp:
            content_type, _ = mimetypes.guess_type(img_path)
            if content_type:
                _, sub_type = content_type.split('/', 1)
                img = MIMEImage(fp.read(), sub_type)
            else:
                img = MIMEImage(fp.read())
        img_uuid = super(MimeMixin, self).add_image(img_path)
        img.add_header('Content-ID', f"<{img_uuid}>")
        img.add_header('Content-Disposition',
                       'attachment',
                       filename=os.path.basename(img_path))

        self.message.attach(img)
        return img_uuid

    def _set_header(self,
                    to=None,
                    subject=None,
                    cc=None,
                    bcc=None,
                    sender=None,
                    **kwargs):
        self.__add_header("From", sender or self.sender_email)
        self.__add_header('Subject', subject)
        self.__add_header('cc', ','.join(self._recipient_to_list(cc)))
        self.__add_header("To", ','.join(self._recipient_to_list(to)))

    def __add_header(self, key, value):
        if value:
            try:
                self.message.replace_header(key, value)
            except KeyError:
                self.message.add_header(key, value)

    def _get_converted_message(self):
        return self.message.as_string()
Example #40
0
def build_message(
        message, subject, sender, recipients, *, encoding='UTF-8',
        cc=None, attachments=None, reply_to=None,
        signature_uid=None, signature_passphrase=None, headers=None):

    # create the textual message
    if isinstance(message, Html):
        htmlmsg = MIMEText(message.body, 'html', message.encoding or encoding)
        make_quoted_printable(htmlmsg)

        if message.plain_version:
            textmsg = MIMEMultipart('alternative')
            plainmsg = MIMEText(message.plain_version,
                                'plain', message.encoding or encoding)
            make_quoted_printable(plainmsg)
            textmsg.attach(htmlmsg)
            textmsg.attach(plainmsg)
        else:
            textmsg = htmlmsg
    else:
        textmsg = MIMEText(message.body, 'plain',
                           message.encoding or encoding)
        make_quoted_printable(textmsg)

    # add attachments if necessary
    if attachments:
        msg = MIMEMultipart()
        msg.attach(textmsg)

        if attachments:
            for attachment in build_attachments(attachments):
                msg.attach(attachment)
    else:
        msg = textmsg

    # sign message
    if signature_uid:
        msg = sign_message(msg, default_key=signature_uid,
                           passphrase=signature_passphrase)

    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = mails_to_string(recipients)
    if cc:
        msg['Cc'] = mails_to_string(cc)

    if reply_to:
        msg['Reply-To'] = reply_to

    uname = platform.uname()
    msg['X-Mailer'] = 'Buttermail v0.1 (Python {}; {}-{})'.format(
        platform.python_version(), uname.machine, uname.system.lower()
    )

    # Note: there might be cases when multiple headers with the same key
    # are necessary.
    for key, value in headers.items():
        if key in msg:
            msg.replace_header(key, value)
        else:
            msg[key] = value

    return msg
Example #41
0
    def send_raw(self, job, message, config):
        config = dict(self.middleware.call_sync('mail.config'), **config)

        if config['fromname']:
            from_addr = Header(config['fromname'], 'utf-8')
            try:
                config['fromemail'].encode('ascii')
            except UnicodeEncodeError:
                from_addr.append(f'<{config["fromemail"]}>', 'utf-8')
            else:
                from_addr.append(f'<{config["fromemail"]}>', 'ascii')
        else:
            try:
                config['fromemail'].encode('ascii')
            except UnicodeEncodeError:
                from_addr = Header(config['fromemail'], 'utf-8')
            else:
                from_addr = Header(config['fromemail'], 'ascii')

        interval = message.get('interval')
        if interval is None:
            interval = timedelta()
        else:
            interval = timedelta(seconds=interval)

        sw_name = self.middleware.call_sync('system.version').split('-', 1)[0]

        channel = message.get('channel')
        if not channel:
            channel = sw_name.lower()
        if interval > timedelta():
            channelfile = '/tmp/.msg.%s' % (channel)
            last_update = datetime.now() - interval
            try:
                last_update = datetime.fromtimestamp(
                    os.stat(channelfile).st_mtime)
            except OSError:
                pass
            timediff = datetime.now() - last_update
            if (timediff >= interval) or (timediff < timedelta()):
                # Make sure mtime is modified
                # We could use os.utime but this is simpler!
                with open(channelfile, 'w') as f:
                    f.write('!')
            else:
                raise CallError(
                    'This message was already sent in the given interval')

        verrors = self.__password_verify(config['pass'], 'mail-config.pass')
        if verrors:
            raise verrors
        to = message.get('to')
        if not to:
            to = [
                self.middleware.call_sync('user.query',
                                          [('username', '=', 'root')],
                                          {'get': True})['email']
            ]
            if not to[0]:
                raise CallError('Email address for root is not configured')

        if message.get('attachments'):
            job.check_pipe("input")

            def read_json():
                f = job.pipes.input.r
                data = b''
                i = 0
                while True:
                    read = f.read(1048576)  # 1MiB
                    if read == b'':
                        break
                    data += read
                    i += 1
                    if i > 50:
                        raise ValueError(
                            'Attachments bigger than 50MB not allowed yet')
                if data == b'':
                    return None
                return json.loads(data)

            attachments = read_json()
        else:
            attachments = None

        if 'html' in message or attachments:
            msg = MIMEMultipart()
            msg.preamble = 'This is a multi-part message in MIME format.'
            if 'html' in message:
                msg2 = MIMEMultipart('alternative')
                msg2.attach(
                    MIMEText(message['text'], 'plain', _charset='utf-8'))
                msg2.attach(MIMEText(message['html'], 'html',
                                     _charset='utf-8'))
                msg.attach(msg2)
            if attachments:
                for attachment in attachments:
                    m = Message()
                    m.set_payload(attachment['content'])
                    for header in attachment.get('headers'):
                        m.add_header(header['name'], header['value'],
                                     **(header.get('params') or {}))
                    msg.attach(m)
        else:
            msg = MIMEText(message['text'], _charset='utf-8')

        msg['Subject'] = message['subject']

        msg['From'] = from_addr
        msg['To'] = ', '.join(to)
        if message.get('cc'):
            msg['Cc'] = ', '.join(message.get('cc'))
        msg['Date'] = formatdate()

        local_hostname = self.middleware.call_sync('system.hostname')

        msg['Message-ID'] = "<%s-%s.%s@%s>" % (
            sw_name.lower(), datetime.utcnow().strftime("%Y%m%d.%H%M%S.%f"),
            base64.urlsafe_b64encode(os.urandom(3)), local_hostname)

        extra_headers = message.get('extra_headers') or {}
        for key, val in list(extra_headers.items()):
            # We already have "Content-Type: multipart/mixed" and setting "Content-Type: text/plain" like some scripts
            # do will break python e-mail module.
            if key.lower() == "content-type":
                continue

            if key in msg:
                msg.replace_header(key, val)
            else:
                msg[key] = val

        syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_MAIL)
        try:
            if config['oauth']:
                self.middleware.call_sync('mail.gmail_send', msg, config)
            else:
                server = self._get_smtp_server(config,
                                               message['timeout'],
                                               local_hostname=local_hostname)
                # NOTE: Don't do this.
                #
                # If smtplib.SMTP* tells you to run connect() first, it's because the
                # mailserver it tried connecting to via the outgoing server argument
                # was unreachable and it tried to connect to 'localhost' and barfed.
                # This is because FreeNAS doesn't run a full MTA.
                # else:
                #    server.connect()
                headers = '\n'.join([f'{k}: {v}' for k, v in msg._headers])
                syslog.syslog(f"sending mail to {', '.join(to)}\n{headers}")
                server.sendmail(from_addr.encode(), to, msg.as_string())
                server.quit()
        except Exception as e:
            # Don't spam syslog with these messages. They should only end up in the
            # test-email pane.
            # We are only interested in ValueError, not subclasses.
            if e.__class__ is ValueError:
                raise CallError(str(e))
            syslog.syslog(f'Failed to send email to {", ".join(to)}: {str(e)}')
            if isinstance(e, smtplib.SMTPAuthenticationError):
                raise CallError(
                    f'Authentication error ({e.smtp_code}): {e.smtp_error}',
                    errno.EAUTH if osc.IS_FREEBSD else errno.EPERM)
            self.logger.warn('Failed to send email: %s', str(e), exc_info=True)
            if message['queue']:
                with MailQueue() as mq:
                    mq.append(msg)
            raise CallError(f'Failed to send email: {e}')
        return True
Example #42
0
class EmailHandler:
    addr = "*****@*****.**"
    password = "******"
    addressBook = {'Danny': '*****@*****.**', 
         'Dr.Bouaynaya': '*****@*****.**', 
         'Oliver': '*****@*****.**',
         'Dimah': '*****@*****.**',
         'Alena': '*****@*****.**',
         'Dr.Hassan': '*****@*****.**',
         'Dr.Rasool': '*****@*****.**'}
    body = ""
    
    ## The constructor for the emailHandler class. This creates the message and sets the from address to the [email protected]
    def __init__(self):
        self.msg = MIMEMultipart()
        self.msg['From']  = self.addr
    
    ## Prepares the message to be sent by setting up the subject and body
    #
    # @param subject Subject line of the email to be sent
    # @param body Body of the email to be sent
    def prepareMessage(self, subject, body):
        self.msg['Subject'] = subject
        self.body = body
    
    ## Connects to the gmail server and logs in using the mrimathnotifier gmail address
    def connectToServer(self):
        self.server = smtplib.SMTP('smtp.gmail.com', 587)
        self.server.ehlo()
        self.server.starttls()
        self.server.ehlo()
        self.server.login(self.addr, self.password)
    
    ## Sends the email to all desired recipients as long as they are within the address book
    #
    # @param recipients a list of the names of desired recipients which have their emails linked in the address book map
    def sendMessage(self, recipients):
        self.msg['To'] = ','.join(list(map(self.addressBook.get, recipients)))
        self.body = "Hello,\n\n" + "This is an automated message from the MRIMath Notifier:" + "\n\n"+ self.body
        self.body = self.body + "\n\n" + "Regards,\nMRIMath Notifier"
        self.msg.attach(MIMEText(self.body, 'plain'))
        self.server.sendmail(self.addr, list(map(self.addressBook.get, recipients)), self.msg.as_string())
    
    ## Clears the body of the email, the attached files, and the list of recipients, and disconnects from the gmail server
    def finish(self):
        self.body = ''
        if 'To' in self.msg:
            self.msg.replace_header('To', '')
        self.msg.set_payload([])
        self.server.quit()
    
    ## Attaches a file to the email
    #
    # @param file the file to attach to the email
    # @param filename the name of the file to attach (may not be necessary actually...)
    def attachFile(self, file, filename):
        attachment = open(file.name, "rb")
        part = MIMEBase('application', 'octet-stream')
        part.set_payload((attachment).read())
        encoders.encode_base64(part)
        part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
        self.msg.attach(part)