def mail_send(self, message, extra_to=list(), extra_cc=list(), attach=list()): """ send message to mail server :param message: TEXT message to be send :param extra_to: Extra user to be send, param is a list :param extra_cc: Extra user to be copy, param is a list :param attach: Attachment file to be send, param is a list :return: None """ if not isinstance(message, email.message.Message): logger.debug("parameter of message is not a valid email context.") raise smtplib.SMTPDataError( 0, "parameter of message is not a valid email context.") if extra_to is not None and len(extra_to) > 0: extra_to = [v.strip(' ') for v in extra_to] self.user_to += extra_to if extra_cc is not None and len(extra_cc) > 0: extra_cc = [v.strip(' ') for v in extra_cc] self.user_cc += extra_cc if self.smtp_address == "" or len(self.user_to) == 0: raise smtplib.SMTPDataError( 1, "sender address or destination address is invalid.") self._connect() try: msg = email.mime.multipart.MIMEMultipart() msg['Subject'] = message['Subject'] msg['From'] = self.smtp_address msg['To'] = email.utils.COMMASPACE.join(self.user_to) msg['Cc'] = email.utils.COMMASPACE.join(self.user_cc) users = self.user_to + self.user_cc msg.attach(message) for attachment in attach: mtype = attachment.get('type', "txt") mcmd = attachment.get('title', "cmd") attachment.add_header('Content-Disposition', 'attachment', filename=mcmd + '.' + mtype) msg.attach(attachment) self.smtp_obj.sendmail(self.smtp_address, users, msg.as_string()) finally: self._disconnec()
def sendmail(self, from_address, to_addresses, message): if self.reject_mail_to in from_address: raise smtplib.SMTPSenderRefused( -1, 'Failed successfuly', from_address) rejected_recipients = dict([ (addr, 'Fail') for addr in to_addresses if self.reject_mail_to in addr]) if rejected_recipients: if len(rejected_recipients) == to_addresses: raise smtplib.SMTPRecipientsRefused(rejected_recipients) else: return rejected_recipients if self.host == 'reject_malformed': raise smtplib.SMTPDataError(-1, 'I pretend that this is bad data')
def sendmail(self, from_addr, to_addrs, msg, mail_options=[], rcpt_options=[]): yield self.ehlo_or_helo_if_needed() esmtp_opts = [] if isinstance(msg, str): msg = smtplib._fix_eols(msg).encode('ascii') if self.does_esmtp: if self.has_extn('size'): esmtp_opts.append("size=%d" % len(msg)) for option in mail_options: esmtp_opts.append(option) (code, resp) = yield self.mail(from_addr, esmtp_opts) if code != 250: if code == 421: self.close() else: yield self._rset() raise smtplib.SMTPSenderRefused(code, resp, from_addr) senderrs = {} if isinstance(to_addrs, str): to_addrs = [to_addrs] for each in to_addrs: (code, resp) = yield self.rcpt(each, rcpt_options) if (code != 250) and (code != 251): senderrs[each] = (code, resp) if code == 421: self.close() raise smtplib.SMTPRecipientsRefused(senderrs) if len(senderrs) == len(to_addrs): # the server refused all our recipients yield self._rset() raise smtplib.SMTPRecipientsRefused(senderrs) (code, resp) = yield self.data(msg) if code != 250: if code == 421: self.close() else: yield self._rset() raise smtplib.SMTPDataError(code, resp) #if we got here then somebody got our mail return senderrs
def test_fallimento_data(self, mock_smtp): """ In caso di fallimento durante il comando data il messaggio viene rimesso in coda, tranne che in caso di errore 5XX che è permanente """ codici = (451, 554, 500, 501, 503, 421, 552, 451, 452) for codice in codici: msg = 'code {}'.format(codice) instance = mock_smtp.return_value instance.sendmail.side_effect = smtplib.SMTPDataError(code=codice, msg=msg) self._invia_msg_singolo() if codice == 501: self.assertEqual(Messaggio.in_coda().count(), 0) else: self.assertEqual(Messaggio.in_coda().count(), 1) self._reset_coda()
def data(self, msg): """SMTP 'DATA' command -- sends message data to server. """ (code, repl) = yield self.docmd(b"data") if code != 354: raise smtplib.SMTPDataError(code, repl) else: if isinstance(msg, str): msg = _fix_eols(msg) q = _quote_periods(msg) if q[-2:] != CRLF: q = q + CRLF q = q + b"." + CRLF #self.send(q) yield self.send(q) (code, msg) = yield self.getreply() raise gen.Return((code, msg))
def data(self, msg): """ SMTP 'DATA' command -- sends message data to server. Automatically quotes lines beginning with a period per rfc821. Raises SMTPDataError if there is an unexpected reply to the DATA command; the return value from this method is the final response code received when the all data is sent. msg: Message in a string buffer or a filename referring to a file containin the data to be send (string). Returns: Tuple with the status code + a message from the SMTP host (tuple/integer, string). """ self.putcmd("data") (code, repl) = self.getreply() if self.debuglevel > 0: print("data:", (code, repl)) if code != 354: raise smtplib.SMTPDataError(code, repl) else: if (not self.__msgInFile): # Data contained in the message in memory. q = smtplib.quotedata(msg) if q[-2:] != smtplib.CRLF: q = q + smtplib.CRLF q = q + "." + smtplib.CRLF self.send(q) else: # Data to send is contained in a file. fo = open(msg) while (1): buf = fo.read(16384) if (buf == ""): break qBuf = smtplib.quotedata(buf) self.send(buf) fo.close() self.send(smtplib.CRLF + "." + smtplib.CRLF) (code, msg) = self.getreply() if (self.debuglevel > 0): print("data:", (code, msg)) return (code, msg)
async def send(to_addrs, subject, mail_msg, attachs=None): if isinstance(to_addrs, str): to_addrs = [to_addrs] logging.debug(('邮件收件人:', to_addrs)) message = await EmailSender._make_email(to_addrs, subject, mail_msg, attachs) # with open('send_email.eml', 'wb') as fd: # logging.debug('邮件已经保存到本地文件') # fd.write(message.as_bytes()) # return # noinspection PyBroadException try: await EmailSender._send_direct(message, to_addrs) except Exception: logging.error(f'邮件直接投递失败') try: await EmailSender._send_proxy(message, to_addrs) except Exception as err: logging.error(f'邮件中转投递失败') logging.exception(err) raise smtplib.SMTPDataError(-1, b'Unknown Error')
async def _send_proxy(message, to_addrs): if not EmailSender.servers: logging.error(f'可用邮件发送服务器为空') raise Exception(b'Email server is empty.') for server in EmailSender.servers: try: logging.debug('使用中转邮件服务器:' + server['host']) with SmtpProxyServer(server['host'], 25, server['user'], server['pass']) as smtp_server: send_errs = smtp_server.sendmail(EmailSender.sender, to_addrs, message.as_string()) if not send_errs: logging.debug('中转邮件发送成功') return else: logging.error('中转邮件发送失败') logging.error(send_errs) except Exception as e: logging.fatal('中转邮件发送异常') logging.exception(e) raise smtplib.SMTPDataError(-1, b'Unknown Error')
def send_email(self, msg): smtp = smtplib.SMTP(host=CONF.get("email", "host"), port=CONF.get("email", "port")) smtp.ehlo() try: code, err = smtp.mail(CONF.get("email", "sender")) if code != 250: raise smtplib.SMTPSenderRefused(code, err, CONF.get("email", "sender")) rcpterrs = {} for rcpt in CONF.get("email", "recipients").split(","): code, err = smtp.rcpt(rcpt) if code not in (250, 251): rcpterrs[rcpt] = (code, err) if rcpterrs: raise smtplib.SMTPRecipientsRefused(rcpterrs) code, err = smtp.data(msg.as_string()) if code != 250: raise smtplib.SMTPDataError(code, err) finally: try: smtp.quit() except smtplib.SMTPServerDisconnected: pass
def sendmail(server, from_addr, to_addrs, msg): server.ehlo_or_helo_if_needed() esmtp_opts = [] if server.does_esmtp and server.has_extn('size'): esmtp_opts.append("size=%d" % len(msg)) (code, resp) = server.mail(from_addr, esmtp_opts) if code != 250: server.rset() raise smtplib.SMTPSenderRefused(code, resp, from_addr) senderrs = {} if isinstance(to_addrs, basestring): to_addrs = [to_addrs] for each in to_addrs: (code, resp) = server.rcpt(each) if (code != 250) and (code != 251): senderrs[each] = (code, resp) if len(senderrs) == len(to_addrs): server.rset() raise smtplib.SMTPRecipientsRefused(senderrs) (code, resp) = server.data(msg) if code != 250: server.rset() raise smtplib.SMTPDataError(code, resp) return resp
def test_email_plugin(mock_smtp, mock_smtpssl): """ API: NotifyEmail Plugin() """ # Disable Throttling to speed testing plugins.NotifyBase.NotifyBase.request_rate_per_sec = 0 # iterate over our dictionary and test it out for (url, meta) in TEST_URLS: # Our expected instance instance = meta.get('instance', None) # Our expected server objects self = meta.get('self', None) # Our expected Query response (True, False, or exception type) response = meta.get('response', True) test_smtplib_exceptions = meta.get('test_smtplib_exceptions', False) # Our mock of our socket action mock_socket = mock.Mock() mock_socket.starttls.return_value = True mock_socket.login.return_value = True # Create a mock SMTP Object mock_smtp.return_value = mock_socket mock_smtpssl.return_value = mock_socket if test_smtplib_exceptions: # Handle exception testing; first we turn the boolean flag ito # a list of exceptions test_smtplib_exceptions = ( smtplib.SMTPHeloError(0, 'smtplib.SMTPHeloError() not handled'), smtplib.SMTPException(0, 'smtplib.SMTPException() not handled'), RuntimeError(0, 'smtplib.HTTPError() not handled'), smtplib.SMTPRecipientsRefused( 'smtplib.SMTPRecipientsRefused() not handled'), smtplib.SMTPSenderRefused( 0, 'smtplib.SMTPSenderRefused() not handled', '*****@*****.**'), smtplib.SMTPDataError(0, 'smtplib.SMTPDataError() not handled'), smtplib.SMTPServerDisconnected( 'smtplib.SMTPServerDisconnected() not handled'), ) try: obj = Apprise.instantiate(url, suppress_exceptions=False) if obj is None: # We're done (assuming this is what we were expecting) assert instance is None continue if instance is None: # Expected None but didn't get it print('%s instantiated %s (but expected None)' % (url, str(obj))) assert (False) assert (isinstance(obj, instance)) if isinstance(obj, plugins.NotifyBase.NotifyBase): # We loaded okay; now lets make sure we can reverse this url assert (isinstance(obj.url(), six.string_types) is True) # Instantiate the exact same object again using the URL from # the one that was already created properly obj_cmp = Apprise.instantiate(obj.url()) # Our object should be the same instance as what we had # originally expected above. if not isinstance(obj_cmp, plugins.NotifyBase.NotifyBase): # Assert messages are hard to trace back with the way # these tests work. Just printing before throwing our # assertion failure makes things easier to debug later on print('TEST FAIL: {} regenerated as {}'.format( url, obj.url())) assert (False) if self: # Iterate over our expected entries inside of our object for key, val in self.items(): # Test that our object has the desired key assert (hasattr(key, obj)) assert (getattr(key, obj) == val) try: if test_smtplib_exceptions is False: # check that we're as expected assert obj.notify(title='test', body='body', notify_type=NotifyType.INFO) == response else: for exception in test_smtplib_exceptions: mock_socket.sendmail.side_effect = exception try: assert obj.notify( title='test', body='body', notify_type=NotifyType.INFO) is False except AssertionError: # Don't mess with these entries raise except Exception: # We can't handle this exception type raise except AssertionError: # Don't mess with these entries print('%s AssertionError' % url) raise except Exception as e: # Check that we were expecting this exception to happen if not isinstance(e, response): raise except AssertionError: # Don't mess with these entries print('%s AssertionError' % url) raise except Exception as e: # Handle our exception if (instance is None): raise if not isinstance(e, instance): raise
def __sendmail(self, from_addr, to_addrs, msg, mail_options=[], rcpt_options=[]): """This command performs an entire mail transaction. The arguments are: - from_addr : The address sending this mail. - to_addrs : A list of addresses to send this mail to. A bare string will be treated as a list with 1 address. - msg : The message to send. - mail_options : List of ESMTP options (such as 8bitmime) for the mail command. - rcpt_options : List of ESMTP options (such as DSN commands) for all the rcpt commands. If there has been no previous EHLO or HELO command this session, this method tries ESMTP EHLO first. If the server does ESMTP, message size and each of the specified options will be passed to it. If EHLO fails, HELO will be tried and ESMTP options suppressed. This method will return normally if the mail is accepted for at least one recipient. It returns a dictionary, with one entry for each recipient that was refused. Each entry contains a tuple of the SMTP error code and the accompanying error message sent by the server. This method may raise the following exceptions: SMTPHeloError The server didn't reply properly to the helo greeting. SMTPRecipientsRefused The server rejected ALL recipients (no mail was sent). SMTPSenderRefused The server didn't accept the from_addr. SMTPDataError The server replied with an unexpected error code (other than a refusal of a recipient). Note: the connection will be open even after an exception is raised. Example: >>> import smtplib >>> s=smtplib.SMTP("localhost") >>> tolist=["*****@*****.**","*****@*****.**","*****@*****.**","*****@*****.**"] >>> msg = '''From: [email protected] ... Subject: testin'... ... ... This is a test ''' >>> s.sendmail("*****@*****.**",tolist,msg) { "*****@*****.**" : ( 550 ,"User unknown" ) } >>> s.quit() In the above example, the message was accepted for delivery to three of the four addresses, and one was rejected, with the error code 550. If all addresses are accepted, then the method will return an empty dictionary. """ self.ehlo_or_helo_if_needed() esmtp_opts = [] if self.does_esmtp: # Hmmm? what's this? -ddm # self.esmtp_features['7bit']="" if self.has_extn('size'): esmtp_opts.append("size=%d" % len(msg)) for option in mail_options: esmtp_opts.append(option) (code, resp) = self.mail(from_addr, esmtp_opts) if code != 250: self.rset() raise smtplib.SMTPSenderRefused(code, resp, from_addr) senderrs = {} if isinstance(to_addrs, basestring): to_addrs = [to_addrs] for each in to_addrs: (code, resp) = self.rcpt(each, rcpt_options) if (code != 250) and (code != 251): senderrs[each] = (code, resp) if len(senderrs) == len(to_addrs): # the server refused all our recipients self.rset() raise smtplib.SMTPRecipientsRefused(senderrs) (code, resp) = self.data(msg) if code != 250: self.rset() raise smtplib.SMTPDataError(code, resp) #if we got here then somebody got our mail return senderrs
def test_email_plugin(mock_smtp, mock_smtpssl): """ API: NotifyEmail Plugin() """ # iterate over our dictionary and test it out for (url, meta) in TEST_URLS: # Our expected instance instance = meta.get('instance', None) # Our expected exception exception = meta.get('exception', None) # Our expected server objects self = meta.get('self', None) # Our expected Query response (True, False, or exception type) response = meta.get('response', True) test_smtplib_exceptions = meta.get('test_smtplib_exceptions', False) # Our mock of our socket action mock_socket = mock.Mock() mock_socket.starttls.return_value = True mock_socket.login.return_value = True # Create a mock SMTP Object mock_smtp.return_value = mock_socket mock_smtpssl.return_value = mock_socket if test_smtplib_exceptions: # Handle exception testing; first we turn the boolean flag ito # a list of exceptions test_smtplib_exceptions = ( smtplib.SMTPHeloError(0, 'smtplib.SMTPHeloError() not handled'), smtplib.SMTPException(0, 'smtplib.SMTPException() not handled'), RuntimeError(0, 'smtplib.HTTPError() not handled'), smtplib.SMTPRecipientsRefused( 'smtplib.SMTPRecipientsRefused() not handled'), smtplib.SMTPSenderRefused( 0, 'smtplib.SMTPSenderRefused() not handled', '*****@*****.**'), smtplib.SMTPDataError(0, 'smtplib.SMTPDataError() not handled'), smtplib.SMTPServerDisconnected( 'smtplib.SMTPServerDisconnected() not handled'), ) try: obj = Apprise.instantiate(url, suppress_exceptions=False) assert (exception is None) if obj is None: # We're done continue if instance is None: # Expected None but didn't get it print('%s instantiated %s' % (url, str(obj))) assert (False) assert (isinstance(obj, instance)) if self: # Iterate over our expected entries inside of our object for key, val in self.items(): # Test that our object has the desired key assert (hasattr(key, obj)) assert (getattr(key, obj) == val) try: if test_smtplib_exceptions is False: # check that we're as expected assert obj.notify(title='test', body='body', notify_type=NotifyType.INFO) == response else: for exception in test_smtplib_exceptions: mock_socket.sendmail.side_effect = exception try: assert obj.notify( title='test', body='body', notify_type=NotifyType.INFO) is False except AssertionError: # Don't mess with these entries raise except Exception as e: # We can't handle this exception type print('%s / %s' % (url, str(e))) assert False except AssertionError: # Don't mess with these entries raise except Exception as e: # Check that we were expecting this exception to happen assert isinstance(e, response) except AssertionError: # Don't mess with these entries print('%s AssertionError' % url) raise except Exception as e: # Handle our exception print('%s / %s' % (url, str(e))) assert (exception is not None) assert (isinstance(e, exception))
def sendmail(self, from_addr, to_addrs, msg, mail_options=None, rcpt_options=None): if not to_addrs: return None rcpt_options = rcpt_options or [] mail_options = mail_options or [] esmtp_opts = [] if self.does_esmtp: if self.has_extn('size'): esmtp_opts.append("size=%d" % len(msg)) for option in mail_options: esmtp_opts.append(option) response = self.make_response() from_addr = sanitize_email(from_addr) response.from_addr = from_addr response.esmtp_opts = esmtp_opts[:] (code, resp) = self.mail(from_addr, esmtp_opts) response.set_status('mail', code, resp) if code != 250: self._rset() exc = smtplib.SMTPSenderRefused(code, resp, from_addr) response.set_exception(exc) return response if not isinstance(to_addrs, (list, tuple)): to_addrs = [to_addrs] to_addrs = [sanitize_email(e) for e in to_addrs] response.to_addrs = to_addrs response.rcpt_options = rcpt_options[:] response.refused_recipients = {} for a in to_addrs: (code, resp) = self.rcpt(a, rcpt_options) response.set_status('rcpt', code, resp, recipient=a) if (code != 250) and (code != 251): response.refused_recipients[a] = (code, resp) if len(response.refused_recipients) == len(to_addrs): # the server refused all our recipients self._rset() exc = smtplib.SMTPRecipientsRefused(response.refused_recipients) response.set_exception(exc) return response (code, resp) = self.data(msg) response.set_status('data', code, resp) if code != 250: self._rset() exc = smtplib.SMTPDataError(code, resp) response.set_exception(exc) return response response._finished = True return response