4 See documentation in docs/topics/email.rst
6 from cStringIO
import StringIO
7 from email
.MIMEMultipart
import MIMEMultipart
8 from email
.MIMENonMultipart
import MIMENonMultipart
9 from email
.MIMEBase
import MIMEBase
10 from email
.MIMEText
import MIMEText
11 from email
.Utils
import COMMASPACE
, formatdate
12 from email
import Encoders
14 from twisted
.internet
import defer
, reactor
15 from twisted
.mail
.smtp
import ESMTPSenderFactory
17 from scrapy
import log
18 from scrapy
.exceptions
import NotConfigured
19 from scrapy
.conf
import settings
20 from scrapy
.utils
.signal
import send_catch_log
23 # signal sent when message is sent
24 # args: to, subject, body, cc, attach, msg
28 class MailSender(object):
30 def __init__(self
, smtphost
=None, mailfrom
=None, smtpuser
=None, smtppass
=None, \
31 smtpport
=None, debug
=False):
32 self
.smtphost
= smtphost
or settings
['MAIL_HOST']
33 self
.smtpport
= smtpport
or settings
.getint('MAIL_PORT')
34 self
.smtpuser
= smtpuser
or settings
['MAIL_USER']
35 self
.smtppass
= smtppass
or settings
['MAIL_PASS']
36 self
.mailfrom
= mailfrom
or settings
['MAIL_FROM']
39 if not self
.smtphost
or not self
.mailfrom
:
40 raise NotConfigured("MAIL_HOST and MAIL_FROM settings are required")
42 def send(self
, to
, subject
, body
, cc
=None, attachs
=()):
46 msg
= MIMENonMultipart('text', 'plain')
47 msg
['From'] = self
.mailfrom
48 msg
['To'] = COMMASPACE
.join(to
)
49 msg
['Date'] = formatdate(localtime
=True)
50 msg
['Subject'] = subject
54 msg
['Cc'] = COMMASPACE
.join(cc
)
57 msg
.attach(MIMEText(body
))
58 for attach_name
, mimetype
, f
in attachs
:
59 part
= MIMEBase(*mimetype
.split('/'))
60 part
.set_payload(f
.read())
61 Encoders
.encode_base64(part
)
62 part
.add_header('Content-Disposition', 'attachment; filename="%s"' \
68 send_catch_log(signal
=mail_sent
, to
=to
, subject
=subject
, body
=body
,
69 cc
=cc
, attach
=attachs
, msg
=msg
)
72 log
.msg('Debug mail sent OK: To=%s Cc=%s Subject="%s" Attachs=%d' % \
73 (to
, cc
, subject
, len(attachs
)), level
=log
.DEBUG
)
76 dfd
= self
._sendmail
(rcpts
, msg
.as_string())
77 dfd
.addCallbacks(self
._sent
_ok
, self
._sent
_failed
,
78 callbackArgs
=[to
, cc
, subject
, len(attachs
)],
79 errbackArgs
=[to
, cc
, subject
, len(attachs
)])
80 reactor
.addSystemEventTrigger('before', 'shutdown', lambda: dfd
)
83 def _sent_ok(self
, result
, to
, cc
, subject
, nattachs
):
84 log
.msg('Mail sent OK: To=%s Cc=%s Subject="%s" Attachs=%d' % \
85 (to
, cc
, subject
, nattachs
))
87 def _sent_failed(self
, failure
, to
, cc
, subject
, nattachs
):
88 errstr
= str(failure
.value
)
89 log
.msg('Unable to send mail: To=%s Cc=%s Subject="%s" Attachs=%d - %s' % \
90 (to
, cc
, subject
, nattachs
, errstr
), level
=log
.ERROR
)
92 def _sendmail(self
, to_addrs
, msg
):
95 factory
= ESMTPSenderFactory(self
.smtpuser
, self
.smtppass
, self
.mailfrom
, \
96 to_addrs
, msg
, d
, heloFallback
=True, requireAuthentication
=False, \
97 requireTransportSecurity
=False)
99 reactor
.connectTCP(self
.smtphost
, self
.smtpport
, factory
)