1515import sys
1616import time
1717import copy
18+ import textwrap
19+ import traceback
20+ from contextlib import contextmanager
1821
1922# Testing mode:
2023# import ietf.utils.mail
@@ -37,6 +40,22 @@ def add_headers(msg):
3740 msg ['From' ] = settings .DEFAULT_FROM_EMAIL
3841 return msg
3942
43+ class SMTPRefusedRecipients (smtplib .SMTPException ):
44+
45+ def __init__ (self , message , original_msg , refusals ):
46+ smtplib .SMTPException .__init__ (self , message )
47+ self .original_msg = original_msg
48+ self .refusals = refusals
49+
50+ def detailed_refusals (self ):
51+ details = "The following recipients were refused:\n "
52+ for recipient in self .refusals :
53+ details += "\n %s: %s" % (recipient ,self .refusals [recipient ])
54+ return details
55+
56+ def summary_refusals (self ):
57+ return ", " .join (["%s (%s)" % (x ,self .refusals [x ][0 ]) for x in self .refusals ])
58+
4059def send_smtp (msg , bcc = None ):
4160 '''
4261 Send a Message via SMTP, based on the django email server settings.
@@ -62,11 +81,11 @@ def send_smtp(msg, bcc=None):
6281 server = None
6382 try :
6483 server = smtplib .SMTP ()
65- log ("SMTP server: %s" % repr (server ))
84+ # log("SMTP server: %s" % repr(server))
6685 #if settings.DEBUG:
6786 # server.set_debuglevel(1)
6887 conn_code , conn_msg = server .connect (settings .EMAIL_HOST , settings .EMAIL_PORT )
69- log ("SMTP connect: code: %s; msg: %s" % (conn_code , conn_msg ))
88+ # log("SMTP connect: code: %s; msg: %s" % (conn_code, conn_msg))
7089 if settings .EMAIL_HOST_USER and settings .EMAIL_HOST_PASSWORD :
7190 server .ehlo ()
7291 if 'starttls' not in server .esmtp_features :
@@ -78,19 +97,21 @@ def send_smtp(msg, bcc=None):
7897 # advertise the AUTH capability.
7998 server .ehlo ()
8099 server .login (settings .EMAIL_HOST_USER , settings .EMAIL_HOST_PASSWORD )
81- server .sendmail (frm , to , msg .as_string ())
82- # note: should pay attention to the return code, as it may
83- # indicate that someone didn't get the email.
84- except :
85- if server :
86- server .quit ()
100+ unhandled = server .sendmail (frm , to , msg .as_string ())
101+ if unhandled != {}:
102+ raise SMTPRefusedRecipients (message = "%d addresses were refused" % len (unhandled ),original_msg = msg ,refusals = unhandled )
103+ except Exception as e :
87104 # need to improve log message
88- log ("got exception '%s' (%s) trying to send email from '%s' to %s subject '%s'" % (sys . exc_info ()[ 0 ], sys . exc_info ()[ 1 ], frm , to , msg .get ('Subject' , '[no subject]' )))
89- if isinstance (sys . exc_info ()[ 0 ] , smtplib .SMTPException ):
90- raise
105+ log ("Exception while trying to send email from '%s' to %s subject '%s'" % (frm , to , msg .get ('Subject' , '[no subject]' )))
106+ if isinstance (e , smtplib .SMTPException ):
107+ raise
91108 else :
92- raise smtplib .SMTPException ({'really' : sys .exc_info ()[0 ], 'value' : sys .exc_info ()[1 ], 'tb' : sys .exc_info ()[2 ]})
93- server .quit ()
109+ raise smtplib .SMTPException ({'really' : sys .exc_info ()[0 ], 'value' : sys .exc_info ()[1 ], 'tb' : traceback .format_tb (sys .exc_info ()[2 ])})
110+ finally :
111+ try :
112+ server .quit ()
113+ except smtplib .SMTPServerDisconnected :
114+ pass
94115 log ("sent email from '%s' to %s subject '%s'" % (frm , to , msg .get ('Subject' , '[no subject]' )))
95116
96117def copy_email (msg , to , toUser = False , originalBcc = None ):
@@ -233,3 +254,68 @@ def send_mail_message(request, message, extra={}):
233254
234255 send_mail_text (request , message .to , message .frm , message .subject ,
235256 message .body , cc = message .cc , bcc = message .bcc , extra = e )
257+
258+ def log_smtp_exception (e ):
259+
260+ # See if it's a non-smtplib exception that we faked
261+
262+ if len (e .args )== 1 and isinstance (e .args [0 ],dict ) and e .args [0 ].has_key ('really' ):
263+ orig = e .args [0 ]
264+ extype = orig ['really' ]
265+ tb = orig ['tb' ]
266+ value = orig ['value' ]
267+ else :
268+ extype = sys .exc_info ()[0 ]
269+ value = sys .exc_info ()[1 ]
270+ tb = traceback .format_tb (sys .exc_info ()[2 ])
271+
272+
273+ log ("SMTP Exception: %s : %s" % (extype ,value ))
274+ if isinstance (e ,SMTPRefusedRecipients ):
275+ log (" Refused: %s" % (e .summary_refusals ()))
276+ log (" Traceback: %s" % tb )
277+ return (extype , value , tb )
278+
279+ @contextmanager
280+ def smtp_error_logging (thing ):
281+ try :
282+ yield thing
283+ except smtplib .SMTPException as e :
284+ (extype , value , tb ) = log_smtp_exception (e )
285+
286+ msg = textwrap .dedent ("""\
287+ To: <action@ietf.org>
288+ From: %s
289+ """ ) % settings .SERVER_EMAIL
290+ if isinstance (e ,SMTPRefusedRecipients ):
291+ msg += textwrap .dedent ("""\
292+ Subject: Recipients were refused while sending mail with Subject: %s
293+
294+ This is a message from the datatracker to IETF-Action about an email
295+ delivery failure, when sending email from the datatracker.
296+
297+ %s
298+
299+ The original message follows:
300+ -------- BEGIN ORIGINAL MESSAGE --------
301+ %s
302+ --------- END ORIGINAL MESSAGE ---------
303+ """ ) % (e .original_msg .get ('Subject' , '[no subject]' ),e .detailed_refusals (),e .original_msg .as_string ())
304+
305+ else :
306+ msg += textwrap .dedent ("""\
307+ Subject: Datatracker error while sending email
308+
309+ This is a message from the datatracker to IETF-Action about an email
310+ delivery failure, when sending email from the datatracker.
311+
312+ SMTP Exception: %s
313+
314+ Error Message: %s
315+ """ ) % (extype ,value )
316+ try :
317+ send_mail_preformatted (request = None , preformatted = msg )
318+ except smtplib .SMTPException as ticket_mail_error :
319+ log ("Exception encountered while send a ticket to the secretariat" )
320+ (extype ,value ) = sys .exc_info ()[:2 ]
321+ log ("SMTP Exception: %s : %s" % (extype ,value ))
0 commit comments