[pycopia] r747 committed - Allow emailing with non-ascii attachments.

4 views
Skip to first unread message

pyc...@googlecode.com

unread,
Apr 25, 2014, 4:58:20 PM4/25/14
to pyc...@googlegroups.com
Revision: 747
Author: keith.dart
Date: Fri Apr 25 20:58:04 2014 UTC
Log: Allow emailing with non-ascii attachments.

http://code.google.com/p/pycopia/source/detail?r=747

Modified:
/trunk/QA/pycopia/reports/Email.py
/trunk/core/pycopia/ezmail.py
/trunk/core/pycopia/inet/SMTP.py

=======================================
--- /trunk/QA/pycopia/reports/Email.py Sat Sep 11 01:04:21 2010 UTC
+++ /trunk/QA/pycopia/reports/Email.py Fri Apr 25 20:58:04 2014 UTC
@@ -1,6 +1,6 @@
#!/usr/bin/python2.4
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-#
+#
# $Id$
#
# Copyright (C) 1999-2006 Keith Dart <ke...@kdart.com>
@@ -23,13 +23,15 @@
import os
from cStringIO import StringIO

+import chardet
+
from pycopia import reports
NO_MESSAGE = reports.NO_MESSAGE

from pycopia import ezmail

class EmailReport(reports.NullReport):
- """Create an a report that is emailed, rather than written to a file.
+ """Create an a report that is emailed, rather than written to a file.
EmailReport(
[formatter="text/plain"], # formatter type
[recipients=None], # list of recipients, or None. If none
the
@@ -67,17 +69,17 @@
def finalize(self):
"""finalizing this Report sends off the email."""
self.write(self._formatter.finalize())
- report = ezmail.MIMEText.MIMEText(self._fo.getvalue(),
+ report = ezmail.MIMEText.MIMEText(self._fo.getvalue(),
self._formatter.MIMETYPE.split("/")[1])
report["Content-Disposition"] = "inline"
self._message.attach(report)
if self._attach_logfile and self._logfile:
try:
- lfd = open(self._logfile).read()
+ lfd = open(self._logfile, "rb").read()
except:
pass # non-fatal
else:
- logmsg = ezmail.MIMEText.MIMEText(lfd)
+ logmsg = ezmail.MIMEText.MIMEText(lfd,
charset=chardet.detect(lfd))
logmsg["Content-Disposition"] = 'attachment;
filename=%s' % (
os.path.basename(self._logfile), )
self._message.attach(logmsg)
=======================================
--- /trunk/core/pycopia/ezmail.py Mon Jun 10 18:37:35 2013 UTC
+++ /trunk/core/pycopia/ezmail.py Fri Apr 25 20:58:04 2014 UTC
@@ -1,8 +1,6 @@
-#!/usr/bin/python2.4
+#!/usr/bin/python2.7
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
#
-# $Id$
-#
# Copyright (C) 1999-2006 Keith Dart <ke...@kdart.com>
#
# This library is free software; you can redistribute it and/or
@@ -59,7 +57,7 @@


class SimpleMessage(Message.Message):
- def __init__(self, _text, mimetype="text/plain", charset="us-ascii"):
+ def __init__(self, _text, mimetype="text/plain", charset="utf-8"):
Message.Message.__init__(self)
self['MIME-Version'] = '1.0'
self.add_header('Content-Type', mimetype)
@@ -146,8 +144,7 @@


class AutoMessage(SimpleMessage, AutoMessageMixin):
- def __init__(self, text, mimetype="text/plain", charset="us-ascii",
- From=None, To=None):
+ def __init__(self, text, mimetype="text/plain", charset="utf-8",
From=None, To=None):
AutoMessageMixin.__init__(self, From, To)
SimpleMessage.__init__(self, text, mimetype, charset)

@@ -193,14 +190,14 @@
parser = get_parser(klass, strict)
return parser.parsestr(s)

-def self_address():
+def self_address(domain=None):
"""self_address()
Returns address string referring to user running this (yourself)."""
global CONFIG
name, longname = getuser()
- domain = CONFIG.get("domain")
- if domain:
- return "%s@%s" % (name, domain), longname
+ dom = domain or CONFIG.get("domain")
+ if dom:
+ return "%s@%s" % (name, dom), longname
else:
return "%s@%s" % (name, _get_hostname()), longname

@@ -235,8 +232,7 @@
multipart.attach(msg)


-def ezmail(obj, To=None, From=None, subject=None, cc=None, bcc=None,
- extra_headers=None):
+def ezmail(obj, To=None, From=None, subject=None, cc=None, bcc=None,
extra_headers=None, mailhost=None):
"""A generic mailer that sends a multipart-mixed message with
attachments.
The 'obj' parameter may be a MIME* message, or another type of object
that
will be converted to text. If it is a list, each element of the list
will
@@ -251,8 +247,10 @@
outer = MultipartMessage()
for part in obj:
_do_attach(outer, part)
+ elif isinstance(obj, unicode):
+ outer = AutoMessage(obj, charset="utf-8")
else:
- outer = AutoMessage(str(obj).encode("us-ascii"))
+ outer = AutoMessage(unicode(obj), charset="utf-8")

outer.From(From)
if To:
@@ -268,16 +266,16 @@
for name, value in extra_headers.items():
outer[name] = value

- mailhost = CONFIG.get("mailhost", "localhost")
+ mhost = mailhost or CONFIG.get("mailhost", "localhost")

- if mailhost == "localhost":
+ if mhost == "localhost":
smtp = LocalSender()
status = outer.send(smtp)
if not status:
raise MailError(str(status))
else:
from pycopia.inet import SMTP
- smtp = SMTP.SMTP(mailhost, bindto=CONFIG.get("bindto"))
+ smtp = SMTP.SMTP(mhost, bindto=CONFIG.get("bindto"))
errs = outer.send(smtp)
smtp.quit()
if errs:
@@ -286,10 +284,9 @@
return outer["Message-ID"]


-def mail(obj, To=None, From=None, subject=None, cc=None, bcc=None,
- extra_headers=None):
+def mail(obj, To=None, From=None, subject=None, cc=None, bcc=None,
extra_headers=None, mailhost=None):
try:
- return ezmail(obj, To, From, subject, cc, bcc, extra_headers)
+ return ezmail(obj, To, From, subject, cc, bcc, extra_headers,
mailhost)
except MailError as err:
print("Error while sending mail!", file=sys.stderr)
print(err, file=sys.stderr)
@@ -316,8 +313,6 @@
proc.wait()
return proc.exitstatus

-def dp(proc):
- print(proc)

# global configuration
CONFIG = get_config()
=======================================
--- /trunk/core/pycopia/inet/SMTP.py Wed Jan 9 11:22:22 2013 UTC
+++ /trunk/core/pycopia/inet/SMTP.py Fri Apr 25 20:58:04 2014 UTC
@@ -41,7 +41,7 @@
sendma...@sendmail.org.
For local information send email to Postmaster at your site.
End of HELP info
- >>> s.putcmd("vrfy","someone@here")
+ >>> s.putcmd(b"vrfy","someone@here")
>>> s.getreply()
(250, "Somebody OverHere <some...@here.my.org>")
>>> s.quit()
@@ -71,7 +71,8 @@


SMTP_PORT = 25
-CRLF="\r\n"
+CRLF=b"\r\n"
+DOTCRLF=b".\r\n"

OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I)

@@ -324,7 +325,7 @@
def send(self, s):
"""Send string to the server."""
if self.logfile:
- self.logfile.write('send: %r\n' % (s,))
+ self.logfile.write(b'send: %r\n' % (s,))
if self.sock:
try:
self.sock.sendall(s)
@@ -337,9 +338,9 @@
def putcmd(self, cmd, args=""):
"""Send a command to the server."""
if args == "":
- out = '%s%s' % (cmd, CRLF)
+ out = b'%s%s' % (cmd, CRLF)
else:
- out = '%s %s%s' % (cmd, args, CRLF)
+ out = b'%s %s%s' % (cmd, args, CRLF)
self.send(out.encode("ascii"))

def getreply(self):
@@ -390,7 +391,7 @@

def docmd(self, cmd, args=""):
"""Send a command, and return its response code."""
- self.putcmd(cmd,args)
+ self.putcmd(cmd, args)
return self.getreply()

# std smtp commands
@@ -401,9 +402,9 @@
"""
name = name or self._bindto
if name:
- self.putcmd("helo", name)
+ self.putcmd(b"helo", name)
else:
- self.putcmd("helo", socket.getfqdn())
+ self.putcmd(b"helo", socket.getfqdn())
(code,msg)=self.getreply()
self.helo_resp=msg
return (code,msg)
@@ -416,9 +417,9 @@
self.esmtp_features = {}
name = name or self._bindto
if name:
- self.putcmd("ehlo", name)
+ self.putcmd(b"ehlo", name)
else:
- self.putcmd("ehlo", socket.getfqdn())
+ self.putcmd(b"ehlo", socket.getfqdn())
(code,msg)=self.getreply()
# According to RFC1869 some (badly written)
# MTA's will disconnect on an ehlo. Toss an exception if
@@ -469,7 +470,7 @@
def help(self, args=''):
"""SMTP 'help' command.
Returns help text from server."""
- self.putcmd("help", args)
+ self.putcmd(b"help", args)
return self.getreply()

def rset(self):
@@ -480,20 +481,20 @@
"""SMTP 'noop' command -- doesn't do anything :>"""
return self.docmd("noop")

- def mail(self,sender, options=[]):
+ def mail(self,sender, options=None):
"""SMTP 'mail' command -- begins mail xfer session."""
optionlist = ''
if options and self.does_esmtp:
optionlist = ' ' + ' '.join(options)
- self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender) ,optionlist))
+ self.putcmd(b"mail", b"FROM:%s%s" %
(quoteaddr(sender) ,optionlist))
return self.getreply()

- def rcpt(self,recip,options=[]):
+ def rcpt(self,recip, options=None):
"""SMTP 'rcpt' command -- indicates 1 recipient for this mail."""
optionlist = ''
if options and self.does_esmtp:
optionlist = ' ' + ' '.join(options)
- self.putcmd("rcpt","TO:%s%s" % (quoteaddr(recip),optionlist))
+ self.putcmd(b"rcpt", b"TO:%s%s" % (quoteaddr(recip),optionlist))
return self.getreply()

def data(self,msg):
@@ -504,7 +505,7 @@
DATA command; the return value from this method is the final
response code received when the all data is sent.
"""
- self.putcmd("data")
+ self.putcmd(b"data")
(code,repl)=self.getreply()
if self.logfile:
self.logfile.write("data: %s %s\n" % (code,repl))
@@ -513,8 +514,8 @@
else:
q = quotedata(msg)
if q[-2:] != CRLF:
- q = q + CRLF
- q = q + "." + CRLF
+ q += CRLF
+ q += DOTCRLF
self.send(q)
(code, msg)=self.getreply()
if self.logfile:
@@ -523,14 +524,14 @@

def verify(self, address):
"""SMTP 'verify' command -- checks for address validity."""
- self.putcmd("vrfy", quoteaddr(address))
+ self.putcmd(b"vrfy", quoteaddr(address))
return self.getreply()
# a.k.a.
vrfy=verify

def expn(self, address):
"""SMTP 'verify' command -- checks for address validity."""
- self.putcmd("expn", quoteaddr(address))
+ self.putcmd(b"expn", quoteaddr(address))
return self.getreply()

# some useful methods
@@ -566,9 +567,9 @@
return encode_base64("%s\0%s\0%s" % (user, user, password),
eol="")


- AUTH_PLAIN = "PLAIN"
- AUTH_CRAM_MD5 = "CRAM-MD5"
- AUTH_LOGIN = "LOGIN"
+ AUTH_PLAIN = b"PLAIN"
+ AUTH_CRAM_MD5 = b"CRAM-MD5"
+ AUTH_LOGIN = b"LOGIN"

if self.helo_resp is None and self.ehlo_resp is None:
if not (200 <= self.ehlo()[0] <= 299):
@@ -633,8 +634,7 @@
self.file = SSLFakeFile(sslobj)
return (resp, reply)

- def sendmail(self, from_addr, to_addrs, msg, mail_options=[],
- rcpt_options=[]):
+ def sendmail(self, from_addr, to_addrs, msg, mail_options=None,
rcpt_options=None):
"""This command performs an entire mail transaction.

The arguments are::
@@ -691,7 +691,6 @@
empty dictionary.

"""
- msg = msg.encode("ascii")
if self.helo_resp is None and self.ehlo_resp is None:
if not (200 <= self.ehlo()[0] <= 299):
(code,resp) = self.helo()
@@ -701,8 +700,9 @@
if self.does_esmtp:
if self.has_extn('size'):
esmtp_opts.append("size={0:d}".format(len(msg)))
- for option in mail_options:
- esmtp_opts.append(option)
+ if mail_options:
+ for option in mail_options:
+ esmtp_opts.append(option)

(code,resp) = self.mail(from_addr, esmtp_opts)
if code != 250:
@@ -815,9 +815,8 @@
if self.message:
self.data = None

- def send(self, smtp, mail_options=[], rcpt_options=[]):
- """send(smtp_client, mail_options=[], rcpt_options=[])
-Mails this envelope using the supplied SMTP client object."""
+ def send(self, smtp, mail_options=None, rcpt_options=None):
+ """Mails this envelope using the supplied SMTP client object."""
if self.message:
return smtp.sendmail(self.mail_from, self.rcpt_to,
self.message.as_string(), mail_options, rcpt_options)
elif self.data:
Reply all
Reply to author
Forward
0 new messages