diff --git a/QA/pycopia/reports/Email.py b/QA/pycopia/reports/Email.py index 1495e3d..c9ab43e 100644 --- a/QA/pycopia/reports/Email.py +++ b/QA/pycopia/reports/Email.py @@ -1,6 +1,6 @@ #!/usr/bin/python2.4 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab -# +# # $Id$ # # Copyright (C) 1999-2006 Keith Dart @@ -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 writeline(self, text): 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) diff --git a/core/pycopia/ezmail.py b/core/pycopia/ezmail.py index b0f84b0..81c62ff 100644 --- a/core/pycopia/ezmail.py +++ b/core/pycopia/ezmail.py @@ -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 # # This library is free software; you can redistribute it and/or @@ -59,7 +57,7 @@ def formatdate(timeval=None): 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 @@ def _add_recipient(self, header, addr, name): 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 @@ def message_from_string(s, klass=SimpleMessage, strict=0): 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 @@ def _do_attach(multipart, obj): 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 @@ def ezmail(obj, To=None, From=None, subject=None, cc=None, bcc=None, 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 @@ def ezmail(obj, To=None, From=None, subject=None, cc=None, bcc=None, 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 @@ def ezmail(obj, To=None, From=None, subject=None, cc=None, bcc=None, 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 @@ def sendmail(self, From, rcpt_to, msg, mopts=None, rcptopts=None): proc.wait() return proc.exitstatus -def dp(proc): - print(proc) # global configuration CONFIG = get_config() diff --git a/core/pycopia/inet/SMTP.py b/core/pycopia/inet/SMTP.py index 84f4b29..ca3a55e 100644 --- a/core/pycopia/inet/SMTP.py +++ b/core/pycopia/inet/SMTP.py @@ -41,7 +41,7 @@ sendmail-bugs@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 ") >>> 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 _connect(self, addr, retries): 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 send(self, s): 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 getreply(self): 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 @@ def helo(self, name=''): """ 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 @@ def ehlo(self, name=''): 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 has_extn(self, opt): 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 @@ def noop(self): """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 @@ def data(self,msg): 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 @@ def data(self,msg): 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 data(self,msg): 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 @@ def encode_plain(user, password): 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 @@ def starttls(self, keyfile = None, certfile = None): 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 @@ def sendmail(self, from_addr, to_addrs, msg, mail_options=[], 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 @@ def sendmail(self, from_addr, to_addrs, msg, mail_options=[], 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 @@ def parse_data(self, parser): 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: