def create_charset(mime_encoding): """Create an appropriate email charset for the given encoding. Valid options are 'base64' for Base64 encoding, 'qp' for Quoted-Printable, and 'none' for no encoding, in which case mails will be sent as 7bit if the content is all ASCII, or 8bit otherwise. """ charset = Charset() charset.input_charset = 'utf-8' charset.output_charset = 'utf-8' charset.input_codec = 'utf-8' charset.output_codec = 'utf-8' pref = mime_encoding.lower() if pref == 'base64': charset.header_encoding = BASE64 charset.body_encoding = BASE64 elif pref in ('qp', 'quoted-printable'): charset.header_encoding = QP charset.body_encoding = QP elif pref == 'none': charset.header_encoding = SHORTEST charset.body_encoding = None else: raise TracError( _("Invalid email encoding setting: %(mime_encoding)s", mime_encoding=mime_encoding)) return charset
def get_emails_header(self, attr): c = Charset(self.charset) c.header_encoding = QP c.body_encoding = 0 r = Charset(self.charset) r.header_encoding = 0 r.body_encoding = 0 h = Header() self.normalize_email_list(attr) emails = self.__getattribute__(attr) for i in range(len(emails)): name, email = emails[i] if i: h.append(',', r) if name: name = name.encode(self.charset, 'xmlcharrefreplace') h.append(name, r if is7bit(name) else c) h.append('<%s>' % email, r) else: h.append(email, r) return h
def create_charset(mime_encoding): """Create an appropriate email charset for the given encoding. Valid options are 'base64' for Base64 encoding, 'qp' for Quoted-Printable, and 'none' for no encoding, in which case mails will be sent as 7bit if the content is all ASCII, or 8bit otherwise. """ charset = Charset() charset.input_charset = 'utf-8' charset.output_charset = 'utf-8' charset.input_codec = 'utf-8' charset.output_codec = 'utf-8' pref = mime_encoding.lower() if pref == 'base64': charset.header_encoding = BASE64 charset.body_encoding = BASE64 elif pref in ('qp', 'quoted-printable'): charset.header_encoding = QP charset.body_encoding = QP elif pref == 'none': charset.header_encoding = SHORTEST charset.body_encoding = None else: raise TracError(_("Invalid email encoding setting: %(mime_encoding)s", mime_encoding=mime_encoding)) return charset
def as_message(self, escape_addresses=True): # http://wordeology.com/computer/how-to-send-good-unicode-email-with-python.html # http://stackoverflow.com/questions/31714221/how-to-send-an-email-with-quoted # http://stackoverflow.com/questions/9403265/how-do-i-use-python/9509718#9509718 charset = Charset('utf-8') charset.header_encoding = QP charset.body_encoding = QP msg = MIMEMultipart() # Headers unixfrom = "From %s %s" % ( self.sender.address, self.archived_date.strftime("%c")) header_from = self.sender.address if self.sender.name and self.sender.name != self.sender.address: header_from = "%s <%s>" % (self.sender.name, header_from) header_to = self.mailinglist.name if escape_addresses: header_from = header_from.replace("@", " at ") header_to = header_to.replace("@", " at ") unixfrom = unixfrom.replace("@", " at ") msg.set_unixfrom(unixfrom) headers = ( ("From", header_from), ("To", header_to), ("Subject", self.subject), ) for header_name, header_value in headers: if not header_value: continue try: msg[header_name] = header_value.encode('ascii') except UnicodeEncodeError: msg[header_name] = Header( header_value.encode('utf-8'), charset).encode() tz = get_fixed_timezone(self.timezone) header_date = self.date.astimezone(tz).replace(microsecond=0) # Date format: http://tools.ietf.org/html/rfc5322#section-3.3 msg["Date"] = header_date.strftime("%a, %d %b %Y %H:%M:%S %z") msg["Message-ID"] = "<%s>" % self.message_id if self.in_reply_to: msg["In-Reply-To"] = self.in_reply_to # Body content = self.ADDRESS_REPLACE_RE.sub(r"\1(a)\2", self.content) # Don't use MIMEText, it won't encode to quoted-printable textpart = MIMENonMultipart("text", "plain", charset='utf-8') textpart.set_payload(content, charset=charset) msg.attach(textpart) # Attachments for attachment in self.attachments.order_by("counter"): mimetype = attachment.content_type.split('/', 1) part = MIMEBase(mimetype[0], mimetype[1]) part.set_payload(attachment.content) encode_base64(part) part.add_header('Content-Disposition', 'attachment', filename=attachment.name) msg.attach(part) return msg
def get_text_message(self, _charset='utf-8', _subtype='plain'): message = MIMENonMultipart('text', _subtype) cs = Charset(_charset) if cs.body_encoding == charset.BASE64: cs.body_encoding = charset.QP message.set_charset(cs) del message['Content-Transfer-Encoding'] return message
def send_report(self, short_description, additional_description, title, report): # Create message container - the correct MIME type is multipart/mixed to allow attachment. full_email = MIMEMultipart('mixed') full_email[ 'Subject'] = '[' + self.conf['subject_keyword'] + '] ' + title full_email['From'] = self.conf['from'] full_email['To'] = self.conf['to'] # Create the body of the message (a plain-text and an HTML version). body = MIMEMultipart('alternative') body.attach( MIMEText((short_description + "\n\n" + additional_description).encode('utf-8'), 'plain', _charset='utf-8')) body.attach( MIMEText(("""\ <html> <head></head> <body> <p>""" + short_description + """</p><br> """ + additional_description + """ </body> </html> """).encode('utf-8'), 'html', _charset='utf-8')) full_email.attach(body) # Create the attachment of the message in text/csv. attachment = MIMENonMultipart('text', 'csv', charset='utf-8') attachment.add_header('Content-Disposition', 'attachment', filename=report['filename']) cs = Charset('utf-8') cs.body_encoding = BASE64 attachment.set_payload(report['content'].encode('utf-8'), charset=cs) full_email.attach(attachment) # Send the message via SMTP server. s = smtplib.SMTP(self.conf['server'], self.conf['port']) if self.conf['tls'] == 'yes': s.starttls() if not self.conf['login'] == '': s.login(self.conf['login'], self.conf['password']) # sendmail function takes 3 arguments: sender's address, recipient's address # and message to send - here it is sent as one string. s.sendmail(self.conf['from'], self.conf['to'], full_email.as_string()) # self.logger.info('email sent') s.quit()
def sendmail(server, from_, to, message): if not isinstance(to, list): to = [to] charset = Charset('UTF-8') charset.header_encoding = QP charset.body_encoding = QP msg = email.message_from_string(message) msg.set_charset(charset) server.sendmail(from_, to, msg.as_string())
def write_patch_file(filename, commit_info, diff): """Write patch file""" if not diff: gbp.log.debug("I won't generate empty diff %s" % filename) return None try: with open(filename, 'wb') as patch: msg = Message() charset = Charset('utf-8') charset.body_encoding = None charset.header_encoding = QP # Write headers name = commit_info['author']['name'] email = commit_info['author']['email'] # Git compat: put name in quotes if special characters found if re.search(r'[,.@()\[\]\\\:;]', name): name = '"%s"' % name from_header = Header(header_name='from') try: from_header.append(name, 'us-ascii') except UnicodeDecodeError: from_header.append(name, charset) from_header.append('<%s>' % email) msg['From'] = from_header date = commit_info['author'].datetime datestr = date.strftime('%a, %-d %b %Y %H:%M:%S %z') msg['Date'] = Header(datestr, 'us-ascii', 'date') subject_header = Header(header_name='subject') try: subject_header.append(commit_info['subject'], 'us-ascii') except UnicodeDecodeError: subject_header.append(commit_info['subject'], charset) msg['Subject'] = subject_header # Write message body if commit_info['body']: # Strip extra linefeeds body = commit_info['body'].rstrip() + '\n' try: msg.set_payload(body.encode('us-ascii')) except (UnicodeEncodeError): msg.set_payload(body, charset) policy = Compat32(max_line_length=77) patch.write(msg.as_bytes(unixfrom=False, policy=policy)) # Write diff patch.write(b'---\n') patch.write(diff) except IOError as err: raise GbpError('Unable to create patch file: %s' % err) return filename
def write_patch_file(filename, commit_info, diff): """Write patch file""" if not diff: gbp.log.debug("I won't generate empty diff %s" % filename) return None try: with open(filename, 'wb') as patch: msg = Message() charset = Charset('utf-8') charset.body_encoding = None charset.header_encoding = QP # Write headers name = commit_info['author']['name'] email = commit_info['author']['email'] # Git compat: put name in quotes if special characters found if re.search("[,.@()\[\]\\\:;]", name): name = '"%s"' % name from_header = Header(header_name='from') try: from_header.append(name, 'us-ascii') except UnicodeDecodeError: from_header.append(name, charset) from_header.append('<%s>' % email) msg['From'] = from_header date = commit_info['author'].datetime datestr = date.strftime('%a, %-d %b %Y %H:%M:%S %z') msg['Date'] = Header(datestr, 'us-ascii', 'date') subject_header = Header(header_name='subject') try: subject_header.append(commit_info['subject'], 'us-ascii') except UnicodeDecodeError: subject_header.append(commit_info['subject'], charset) msg['Subject'] = subject_header # Write message body if commit_info['body']: # Strip extra linefeeds body = commit_info['body'].rstrip() + '\n' try: msg.set_payload(body.encode('us-ascii')) except (UnicodeEncodeError): msg.set_payload(body, charset) patch.write( msg.as_string(unixfrom=False, maxheaderlen=77).encode('utf-8')) # Write diff patch.write(b'---\n') patch.write(diff) except IOError as err: raise GbpError('Unable to create patch file: %s' % err) return filename
def _make_charset(self): charset = Charset() charset.input_charset = 'utf-8' pref = self.mime_encoding.lower() if pref == 'base64': charset.header_encoding = BASE64 charset.body_encoding = BASE64 charset.output_charset = 'utf-8' charset.input_codec = 'utf-8' charset.output_codec = 'utf-8' elif pref in ['qp', 'quoted-printable']: charset.header_encoding = QP charset.body_encoding = QP charset.output_charset = 'utf-8' charset.input_codec = 'utf-8' charset.output_codec = 'utf-8' elif pref == 'none': charset.header_encoding = None charset.body_encoding = None charset.input_codec = None charset.output_charset = 'ascii' else: raise TracError(_('Invalid email encoding setting: %s' % pref)) return charset
def emailPlain( template, subject=None, toinform=[], context={}, attachments=[], forreal=True ): # try to stick to rfc822 (django default is base64) religiously; also # as it helps with spam filters. cs = Charset("utf-8") cs.body_encoding = QP # Weed out duplicates. to = list(set(flatten(toinform))) context["base"] = settings.BASE body = render_to_string(template, context) if not subject: body = body.split("\n") subject = body[0].rstrip() subject = re.sub("^Subject:\s+", string=subject, repl="", flags=re.IGNORECASE) body = "\n".join(body[1:]) body_html = ( "<html><head><title>%s</title></head><body><pre>%s</pre></body><html>" % (subject, body) ) msg = MIMEMultipart("alternative") part1 = MIMEText(body, "plain", _charset=cs) part2 = MIMEMultipart("related") part2.attach(MIMEText(body_html, "html", _charset=cs)) email = EmailMessage( subject.strip(), None, to=to, from_email=settings.DEFAULT_FROM_EMAIL ) for attachment in attachments: part2.attach(attachment) msg.attach(part1) msg.attach(part2) email.attach(msg) if forreal: email.send() else: print("To:\t%s\nSubject: %s\n%s\n\n" % (to, subject, body))
def _mail(self, fromaddr, to, subject, payload): # prepare charset = Charset("utf-8") charset.header_encoding = QP charset.body_encoding = QP # create method and set headers msg = Message() msg.set_payload(payload.encode("utf8")) msg.set_charset(charset) msg['Subject'] = Header(subject, "utf8") msg['From'] = fromaddr msg['To'] = to self.server.connect() self.server.sendmail(fromaddr, [to], msg.as_string()) self.server.quit()
def write_patch_file(filename, commit_info, diff): """Write patch file""" if not diff: gbp.log.debug("I won't generate empty diff %s" % filename) return None try: with open(filename, "w") as patch: msg = Message() charset = Charset("utf-8") charset.body_encoding = None charset.header_encoding = QP # Write headers name = commit_info["author"]["name"] email = commit_info["author"]["email"] # Git compat: put name in quotes if special characters found if re.search("[,.@()\[\]\\\:;]", name): name = '"%s"' % name from_header = Header(unicode(name, "utf-8"), charset, 77, "from") from_header.append(unicode("<%s>" % email)) msg["From"] = from_header date = commit_info["author"].datetime datestr = date.strftime("%a, %-d %b %Y %H:%M:%S %z") msg["Date"] = Header(unicode(datestr, "utf-8"), charset, 77, "date") msg["Subject"] = Header(unicode(commit_info["subject"], "utf-8"), charset, 77, "subject") # Write message body if commit_info["body"]: # Strip extra linefeeds body = commit_info["body"].rstrip() + "\n" try: msg.set_payload(body.encode("ascii")) except UnicodeDecodeError: msg.set_payload(body, charset) patch.write(msg.as_string(unixfrom=False)) # Write diff patch.write("---\n") patch.write(diff) except IOError as err: raise GbpError("Unable to create patch file: %s" % err) return filename
def init_payload(self): super().init_payload() self.all_recipients = [] self.mime_message = self.message.message() # Work around an Amazon SES bug where, if all of: # - the message body (text or html) contains non-ASCII characters # - the body is sent with `Content-Transfer-Encoding: 8bit` # (which is Django email's default for most non-ASCII bodies) # - you are using an SES ConfigurationSet with open or click tracking enabled # then SES replaces the non-ASCII characters with question marks as it rewrites # the message to add tracking. Forcing `CTE: quoted-printable` avoids the problem. # (https://forums.aws.amazon.com/thread.jspa?threadID=287048) for part in self.mime_message.walk(): if part.get_content_maintype() == "text" and part["Content-Transfer-Encoding"] == "8bit": content = part.get_payload() del part["Content-Transfer-Encoding"] qp_charset = Charset(part.get_content_charset("us-ascii")) qp_charset.body_encoding = QP # (can't use part.set_payload, because SafeMIMEText can undo this workaround) MIMEText.set_payload(part, content, charset=qp_charset)
def send_mail(self, mail_template, in_reply_to = None, **kwargs): msgid = make_msgid() subject = mail_template['subject'].format(**kwargs) message = mail_template['message'].format(**kwargs) msg = MIMEMultipart('alternative') msg['Subject'] = str(Header(subject, 'utf-8')) msg['From'] = str(Header(SMTP_FROM, 'utf-8')) msg['To'] = str(Header(self.email, 'utf-8')) msg['Message-ID'] = msgid msg['Reply-To'] = SMTP_REPLY_TO_EMAIL msg['Date'] = datetime.datetime.now(pytz.utc).strftime("%a, %e %b %Y %T %z") if in_reply_to: msg['In-Reply-To'] = in_reply_to msg['References'] = in_reply_to # add message charset = Charset('utf-8') # QP = quoted printable; this is better readable instead of base64, when # the mail is read in plaintext! charset.body_encoding = QP message_part = MIMEText(message.encode('utf-8'), 'plain', charset) msg.attach(message_part) if DEBUG: with open("/tmp/keepitup_mails.log", "a") as f: f.write(msg.as_string() + "\n") else: with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server: server.ehlo() if SMTP_USE_STARTTLS: context = ssl.create_default_context() server.starttls(context=context) server.sendmail(SMTP_FROM, self.email, msg.as_string()) return msgid
def build_message( subject, from_email, html, to_recipients=[], bcc_recipients=[], attachments=[], headers=[], ): """Build raw email for sending.""" message = MIMEMultipart("alternative") cs = Charset("utf-8") cs.body_encoding = QP message["Subject"] = subject message["From"] = from_email if to_recipients: message["To"] = ",".join(to_recipients) if bcc_recipients: message["Bcc"] = ",".join(bcc_recipients) text = get_text_from_html(html) plain_text = MIMEText(text, "plain", cs) message.attach(plain_text) html_text = MIMEText(html, "html", cs) message.attach(html_text) for header in headers: message[header["key"]] = header["value"] for filename in attachments: with open(filename, "rb") as attachment: part = MIMEApplication(attachment.read()) part.add_header("Content-Disposition", "attachment", filename="report.pdf") message.attach(part) return message.as_string()
def sendMailMessage(self, xMailMessage): COMMASPACE = ', ' if dbg: print("PyMailSMTPService sendMailMessage", file=dbgout) recipients = xMailMessage.getRecipients() sendermail = xMailMessage.SenderAddress sendername = xMailMessage.SenderName subject = xMailMessage.Subject ccrecipients = xMailMessage.getCcRecipients() bccrecipients = xMailMessage.getBccRecipients() if dbg: print("PyMailSMTPService subject: " + subject, file=dbgout) print("PyMailSMTPService from: " + sendername, file=dbgout) print("PyMailSMTPService from: " + sendermail, file=dbgout) print("PyMailSMTPService send to: %s" % (recipients, ), file=dbgout) attachments = xMailMessage.getAttachments() textmsg = Message() content = xMailMessage.Body flavors = content.getTransferDataFlavors() if dbg: print("PyMailSMTPService flavors len: %d" % (len(flavors), ), file=dbgout) #Use first flavor that's sane for an email body for flavor in flavors: if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find( 'text/plain') != -1: if dbg: print("PyMailSMTPService mimetype is: " + flavor.MimeType, file=dbgout) textbody = content.getTransferData(flavor) if len(textbody): mimeEncoding = re.sub("charset=.*", "charset=UTF-8", flavor.MimeType) if mimeEncoding.find('charset=UTF-8') == -1: mimeEncoding = mimeEncoding + "; charset=UTF-8" textmsg['Content-Type'] = mimeEncoding textmsg['MIME-Version'] = '1.0' try: #it's a string, get it as utf-8 bytes textbody = textbody.encode('utf-8') except: #it's a bytesequence, get raw bytes textbody = textbody.value textbody = textbody.decode('utf-8') c = Charset('utf-8') c.body_encoding = QP textmsg.set_payload(textbody, c) break if (len(attachments)): msg = MIMEMultipart() msg.epilogue = '' msg.attach(textmsg) else: msg = textmsg hdr = Header(sendername, 'utf-8') hdr.append('<' + sendermail + '>', 'us-ascii') msg['Subject'] = subject msg['From'] = hdr msg['To'] = COMMASPACE.join(recipients) if len(ccrecipients): msg['Cc'] = COMMASPACE.join(ccrecipients) if xMailMessage.ReplyToAddress != '': msg['Reply-To'] = xMailMessage.ReplyToAddress mailerstring = "LibreOffice via Caolan's mailmerge component" try: ctx = uno.getComponentContext() aConfigProvider = ctx.ServiceManager.createInstance( "com.sun.star.configuration.ConfigurationProvider") prop = uno.createUnoStruct('com.sun.star.beans.PropertyValue') prop.Name = "nodepath" prop.Value = "/org.openoffice.Setup/Product" aSettings = aConfigProvider.createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", (prop, )) mailerstring = aSettings.getByName("ooName") + " " + \ aSettings.getByName("ooSetupVersion") + " via Caolan's mailmerge component" except: pass msg['X-Mailer'] = mailerstring msg['Date'] = formatdate(localtime=True) for attachment in attachments: content = attachment.Data flavors = content.getTransferDataFlavors() flavor = flavors[0] ctype = flavor.MimeType maintype, subtype = ctype.split('/', 1) msgattachment = MIMEBase(maintype, subtype) data = content.getTransferData(flavor) msgattachment.set_payload(data.value) encode_base64(msgattachment) fname = attachment.ReadableName try: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=fname) except: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=('utf-8','',fname)) if dbg: print(("PyMailSMTPService attachmentheader: ", str(msgattachment)), file=dbgout) msg.attach(msgattachment) uniquer = {} for key in recipients: uniquer[key] = True if len(ccrecipients): for key in ccrecipients: uniquer[key] = True if len(bccrecipients): for key in bccrecipients: uniquer[key] = True truerecipients = uniquer.keys() if dbg: print(("PyMailSMTPService recipients are: ", truerecipients), file=dbgout) self.server.sendmail(sendermail, truerecipients, msg.as_string())
def _getMessage(ctx, message): COMMASPACE = ', ' sendermail = message.SenderAddress sendername = message.SenderName subject = message.Subject if isDebugMode(): msg = getMessage(ctx, g_message, 251, subject) logMessage(ctx, INFO, msg, 'SmtpService', 'sendMailMessage()') textmsg = Message() content = message.Body flavors = content.getTransferDataFlavors() #Use first flavor that's sane for an email body for flavor in flavors: if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find( 'text/plain') != -1: textbody = content.getTransferData(flavor) if len(textbody): mimeEncoding = re.sub('charset=.*', 'charset=UTF-8', flavor.MimeType) if mimeEncoding.find('charset=UTF-8') == -1: mimeEncoding = mimeEncoding + '; charset=UTF-8' textmsg['Content-Type'] = mimeEncoding textmsg['MIME-Version'] = '1.0' try: #it's a string, get it as utf-8 bytes textbody = textbody.encode('utf-8') except: #it's a bytesequence, get raw bytes textbody = textbody.value if sys.version >= '3': if sys.version_info.minor < 3 or ( sys.version_info.minor == 3 and sys.version_info.micro <= 1): #http://stackoverflow.com/questions/9403265/how-do-i-use-python-3-2-email-module-to-send-unicode-messages-encoded-in-utf-8-w #see http://bugs.python.org/16564, etc. basically it now *seems* to be all ok #in python 3.3.2 onwards, but a little busted in 3.3.0 textbody = textbody.decode('iso8859-1') else: textbody = textbody.decode('utf-8') c = Charset('utf-8') c.body_encoding = QP textmsg.set_payload(textbody, c) else: textmsg.set_payload(textbody) break if message.hasAttachments(): msg = MIMEMultipart() msg.epilogue = '' msg.attach(textmsg) else: msg = textmsg header = Header(sendername, 'utf-8') header.append('<' + sendermail + '>', 'us-ascii') msg['Subject'] = subject msg['From'] = header msg['To'] = COMMASPACE.join(message.getRecipients()) msg['Message-ID'] = message.MessageId if message.ThreadId: msg['References'] = message.ThreadId if message.hasCcRecipients(): msg['Cc'] = COMMASPACE.join(message.getCcRecipients()) if message.ReplyToAddress != '': msg['Reply-To'] = message.ReplyToAddress xmailer = "LibreOffice / OpenOffice via smtpMailerOOo extention" try: configuration = getConfiguration(ctx, '/org.openoffice.Setup/Product') name = configuration.getByName('ooName') version = configuration.getByName('ooSetupVersion') xmailer = "%s %s via smtpMailerOOo extention" % (name, version) except: pass msg['X-Mailer'] = xmailer msg['Date'] = formatdate(localtime=True) for attachment in message.getAttachments(): content = attachment.Data flavors = content.getTransferDataFlavors() flavor = flavors[0] ctype = flavor.MimeType maintype, subtype = ctype.split('/', 1) msgattachment = MIMEBase(maintype, subtype) data = content.getTransferData(flavor) msgattachment.set_payload(data.value) encode_base64(msgattachment) fname = attachment.ReadableName try: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=fname) except: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=('utf-8','',fname)) msg.attach(msgattachment) return msg
def send(self): """Perform all send operations related to this email... These consists in: - send the notification email; - call self.filer_cmd if not None. REMARKS If the GIT_HOOKS_TESTSUITE_MODE environment variable is set, then a trace of the email is printed, instead of sending it. This is for testing purposes. """ # Force the charset being used to UTF-8. We could possibly try # to guess whether more primitive charsets might work such as # ASCII or IS0-8859-15, but UTF-8 is so close to those encodings # that it is not worth the extra complication. # # The one situation where it might be worth guessing the charset # is when the email body contains some characters which are not # available in UTF-8. Since UTF-8 is so widely used, we'll assume # for now that it's not necessary in practice to support this # scenario. e_msg_charset = Charset("UTF-8") # Force quoted-printable encoding for our emails. # # Using this encoding helps ensure that the email payload # does not exceed any of the limitations that SMTP servers # might have. In particular, while RFC 6152 now defines # the "8bit" Content-Transfer-Encoding as being a legal # extension, it also warns us of some limitations: # # | Note that this extension does NOT eliminate # | the possibility of an SMTP server limiting line # | length; servers are free to implement this extension # | but nevertheless set a line length limit no lower # | than 1000 octets. # # We also prefer the quoted-printable encoding over the base64 # one because: # # - The output that's generally easier for humans to read'; # - The output is also usually smaller in size for typical # text. e_msg_charset.body_encoding = QP e_msg_body = self.__email_body_with_diff # Handle the situation where we were manually called by a user # (as opposed by Git itself) who would like us to re-send the emails, # with a warning banner indicating that the emails were re-generated. # The banner is added at the beginning of the email body. # # The main reason for adding the banner is that it helps prevent users # from thinking the commit was pushed at the time the email was sent. # # Note that the option of allowing the user to use the banner # of his choice was considered. In the end, we decided against it # for now, because we wanted it to be very simple for the user # to trigger the addition of the banner during the re-send, # while at the same time keeping the code in the git-hooks # as simple as possible also (i.e. we avoided the introduction # of multiple environment variables, for instance). manual_replay_reason = os.environ.get("GIT_HOOKS_EMAIL_REPLAY_REASON") if manual_replay_reason is not None: warning_banner = EMAIL_REPLAY_WARNING_BANNER.format( reason=manual_replay_reason) e_msg_body = warning_banner + "\n" + e_msg_body e_msg = MIMEText(e_msg_body, _charset=e_msg_charset) # Create the email's header. e_msg["From"] = sanitized_email_address(self.email_info.email_from) e_msg["To"] = ", ".join(map(sanitized_email_address, self.email_to)) if self.email_bcc: e_msg["Bcc"] = ", ".join( map(sanitized_email_address, self.email_bcc)) e_msg["Subject"] = sanitized_email_header_field(self.email_subject) e_msg["X-Act-Checkin"] = self.email_info.project_name e_msg["X-Git-Author"] = sanitized_email_address( self.author or self.email_info.email_from) e_msg["X-Git-Refname"] = self.ref_name e_msg["X-Git-Oldrev"] = self.old_rev e_msg["X-Git-Newrev"] = self.new_rev # email_from = e_msg.get('From') email_recipients = [ addr[1] for addr in getaddresses( e_msg.get_all("To", []) + e_msg.get_all("Cc", []) + e_msg.get_all("Bcc", [])) ] sendmail( self.email_info.email_from, email_recipients, e_msg.as_string(), "localhost", ) if self.filer_cmd is not None: self.__call_filer_cmd()
# # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. # Email: [email protected]; WWW: http://www.mysociety.org/ # # $Id: sendemail.py,v 1.5 2009/12/17 17:31:04 francis dead $ # import re, smtplib from minimock import mock, Mock from email.message import Message from email.header import Header from email.utils import formataddr, make_msgid, formatdate from email.charset import Charset, QP charset = Charset('utf-8') charset.body_encoding = QP def send_email(sender, to, message, headers={}): """Sends MESSAGE from SENDER to TO, with HEADERS Returns True if successful, False if not >>> mock('smtplib.SMTP', returns=Mock('smtp_connection')) >>> send_email("[email protected]", "[email protected]", "Hello, this is a message!", { ... 'Subject': 'Mapumental message', ... 'From': ("[email protected]", "Ms. A"), ... 'To': "[email protected]" ... }) # doctest:+ELLIPSIS Called smtplib.SMTP('localhost') Called smtp_connection.sendmail( '[email protected]',
def as_message(self, escape_addresses=True): # http://wordeology.com/computer/how-to-send-good-unicode-email-with-python.html # http://stackoverflow.com/questions/31714221/how-to-send-an-email-with-quoted # http://stackoverflow.com/questions/9403265/how-do-i-use-python/9509718#9509718 charset = Charset('utf-8') charset.header_encoding = QP charset.body_encoding = QP msg = MIMEMultipart() # Headers unixfrom = "From %s %s" % (self.sender.address, self.archived_date.strftime("%c")) header_from = self.sender.address if self.sender_name and self.sender_name != self.sender.address: header_from = "%s <%s>" % (self.sender_name, header_from) header_to = self.mailinglist.name if escape_addresses: header_from = header_from.replace("@", " at ") header_to = header_to.replace("@", " at ") unixfrom = unixfrom.replace("@", " at ") msg.set_unixfrom(unixfrom) headers = ( ("From", header_from), ("To", header_to), ("Subject", self.subject), ) for header_name, header_value in headers: if not header_value: continue try: msg[header_name] = header_value.encode('ascii') except UnicodeEncodeError: msg[header_name] = Header(header_value.encode('utf-8'), charset).encode() tz = get_fixed_timezone(self.timezone) header_date = self.date.astimezone(tz).replace(microsecond=0) # Date format: http://tools.ietf.org/html/rfc5322#section-3.3 msg["Date"] = header_date.strftime("%a, %d %b %Y %H:%M:%S %z") msg["Message-ID"] = "<%s>" % self.message_id if self.in_reply_to: msg["In-Reply-To"] = self.in_reply_to # Body content = self.ADDRESS_REPLACE_RE.sub(r"\1(a)\2", self.content) # Don't use MIMEText, it won't encode to quoted-printable textpart = MIMENonMultipart("text", "plain", charset='utf-8') textpart.set_payload(content, charset=charset) msg.attach(textpart) # Attachments for attachment in self.attachments.order_by("counter"): mimetype = attachment.content_type.split('/', 1) part = MIMEBase(mimetype[0], mimetype[1]) part.set_payload(attachment.content) encode_base64(part) part.add_header('Content-Disposition', 'attachment', filename=attachment.name) msg.attach(part) return msg
def sendMailMessage(self, xMailMessage): COMMASPACE = ', ' if dbg: print("PyMailSMTPService sendMailMessage", file=dbgout) recipients = xMailMessage.getRecipients() sendermail = xMailMessage.SenderAddress sendername = xMailMessage.SenderName subject = xMailMessage.Subject ccrecipients = xMailMessage.getCcRecipients() bccrecipients = xMailMessage.getBccRecipients() if dbg: print("PyMailSMTPService subject: " + subject, file=dbgout) print("PyMailSMTPService from: " + sendername, file=dbgout) print("PyMailSMTPService from: " + sendermail, file=dbgout) print("PyMailSMTPService send to: %s" % (recipients, ), file=dbgout) attachments = xMailMessage.getAttachments() textmsg = Message() content = xMailMessage.Body flavors = content.getTransferDataFlavors() if dbg: print("PyMailSMTPService flavors len: %d" % (len(flavors), ), file=dbgout) #Use first flavor that's sane for an email body for flavor in flavors: if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find( 'text/plain') != -1: if dbg: print("PyMailSMTPService mimetype is: " + flavor.MimeType, file=dbgout) textbody = content.getTransferData(flavor) if len(textbody): mimeEncoding = re.sub("charset=.*", "charset=UTF-8", flavor.MimeType) if mimeEncoding.find('charset=UTF-8') == -1: mimeEncoding = mimeEncoding + "; charset=UTF-8" textmsg['Content-Type'] = mimeEncoding textmsg['MIME-Version'] = '1.0' try: #it's a string, get it as utf-8 bytes textbody = textbody.encode('utf-8') except: #it's a bytesequence, get raw bytes textbody = textbody.value if sys.version >= '3': if sys.version_info.minor < 3 or ( sys.version_info.minor == 3 and sys.version_info.micro <= 1): #http://stackoverflow.com/questions/9403265/how-do-i-use-python-3-2-email-module-to-send-unicode-messages-encoded-in-utf-8-w #see http://bugs.python.org/16564, etc. basically it now *seems* to be all ok #in python 3.3.2 onwards, but a little busted in 3.3.0 textbody = textbody.decode('iso8859-1') else: textbody = textbody.decode('utf-8') c = Charset('utf-8') c.body_encoding = QP textmsg.set_payload(textbody, c) else: textmsg.set_payload(textbody) break if (len(attachments)): msg = MIMEMultipart() msg.epilogue = '' msg.attach(textmsg) else: msg = textmsg hdr = Header(sendername, 'utf-8') hdr.append('<' + sendermail + '>', 'us-ascii') msg['Subject'] = subject msg['From'] = hdr msg['To'] = COMMASPACE.join(recipients) if len(ccrecipients): msg['Cc'] = COMMASPACE.join(ccrecipients) if xMailMessage.ReplyToAddress != '': msg['Reply-To'] = xMailMessage.ReplyToAddress mailerstring = "LibreOffice via Caolan's mailmerge component" try: configuration = self._getConfiguration( "/org.openoffice.Setup/Product") mailerstring = "%s %s via Caolan's mailmerge component" % ( configuration.getByName("ooName"), configuration.getByName("ooSetupVersion")) except: pass msg['X-Mailer'] = mailerstring msg['Date'] = formatdate(localtime=True) for attachment in attachments: content = attachment.Data flavors = content.getTransferDataFlavors() flavor = flavors[0] ctype = flavor.MimeType maintype, subtype = ctype.split('/', 1) msgattachment = MIMEBase(maintype, subtype) data = content.getTransferData(flavor) msgattachment.set_payload(data.value) encode_base64(msgattachment) fname = attachment.ReadableName try: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=fname) except: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=('utf-8','',fname)) if dbg: print(("PyMailSMTPService attachmentheader: ", str(msgattachment)), file=dbgout) msg.attach(msgattachment) uniquer = {} for key in recipients: uniquer[key] = True if len(ccrecipients): for key in ccrecipients: uniquer[key] = True if len(bccrecipients): for key in bccrecipients: uniquer[key] = True truerecipients = uniquer.keys() if dbg: print(("PyMailSMTPService recipients are: ", truerecipients), file=dbgout) self.server.sendmail(sendermail, truerecipients, msg.as_string())
def sendmail(request, to, subject, text, mail_from=None): """ Create and send a text/plain message Return a tuple of success or error indicator and message. @param request: the request object @param to: recipients (list) @param subject: subject of email (unicode) @param text: email body text (unicode) @param mail_from: override default mail_from @type mail_from: unicode @rtype: tuple @return: (is_ok, Description of error or OK message) """ import smtplib, socket from email.message import Message from email.charset import Charset, QP from email.utils import formatdate, make_msgid _ = request.getText cfg = request.cfg mail_from = mail_from or cfg.mail_from logging.debug("send mail, from: %r, subj: %r" % (mail_from, subject)) logging.debug("send mail, to: %r" % (to, )) if not to: return (1, _("No recipients, nothing to do")) subject = subject.encode(config.charset) # Create a text/plain body using CRLF (see RFC2822) text = text.replace(u'\n', u'\r\n') text = text.encode(config.charset) # Create a message using config.charset and quoted printable # encoding, which should be supported better by mail clients. # TODO: check if its really works better for major mail clients msg = Message() charset = Charset(config.charset) charset.header_encoding = QP charset.body_encoding = QP msg.set_charset(charset) # work around a bug in python 2.4.3 and above: msg.set_payload('=') if msg.as_string().endswith('='): text = charset.body_encode(text) msg.set_payload(text) # Create message headers # Don't expose emails addreses of the other subscribers, instead we # use the same mail_from, e.g. u"Jürgen Wiki <*****@*****.**>" address = encodeAddress(mail_from, charset) msg['From'] = address msg['To'] = address msg['Date'] = formatdate() msg['Message-ID'] = make_msgid() msg['Subject'] = Header(subject, charset) # See RFC 3834 section 5: msg['Auto-Submitted'] = 'auto-generated' if cfg.mail_sendmail: # Set the BCC. This will be stripped later by sendmail. msg['BCC'] = ','.join(to) # Set Return-Path so that it isn't set (generally incorrectly) for us. msg['Return-Path'] = address # Send the message if not cfg.mail_sendmail: try: logging.debug("trying to send mail (smtp) via smtp server '%s'" % cfg.mail_smarthost) host, port = (cfg.mail_smarthost + ':25').split(':')[:2] server = smtplib.SMTP(host, int(port)) try: #server.set_debuglevel(1) if cfg.mail_login: user, pwd = cfg.mail_login.split() try: # try to do tls server.ehlo() if server.has_extn('starttls'): server.starttls() server.ehlo() logging.debug( "tls connection to smtp server established") except: logging.debug( "could not establish a tls connection to smtp server, continuing without tls" ) logging.debug( "trying to log in to smtp server using account '%s'" % user) server.login(user, pwd) server.sendmail(mail_from, to, msg.as_string()) finally: try: server.quit() except AttributeError: # in case the connection failed, SMTP has no "sock" attribute pass except UnicodeError, e: logging.exception("unicode error [%r -> %r]" % ( mail_from, to, )) return (0, str(e)) except smtplib.SMTPException, e: logging.exception("smtp mail failed with an exception.") return (0, str(e))
def send_email(self, recipients, cc, subject, content, report): """Sends an email with attachment. Refer to https://gist.github.com/BietteMaxime/f75ae41f7b4557274a9f Args: recipients: To whom to send the email. cc: To whom to cc the email. subject: Email subject. content: Email body content report: A dictionary containing "filename", "data" to construct a CSV attachment. Returns: None """ # Get sender from configuration sender = self.smtp["smtp_from"] # Create message container - the correct MIME type is multipart/mixed # to allow attachment. full_email = MIMEMultipart("mixed") full_email["Subject"] = subject full_email["From"] = sender full_email["To"] = ", ".join(recipients) full_email["CC"] = ", ".join(cc) # Create the body of the message (a plain-text version). content = content.encode(ENCODING) content = MIMEText(content, "plain", _charset=ENCODING) body = MIMEMultipart("alternative") body.attach(content) full_email.attach(body) # Create the attachment of the message in text/csv. attachment = MIMENonMultipart("text", "csv", charset=ENCODING) attachment.add_header("Content-Disposition", "attachment", filename=report["filename"]) cs = Charset(ENCODING) cs.body_encoding = BASE64 attachment.set_payload(report["data"].encode(ENCODING), charset=cs) full_email.attach(attachment) try: with smtplib.SMTP(self.smtp["smtp_url"]) as server: server.starttls() username = self.smtp["smtp_auth_username"] password = self.smtp["smtp_auth_password"] server.login(username, password) receivers = recipients + cc server.sendmail(sender, receivers, full_email.as_string()) self.logger.info("Successfully sent email to %s and cc %s", ", ".join(recipients), ", ".join(cc)) except smtplib.SMTPAuthenticationError: self.logger.warning("The server didn\'t accept the user\\password " "combination.") except smtplib.SMTPServerDisconnected: self.logger.warning("Server unexpectedly disconnected") except smtplib.SMTPException as e: self.logger.exception("SMTP error occurred: %s", e)
# # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. # Email: [email protected]; WWW: http://www.mysociety.org/ # # $Id: sendemail.py,v 1.5 2009/12/17 17:31:04 francis dead $ # import re, smtplib from minimock import mock, Mock from email.message import Message from email.header import Header from email.utils import formataddr, make_msgid, formatdate from email.charset import Charset, QP charset = Charset('utf-8') charset.body_encoding = QP def send_email(sender, to, message, headers={}): """Sends MESSAGE from SENDER to TO, with HEADERS Returns True if successful, False if not >>> mock('smtplib.SMTP', returns=Mock('smtp_connection')) >>> send_email("[email protected]", "[email protected]", "Hello, this is a message!", { ... 'Subject': 'Mapumental message', ... 'From': ("[email protected]", "Ms. A"), ... 'To': "[email protected]" ... }) # doctest:+ELLIPSIS Called smtplib.SMTP('localhost') Called smtp_connection.sendmail( '[email protected]', '[email protected]',
def send(self, emails): if isinstance(emails, Email): emails = [emails] if len([e for e in emails if e.__class__ != Email]): raise TypeError('emails must be Email or list of Email instances') smtpclass = SMTP_SSL if self.ssl else SMTP if self.server == 'localhost': smtp = smtpclass(self.server) else: smtp = smtpclass(self.server, self.port) if self.login and self.password: smtp.login(self.login, self.password) for email in emails: c = Charset(email.charset) c.header_encoding = QP c.body_encoding = 0 r = Charset(email.charset) r.header_encoding = 0 r.body_encoding = 0 mime1, mime2 = email.mimetype.split('/') mainpart = MIMEBase(mime1, mime2) if not email.force_7bit: mainpart.set_param('charset', email.charset) if len(email.attachments): message = MIMEMultipart('mixed') message.attach(mainpart) del mainpart['mime-version'] else: message = mainpart message['Date'] = datetime.datetime.now().strftime( '%a, %d %b %Y %H:%M:%S') + (" +%04d" % (time.timezone / -36, )) h = Header() fromname = self.fromname.encode(email.charset, 'xmlcharrefreplace') h.append(fromname, r if is7bit(fromname) else c) h.append('<%s>' % self.email, r) message['From'] = h message['To'] = email.get_emails_header('rcpt') if len(email.cc): message['CC'] = email.get_emails_header('cc') if len(email.bcc): message['BCC'] = email.get_emails_header('bcc') subject = email.subject.encode(email.charset, 'xmlcharrefreplace') message['Subject'] = Header(subject, r if is7bit(subject) else c) if email.force_7bit: body = email.body.encode('ascii', 'xmlcharrefreplace') else: body = email.body.encode(email.charset, 'xmlcharrefreplace') mainpart.set_payload(body) for hn, hv in email.headers.items(): if hn == 'X-Mailgun-Campaign-Tag': hn = 'X-Mailgun-Tag' message[hn] = hv if is7bit(body): mainpart['Content-Transfer-Encoding'] = '7bit' else: encode_quopri(mainpart) for attachment in email.attachments: if attachment.__class__ != Attachment: raise TypeError("invalid attachment") mimetype = attachment.mimetype if not mimetype: mimetype, encoding = guess_type(attachment.filename) if not mimetype: mimetype = 'application/octet-stream' mime1, mime2 = mimetype.split('/') part = MIMEBase(mime1, mime2) # using newer rfc2231 (not supported by Outlook): # part.set_param('name', attachment.filename.encode('utf-8'), charset = 'utf-8') # hack: using deprecated rfc2047 - supported by Outlook: part.set_param('name', str(Header(attachment.filename))) del part['mime-version'] if attachment.id: part['Content-Disposition'] = 'inline' else: part['Content-Disposition'] = 'attachment' # using newer rfc2231 (not supported by Outlook): # part.set_param('filename', # attachment.filename.encode('utf-8'), # 'Content-Disposition', # charset = 'utf-8') # hack: using deprecated rfc2047 - supported by Outlook: part.set_param('filename', str(Header(attachment.filename)), 'Content-Disposition') if attachment.id: part['Content-ID'] = '<%s>' % attachment.id part.set_payload(attachment.content) encode_base64(part) message.attach(part) emails = email.rcpt + email.cc + email.bcc smtp.sendmail(self.email, [email for _, email in emails], message.as_string()) smtp.quit()
def send(self, emails): if isinstance(emails, Email): emails = [emails] if len([e for e in emails if e.__class__ != Email]): raise TypeError('emails must be Email or list of Email instances') smtpclass = SMTP_SSL if self.ssl else SMTP if self.server == 'localhost': smtp = smtpclass(self.server) else: smtp = smtpclass(self.server, self.port) if self.tls: smtp.starttls() if self.login and self.password: smtp.login(self.login, self.password) for email in emails: c = Charset(email.charset) c.header_encoding = QP c.body_encoding = 0 r = Charset(email.charset) r.header_encoding = 0 r.body_encoding = 0 email.normalize_email_list('rcpt') email.normalize_email_list('cc') email.normalize_email_list('bcc') mime1, mime2 = email.mimetype.split('/') mainpart = MIMEBase(mime1, mime2) if not email.force_7bit: mainpart.set_param('charset', email.charset) if len(email.attachments): message = MIMEMultipart('mixed') message.attach(mainpart) del mainpart['mime-version'] else: message = mainpart message['Date'] = datetime.datetime.now().strftime( '%a, %d %b %Y %H:%M:%S') + (" +%04d" % (time.timezone/-36,)) h = Header(maxlinelen=1000) # FIXME: what is correct max length? fromname = self.fromname.encode(email.charset, 'xmlcharrefreplace') h.append(fromname, r if is7bit(fromname) else c) h.append('<%s>' % self.email, r) message['From'] = h message['To'] = email.get_emails_header('rcpt') if len(email.cc): message['CC'] = email.get_emails_header('cc') if len(email.bcc): message['BCC'] = email.get_emails_header('bcc') subject = email.subject.encode(email.charset, 'xmlcharrefreplace') message['Subject'] = Header(subject, r if is7bit(subject) else c) if email.reply_to: message['Reply-To'] = email.get_emails_header('reply_to') if email.force_7bit: body = email.body.encode('ascii', 'xmlcharrefreplace') else: body = email.body.encode(email.charset, 'xmlcharrefreplace') mainpart.set_payload(body) if is7bit(body): mainpart['Content-Transfer-Encoding'] = '7bit' else: encode_quopri(mainpart) for attachment in email.attachments: if attachment.__class__ != Attachment: raise TypeError("invalid attachment") mimetype = attachment.mimetype if not mimetype: mimetype, encoding = guess_type(attachment.filename) if not mimetype: mimetype = 'application/octet-stream' mime1, mime2 = mimetype.split('/') part = MIMEBase(mime1, mime2) # using newer rfc2231 (not supported by Outlook): # part.set_param('name', attachment.filename.encode('utf-8'), charset = 'utf-8') # hack: using deprecated rfc2047 - supported by Outlook: part.set_param('name', str(Header(attachment.filename))) del part['mime-version'] if attachment.id: part['Content-Disposition'] = 'inline' else: part['Content-Disposition'] = 'attachment' # using newer rfc2231 (not supported by Outlook): # part.set_param('filename', # attachment.filename.encode('utf-8'), # 'Content-Disposition', # charset = 'utf-8') # hack: using deprecated rfc2047 - supported by Outlook: part.set_param('filename', str(Header(attachment.filename)), 'Content-Disposition') if attachment.id: part['Content-ID'] = '<%s>' % attachment.id part.set_payload(attachment.content) encode_base64(part) # Do this AFTER encode_base64(part), or Content-Transfer-Encoding header will duplicate, # or even happen 2 times with different values. if attachment.charset: part.set_charset(attachment.charset) message.attach(part) smtp.sendmail(self.email, [rcpt[1] for rcpt in email.rcpt] + [cc[1] for cc in email.cc] + [bcc[1] for bcc in email.bcc], message.as_string()) smtp.quit()
def sendmail(subject, text, to=None, cc=None, bcc=None, mail_from=None, html=None): """ Create and send a text/plain message Return a tuple of success or error indicator and message. :param subject: subject of email :type subject: unicode :param text: email body text :type text: unicode :param to: recipients :type to: list :param cc: recipients (CC) :type cc: list :param bcc: recipients (BCC) :type bcc: list :param mail_from: override default mail_from :type mail_from: unicode :param html: html email body text :type html: unicode :rtype: tuple :returns: (is_ok, Description of error or OK message) """ import smtplib import socket from email.message import Message from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.charset import Charset, QP from email.utils import formatdate, make_msgid cfg = app.cfg if not cfg.mail_enabled: return (0, _("Contact administrator: cannot send password recovery e-mail " "because mail configuration is incomplete.")) mail_from = mail_from or cfg.mail_from logging.debug("send mail, from: {0!r}, subj: {1!r}".format(mail_from, subject)) logging.debug("send mail, to: {0!r}".format(to)) if not to and not cc and not bcc: return 1, _("No recipients, nothing to do") subject = subject.encode(CHARSET) # Create a text/plain body using CRLF (see RFC2822) text = text.replace(u'\n', u'\r\n') text = text.encode(CHARSET) # Create a message using CHARSET and quoted printable # encoding, which should be supported better by mail clients. # TODO: check if its really works better for major mail clients text_msg = Message() charset = Charset(CHARSET) charset.header_encoding = QP charset.body_encoding = QP text_msg.set_charset(charset) # work around a bug in python 2.4.3 and above: text_msg.set_payload('=') if text_msg.as_string().endswith('='): text = charset.body_encode(text) text_msg.set_payload(text) if html: msg = MIMEMultipart('alternative') msg.attach(text_msg) html = html.encode(CHARSET) html_msg = MIMEText(html, 'html') html_msg.set_charset(charset) msg.attach(html_msg) else: msg = text_msg address = encodeAddress(mail_from, charset) msg['From'] = address if to: msg['To'] = ','.join(to) if cc: msg['CC'] = ','.join(cc) msg['Date'] = formatdate() msg['Message-ID'] = make_msgid() msg['Subject'] = Header(subject, charset) # See RFC 3834 section 5: msg['Auto-Submitted'] = 'auto-generated' if cfg.mail_sendmail: if bcc: # Set the BCC. This will be stripped later by sendmail. msg['BCC'] = ','.join(bcc) # Set Return-Path so that it isn't set (generally incorrectly) for us. msg['Return-Path'] = address # Send the message if not cfg.mail_sendmail: try: logging.debug("trying to send mail (smtp) via smtp server '{0}'".format(cfg.mail_smarthost)) host, port = (cfg.mail_smarthost + ':25').split(':')[:2] server = smtplib.SMTP(host, int(port)) try: #server.set_debuglevel(1) if cfg.mail_username is not None and cfg.mail_password is not None: try: # try to do TLS server.ehlo() if server.has_extn('starttls'): server.starttls() server.ehlo() logging.debug("tls connection to smtp server established") except: logging.debug("could not establish a tls connection to smtp server, continuing without tls") logging.debug("trying to log in to smtp server using account '{0}'".format(cfg.mail_username)) server.login(cfg.mail_username, cfg.mail_password) server.sendmail(mail_from, (to or []) + (cc or []) + (bcc or []), msg.as_string()) finally: try: server.quit() except AttributeError: # in case the connection failed, SMTP has no "sock" attribute pass except smtplib.SMTPException as e: logging.exception("smtp mail failed with an exception.") return 0, str(e) except (os.error, socket.error) as e: logging.exception("smtp mail failed with an exception.") return (0, _("Connection to mailserver '%(server)s' failed: %(reason)s", server=cfg.mail_smarthost, reason=str(e) )) else: try: logging.debug("trying to send mail (sendmail)") sendmailp = os.popen(cfg.mail_sendmail, "w") # msg contains everything we need, so this is a simple write sendmailp.write(msg.as_string()) sendmail_status = sendmailp.close() if sendmail_status: logging.error("sendmail failed with status: {0!s}".format(sendmail_status)) return 0, str(sendmail_status) except: logging.exception("sendmail failed with an exception.") return 0, _("Mail not sent") logging.debug("Mail sent successfully") return 1, _("Mail sent successfully")
def sendmail(subject, text, to=None, cc=None, bcc=None, mail_from=None, html=None): """ Create and send a text/plain message Return a tuple of success or error indicator and message. :param subject: subject of email :type subject: unicode :param text: email body text :type text: unicode :param to: recipients :type to: list :param cc: recipients (CC) :type cc: list :param bcc: recipients (BCC) :type bcc: list :param mail_from: override default mail_from :type mail_from: unicode :param html: html email body text :type html: unicode :rtype: tuple :returns: (is_ok, Description of error or OK message) """ import smtplib import socket from email.message import Message from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.charset import Charset, QP from email.utils import formatdate, make_msgid cfg = app.cfg if not cfg.mail_enabled: return ( 0, _("Contact administrator: cannot send password recovery e-mail " "because mail configuration is incomplete.")) mail_from = mail_from or cfg.mail_from logging.debug("send mail, from: {0!r}, subj: {1!r}".format( mail_from, subject)) logging.debug("send mail, to: {0!r}".format(to)) if not to and not cc and not bcc: return 1, _("No recipients, nothing to do") subject = subject.encode(CHARSET) # Create a text/plain body using CRLF (see RFC2822) text = text.replace(u'\n', u'\r\n') text = text.encode(CHARSET) # Create a message using CHARSET and quoted printable # encoding, which should be supported better by mail clients. # TODO: check if its really works better for major mail clients text_msg = Message() charset = Charset(CHARSET) charset.header_encoding = QP charset.body_encoding = QP text_msg.set_charset(charset) # work around a bug in python 2.4.3 and above: text_msg.set_payload('=') if text_msg.as_string().endswith('='): text = charset.body_encode(text) text_msg.set_payload(text) if html: msg = MIMEMultipart('alternative') msg.attach(text_msg) html = html.encode(CHARSET) html_msg = MIMEText(html, 'html') html_msg.set_charset(charset) msg.attach(html_msg) else: msg = text_msg address = encodeAddress(mail_from, charset) msg['From'] = address if to: msg['To'] = ','.join(to) if cc: msg['CC'] = ','.join(cc) msg['Date'] = formatdate() msg['Message-ID'] = make_msgid() msg['Subject'] = Header(subject, charset) # See RFC 3834 section 5: msg['Auto-Submitted'] = 'auto-generated' if cfg.mail_sendmail: if bcc: # Set the BCC. This will be stripped later by sendmail. msg['BCC'] = ','.join(bcc) # Set Return-Path so that it isn't set (generally incorrectly) for us. msg['Return-Path'] = address # Send the message if not cfg.mail_sendmail: try: logging.debug( "trying to send mail (smtp) via smtp server '{0}'".format( cfg.mail_smarthost)) host, port = (cfg.mail_smarthost + ':25').split(':')[:2] server = smtplib.SMTP(host, int(port)) try: # server.set_debuglevel(1) if cfg.mail_username is not None and cfg.mail_password is not None: try: # try to do TLS server.ehlo() if server.has_extn('starttls'): server.starttls() server.ehlo() logging.debug( "tls connection to smtp server established") except Exception: logging.debug( "could not establish a tls connection to smtp server, continuing without tls" ) logging.debug( "trying to log in to smtp server using account '{0}'". format(cfg.mail_username)) server.login(cfg.mail_username, cfg.mail_password) server.sendmail(mail_from, (to or []) + (cc or []) + (bcc or []), msg.as_string()) finally: try: server.quit() except AttributeError: # in case the connection failed, SMTP has no "sock" attribute pass except smtplib.SMTPException as e: logging.exception("smtp mail failed with an exception.") return 0, str(e) except (os.error, socket.error) as e: logging.exception("smtp mail failed with an exception.") return ( 0, _("Connection to mailserver '%(server)s' failed: %(reason)s", server=cfg.mail_smarthost, reason=str(e))) else: try: logging.debug("trying to send mail (sendmail)") sendmailp = os.popen(cfg.mail_sendmail, "w") # msg contains everything we need, so this is a simple write sendmailp.write(msg.as_string()) sendmail_status = sendmailp.close() if sendmail_status: logging.error("sendmail failed with status: {0!s}".format( sendmail_status)) return 0, str(sendmail_status) except Exception: logging.exception("sendmail failed with an exception.") return 0, _("Mail not sent") logging.debug("Mail sent successfully") return 1, _("Mail sent successfully")
def __init__(self, payload, charset='utf-8'): MIMENonMultipart.__init__(self, 'text', 'plain', charset=charset) utf8qp = Charset(charset) utf8qp.body_encoding = QP self.set_payload(payload, charset=utf8qp)
def sendMailMessage(self, xMailMessage): COMMASPACE = ', ' if dbg: print("PyMailSMTPService sendMailMessage", file=dbgout) recipients = xMailMessage.getRecipients() sendermail = xMailMessage.SenderAddress sendername = xMailMessage.SenderName subject = xMailMessage.Subject ccrecipients = xMailMessage.getCcRecipients() bccrecipients = xMailMessage.getBccRecipients() if dbg: print("PyMailSMTPService subject: " + subject, file=dbgout) print("PyMailSMTPService from: " + sendername, file=dbgout) print("PyMailSMTPService from: " + sendermail, file=dbgout) print("PyMailSMTPService send to: %s" % (recipients,), file=dbgout) attachments = xMailMessage.getAttachments() textmsg = Message() content = xMailMessage.Body flavors = content.getTransferDataFlavors() if dbg: print("PyMailSMTPService flavors len: %d" % (len(flavors),), file=dbgout) #Use first flavor that's sane for an email body for flavor in flavors: if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find('text/plain') != -1: if dbg: print("PyMailSMTPService mimetype is: " + flavor.MimeType, file=dbgout) textbody = content.getTransferData(flavor) if len(textbody): mimeEncoding = re.sub("charset=.*", "charset=UTF-8", flavor.MimeType) if mimeEncoding.find('charset=UTF-8') == -1: mimeEncoding = mimeEncoding + "; charset=UTF-8" textmsg['Content-Type'] = mimeEncoding textmsg['MIME-Version'] = '1.0' try: #it's a string, get it as utf-8 bytes textbody = textbody.encode('utf-8') except: #it's a bytesequence, get raw bytes textbody = textbody.value if sys.version >= '3': if sys.version_info.minor < 3 or (sys.version_info.minor == 3 and sys.version_info.micro <= 1): #http://stackoverflow.com/questions/9403265/how-do-i-use-python-3-2-email-module-to-send-unicode-messages-encoded-in-utf-8-w #see http://bugs.python.org/16564, etc. basically it now *seems* to be all ok #in python 3.3.2 onwards, but a little busted in 3.3.0 textbody = textbody.decode('iso8859-1') else: textbody = textbody.decode('utf-8') c = Charset('utf-8') c.body_encoding = QP textmsg.set_payload(textbody, c) else: textmsg.set_payload(textbody) break if (len(attachments)): msg = MIMEMultipart() msg.epilogue = '' msg.attach(textmsg) else: msg = textmsg hdr = Header(sendername, 'utf-8') hdr.append('<'+sendermail+'>','us-ascii') msg['Subject'] = subject msg['From'] = hdr msg['To'] = COMMASPACE.join(recipients) if len(ccrecipients): msg['Cc'] = COMMASPACE.join(ccrecipients) if xMailMessage.ReplyToAddress != '': msg['Reply-To'] = xMailMessage.ReplyToAddress mailerstring = "LibreOffice via Caolan's mailmerge component" try: ctx = uno.getComponentContext() aConfigProvider = ctx.ServiceManager.createInstance("com.sun.star.configuration.ConfigurationProvider") prop = uno.createUnoStruct('com.sun.star.beans.PropertyValue') prop.Name = "nodepath" prop.Value = "/org.openoffice.Setup/Product" aSettings = aConfigProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess", (prop,)) mailerstring = aSettings.getByName("ooName") + " " + \ aSettings.getByName("ooSetupVersion") + " via Caolan's mailmerge component" except: pass msg['X-Mailer'] = mailerstring msg['Date'] = formatdate(localtime=True) for attachment in attachments: content = attachment.Data flavors = content.getTransferDataFlavors() flavor = flavors[0] ctype = flavor.MimeType maintype, subtype = ctype.split('/', 1) msgattachment = MIMEBase(maintype, subtype) data = content.getTransferData(flavor) msgattachment.set_payload(data.value) encode_base64(msgattachment) fname = attachment.ReadableName try: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=fname) except: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=('utf-8','',fname)) if dbg: print(("PyMailSMTPService attachmentheader: ", str(msgattachment)), file=dbgout) msg.attach(msgattachment) uniquer = {} for key in recipients: uniquer[key] = True if len(ccrecipients): for key in ccrecipients: uniquer[key] = True if len(bccrecipients): for key in bccrecipients: uniquer[key] = True truerecipients = uniquer.keys() if dbg: print(("PyMailSMTPService recipients are: ", truerecipients), file=dbgout) self.server.sendmail(sendermail, truerecipients, msg.as_string())
def sendMailMessage(self, message): COMMASPACE = ', ' recipients = message.getRecipients() sendermail = message.SenderAddress sendername = message.SenderName subject = message.Subject if isDebugMode(): msg = getMessage(self._ctx, g_message, 161, subject) logMessage(self._ctx, INFO, msg, 'SmtpService', 'sendMailMessage()') ccrecipients = message.getCcRecipients() bccrecipients = message.getBccRecipients() attachments = message.getAttachments() textmsg = Message() content = message.Body flavors = content.getTransferDataFlavors() #Use first flavor that's sane for an email body for flavor in flavors: if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find( 'text/plain') != -1: textbody = content.getTransferData(flavor) if len(textbody): mimeEncoding = re.sub('charset=.*', 'charset=UTF-8', flavor.MimeType) if mimeEncoding.find('charset=UTF-8') == -1: mimeEncoding = mimeEncoding + '; charset=UTF-8' textmsg['Content-Type'] = mimeEncoding textmsg['MIME-Version'] = '1.0' try: #it's a string, get it as utf-8 bytes textbody = textbody.encode('utf-8') except: #it's a bytesequence, get raw bytes textbody = textbody.value if sys.version >= '3': if sys.version_info.minor < 3 or ( sys.version_info.minor == 3 and sys.version_info.micro <= 1): #http://stackoverflow.com/questions/9403265/how-do-i-use-python-3-2-email-module-to-send-unicode-messages-encoded-in-utf-8-w #see http://bugs.python.org/16564, etc. basically it now *seems* to be all ok #in python 3.3.2 onwards, but a little busted in 3.3.0 textbody = textbody.decode('iso8859-1') else: textbody = textbody.decode('utf-8') c = Charset('utf-8') c.body_encoding = QP textmsg.set_payload(textbody, c) else: textmsg.set_payload(textbody) break if len(attachments): msg = MIMEMultipart() msg.epilogue = '' msg.attach(textmsg) else: msg = textmsg header = Header(sendername, 'utf-8') header.append('<' + sendermail + '>', 'us-ascii') msg['Subject'] = subject msg['From'] = header msg['To'] = COMMASPACE.join(recipients) if len(ccrecipients): msg['Cc'] = COMMASPACE.join(ccrecipients) if message.ReplyToAddress != '': msg['Reply-To'] = message.ReplyToAddress mailerstring = "LibreOffice via smtpMailerOOo component" try: configuration = getConfiguration(self._ctx, '/org.openoffice.Setup/Product') name = configuration.getByName('ooName') version = configuration.getByName('ooSetupVersion') mailerstring = "%s %s via via smtpMailerOOo component" % (name, version) except: pass msg['X-Mailer'] = mailerstring msg['Date'] = formatdate(localtime=True) for attachment in attachments: content = attachment.Data flavors = content.getTransferDataFlavors() flavor = flavors[0] ctype = flavor.MimeType maintype, subtype = ctype.split('/', 1) msgattachment = MIMEBase(maintype, subtype) data = content.getTransferData(flavor) msgattachment.set_payload(data.value) encode_base64(msgattachment) fname = attachment.ReadableName try: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=fname) except: msgattachment.add_header('Content-Disposition', 'attachment', \ filename=('utf-8','',fname)) msg.attach(msgattachment) uniquer = {} for key in recipients: uniquer[key] = True if len(ccrecipients): for key in ccrecipients: uniquer[key] = True if len(bccrecipients): for key in bccrecipients: uniquer[key] = True truerecipients = uniquer.keys() error = None try: refused = self._server.sendmail(sendermail, truerecipients, msg.as_string()) except smtplib.SMTPSenderRefused as e: msg = getMessage(self._ctx, g_message, 162, (subject, getExceptionMessage(e))) error = MailException(msg, self) except smtplib.SMTPRecipientsRefused as e: msg = getMessage(self._ctx, g_message, 163, (subject, getExceptionMessage(e))) # TODO: return SendMailMessageFailedException in place of MailException # TODO: error = SendMailMessageFailedException(msg, self) error = MailException(msg, self) except smtplib.SMTPDataError as e: msg = getMessage(self._ctx, g_message, 163, (subject, getExceptionMessage(e))) error = MailException(msg, self) except Exception as e: msg = getMessage(self._ctx, g_message, 163, (subject, getExceptionMessage(e))) error = MailException(msg, self) else: if len(refused) > 0: for address, result in refused.items(): code, reply = getReply(*result) msg = getMessage(self._ctx, g_message, 164, (subject, address, code, reply)) logMessage(self._ctx, SEVERE, msg, 'SmtpService', 'sendMailMessage()') elif isDebugMode(): msg = getMessage(self._ctx, g_message, 165, subject) logMessage(self._ctx, INFO, msg, 'SmtpService', 'sendMailMessage()') if error is not None: logMessage(self._ctx, SEVERE, error.Message, 'SmtpService', 'sendMailMessage()') raise error
def send_email(smtp, recipients, cc, subject, content, csv_reports): """Sends an email with attachment. Refer to https://gist.github.com/BietteMaxime/f75ae41f7b4557274a9f Args: smtp: A dictionary containing smtp info: - smtp_url - smtp_auth_username # optional - smtp_auth_password # optional - smtp_from recipients: To whom to send the email. cc: To whom to cc the email. subject: Email subject. content: Email body content csv_reports: List of dictionaries containing "filename", "data" to construct CSV attachments. Returns: None """ if not isinstance(smtp, dict): logger.warning("smtp is not a dictionary. Skip.") return sender = smtp.get("smtp_from", None) smtp_url = smtp.get("smtp_url", None) smtp_auth_username = smtp.get("smtp_auth_username", None) smtp_auth_password = smtp.get("smtp_auth_password", None) if sender is None or smtp_url is None: logger.warning("Some fields in smtp %s is None. Skip.", smtp) return # Create message container - the correct MIME type is multipart/mixed # to allow attachment. full_email = MIMEMultipart("mixed") full_email["Subject"] = subject full_email["From"] = sender full_email["To"] = ", ".join(recipients) full_email["CC"] = ", ".join(cc) # Create the body of the message (a plain-text version). content = content.encode(ENCODING) content = MIMEText(content, "plain", _charset=ENCODING) full_email.attach(content) # Create the attachment of the message in text/csv. for report in csv_reports: attachment = MIMENonMultipart("text", "csv", charset=ENCODING) attachment.add_header("Content-Disposition", "attachment", filename=report["filename"]) cs = Charset(ENCODING) cs.body_encoding = BASE64 attachment.set_payload(report["data"].encode(ENCODING), charset=cs) full_email.attach(attachment) try: with smtplib.SMTP(smtp_url) as server: if smtp_auth_username is not None and smtp_auth_password is not None: server.starttls() server.login(smtp_auth_username, smtp_auth_password) receivers = recipients + cc server.sendmail(sender, receivers, full_email.as_string()) logger.info("Successfully sent email to %s and cc %s", ", ".join(recipients), ", ".join(cc)) except smtplib.SMTPAuthenticationError: logger.warning("The server didn\'t accept the user\\password " "combination.") except smtplib.SMTPServerDisconnected: logger.warning("Server unexpectedly disconnected") except smtplib.SMTPException as e: logger.exception("SMTP error occurred: %s", e)
def send_email_report(self, _id, subject, sender, recipients, text_body='', html_body='', cc=None, bcc=None, attachments=None, txt_template='analytics_scheduled_report.txt', html_template='analytics_scheduled_report.html'): lock_id = 'analytics_email:{}'.format(str(_id)) if not lock(lock_id, expire=120): return try: charset = Charset('utf-8') charset.header_encoding = QP charset.body_encoding = QP msg = AnalyticsMessage(subject, sender=sender, recipients=recipients, cc=cc, bcc=bcc, body=text_body, charset=charset) reports = [] if attachments is not None: for attachment in attachments: try: uuid = str(uuid4()) if attachment.get('mimetype') == MIME_TYPES.HTML: reports.append({ 'id': uuid, 'type': 'html', 'html': attachment.get('file') }) else: msg.attach(filename=attachment.get('filename'), content_type='{}; name="{}"'.format( attachment.get('mimetype'), attachment.get('filename')), data=b64decode(attachment.get('file')), disposition='attachment', headers={ 'Content-ID': '<{}>'.format(uuid), 'X-Attachment-Id': uuid }.items()) reports.append({ 'id': uuid, 'type': 'image', 'filename': attachment.get('filename'), 'width': attachment.get('width') }) msg.body += '\n[image: {}]'.format( attachment.get('filename')) except Exception as e: logger.error('Failed to generate attachment.') logger.exception(e) msg.body = render_template(txt_template, text_body=text_body, reports=reports) msg.html = render_template(html_template, html_body=html_body.replace( '\r', '').replace('\n', '<br>'), reports=reports) return app.mail.send(msg) except Exception as e: logger.error('Failed to send report email. Error: {}'.format(str(e))) logger.exception(e) finally: unlock(lock_id, remove=True)