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()