Ticket #5685: 5685_2.2.patch
File 5685_2.2.patch, 24.2 KB (added by , 6 years ago) |
---|
-
twisted/mail/topfiles/5685.doc
1 The SMTP Client Tutorial is revised and is now self-contained. -
doc/mail/tutorial/smtpclient/hosts
1 127.0.0.1 example.net -
doc/mail/tutorial/smtpclient/smtpclient-10.tac
50 50 def cbMailExchange(exchange): 51 51 smtpClientFactory = SMTPClientFactory() 52 52 53 smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory)53 smtpClientService = internet.TCPClient(exchange, 8025, smtpClientFactory) 54 54 smtpClientService.setServiceParent(application) 55 55 56 56 getMailExchange('example.net').addCallback(cbMailExchange) -
doc/mail/tutorial/smtpclient/smtpclient-11.tac
8 8 from twisted.internet import protocol 9 9 from twisted.internet import defer 10 10 from twisted.mail import smtp, relaymanager 11 from twisted.names.client import createResolver 11 12 12 13 class SMTPTutorialClient(smtp.ESMTPClient): 13 14 mailFrom = "tutorial_sender@example.com" … … 47 48 def getMailExchange(host): 48 49 def cbMX(mxRecord): 49 50 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)) 51 53 52 54 def cbMailExchange(exchange): 53 55 smtpClientFactory = SMTPClientFactory() 54 56 55 smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory)57 smtpClientService = internet.TCPClient(exchange, 8025, smtpClientFactory) 56 58 smtpClientService.setServiceParent(application) 57 59 58 60 getMailExchange('example.net').addCallback(cbMailExchange) -
doc/mail/tutorial/smtpclient/smtpclient-3.tac
6 6 from twisted.internet import protocol 7 7 8 8 smtpClientFactory = protocol.ClientFactory() 9 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)9 smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory) 10 10 smtpClientService.setServiceParent(application) -
doc/mail/tutorial/smtpclient/smtpclient-11a.tac
1 import StringIO 2 3 from twisted.application import service 4 5 application = service.Application("SMTP Client Tutorial") 6 7 from twisted.application import internet 8 from twisted.internet import protocol 9 from twisted.internet import defer 10 from twisted.mail import smtp, relaymanager 11 from twisted.names.client import createResolver 12 13 class SMTPTutorialClient(smtp.ESMTPClient): 14 mailFrom = "tutorial_sender@example.com" 15 mailTo = "tutorial_recipient@example.net" 16 mailData = '''\ 17 Date: Fri, 6 Feb 2004 10:14:39 -0800 18 From: Tutorial Guy <tutorial_sender@example.com> 19 To: Tutorial Gal <tutorial_recipient@example.net> 20 Subject: Tutorate! 21 22 Hello, 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 42 class SMTPClientFactory(protocol.ClientFactory): 43 protocol = SMTPTutorialClient 44 45 def buildProtocol(self, addr): 46 return self.protocol(secret=None, identity='example.com') 47 48 def getMailExchange(host): 49 def cbMX(mxRecord): 50 return str(mxRecord.name) 51 return relaymanager.MXCalculator().getMX(host).addCallback(cbMX) 52 53 def cbMailExchange(exchange): 54 smtpClientFactory = SMTPClientFactory() 55 56 smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory) 57 smtpClientService.setServiceParent(application) 58 59 getMailExchange('example.net').addCallback(cbMailExchange) -
doc/mail/tutorial/smtpclient/smtpclient-4.tac
8 8 smtpClientFactory = protocol.ClientFactory() 9 9 smtpClientFactory.protocol = protocol.Protocol 10 10 11 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)11 smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory) 12 12 smtpClientService.setServiceParent(application) -
doc/mail/tutorial/smtpclient/smtpclient-5.tac
10 10 from twisted.mail import smtp 11 11 smtpClientFactory.protocol = smtp.ESMTPClient 12 12 13 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)13 smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory) 14 14 smtpClientService.setServiceParent(application) -
doc/mail/tutorial/smtpclient/smtpclient-6.tac
14 14 15 15 smtpClientFactory = SMTPClientFactory() 16 16 17 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)17 smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory) 18 18 smtpClientService.setServiceParent(application) -
doc/mail/tutorial/smtpclient/smtpclient-7.tac
10 10 11 11 class SMTPTutorialClient(smtp.ESMTPClient): 12 12 mailFrom = "tutorial_sender@example.com" 13 mailTo = "tutorial_recipient@ example.net"13 mailTo = "tutorial_recipient@localhost" 14 14 mailData = '''\ 15 15 Date: Fri, 6 Feb 2004 10:14:39 -0800 16 16 From: Tutorial Guy <tutorial_sender@example.com> 17 To: Tutorial Gal <tutorial_recipient@ example.net>17 To: Tutorial Gal <tutorial_recipient@localhost> 18 18 Subject: Tutorate! 19 19 20 20 Hello, how are you, goodbye. … … 42 42 43 43 smtpClientFactory = SMTPClientFactory() 44 44 45 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)45 smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory) 46 46 smtpClientService.setServiceParent(application) -
doc/mail/tutorial/smtpclient/smtpclient-8.tac
10 10 11 11 class SMTPTutorialClient(smtp.ESMTPClient): 12 12 mailFrom = "tutorial_sender@example.com" 13 mailTo = "tutorial_recipient@ example.net"13 mailTo = "tutorial_recipient@localhost" 14 14 mailData = '''\ 15 15 Date: Fri, 6 Feb 2004 10:14:39 -0800 16 16 From: Tutorial Guy <tutorial_sender@example.com> 17 To: Tutorial Gal <tutorial_recipient@ example.net>17 To: Tutorial Gal <tutorial_recipient@localhost> 18 18 Subject: Tutorate! 19 19 20 20 Hello, how are you, goodbye. … … 45 45 46 46 smtpClientFactory = SMTPClientFactory() 47 47 48 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)48 smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory) 49 49 smtpClientService.setServiceParent(application) -
doc/mail/tutorial/smtpclient/smtpclient.xhtml
19 19 SMTP protocol, have it connect to an appropriate mail exchange server, 20 20 and transmit a message for delivery.</p> 21 21 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> 23 to launch the client and server applications. Make 25 24 sure that you have <code>twistd</code> installed and conveniently 26 25 accessible for use in running each of the example <code>.tac</code> 27 26 files.</p> 28 27 29 <h 3>SMTP Client 1</h3>28 <h2>SMTP Client 1</h2> 30 29 31 30 <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> 31 minimal <code>.tac</code> file</a> possible for use by <code>twistd</code>.</p> 33 32 34 33 <pre class="python"> 35 34 from twisted.application import service 36 35 </pre> 37 36 38 37 <p>The first line of the <code>.tac</code> file 39 imports <code >twisted.application.service</code>, a module which38 imports <code class="API">twisted.application.service</code>, a module which 40 39 contains many of the basic <em>service</em> classes and helper 41 40 functions available in Twisted. In particular, we will be using 42 41 the <code>Application</code> function to create a new <em>application … … 62 61 run <a href="smtpclient-1.tac">smtpclient-1.tac</a> using 63 62 the <code>twistd</code> command line:</p> 64 63 65 <pre class=" python">64 <pre class="shell"> 66 65 twistd -ny smtpclient-1.tac 67 66 </pre> 68 67 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 69 release and platform):</p> 70 70 71 71 <pre class="shell"> 72 72 exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-1.tac … … 78 78 </pre> 79 79 80 80 <p>As we expected, not much is going on. We can shutdown this server 81 by issuing <code>^C</code>:</p>81 by issuing Control-C (<code>^C</code>):</p> 82 82 83 83 <pre class="shell"> 84 84 18:34 EST [-] Received SIGINT, shutting down. … … 87 87 exarkun@boson:~/mail/tutorial/smtpclient$ 88 88 </pre> 89 89 90 <h 3>SMTP Client 2</h3>90 <h2>SMTP Client 2</h2> 91 91 92 92 <p>The first version of our SMTP client wasn't very interesting. It 93 93 didn't even establish any TCP connections! The <a … … 100 100 from twisted.internet import protocol 101 101 </pre> 102 102 103 <p><code >twisted.application.internet</code> is103 <p><code class="API">twisted.application.internet</code> is 104 104 another <em>application service</em> module. It provides services for 105 105 establishing outgoing connections (as well as creating network 106 106 servers, though we are not interested in those parts for the 107 moment). <code >twisted.internet.protocol</code> provides base107 moment). <code class="API">twisted.internet.protocol</code> provides base 108 108 implementations of many of the core Twisted concepts, such 109 109 as <em>factories</em> and <em>protocols</em>.</p> 110 110 … … 181 181 turned out to be important after all. We'll get to them in the next 182 182 example.</p> 183 183 184 <h 3>SMTP Client 3</h3>184 <h2>SMTP Client 3</h2> 185 185 186 186 <p>Version three of our SMTP client only changes one thing. The line 187 187 from version two:</p> … … 194 194 something with a bit more meaning:</p> 195 195 196 196 <pre class="python"> 197 smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory)197 smtpClientService = internet.TCPClient('localhost', 8025, smtpClientFactory) 198 198 </pre> 199 199 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 201 of localhost. This isn't the address we want ultimately, but it's 202 a good placeholder for the time being. </p> 203 <p> 204 Before we run this change, we should start an SMTP server for the client to 205 contact. The following command line starts a server on port 8025 of 206 localhost which accepts mail addressed to 207 <code>tutorial_recipient@localhost</code>, stores the 208 email in <code>/tmp/emails</code>, and stores the process id in the 209 <code>server.pid</code> file: 210 <pre class="shell"> 211 twistd --pidfile=server.pid mail -H localhost -s 8025 \ 212 -d localhost=/tmp/emails -u tutorial_recipient=pwd 213 </pre> 214 Now, we can 203 215 run <a href="smtpclient-3.tac">smtpclient-3.tac</a> and see what this 204 216 change gets us:</p> 205 217 … … 249 261 a <em>protocol class</em> for the factory to use. We'll do that in 250 262 the next example.</p> 251 263 252 <h 3>SMTP Client 4</h3>264 <h2>SMTP Client 4</h2> 253 265 254 266 <p>In the previous example, we ran into a problem because we hadn't 255 267 set up our <em>client factory's</em> <em>protocol</em> attribute … … 266 278 </pre> 267 279 268 280 <p>Running this version of the client, we can see the output is once 269 again traceback 281 again traceback-free:</p> 270 282 271 283 <pre class="shell"> 272 284 exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-4.tac … … 288 300 </pre> 289 301 290 302 <p>But what does this 291 mean? <code >twisted.internet.protocol.Protocol</code> is the303 mean? <code class="API">twisted.internet.protocol.Protocol</code> is the 292 304 base <em>protocol</em> implementation. For those familiar with the 293 305 classic UNIX network services, it is equivalent to 294 306 the <em>discard</em> service. It never produces any output and it … … 296 308 like an SMTP client. Let's see how we can improve this in the next 297 309 example.</p> 298 310 299 <h 3>SMTP Client 5</h3>311 <h2>SMTP Client 5</h2> 300 312 301 <p>In <a href="smtpclient-5.tac">smtpclient-5.tac</a>, we will begin302 touse Twisted's SMTP protocol implementation for the first time.313 <p>In <a href="smtpclient-5.tac">smtpclient-5.tac</a>, we will 314 use Twisted's SMTP protocol implementation for the first time. 303 315 We'll make the obvious change, simply swapping 304 316 out <code>twisted.internet.protocol.Protocol</code> in favor 305 of <code >twisted.mail.smtp.ESMTPClient</code>. Don't worry about317 of <code class="API">twisted.mail.smtp.ESMTPClient</code>. Don't worry about 306 318 the <em>E</em> in <em>ESMTP</em>. It indicates we're actually using a 307 319 newer version of the SMTP protocol. There is 308 320 an <code>SMTPClient</code> in Twisted, but there's essentially no … … 314 326 from twisted.mail import smtp 315 327 </pre> 316 328 317 <p>All of the mail related code in Twisted exists beneath 318 the <code>twisted.mail</code> package. More specifically, everything329 <p>All of the mail related code in Twisted exists beneath the 330 <code class="API">twisted.mail</code> package. More specifically, everything 319 331 having to do with the SMTP protocol implementation is defined in 320 the <code >twisted.mail.smtp</code> module.</p>332 the <code class="API">twisted.mail.smtp</code> module.</p> 321 333 322 334 <p>Next we remove a line we added in smtpclient-4.tac:</p> 323 335 … … 384 396 version of the client, we'll override <code>buildProtocol</code> to 385 397 fix this problem.</p> 386 398 387 <h 3>SMTP Client 6</h3>399 <h2>SMTP Client 6</h2> 388 400 389 401 <p><a href="smtpclient-6.tac">smtpclient-6.tac</a> introduces 390 a <code >twisted.internet.protocol.ClientFactory</code> subclass with402 a <code class="API">twisted.internet.protocol.ClientFactory</code> subclass with 391 403 an overridden <code>buildProtocol</code> method to overcome the 392 404 problem encountered in the previous example.</p> 393 405 … … 404 416 arguments to <code>twisted.mail.smtp.ESMTPClient</code>'s initializer. 405 417 The <code>secret</code> argument is used for SMTP authentication 406 418 (which we will not attempt yet). The <code>identity</code> argument 407 is used as a to identify ourselvesAnother minor change to note is419 is used to identify ourselves. Another minor change to note is 408 420 that the <code>protocol</code> attribute is now defined in the class 409 421 definition, rather than tacked onto an instance after one is created. 410 422 This means it is a class attribute, rather than an instance attribute, … … 413 425 you understand the implications of each approach when creating your 414 426 own factories.</p> 415 427 416 <p>One other change is required : instead of428 <p>One other change is required. Instead of 417 429 instantiating <code>twisted.internet.protocol.ClientFactory</code>, we 418 430 will now instantiate <code>SMTPClientFactory</code>:</p> 419 431 … … 478 490 its next step should be. In the next example, we'll see how to 479 491 provide that information to it.</p> 480 492 481 <h 3>SMTP Client 7</h3>493 <h2>SMTP Client 7</h2> 482 494 483 495 <p>SMTP Client 7 is the first version of our SMTP client which 484 496 actually includes message data to transmit. For simplicity's sake, … … 493 505 <pre class="python"> 494 506 class SMTPTutorialClient(smtp.ESMTPClient): 495 507 mailFrom = "tutorial_sender@example.com" 496 mailTo = "tutorial_recipient@ example.net"508 mailTo = "tutorial_recipient@localhost" 497 509 mailData = '''\ 498 510 Date: Fri, 6 Feb 2004 10:14:39 -0800 499 511 From: Tutorial Guy <tutorial_sender@example.com> 500 To: Tutorial Gal <tutorial_recipient@ example.net>512 To: Tutorial Gal <tutorial_recipient@localhost> 501 513 Subject: Tutorate! 502 514 503 515 Hello, how are you, goodbye. … … 557 569 message. If the contents of the file returned 558 570 by <code>getMailData</code> span multiple lines (as email messages 559 571 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 performed572 would be when opening a text file in the <code>"rt"</code> mode). 573 Necessary newline translation will be performed 562 574 by <code>SMTPClient</code> automatically.</p> 563 575 564 576 <p>There is one more new callback method defined in smtpclient-7.tac. … … 579 591 499, inclusive. For permanent failures (this which will never work, 580 592 no matter how many times you retry them), it will be between 500 and 581 593 599.</p> 594 <p> 595 When this version of the code is run, the message is successfully delivered 596 to the server and can be found in the 597 <code>/tmp/emails/tutorial_recipient/new</code> directory. 598 </p> 582 599 583 <h 3>SMTP Client 8</h3>600 <h2>SMTP Client 8</h2> 584 601 585 602 <p>Thus far we have succeeded in creating a Twisted client application 586 603 which starts up, connects to a (possibly) remote host, transmits some 587 604 data, and disconnects. Notably missing, however, is application 588 shutdown. Hitting ^C is fine during development, but it's not exactly 605 shutdown. Hitting <code>^C</code> is fine during development, 606 but it's not exactly 589 607 a long-term solution. Fortunately, programmatic shutdown is extremely 590 608 simple. <a href="smtpclient-8.tac">smtpclient-8.tac</a> 591 609 extends <code>sentMail</code> with these two lines:</p> … … 618 636 exarkun@boson:~/doc/mail/tutorial/smtpclient$ 619 637 </pre> 620 638 621 <h 3>SMTP Client 9</h3>639 <h2>SMTP Client 9</h2> 622 640 623 641 <p>One task remains to be completed in this tutorial SMTP client: 624 642 instead of always sending mail through a well-known host, we will look 625 643 up the mail exchange server for the recipient address and try to 626 644 deliver the message to that host.</p> 627 645 646 <p>To illustrate this, we'll change the 647 domain to which the mail is addressed from <code>localhost</code> 648 to <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 656 for <code>tutorial_recipient@example.net</code> 657 by running the following from the command prompt:</p> 658 <pre class="shell"> 659 kill `cat server.pid` 660 twistd --pidfile=server.pid mail -H example.net -s 8025 \ 661 -d example.net=/tmp/emails -u tutorial_recipient=pwd 662 </pre> 628 663 <p>In <a href="smtpclient-9.tac">smtpclient-9.tac</a>, we'll take the 629 664 first step towards this feature by defining a function which returns 630 665 the mail exchange host for a particular domain:</p> … … 643 678 644 679 <pre class="python"> 645 680 smtpClientService = internet.TCPClient( 646 getMailExchange('example.net'), 25, smtpClientFactory)681 getMailExchange('example.net'), 8025, smtpClientFactory) 647 682 </pre> 648 683 649 684 <p>We'll expand on the definition of <code>getMailExchange</code> in 650 685 the next example.</p> 651 686 652 <h 3>SMTP Client 10</h3>687 <h2>SMTP Client 10</h2> 653 688 654 689 <p>In the previous example we defined <code>getMailExchange</code> to 655 690 return a string representing the mail exchange host for a particular … … 677 712 def cbMailExchange(exchange): 678 713 smtpClientFactory = SMTPClientFactory() 679 714 680 smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory)715 smtpClientService = internet.TCPClient(exchange, 8025, smtpClientFactory) 681 716 smtpClientService.setServiceParent(application) 682 717 683 718 getMailExchange('example.net').addCallback(cbMailExchange) … … 694 729 as set the <code>TCPClient</code>'s service parent, just as we did in 695 730 the previous examples.</p> 696 731 697 <h 3>SMTP Client 11</h3>732 <h2>SMTP Client 11</h2> 698 733 699 734 <p>At last we're ready to perform the mail exchange lookup. We do 700 735 this by calling on an object provided specifically for this 701 task, <code>twisted.mail.relaymanager.MXCalculator</code>:</p> 736 task, <code class="API">twisted.mail.relaymanager.MXCalculator</code>.</p> 737 <p> 738 Ideally, we would lookup the actual address of <code>example.net</code> and 739 then contact it on port 25 which is the well-known port designated for SMTP. 740 However, many ISPs block outgoing connections on port 25 because of spam. 741 So, for the purposes of the tutorial, we'll configure <code>MXCalculator</code> 742 to consult a file which specifies the mapping of domains to addresses 743 instead of contacting a DNS server. 744 The <a href="hosts"><code>hosts</code></a> file maps <code>example.net</code> 745 to address <code>127.0.0.1</code> which is the same as <code>localhost</code>. 746 </p> 702 747 748 <p> 749 The following modification in <a href="smtpclient-11.tac">smtpclient-11.tac</a> 750 creates such an <code>MXCalculator</code>: 751 </p> 703 752 <pre class="python"> 704 753 def getMailExchange(host): 705 754 def cbMX(mxRecord): 706 755 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)) 708 758 </pre> 709 759 710 760 <p>Because <code>getMX</code> returns a <code>Record_MX</code> object … … 712 762 results we want. We have already converted the rest of the tutorial 713 763 application to expect a <code>Deferred</code> 714 764 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> 765 required. </p> 719 766 720 <!-- TODO: write a conclusion to wrap it up --> 767 <p> 768 In case your ISP does not block port 25 and you'd like to try sending mail 769 to 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> 721 774 775 776 <h2>Summary</h2> 777 <p> 778 This tutorial has worked its way from the skeleton of a Twisted application, 779 through an SMTP client which sends a message to a recipient on an SMTP server 780 at a fixed address and must be manually terminated, and finally onto an SMTP 781 client which looks up the mail exchange host for the recipient domain, 782 connects to it, delivers a message, reports its results, and 783 terminates the program. 784 </p> 722 785 </body> 723 786 </html> -
doc/mail/tutorial/smtpclient/smtpclient-9.tac
49 49 smtpClientFactory = SMTPClientFactory() 50 50 51 51 smtpClientService = internet.TCPClient( 52 getMailExchange('example.net'), 25, smtpClientFactory)52 getMailExchange('example.net'), 8025, smtpClientFactory) 53 53 smtpClientService.setServiceParent(application)