diff --git a/doc/mail/tutorial/smtpclient/smtpclient.xhtml b/doc/mail/tutorial/smtpclient/smtpclient.xhtml
index aea44ba..9e89a84 100644
|
a
|
b
|
|
| 29 | 29 | <h3>SMTP Client 1</h3> |
| 30 | 30 | |
| 31 | 31 | <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 |
| 33 | | <code>twistd</code>.</p> |
| | 32 | minimal <code>.tac</code> file</a> possible for use by <code>twistd</code> .</p> |
| 34 | 33 | |
| 35 | 34 | <pre class="python"> |
| 36 | 35 | from twisted.application import service |
| 37 | 36 | </pre> |
| 38 | 37 | |
| 39 | | <p>The first line of the <code>.tac</code> file imports |
| 40 | | <code>twisted.application.service</code>, a module which contains many |
| 41 | | of the basic <em>service</em> classes and helper functions available |
| 42 | | in Twisted. In particular, we will be using the |
| 43 | | <code>Application</code> function to create a new <em>application |
| | 38 | <p>The first line of the <code>.tac</code> file |
| | 39 | imports <code>twisted.application.service</code>, a module which |
| | 40 | contains many of the basic <em>service</em> classes and helper |
| | 41 | functions available in Twisted. In particular, we will be using |
| | 42 | the <code>Application</code> function to create a new <em>application |
| 44 | 43 | service</em>. An <em>application service</em> simply acts as a |
| 45 | 44 | central object on which to store certain kinds of deployment |
| 46 | 45 | configuration.</p> |
| … |
… |
|
| 49 | 48 | application = service.Application("SMTP Client Tutorial") |
| 50 | 49 | </pre> |
| 51 | 50 | |
| 52 | | <p>The second line of the <code>.tac</code> file creates a new |
| 53 | | <em>application service</em> and binds it to the local name |
| 54 | | <code>application</code>. <code>twistd</code> requires this local |
| 55 | | name in each <code>.tac</code> file it runs. It uses various pieces |
| 56 | | of configuration on the object to determine its behavior. For |
| | 51 | <p>The second line of the <code>.tac</code> file creates a |
| | 52 | new <em>application service</em> and binds it to the local |
| | 53 | name <code>application</code>. <code>twistd</code> requires this |
| | 54 | local name in each <code>.tac</code> file it runs. It uses various |
| | 55 | pieces of configuration on the object to determine its behavior. For |
| 57 | 56 | example, <code>"SMTP Client Tutorial"</code> will be used as the name |
| 58 | 57 | of the <code>.tap</code> file into which to serialize application |
| 59 | 58 | state, should it be necessary to do so.</p> |
| 60 | 59 | |
| 61 | | <p>That does it for the first example. We now have enough of a |
| 62 | | <code>.tac</code> file to pass to <code>twistd</code>. If we run <a |
| 63 | | href="smtpclient-1.tac">smtpclient-1.tac</a> using the |
| 64 | | <code>twistd</code> command line:</p> |
| | 60 | <p>That does it for the first example. We now have enough of |
| | 61 | a <code>.tac</code> file to pass to <code>twistd</code>. If we |
| | 62 | run <a href="smtpclient-1.tac">smtpclient-1.tac</a> using |
| | 63 | the <code>twistd</code> command line:</p> |
| 65 | 64 | |
| 66 | 65 | <pre class="python"> |
| 67 | 66 | twistd -ny smtpclient-1.tac |
| … |
… |
|
| 101 | 100 | from twisted.internet import protocol |
| 102 | 101 | </pre> |
| 103 | 102 | |
| 104 | | <p><code>twisted.application.internet</code> is another |
| 105 | | <em>application service</em> module. It provides services for |
| | 103 | <p><code>twisted.application.internet</code> is |
| | 104 | another <em>application service</em> module. It provides services for |
| 106 | 105 | establishing outgoing connections (as well as creating network |
| 107 | | servers, though we are not interested in those parts for the moment). |
| 108 | | <code>twisted.internet.protocol</code> provides base implementations |
| 109 | | of many of the core Twisted concepts, such as <em>factories</em> and |
| 110 | | <em>protocols</em>.</p> |
| | 106 | servers, though we are not interested in those parts for the |
| | 107 | moment). <code>twisted.internet.protocol</code> provides base |
| | 108 | implementations of many of the core Twisted concepts, such |
| | 109 | as <em>factories</em> and <em>protocols</em>.</p> |
| 111 | 110 | |
| 112 | 111 | <p>The next line of <a href="smtpclient-2.tac">smtpclient-2.tac</a> |
| 113 | 112 | instantiates a new <em>client factory</em>.</p> |
| … |
… |
|
| 116 | 115 | smtpClientFactory = protocol.ClientFactory() |
| 117 | 116 | </pre> |
| 118 | 117 | |
| 119 | | <p><em>Client factories</em> are responsible for constructing |
| 120 | | <em>protocol instances</em> whenever connections are established. |
| 121 | | They may be required to create just one instance, or many instances if |
| 122 | | many different connections are established, or they may never be |
| 123 | | required to create one at all, if no connection ever manages to be |
| 124 | | established.</p> |
| | 118 | <p><em>Client factories</em> are responsible for |
| | 119 | constructing <em>protocol instances</em> whenever connections are |
| | 120 | established. They may be required to create just one instance, or |
| | 121 | many instances if many different connections are established, or they |
| | 122 | may never be required to create one at all, if no connection ever |
| | 123 | manages to be established.</p> |
| 125 | 124 | |
| 126 | 125 | <p>Now that we have a client factory, we'll need to hook it up to the |
| 127 | 126 | network somehow. The next line of <code>smtpclient-2.tac</code> does |
| … |
… |
|
| 131 | 130 | smtpClientService = internet.TCPClient(None, None, smtpClientFactory) |
| 132 | 131 | </pre> |
| 133 | 132 | |
| 134 | | <p>We'll ignore the first two arguments to |
| 135 | | <code>internet.TCPClient</code> for the moment and instead focus on |
| | 133 | <p>We'll ignore the first two arguments |
| | 134 | to <code>internet.TCPClient</code> for the moment and instead focus on |
| 136 | 135 | the third. <code>TCPClient</code> is one of those <em>application |
| 137 | 136 | service</em> classes. It creates TCP connections to a specified |
| 138 | 137 | address and then uses its third argument, a <em>client factory</em>, |
| 139 | 138 | to get a <em>protocol instance</em>. It then associates the TCP |
| 140 | 139 | connection with the protocol instance and gets out of the way.</p> |
| 141 | 140 | |
| 142 | | <p>We can try to run <code>smtpclient-2.tac</code> the same way we ran |
| 143 | | <code>smtpclient-1.tac</code>, but the results might be a little |
| | 141 | <p>We can try to run <code>smtpclient-2.tac</code> the same way we |
| | 142 | ran <code>smtpclient-1.tac</code>, but the results might be a little |
| 144 | 143 | disappointing:</p> |
| 145 | 144 | |
| 146 | 145 | <pre class="shell"> |
| … |
… |
|
| 198 | 197 | smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory) |
| 199 | 198 | </pre> |
| 200 | 199 | |
| 201 | | <p>This directs the client to connect to <em>localhost</em> on port |
| 202 | | <em>25</em>. This isn't the address we want ultimately, but it's a |
| 203 | | good place-holder for the time being. We can run <a |
| 204 | | href="smtpclient-3.tac">smtpclient-3.tac</a> and see what this change |
| 205 | | gets us:</p> |
| | 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 |
| | 203 | run <a href="smtpclient-3.tac">smtpclient-3.tac</a> and see what this |
| | 204 | change gets us:</p> |
| 206 | 205 | |
| 207 | 206 | <pre class="shell"> |
| 208 | 207 | exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-3.tac |
| … |
… |
|
| 246 | 245 | </pre> |
| 247 | 246 | |
| 248 | 247 | <p>A meagre amount of progress, but the service still raises an |
| 249 | | exception. This time, it's because we haven't specified a |
| 250 | | <em>protocol class</em> for the factory to use. We'll do that in the |
| 251 | | next example.</p> |
| | 248 | exception. This time, it's because we haven't specified |
| | 249 | a <em>protocol class</em> for the factory to use. We'll do that in |
| | 250 | the next example.</p> |
| 252 | 251 | |
| 253 | 252 | <h3>SMTP Client 4</h3> |
| 254 | 253 | |
| … |
… |
|
| 288 | 287 | exarkun@boson:~/doc/mail/tutorial/smtpclient$ |
| 289 | 288 | </pre> |
| 290 | 289 | |
| 291 | | <p>But what does this mean? |
| 292 | | <code>twisted.internet.protocol.Protocol</code> is the base |
| 293 | | <em>protocol</em> implementation. For those familiar with the classic |
| 294 | | UNIX network services, it is equivalent to the <em>discard</em> |
| 295 | | service. It never produces any output and it discards all its input. |
| 296 | | Not terribly useful, and certainly nothing like an SMTP client. Let's |
| 297 | | see how we can improve this in the next example.</p> |
| | 290 | <p>But what does this |
| | 291 | mean? <code>twisted.internet.protocol.Protocol</code> is the |
| | 292 | base <em>protocol</em> implementation. For those familiar with the |
| | 293 | classic UNIX network services, it is equivalent to |
| | 294 | the <em>discard</em> service. It never produces any output and it |
| | 295 | discards all its input. Not terribly useful, and certainly nothing |
| | 296 | like an SMTP client. Let's see how we can improve this in the next |
| | 297 | example.</p> |
| 298 | 298 | |
| 299 | 299 | <h3>SMTP Client 5</h3> |
| 300 | 300 | |
| 301 | 301 | <p>In <a href="smtpclient-5.tac">smtpclient-5.tac</a>, we will begin |
| 302 | 302 | to use Twisted's SMTP protocol implementation for the first time. |
| 303 | | We'll make the obvious change, simply swapping out |
| 304 | | <code>twisted.internet.protocol.Protocol</code> in favor of |
| 305 | | <code>twisted.mail.smtp.ESMTPClient</code>. Don't worry about the |
| 306 | | <em>E</em> in <em>ESMTP</em>. It indicates we're actually using a |
| 307 | | newer version of the SMTP protocol. There is an |
| 308 | | <code>SMTPClient</code> in Twisted, but there's essentially no reason |
| 309 | | to ever use it.</p> |
| | 303 | We'll make the obvious change, simply swapping |
| | 304 | out <code>twisted.internet.protocol.Protocol</code> in favor |
| | 305 | of <code>twisted.mail.smtp.ESMTPClient</code>. Don't worry about |
| | 306 | the <em>E</em> in <em>ESMTP</em>. It indicates we're actually using a |
| | 307 | newer version of the SMTP protocol. There is |
| | 308 | an <code>SMTPClient</code> in Twisted, but there's essentially no |
| | 309 | reason to ever use it.</p> |
| 310 | 310 | |
| 311 | 311 | <p>smtpclient-5.tac adds a new import:</p> |
| 312 | 312 | |
| … |
… |
|
| 314 | 314 | from twisted.mail import smtp |
| 315 | 315 | </pre> |
| 316 | 316 | |
| 317 | | <p>All of the mail related code in Twisted exists beneath the |
| 318 | | <code>twisted.mail</code> package. More specifically, everything |
| 319 | | having to do with the SMTP protocol implementation is defined in the |
| 320 | | <code>twisted.mail.smtp</code> module.</p> |
| | 317 | <p>All of the mail related code in Twisted exists beneath |
| | 318 | the <code>twisted.mail</code> package. More specifically, everything |
| | 319 | having to do with the SMTP protocol implementation is defined in |
| | 320 | the <code>twisted.mail.smtp</code> module.</p> |
| 321 | 321 | |
| 322 | 322 | <p>Next we remove a line we added in smtpclient-4.tac:</p> |
| 323 | 323 | |
| … |
… |
|
| 379 | 379 | |
| 380 | 380 | <p>Oops, back to getting a traceback. This time, the default |
| 381 | 381 | implementation of <code>buildProtocol</code> seems no longer to be |
| 382 | | sufficient. It instantiates the protocol with no arguments, but |
| 383 | | <code>ESMTPClient</code> wants at least one argument. In the next |
| | 382 | sufficient. It instantiates the protocol with no arguments, |
| | 383 | but <code>ESMTPClient</code> wants at least one argument. In the next |
| 384 | 384 | version of the client, we'll override <code>buildProtocol</code> to |
| 385 | 385 | fix this problem.</p> |
| 386 | 386 | |
| 387 | 387 | <h3>SMTP Client 6</h3> |
| 388 | 388 | |
| 389 | | <p><a href="smtpclient-6.tac">smtpclient-6.tac</a> introduces a |
| 390 | | <code>twisted.internet.protocol.ClientFactory</code> subclass with an |
| 391 | | overridden <code>buildProtocol</code> method to overcome the problem |
| 392 | | encountered in the previous example.</p> |
| | 389 | <p><a href="smtpclient-6.tac">smtpclient-6.tac</a> introduces |
| | 390 | a <code>twisted.internet.protocol.ClientFactory</code> subclass with |
| | 391 | an overridden <code>buildProtocol</code> method to overcome the |
| | 392 | problem encountered in the previous example.</p> |
| 393 | 393 | |
| 394 | 394 | <pre class="python"> |
| 395 | 395 | class SMTPClientFactory(protocol.ClientFactory): |
| … |
… |
|
| 421 | 421 | smtpClientFactory = SMTPClientFactory() |
| 422 | 422 | </pre> |
| 423 | 423 | |
| 424 | | <p>Running this version of the code, we observe that the code |
| 425 | | <strong>still</strong> isn't quite traceback-free.</p> |
| | 424 | <p>Running this version of the code, we observe that the |
| | 425 | code <strong>still</strong> isn't quite traceback-free.</p> |
| 426 | 426 | |
| 427 | 427 | <pre class="shell"> |
| 428 | 428 | exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-6.tac |
| … |
… |
|
| 484 | 484 | actually includes message data to transmit. For simplicity's sake, |
| 485 | 485 | the message is defined as part of a new class. In a useful program |
| 486 | 486 | which sent email, message data might be pulled in from the filesystem, |
| 487 | | a database, or be generated based on user-input. <a |
| 488 | | href="smtpclient-7.tac">smtpclient-7.tac</a>, however, defines a new |
| 489 | | class, <code>SMTPTutorialClient</code>, with three class attributes |
| 490 | | (<code>mailFrom</code>, <code>mailTo</code>, and |
| 491 | | <code>mailData</code>):</p> |
| | 487 | a database, or be generated based on |
| | 488 | user-input. <a href="smtpclient-7.tac">smtpclient-7.tac</a>, however, |
| | 489 | defines a new class, <code>SMTPTutorialClient</code>, with three class |
| | 490 | attributes (<code>mailFrom</code>, <code>mailTo</code>, |
| | 491 | and <code>mailData</code>):</p> |
| 492 | 492 | |
| 493 | 493 | <pre class="python"> |
| 494 | 494 | class SMTPTutorialClient(smtp.ESMTPClient): |
| … |
… |
|
| 552 | 552 | </pre> |
| 553 | 553 | |
| 554 | 554 | <p>This one is quite simple as well: it returns a file or a file-like |
| 555 | | object which contains the message contents. In our case, we return a |
| 556 | | <code>StringIO</code> since we already have a string containing our |
| 557 | | message. If the contents of the file returned by |
| 558 | | <code>getMailData</code> span multiple lines (as email messages often |
| 559 | | do), the lines should be <code>\n</code> delimited (as they would be |
| 560 | | when opening a text file in the <code>"rt"</code> mode): necessary |
| 561 | | newline translation will be performed by <code>SMTPClient</code> |
| 562 | | automatically.</p> |
| | 555 | object which contains the message contents. In our case, we return |
| | 556 | a <code>StringIO</code> since we already have a string containing our |
| | 557 | message. If the contents of the file returned |
| | 558 | by <code>getMailData</code> span multiple lines (as email messages |
| | 559 | often 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 |
| | 562 | by <code>SMTPClient</code> automatically.</p> |
| 563 | 563 | |
| 564 | 564 | <p>There is one more new callback method defined in smtpclient-7.tac. |
| 565 | 565 | This one isn't for providing information about the messages to |
| … |
… |
|
| 587 | 587 | data, and disconnects. Notably missing, however, is application |
| 588 | 588 | shutdown. Hitting ^C is fine during development, but it's not exactly |
| 589 | 589 | a long-term solution. Fortunately, programmatic shutdown is extremely |
| 590 | | simple. <a href="smtpclient-8.tac">smtpclient-8.tac</a> extends |
| 591 | | <code>sentMail</code> with these two lines:</p> |
| | 590 | simple. <a href="smtpclient-8.tac">smtpclient-8.tac</a> |
| | 591 | extends <code>sentMail</code> with these two lines:</p> |
| 592 | 592 | |
| 593 | 593 | <pre class="python"> |
| 594 | 594 | from twisted.internet import reactor |
| … |
… |
|
| 667 | 667 | return defer.succeed('localhost') |
| 668 | 668 | </pre> |
| 669 | 669 | |
| 670 | | <p><code>defer.succeed</code> is a function which creates a new |
| 671 | | <code>Deferred</code> which already has a result, in this case |
| 672 | | <code>'localhost'</code>. Now we need to adjust our |
| 673 | | <code>TCPClient</code>-constructing code to expect and properly handle |
| 674 | | this <code>Deferred</code>:</p> |
| | 670 | <p><code>defer.succeed</code> is a function which creates a |
| | 671 | new <code>Deferred</code> which already has a result, in this |
| | 672 | case <code>'localhost'</code>. Now we need to adjust |
| | 673 | our <code>TCPClient</code>-constructing code to expect and properly |
| | 674 | handle this <code>Deferred</code>:</p> |
| 675 | 675 | |
| 676 | 676 | <pre class="python"> |
| 677 | 677 | def cbMailExchange(exchange): |
| … |
… |
|
| 687 | 687 | scope of this document. For such a look, see |
| 688 | 688 | the <a href="../../../core/howto/defer.html">Deferred Reference</a>. |
| 689 | 689 | However, in brief, what this version of the code does is to delay the |
| 690 | | creation of the <code>TCPClient</code> until the |
| 691 | | <code>Deferred</code> returned by <code>getMailExchange</code> fires. |
| 692 | | Once it does, we proceed normally through the creation of our |
| 693 | | <code>SMTPClientFactory</code> and <code>TCPClient</code>, as well as |
| 694 | | set the <code>TCPClient</code>'s service parent, just as we did in the |
| 695 | | previous examples.</p> |
| | 690 | creation of the <code>TCPClient</code> until the <code>Deferred</code> |
| | 691 | returned by <code>getMailExchange</code> fires. Once it does, we |
| | 692 | proceed normally through the creation of |
| | 693 | our <code>SMTPClientFactory</code> and <code>TCPClient</code>, as well |
| | 694 | as set the <code>TCPClient</code>'s service parent, just as we did in |
| | 695 | the previous examples.</p> |
| 696 | 696 | |
| 697 | 697 | <h3>SMTP Client 11</h3> |
| 698 | 698 | |
| 699 | 699 | <p>At last we're ready to perform the mail exchange lookup. We do |
| 700 | | this by calling on an object provided specifically for this task, |
| 701 | | <code>twisted.mail.relaymanager.MXCalculator</code>:</p> |
| | 700 | this by calling on an object provided specifically for this |
| | 701 | task, <code>twisted.mail.relaymanager.MXCalculator</code>:</p> |
| 702 | 702 | |
| 703 | 703 | <pre class="python"> |
| 704 | 704 | def getMailExchange(host): |
| … |
… |
|
| 710 | 710 | <p>Because <code>getMX</code> returns a <code>Record_MX</code> object |
| 711 | 711 | rather than a string, we do a little bit of post-processing to get the |
| 712 | 712 | results we want. We have already converted the rest of the tutorial |
| 713 | | application to expect a <code>Deferred</code> from |
| 714 | | <code>getMailExchange</code>, so no further changes are required. <a |
| 715 | | href="smtpclient-11.tac">smtpclient-11.tac</a> completes this tutorial |
| 716 | | by being able to both look up the mail exchange host for the recipient |
| 717 | | domain, connect to it, complete an SMTP transaction, report its |
| 718 | | results, and finally shut down the reactor.</p> |
| 719 | | |
| 720 | | <!-- TODO: write a conclusion |
| 721 | | |
| 722 | | <h3>Conclusion</h3> |
| | 713 | application to expect a <code>Deferred</code> |
| | 714 | from <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> |
| 723 | 719 | |
| 724 | | <p>XXX wrap it up</p> |
| | 720 | <!-- TODO: write a conclusion to wrap it up --> |
| 725 | 721 | |
| 726 | | --> |
| 727 | 722 | </body> |
| 728 | 723 | </html> |