def send(self, strg): """Send `strg' to the server.""" if self.debuglevel > 0: print('send:', repr(strg), file=sys.stderr) if hasattr(self, 'sock') and self.sock: try: if self.transferSize: lock = threading.Lock() lock.acquire() self.transferSize = len(strg) lock.release() for i in range(0, self.transferSize, chunksize): if type(strg) == bytes: self.sock.send((strg[i:i + chunksize])) else: self.sock.send( (strg[i:i + chunksize]).encode('utf-8')) lock.acquire() self.progress = i lock.release() else: self.sock.sendall(strg.encode('utf-8')) except socket.error: self.close() raise smtplib.SMTPServerDisconnected('Server not connected') else: raise smtplib.SMTPServerDisconnected('please run connect() first')
def send(self, strg): """Send `strg' to the server.""" log.debug_no_auth('send: {}'.format(strg[:300])) if hasattr(self, 'sock') and self.sock: try: if self.transferSize: lock=threading.Lock() lock.acquire() self.transferSize = len(strg) lock.release() for i in range(0, self.transferSize, CHUNKSIZE): if isinstance(strg, bytes): self.sock.send((strg[i:i + CHUNKSIZE])) else: self.sock.send((strg[i:i + CHUNKSIZE]).encode('utf-8')) lock.acquire() self.progress = i lock.release() else: self.sock.sendall(strg.encode('utf-8')) except socket.error: self.close() raise smtplib.SMTPServerDisconnected('Server not connected') else: raise smtplib.SMTPServerDisconnected('please run connect() first')
def send(self, str_): """Send `str_' to the server.""" if self.debuglevel > 0: print>>sys.stderr, 'send:', repr(str_) if self.sock: try: # Loop: Wait for select() to indicate that the socket is ready # for data, and call send(). If send returns a value smaller # than the total length of str_, save the remaining data, and # continue to attempt to send it. If select() times out, raise # an exception and let the handler close the connection. while str_: readySocks = select.select([], [self.sock], [], smtpTimeout) if not readySocks[1]: raise socket.error('Write timed out.') sent = self.sock.send(str_) if sent < len(str_): str_ = str_[sent:] else: # All the data was written, break the loop. break except socket.error: self.close() raise smtplib.SMTPServerDisconnected('Server not connected') else: raise smtplib.SMTPServerDisconnected('please run connect() first')
async def getreply(self): """Get a reply from the server. Returns a tuple consisting of: - server response code (e.g. '250', or such, if all goes well) Note: returns -1 if it can't read response code. - server response string corresponding to response code (multiline responses are converted to a single, multiline string). Raises SMTPServerDisconnected if end-of-file is reached. """ resp = [] if self.file is None: self.file = self.sock.makefile('rb') while 1: try: line = await self.file.readline() except OSError as e: self.close() raise smtplib.SMTPServerDisconnected( "Connection unexpectedly closed: " + str(e)) if not line: self.close() raise smtplib.SMTPServerDisconnected( "Connection unexpectedly closed") if self.debuglevel > 0: self._print_debug('reply:', repr(line)) if len(line) > MAXLINE: self.close() raise smtplib.SMTPResponseException(500, "Line too long.") resp.append(line[4:].strip(b' \t\r\n')) code = line[:3] # Check that the error code is syntactically correct. # Don't attempt to read a continuation line if it is broken. try: errcode = int(code) except ValueError: errcode = -1 break # Check if multiline response. if line[3:4] != b"-": break errmsg = b"\n".join(resp) if self.debuglevel > 0: self._print_debug('reply: retcode (%s); Msg: %a' % (errcode, errmsg)) return errcode, errmsg
def getreply(self): resp = [] while True: try: response = yield self.stream.read_until(CRLF) except socket.error as e: logger.exception(e) raise smtplib.SMTPServerDisconnected("Connection unexpectedly closed") resp.append(response[4:]) code = response[0:3] try: code= int(code) except ValueError: code = -1 break if response[3] in b' \r\n': break msg = b'\n'.join(resp) raise gen.Return((code, msg))
def test_connection_closed_if_send_gives_error(self, mock_smtp_class): mock_smtp = mock_smtp_class.return_value mock_smtp.send_message.side_effect = smtplib.SMTPServerDisconnected() try: self.action.execute("MSFT has crossed $10 price level") except Exception: pass self.assertTrue(mock_smtp.quit.called)
def test_fallimento_disconnect(self, mock_smtp): """ In caso di disconessione del server il messaggio viene rimesso in coda """ instance = mock_smtp.return_value instance.sendmail.side_effect = smtplib.SMTPServerDisconnected({}) self._invia_msg_singolo() self.assertEqual(Messaggio.in_coda().count(), 1) self._reset_coda()
def test_expecting_SMTP_server_disconnected_error(self) -> None: check = UnitTestSmtpCredentials._create_mocked_method_raising( smtplib.SMTPServerDisconnected()) status, message = UnitTestSmtpCredentials._run_mocked_check( self.test_expecting_SMTP_server_disconnected_error.__name__, check) self.assertEqual( (status, message), (False, smtpcheck.Messages.SMTP_DISCONNECTION_ERROR.value))
def test_remote_dropped_connection(mock_send, mailer, message, caplog): mock_send.side_effect = smtplib.SMTPServerDisconnected() mailer.send(message) assert len(caplog.record_tuples) == 1 _, severity, msg = caplog.record_tuples[0] assert severity == logging.ERROR assert 'Failed to send message: Connection closed by remote host.' in msg
def test_send_fail_disconnected(self, connection_mock): connection_mock.return_value.send_messages.side_effect = ( smtplib.SMTPServerDisconnected("Something failed")) result = toolkit.members.tasks.send_mailout( u"The \xa31 Subject!", u"The Body!\nThat will be $1, please\nTa!", None) self.assertEqual(result, (True, 0, "Mailout job died: 'Something failed'"))
async def send(self, s): """Send `s' to the server.""" if self.debuglevel > 0: self._print_debug('send:', repr(s)) if hasattr(self, 'sock') and self.sock: if isinstance(s, str): # send is used by the 'data' command, where command_encoding # should not be used, but 'data' needs to convert the string to # binary itself anyway, so that's not a problem. s = s.encode(self.command_encoding) try: await self.sock.sendall(s) except OSError: self.close() raise smtplib.SMTPServerDisconnected('Server not connected') else: raise smtplib.SMTPServerDisconnected('please run connect() first') return
def test_disconnect_fail(self, close_mock): subject = u"The Subject \u2603!" body = u"The Body!\nThat will be \u20ac1, please\nTa \u2603!" close_mock.side_effect = ( smtplib.SMTPServerDisconnected("Already disconnected?")) result = toolkit.members.tasks.send_mailout(subject, body, None) self._assert_mail_sent(result, subject, body, None, True) close_mock.assert_called_once()
def test_smtp_reconnect(self, mock_smtp): self.config.email.use_ssl = False sender = email_sender.SMTPEmailSender(self.config) smtp = mock_smtp.return_value smtp.noop.side_effect = smtplib.SMTPServerDisconnected() sender.send('*****@*****.**', 'Test!', 'foo bar.') self.assertEqual(mock_smtp.call_args_list, [call(SMTP_HOST), call(SMTP_HOST)]) smtp.sendmail.assert_called_once()
async def ehlo(self, name=''): """ SMTP 'ehlo' command. Hostname to send for this command defaults to the FQDN of the local host. """ self.esmtp_features = {} await self.putcmd(self.ehlo_msg, name or self.local_hostname) (code, msg) = await self.getreply() # According to RFC1869 some (badly written) # MTA's will disconnect on an ehlo. Toss an exception if # that happens -ddm if code == -1 and len(msg) == 0: self.close() raise smtplib.SMTPServerDisconnected("Server not connected") self.ehlo_resp = msg if code != 250: return (code, msg) self.does_esmtp = 1 #parse the ehlo response -ddm assert isinstance(self.ehlo_resp, bytes), repr(self.ehlo_resp) resp = self.ehlo_resp.decode("latin-1").split('\n') del resp[0] for each in resp: # To be able to communicate with as many SMTP servers as possible, # we have to take the old-style auth advertisement into account, # because: # 1) Else our SMTP feature parser gets confused. # 2) There are some servers that only advertise the auth methods we # support using the old style. auth_match = smtplib.OLDSTYLE_AUTH.match(each) if auth_match: # This doesn't remove duplicates, but that's no problem self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \ + " " + auth_match.groups(0)[0] continue # RFC 1869 requires a space between ehlo keyword and parameters. # It's actually stricter, in that only spaces are allowed between # parameters, but were not going to check for that here. Note # that the space isn't present if there are no parameters. m = re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?', each) if m: feature = m.group("feature").lower() params = m.string[m.end("feature"):].strip() if feature == "auth": self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \ + " " + params else: self.esmtp_features[feature] = params return (code, msg)
def ehlo(self, name=''): self.esmtp_features = {} yield self.putcmd(self.ehlo_msg, name or self.local_hostname) (code, msg) = yield self.getreply() self.ehlo_resp = msg if code == -1 and len (msg) == 0 : self.close() raise smtplib.SMTPServerDisconnected("Server not connected") if code != 250: raise gen.Return((code, msg)) self.does_esmtp =1 #parse the ehlo response -ddm resp=self.ehlo_resp.split(b'\n') del resp[0] for each in resp: val = each.decode() auth_match = smtplib.OLDSTYLE_AUTH.match(val) if auth_match: # This doesn't remove duplicates, but that's no problem self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \ + " " + auth_match.groups(0)[0] continue # RFC 1869 requires a space between ehlo keyword and parameters. # It's actually stricter, in that only spaces are allowed between # parameters, but were not going to check for that here. Note # that the space isn't present if there are no parameters. m= re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?',val) if m: feature=m.group("feature").lower() params=m.string[m.end("feature"):].strip() if feature == "auth": self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \ + " " + params else: self.esmtp_features[feature]=params raise gen.Return((code, msg))
def getreply(self): """Get a reply from the server. Returns a tuple consisting of: - server response code (e.g. '250', or such, if all goes well) Note: returns -1 if it can't read response code. - server response string corresponding to response code (multiline responses are converted to a single, multiline string). Raises SMTPServerDisconnected if end-of-file is reached. """ resp=[] while 1: try: line = self._nonblockReadline() except socket.error: self.close() raise smtplib.SMTPServerDisconnected("Connection unexpectedly closed") if self.debuglevel > 0: print>>sys.stderr, 'reply:', repr(line) resp.append(line[4:].strip()) code=line[:3] # Check that the error code is syntactically correct. # Don't attempt to read a continuation line if it is broken. try: errcode = int(code) except ValueError: errcode = -1 break # Check if multiline response. if line[3:4]!="-": break errmsg = "\n".join(resp) if self.debuglevel > 0: print>>sys.stderr, 'reply: retcode (%s); Msg: %s' % (errcode,errmsg) return errcode, errmsg
def lhlo(self, name='localhost'): """ LMTP 'lhlo' command. Hostname to send for this command defaults to localhost. """ self.putcmd("lhlo",name) (code, msg) = self.getreply() if code == -1 and len(msg) == 0: raise smtplib.SMTPServerDisconnected("Server not connected") self.lhlo_resp = msg self.ehlo_resp = msg if code != 250: return (code, msg) self.does_esmtp = 1 # parse the lhlo response resp = self.lhlo_resp.split('\n') del resp[0] for each in resp: m = re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*)',each) if m: feature = m.group("feature").lower() params = m.string[m.end("feature"):].strip() self.lmtp_features[feature] = params return (code, msg)
def test_mail_mail_send_exceptions_raise_management(self): """ Test various use case with exceptions and errors and see how they are managed and stored at mail and notification level. """ mail, notification = self.test_mail, self.test_notification mail.write({ 'email_from': '*****@*****.**', 'email_to': '*****@*****.**' }) # SMTP connecting issues with self.mock_mail_gateway(): _connect_current = self.connect_mocked.side_effect # classic errors that may be raised during sending, just to test their current support for error, msg in [ (smtplib.SMTPServerDisconnected('SMTPServerDisconnected'), 'SMTPServerDisconnected'), (smtplib.SMTPResponseException('code', 'SMTPResponseException'), 'code\nSMTPResponseException'), (smtplib.SMTPNotSupportedError('SMTPNotSupportedError'), 'SMTPNotSupportedError'), (smtplib.SMTPException('SMTPException'), 'SMTPException'), (SSLError('SSLError'), 'SSLError'), (gaierror('gaierror'), 'gaierror'), (timeout('timeout'), 'timeout') ]: def _connect(*args, **kwargs): raise error self.connect_mocked.side_effect = _connect mail.send(raise_exception=False) self.assertFalse(mail.failure_type) self.assertEqual(mail.failure_reason, msg) self.assertEqual(mail.state, 'exception') self.assertEqual(notification.failure_type, 'mail_smtp') self.assertEqual(notification.notification_status, 'exception') self._reset_data() self.connect_mocked.side_effect = _connect_current # SMTP sending issues with self.mock_mail_gateway(): _send_current = self.send_email_mocked.side_effect self._reset_data() mail.write({'email_to': '*****@*****.**'}) # should always raise for those errors, even with raise_exception=False for error, error_class in [ (smtplib.SMTPServerDisconnected("Some exception"), smtplib.SMTPServerDisconnected), (MemoryError("Some exception"), MemoryError) ]: def _send_email(*args, **kwargs): raise error self.send_email_mocked.side_effect = _send_email with self.assertRaises(error_class): mail.send(raise_exception=False) self.assertFalse( mail.failure_reason, 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback' ) self.assertFalse( mail.failure_type, 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback' ) self.assertEqual( mail.state, 'outgoing', 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback' ) self.assertFalse( notification.failure_type, 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback' ) self.assertEqual( notification.notification_status, 'ready', 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback' ) # MailDeliveryException: should be catched; other issues are sub-catched under # a MailDeliveryException and are catched for error, msg in [ (MailDeliveryException("Some exception"), 'Some exception'), (ValueError("Unexpected issue"), 'Unexpected issue') ]: def _send_email(*args, **kwargs): raise error self.send_email_mocked.side_effect = _send_email self._reset_data() mail.send(raise_exception=False) self.assertEqual(mail.failure_reason, msg) self.assertFalse(mail.failure_type, 'Mail: unlogged failure type to fix') self.assertEqual(mail.state, 'exception') self.assertEqual(notification.failure_type, 'unknown', 'Mail: generic failure type') self.assertEqual(notification.notification_status, 'exception') self.send_email_mocked.side_effect = _send_current
def _mock_send_email(*args, **kwargs): raise smtplib.SMTPServerDisconnected()
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 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 simulate_disconnect(self): raise smtplib.SMTPServerDisconnected()