Ticket #5685: 5685_2.patch

File 5685_2.patch, 24.2 KB (added by sdsern, 4 years ago)
  • twisted/mail/topfiles/5685.doc

     
     1The SMTP Client Tutorial is revised and is now self-contained.
  • doc/mail/tutorial/smtpclient/hosts

     
     1127.0.0.1 example.net
  • doc/mail/tutorial/smtpclient/smtpclient-10.tac

     
    5050def cbMailExchange(exchange):
    5151    smtpClientFactory = SMTPClientFactory()
    5252
    53     smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory)
     53    smtpClientService = internet.TCPClient(exchange, 8025, smtpClientFactory)
    5454    smtpClientService.setServiceParent(application)
    5555
    5656getMailExchange('example.net').addCallback(cbMailExchange)
  • doc/mail/tutorial/smtpclient/smtpclient-11.tac

     
    88from twisted.internet import protocol
    99from twisted.internet import defer
    1010from twisted.mail import smtp, relaymanager
     11from twisted.names.client import createResolver
    1112
    1213class SMTPTutorialClient(smtp.ESMTPClient):
    1314    mailFrom = "tutorial_sender@example.com"
     
    4748def getMailExchange(host):
    4849    def cbMX(mxRecord):
    4950        return str(mxRecord.name)
    50     return relaymanager.MXCalculator().getMX(host).addCallback(cbMX)
     51    return (relaymanager.MXCalculator(createResolver(None, None, b"hosts"))
     52            .getMX(host).addCallback(cbMX))
    5153
    5254def cbMailExchange(exchange):
    5355    smtpClientFactory = SMTPClientFactory()
    5456
    55     smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory)
     57    smtpClientService = internet.TCPClient(exchange, 8025, smtpClientFactory)
    5658    smtpClientService.setServiceParent(application)
    5759
    5860getMailExchange('example.net').addCallback(cbMailExchange)
  • doc/mail/tutorial/smtpclient/smtpclient-3.tac

     
    66from twisted.internet import protocol
    77
    88smtpClientFactory = protocol.ClientFactory()
    9 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)
     9smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory)
    1010smtpClientService.setServiceParent(application)
  • doc/mail/tutorial/smtpclient/smtpclient-11a.tac

     
     1import StringIO
     2
     3from twisted.application import service
     4
     5application = service.Application("SMTP Client Tutorial")
     6
     7from twisted.application import internet
     8from twisted.internet import protocol
     9from twisted.internet import defer
     10from twisted.mail import smtp, relaymanager
     11from twisted.names.client import createResolver
     12
     13class SMTPTutorialClient(smtp.ESMTPClient):
     14    mailFrom = "tutorial_sender@example.com"
     15    mailTo = "tutorial_recipient@example.net"
     16    mailData = '''\
     17Date: Fri, 6 Feb 2004 10:14:39 -0800
     18From: Tutorial Guy <tutorial_sender@example.com>
     19To: Tutorial Gal <tutorial_recipient@example.net>
     20Subject: Tutorate!
     21
     22Hello, how are you, goodbye.
     23'''
     24
     25    def getMailFrom(self):
     26        result = self.mailFrom
     27        self.mailFrom = None
     28        return result
     29
     30    def getMailTo(self):
     31        return [self.mailTo]
     32
     33    def getMailData(self):
     34        return StringIO.StringIO(self.mailData)
     35
     36    def sentMail(self, code, resp, numOk, addresses, log):
     37        print 'Sent', numOk, 'messages'
     38
     39        from twisted.internet import reactor
     40        reactor.stop()
     41
     42class SMTPClientFactory(protocol.ClientFactory):
     43    protocol = SMTPTutorialClient
     44
     45    def buildProtocol(self, addr):
     46        return self.protocol(secret=None, identity='example.com')
     47
     48def getMailExchange(host):
     49    def cbMX(mxRecord):
     50        return str(mxRecord.name)
     51    return relaymanager.MXCalculator().getMX(host).addCallback(cbMX)
     52
     53def cbMailExchange(exchange):
     54    smtpClientFactory = SMTPClientFactory()
     55
     56    smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory)
     57    smtpClientService.setServiceParent(application)
     58
     59getMailExchange('example.net').addCallback(cbMailExchange)
  • doc/mail/tutorial/smtpclient/smtpclient-4.tac

     
    88smtpClientFactory = protocol.ClientFactory()
    99smtpClientFactory.protocol = protocol.Protocol
    1010
    11 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)
     11smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory)
    1212smtpClientService.setServiceParent(application)
  • doc/mail/tutorial/smtpclient/smtpclient-5.tac

     
    1010from twisted.mail import smtp
    1111smtpClientFactory.protocol = smtp.ESMTPClient
    1212
    13 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)
     13smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory)
    1414smtpClientService.setServiceParent(application)
  • doc/mail/tutorial/smtpclient/smtpclient-6.tac

     
    1414
    1515smtpClientFactory = SMTPClientFactory()
    1616
    17 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)
     17smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory)
    1818smtpClientService.setServiceParent(application)
  • doc/mail/tutorial/smtpclient/smtpclient-7.tac

     
    1010
    1111class SMTPTutorialClient(smtp.ESMTPClient):
    1212    mailFrom = "tutorial_sender@example.com"
    13     mailTo = "tutorial_recipient@example.net"
     13    mailTo = "tutorial_recipient@localhost"
    1414    mailData = '''\
    1515Date: Fri, 6 Feb 2004 10:14:39 -0800
    1616From: Tutorial Guy <tutorial_sender@example.com>
    17 To: Tutorial Gal <tutorial_recipient@example.net>
     17To: Tutorial Gal <tutorial_recipient@localhost>
    1818Subject: Tutorate!
    1919
    2020Hello, how are you, goodbye.
     
    4242
    4343smtpClientFactory = SMTPClientFactory()
    4444
    45 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)
     45smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory)
    4646smtpClientService.setServiceParent(application)
  • doc/mail/tutorial/smtpclient/smtpclient-8.tac

     
    1010
    1111class SMTPTutorialClient(smtp.ESMTPClient):
    1212    mailFrom = "tutorial_sender@example.com"
    13     mailTo = "tutorial_recipient@example.net"
     13    mailTo = "tutorial_recipient@localhost"
    1414    mailData = '''\
    1515Date: Fri, 6 Feb 2004 10:14:39 -0800
    1616From: Tutorial Guy <tutorial_sender@example.com>
    17 To: Tutorial Gal <tutorial_recipient@example.net>
     17To: Tutorial Gal <tutorial_recipient@localhost>
    1818Subject: Tutorate!
    1919
    2020Hello, how are you, goodbye.
     
    4545
    4646smtpClientFactory = SMTPClientFactory()
    4747
    48 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)
     48smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory)
    4949smtpClientService.setServiceParent(application)
  • doc/mail/tutorial/smtpclient/smtpclient.xhtml

     
    1919SMTP protocol, have it connect to an appropriate mail exchange server,
    2020and transmit a message for delivery.</p>
    2121
    22 <p>For the majority of this tutorial, <code>twistd</code> will be used
    23 to launch the application.  Near the end we will explore other
    24 possibilities for starting a Twisted application.  Until then, make
     22<p>This tutorial uses <code>twistd</code>
     23to launch the client and server applications.  Make
    2524sure that you have <code>twistd</code> installed and conveniently
    2625accessible for use in running each of the example <code>.tac</code>
    2726files.</p>
    2827
    29 <h3>SMTP Client 1</h3>
     28<h2>SMTP Client 1</h2>
    3029
    3130<p>The first step is to create <a href="smtpclient-1.tac">the most
    32 minimal <code>.tac</code> file</a> possible for use by <code>twistd</code> .</p>
     31minimal <code>.tac</code> file</a> possible for use by <code>twistd</code>.</p>
    3332
    3433<pre class="python">
    3534from twisted.application import service
    3635</pre>
    3736
    3837<p>The first line of the <code>.tac</code> file
    39 imports <code>twisted.application.service</code>, a module which
     38imports <code class="API">twisted.application.service</code>, a module which
    4039contains many of the basic <em>service</em> classes and helper
    4140functions available in Twisted.  In particular, we will be using
    4241the <code>Application</code> function to create a new <em>application
     
    6261run <a href="smtpclient-1.tac">smtpclient-1.tac</a> using
    6362the <code>twistd</code> command line:</p>
    6463
    65 <pre class="python">
     64<pre class="shell">
    6665twistd -ny smtpclient-1.tac
    6766</pre>
    6867
    69 <p>we are rewarded with the following output:</p>
     68<p>we are rewarded with output similar to the following (exact output varies by
     69release and platform):</p>
    7070
    7171<pre class="shell">
    7272exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-1.tac
     
    7878</pre>
    7979
    8080<p>As we expected, not much is going on.  We can shutdown this server
    81 by issuing <code>^C</code>:</p>
     81by issuing Control-C (<code>^C</code>):</p>
    8282
    8383<pre class="shell">
    848418:34 EST [-] Received SIGINT, shutting down.
     
    8787exarkun@boson:~/mail/tutorial/smtpclient$
    8888</pre>
    8989
    90 <h3>SMTP Client 2</h3>
     90<h2>SMTP Client 2</h2>
    9191
    9292<p>The first version of our SMTP client wasn't very interesting.  It
    9393didn't even establish any TCP connections!  The <a
     
    100100from twisted.internet import protocol
    101101</pre>
    102102
    103 <p><code>twisted.application.internet</code> is
     103<p><code class="API">twisted.application.internet</code> is
    104104another <em>application service</em> module.  It provides services for
    105105establishing outgoing connections (as well as creating network
    106106servers, though we are not interested in those parts for the
    107 moment). <code>twisted.internet.protocol</code> provides base
     107moment). <code class="API">twisted.internet.protocol</code> provides base
    108108implementations of many of the core Twisted concepts, such
    109109as <em>factories</em> and <em>protocols</em>.</p>
    110110
     
    181181turned out to be important after all.  We'll get to them in the next
    182182example.</p>
    183183
    184 <h3>SMTP Client 3</h3>
     184<h2>SMTP Client 3</h2>
    185185
    186186<p>Version three of our SMTP client only changes one thing.  The line
    187187from version two:</p>
     
    194194something with a bit more meaning:</p>
    195195
    196196<pre class="python">
    197 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)
     197smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory)
    198198</pre>
    199199
    200 <p>This directs the client to connect to <em>localhost</em> on
    201 port <em>25</em>.  This isn't the address we want ultimately, but it's
    202 a good place-holder for the time being.  We can
     200<p>This directs the client to connect to an SMTP server running on port 8025
     201of localhost.  This isn't the address we want ultimately, but it's
     202a good placeholder for the time being.  </p>
     203<p>
     204Before we run this change, we should start an SMTP server for the client to
     205contact.  The following command line starts a server on port 8025 of
     206localhost which accepts mail addressed to
     207<code>tutorial_recipient@localhost</code>, stores the
     208email in <code>/tmp/emails</code>, and stores the process id in the
     209<code>server.pid</code> file:
     210<pre class="shell">
     211twistd --pidfile=server.pid mail -H localhost -s 8025 \
     212       -d localhost=/tmp/emails -u tutorial_recipient=pwd
     213</pre>
     214Now, we can
    203215run <a href="smtpclient-3.tac">smtpclient-3.tac</a> and see what this
    204216change gets us:</p>
    205217
     
    249261a <em>protocol class</em> for the factory to use.  We'll do that in
    250262the next example.</p>
    251263
    252 <h3>SMTP Client 4</h3>
     264<h2>SMTP Client 4</h2>
    253265
    254266<p>In the previous example, we ran into a problem because we hadn't
    255267set up our <em>client factory's</em> <em>protocol</em> attribute
     
    266278</pre>
    267279
    268280<p>Running this version of the client, we can see the output is once
    269 again traceback free:</p>
     281again traceback-free:</p>
    270282
    271283<pre class="shell">
    272284exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-4.tac
     
    288300</pre>
    289301
    290302<p>But what does this
    291 mean? <code>twisted.internet.protocol.Protocol</code> is the
     303mean? <code class="API">twisted.internet.protocol.Protocol</code> is the
    292304base <em>protocol</em> implementation.  For those familiar with the
    293305classic UNIX network services, it is equivalent to
    294306the <em>discard</em> service.  It never produces any output and it
     
    296308like an SMTP client.  Let's see how we can improve this in the next
    297309example.</p>
    298310
    299 <h3>SMTP Client 5</h3>
     311<h2>SMTP Client 5</h2>
    300312
    301 <p>In <a href="smtpclient-5.tac">smtpclient-5.tac</a>, we will begin
    302 to use Twisted's SMTP protocol implementation for the first time.
     313<p>In <a href="smtpclient-5.tac">smtpclient-5.tac</a>, we will
     314use Twisted's SMTP protocol implementation for the first time.
    303315We'll make the obvious change, simply swapping
    304316out <code>twisted.internet.protocol.Protocol</code> in favor
    305 of <code>twisted.mail.smtp.ESMTPClient</code>.  Don't worry about
     317of <code class="API">twisted.mail.smtp.ESMTPClient</code>.  Don't worry about
    306318the <em>E</em> in <em>ESMTP</em>.  It indicates we're actually using a
    307319newer version of the SMTP protocol.  There is
    308320an <code>SMTPClient</code> in Twisted, but there's essentially no
     
    314326from twisted.mail import smtp
    315327</pre>
    316328
    317 <p>All of the mail related code in Twisted exists beneath
    318 the <code>twisted.mail</code> package.  More specifically, everything
     329<p>All of the mail related code in Twisted exists beneath the
     330<code class="API">twisted.mail</code> package.  More specifically, everything
    319331having to do with the SMTP protocol implementation is defined in
    320 the <code>twisted.mail.smtp</code> module.</p>
     332the <code class="API">twisted.mail.smtp</code> module.</p>
    321333
    322334<p>Next we remove a line we added in smtpclient-4.tac:</p>
    323335
     
    384396version of the client, we'll override <code>buildProtocol</code> to
    385397fix this problem.</p>
    386398
    387 <h3>SMTP Client 6</h3>
     399<h2>SMTP Client 6</h2>
    388400
    389401<p><a href="smtpclient-6.tac">smtpclient-6.tac</a> introduces
    390 a <code>twisted.internet.protocol.ClientFactory</code> subclass with
     402a <code class="API">twisted.internet.protocol.ClientFactory</code> subclass with
    391403an overridden <code>buildProtocol</code> method to overcome the
    392404problem encountered in the previous example.</p>
    393405
     
    404416arguments to <code>twisted.mail.smtp.ESMTPClient</code>'s initializer.
    405417The <code>secret</code> argument is used for SMTP authentication
    406418(which we will not attempt yet).  The <code>identity</code> argument
    407 is used as a to identify ourselves Another minor change to note is
     419is used to identify ourselves. Another minor change to note is
    408420that the <code>protocol</code> attribute is now defined in the class
    409421definition, rather than tacked onto an instance after one is created.
    410422This means it is a class attribute, rather than an instance attribute,
     
    413425you understand the implications of each approach when creating your
    414426own factories.</p>
    415427
    416 <p>One other change is required: instead of
     428<p>One other change is required.  Instead of
    417429instantiating <code>twisted.internet.protocol.ClientFactory</code>, we
    418430will now instantiate <code>SMTPClientFactory</code>:</p>
    419431
     
    478490its next step should be.  In the next example, we'll see how to
    479491provide that information to it.</p>
    480492
    481 <h3>SMTP Client 7</h3>
     493<h2>SMTP Client 7</h2>
    482494
    483495<p>SMTP Client 7 is the first version of our SMTP client which
    484496actually includes message data to transmit.  For simplicity's sake,
     
    493505<pre class="python">
    494506class SMTPTutorialClient(smtp.ESMTPClient):
    495507    mailFrom = "tutorial_sender@example.com"
    496     mailTo = "tutorial_recipient@example.net"
     508    mailTo = "tutorial_recipient@localhost"
    497509    mailData = '''\
    498510Date: Fri, 6 Feb 2004 10:14:39 -0800
    499511From: Tutorial Guy &lt;tutorial_sender@example.com&gt;
    500 To: Tutorial Gal &lt;tutorial_recipient@example.net&gt;
     512To: Tutorial Gal &lt;tutorial_recipient@localhost&gt;
    501513Subject: Tutorate!
    502514
    503515Hello, how are you, goodbye.
     
    557569message.  If the contents of the file returned
    558570by <code>getMailData</code> span multiple lines (as email messages
    559571often do), the lines should be <code>\n</code> delimited (as they
    560 would be when opening a text file in the <code>"rt"</code> mode):
    561 necessary newline translation will be performed
     572would be when opening a text file in the <code>"rt"</code> mode).
     573Necessary newline translation will be performed
    562574by <code>SMTPClient</code> automatically.</p>
    563575
    564576<p>There is one more new callback method defined in smtpclient-7.tac.
     
    579591499, inclusive.  For permanent failures (this which will never work,
    580592no matter how many times you retry them), it will be between 500 and
    581593599.</p>
     594<p>
     595When this version of the code is run, the message is successfully delivered
     596to the server and can be found in the
     597<code>/tmp/emails/tutorial_recipient/new</code> directory.
     598</p>
    582599
    583 <h3>SMTP Client 8</h3>
     600<h2>SMTP Client 8</h2>
    584601
    585602<p>Thus far we have succeeded in creating a Twisted client application
    586603which starts up, connects to a (possibly) remote host, transmits some
    587604data, and disconnects.  Notably missing, however, is application
    588 shutdown.  Hitting ^C is fine during development, but it's not exactly
     605shutdown.  Hitting <code>^C</code> is fine during development,
     606but it's not exactly
    589607a long-term solution.  Fortunately, programmatic shutdown is extremely
    590608simple.  <a href="smtpclient-8.tac">smtpclient-8.tac</a>
    591609extends <code>sentMail</code> with these two lines:</p>
     
    618636exarkun@boson:~/doc/mail/tutorial/smtpclient$
    619637</pre>
    620638
    621 <h3>SMTP Client 9</h3>
     639<h2>SMTP Client 9</h2>
    622640
    623641<p>One task remains to be completed in this tutorial SMTP client:
    624642instead of always sending mail through a well-known host, we will look
    625643up the mail exchange server for the recipient address and try to
    626644deliver the message to that host.</p>
    627645
     646<p>To illustrate this, we'll change the
     647domain to which the mail is addressed from <code>localhost</code>
     648to <code>example.net</code>:</p>
     649
     650<pre class="python">
     651    mailTo = "tutorial_recipient@example.net"
     652</pre>
     653
     654<p>We'll also have to stop the server that is accepting mail for
     655<code>tutorial_recipient@localhost</code> and start a server to accept mail
     656for <code>tutorial_recipient@example.net</code>
     657by running the following from the command prompt:</p>
     658<pre class="shell">
     659kill `cat server.pid`
     660twistd --pidfile=server.pid mail -H example.net -s 8025 \
     661       -d example.net=/tmp/emails -u tutorial_recipient=pwd
     662</pre>
    628663<p>In <a href="smtpclient-9.tac">smtpclient-9.tac</a>, we'll take the
    629664first step towards this feature by defining a function which returns
    630665the mail exchange host for a particular domain:</p>
     
    643678
    644679<pre class="python">
    645680smtpClientService = internet.TCPClient(
    646     getMailExchange('example.net'), 25, smtpClientFactory)
     681    getMailExchange('example.net'), 8025, smtpClientFactory)
    647682</pre>
    648683
    649684<p>We'll expand on the definition of <code>getMailExchange</code> in
    650685the next example.</p>
    651686
    652 <h3>SMTP Client 10</h3>
     687<h2>SMTP Client 10</h2>
    653688
    654689<p>In the previous example we defined <code>getMailExchange</code> to
    655690return a string representing the mail exchange host for a particular
     
    677712def cbMailExchange(exchange):
    678713    smtpClientFactory = SMTPClientFactory()
    679714
    680     smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory)
     715    smtpClientService = internet.TCPClient(exchange, 8025, smtpClientFactory)
    681716    smtpClientService.setServiceParent(application)
    682717
    683718getMailExchange('example.net').addCallback(cbMailExchange)
     
    694729as set the <code>TCPClient</code>'s service parent, just as we did in
    695730the previous examples.</p>
    696731
    697 <h3>SMTP Client 11</h3>
     732<h2>SMTP Client 11</h2>
    698733
    699734<p>At last we're ready to perform the mail exchange lookup.  We do
    700735this by calling on an object provided specifically for this
    701 task, <code>twisted.mail.relaymanager.MXCalculator</code>:</p>
     736task, <code class="API">twisted.mail.relaymanager.MXCalculator</code>.</p>
     737<p>
     738Ideally, we would lookup the actual address of <code>example.net</code> and
     739then contact it on port 25 which is the well-known port designated for SMTP.
     740However, many ISPs block outgoing connections on port 25 because of spam.
     741So, for the purposes of the tutorial, we'll configure <code>MXCalculator</code>
     742to consult a file which specifies the mapping of domains to addresses
     743instead of contacting a DNS server.
     744The <a href="hosts"><code>hosts</code></a> file maps <code>example.net</code>
     745to address <code>127.0.0.1</code> which is the same as <code>localhost</code>.
     746</p>
    702747
     748<p>
     749The following modification in <a href="smtpclient-11.tac">smtpclient-11.tac</a>
     750creates such an <code>MXCalculator</code>:
     751</p>
    703752<pre class="python">
    704753def getMailExchange(host):
    705754    def cbMX(mxRecord):
    706755        return str(mxRecord.name)
    707     return relaymanager.MXCalculator().getMX(host).addCallback(cbMX)
     756    return (relaymanager.MXCalculator(createResolver(None, None, b"hosts"))
     757            .getMX(host).addCallback(cbMX))
    708758</pre>
    709759
    710760<p>Because <code>getMX</code> returns a <code>Record_MX</code> object
     
    712762results we want.  We have already converted the rest of the tutorial
    713763application to expect a <code>Deferred</code>
    714764from <code>getMailExchange</code>, so no further changes are
    715 required.  <a href="smtpclient-11.tac">smtpclient-11.tac</a> completes
    716 this tutorial by being able to both look up the mail exchange host for
    717 the recipient domain, connect to it, complete an SMTP transaction,
    718 report its results, and finally shut down the reactor.</p>
     765required.  </p>
    719766
    720 <!-- TODO: write a conclusion to wrap it up -->
     767<p>
     768In case your ISP does not block port 25 and you'd like to try sending mail
     769to port 25 on <code>example.net</code>, try running
     770<a href="smtpclient-11a.tac">smtpclient-11a.tac</a>.  It configures the
     771<code>MXCalculator</code> to contact a DNS server to get the address of
     772<code>example.net</code> and sends the message to its port 25.
     773</p>
    721774
     775
     776<h2>Summary</h2>
     777<p>
     778This tutorial has worked its way from the skeleton of a Twisted application,
     779through an SMTP client which sends a message to a recipient on an SMTP server
     780at a fixed address and must be manually terminated, and finally onto an SMTP
     781client which looks up the mail exchange host for the recipient domain,
     782connects to it, delivers a message, reports its results, and
     783terminates the program.
     784</p>
    722785</body>
    723786</html>
  • doc/mail/tutorial/smtpclient/smtpclient-9.tac

     
    4949smtpClientFactory = SMTPClientFactory()
    5050
    5151smtpClientService = internet.TCPClient(
    52     getMailExchange('example.net'), 25, smtpClientFactory)
     52    getMailExchange('example.net'), 8025, smtpClientFactory)
    5353smtpClientService.setServiceParent(application)