def sendmail(smtphost, port, from_addr, to_addrs, msg): msg = StringIO(str(msg)) d = defer.Deferred() factory = smtp.SMTPSenderFactory(from_addr, to_addrs, msg, d) factory.noisy = False reactor.connectTCP(smtphost, port, factory) return d
def testSMTPClient(self): onDone = defer.Deferred() clientFactory = smtp.SMTPSenderFactory('source@address', 'recipient@address', StringIO("Message body"), onDone, retries=0, timeout=0.5) return self._timeoutTest(onDone, clientFactory)
def test_SMTPClient(self): """ Test timeout for L{smtp.SMTPSenderFactory}: the response L{Deferred} should be errback with a L{smtp.SMTPTimeoutError}. """ onDone = defer.Deferred() clientFactory = smtp.SMTPSenderFactory( 'source@address', 'recipient@address', StringIO("Message body"), onDone, retries=0, timeout=0.5) return self._timeoutTest(onDone, clientFactory)
def test_retryAfterDisconnect(self): """ If the protocol created by L{SMTPSenderFactory} loses its connection before receiving confirmation of message delivery, it reconnects and tries to deliver the message again. """ recipient = 'alice' message = "some message text" domain = DummyDomain([recipient]) class CleanSMTP(smtp.SMTP): """ An SMTP subclass which ensures that its transport will be disconnected before the test ends. """ def makeConnection(innerSelf, transport): self.addCleanup(transport.loseConnection) smtp.SMTP.makeConnection(innerSelf, transport) # Create a server which will fail the first message deliver attempt to # it with a 500 and a disconnect, but which will accept a message # delivered over the 2nd connection to it. serverFactory = MultipleDeliveryFactorySMTPServerFactory([ BrokenMessage, lambda user: DummyMessage(domain, user)]) serverFactory.protocol = CleanSMTP serverPort = reactor.listenTCP(0, serverFactory, interface='127.0.0.1') serverHost = serverPort.getHost() self.addCleanup(serverPort.stopListening) # Set up a client to try to deliver a message to the above created # server. sentDeferred = defer.Deferred() clientFactory = smtp.SMTPSenderFactory( "*****@*****.**", recipient + "@example.com", StringIO(message), sentDeferred) clientFactory.domain = "example.org" clientConnector = reactor.connectTCP( serverHost.host, serverHost.port, clientFactory) self.addCleanup(clientConnector.disconnect) def cbSent(ignored): """ Verify that the message was successfully delivered and flush the error which caused the first attempt to fail. """ self.assertEquals( domain.messages, {recipient: ["\n%s\n" % (message,)]}) # Flush the RuntimeError that BrokenMessage caused to be logged. self.assertEqual(len(self.flushLoggedErrors(RuntimeError)), 1) sentDeferred.addCallback(cbSent) return sentDeferred
def test_onlyLogFailedAddresses(self): """ L{smtp.SenderMixin.sentMail} adds only the addresses with failing SMTP response codes to the log passed to the factory's errback. """ onDone = self.assertFailure(defer.Deferred(), smtp.SMTPDeliveryError) onDone.addCallback(lambda e: self.assertEquals( e.log, "[email protected]: 199 Error in sending.\n")) clientFactory = smtp.SMTPSenderFactory( 'source@address', 'recipient@address', StringIO("Message body"), onDone, retries=0, timeout=0.5) client = clientFactory.buildProtocol( address.IPv4Address('TCP', 'example.net', 25)) addresses = [("*****@*****.**", 200, "No errors here!"), ("*****@*****.**", 199, "Error in sending.")] client.sentMail(199, "Test response", 1, addresses, client.log) return onDone
def send(self, response, retries=0, timeout=30, reaktor=reactor): """Send our **response** in reply to :data:`incoming`. :type client: :api:`twisted.mail.smtp.Address` :param client: The email address of the client. :param response: A :class:`EmailResponse`. :param int retries: Try resending this many times. (default: ``0``) :param int timeout: Timeout after this many seconds. (default: ``30``) :rtype: :api:`Deferred <twisted.internet.defer.Deferred>` :returns: Our :data:`deferred`. """ logging.info("Sending reply to %s ..." % str(response.to)) factory = smtp.SMTPSenderFactory(self.incoming.context.smtpFromAddr, str(response.to), response, self.deferred, retries=retries, timeout=timeout) factory.domain = smtp.DNSNAME reaktor.connectTCP(self.incoming.context.smtpServerIP, self.incoming.context.smtpServerPort, factory) return self.deferred
def test_resetTimeoutWhileSending(self): """ The timeout is not allowed to expire after the server has accepted a DATA command and the client is actively sending data to it. """ class SlowFile: """ A file-like which returns one byte from each read call until the specified number of bytes have been returned. """ def __init__(self, size): self._size = size def read(self, max=None): if self._size: self._size -= 1 return 'x' return '' failed = [] onDone = defer.Deferred() onDone.addErrback(failed.append) clientFactory = smtp.SMTPSenderFactory( 'source@address', 'recipient@address', SlowFile(1), onDone, retries=0, timeout=3) clientFactory.domain = "example.org" clock = task.Clock() client = clientFactory.buildProtocol( address.IPv4Address('TCP', 'example.net', 25)) client.callLater = clock.callLater transport = StringTransport() client.makeConnection(transport) client.dataReceived( "220 Ok\r\n" # Greet the client "250 Ok\r\n" # Respond to HELO "250 Ok\r\n" # Respond to MAIL FROM "250 Ok\r\n" # Respond to RCPT TO "354 Ok\r\n" # Respond to DATA ) # Now the client is producing data to the server. Any time # resumeProducing is called on the producer, the timeout should be # extended. First, a sanity check. This test is only written to # handle pull producers. self.assertNotIdentical(transport.producer, None) self.assertFalse(transport.streaming) # Now, allow 2 seconds (1 less than the timeout of 3 seconds) to # elapse. clock.advance(2) # The timeout has not expired, so the failure should not have happened. self.assertEqual(failed, []) # Let some bytes be produced, extending the timeout. Then advance the # clock some more and verify that the timeout still hasn't happened. transport.producer.resumeProducing() clock.advance(2) self.assertEqual(failed, []) # The file has been completely produced - the next resume producing # finishes the upload, successfully. transport.producer.resumeProducing() client.dataReceived("250 Ok\r\n") self.assertEqual(failed, []) # Verify that the client actually did send the things expected. self.assertEqual( transport.value(), "HELO example.org\r\n" "MAIL FROM:<source@address>\r\n" "RCPT TO:<recipient@address>\r\n" "DATA\r\n" "x\r\n" ".\r\n" # This RSET is just an implementation detail. It's nice, but this # test doesn't really care about it. "RSET\r\n")