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'
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)
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()
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()
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)
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()
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()
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
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
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())
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'))
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
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)
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
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
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
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')
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')
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()
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
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)
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":
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
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
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
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
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
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
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()
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())
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__)
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")
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()
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
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
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
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...")
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()
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
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
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)