Initial import to Tizen
[profile/ivi/python-twisted.git] / doc / mail / tutorial / smtpclient / smtpclient.html
1 <?xml version="1.0" encoding="utf-8"?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
2   <head>
3 <title>Twisted Documentation: Twisted Mail Tutorial: Building an SMTP Client from Scratch</title>
4 <link href="../../howto/stylesheet.css" rel="stylesheet" type="text/css"/>
5   </head>
6
7   <body bgcolor="white">
8     <h1 class="title">Twisted Mail Tutorial: Building an SMTP Client from Scratch</h1>
9     <div class="toc"><ol><li><a href="#auto0">Introduction</a></li><ul><li><a href="#auto1">SMTP Client 1</a></li><li><a href="#auto2">SMTP Client 2</a></li><li><a href="#auto3">SMTP Client 3</a></li><li><a href="#auto4">SMTP Client 4</a></li><li><a href="#auto5">SMTP Client 5</a></li><li><a href="#auto6">SMTP Client 6</a></li><li><a href="#auto7">SMTP Client 7</a></li><li><a href="#auto8">SMTP Client 8</a></li><li><a href="#auto9">SMTP Client 9</a></li><li><a href="#auto10">SMTP Client 10</a></li><li><a href="#auto11">SMTP Client 11</a></li></ul></ol></div>
10     <div class="content">
11
12 <span/>
13
14 <h2>Introduction<a name="auto0"/></h2>
15
16 <p>This tutorial will walk you through the creation of an extremely
17 simple SMTP client application.  By the time the tutorial is complete,
18 you will understand how to create and start a TCP client speaking the
19 SMTP protocol, have it connect to an appropriate mail exchange server,
20 and transmit a message for delivery.</p>
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
25 sure that you have <code>twistd</code> installed and conveniently
26 accessible for use in running each of the example <code>.tac</code>
27 files.</p>
28
29 <h3>SMTP Client 1<a name="auto1"/></h3>
30
31 <p>The first step is to create <a href="smtpclient-1.tac" shape="rect">the most
32 minimal <code>.tac</code> file</a> possible for use by <code>twistd</code> .</p>
33
34 <pre class="python"><p class="py-linenumber">1
35 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">service</span>
36 </pre>
37
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
43 service</em>.  An <em>application service</em> simply acts as a
44 central object on which to store certain kinds of deployment
45 configuration.</p>
46
47 <pre class="python"><p class="py-linenumber">1
48 </p><span class="py-src-variable">application</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">Application</span>(<span class="py-src-string">&quot;SMTP Client Tutorial&quot;</span>)
49 </pre>
50
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
56 example, <code>&quot;SMTP Client Tutorial&quot;</code> will be used as the name
57 of the <code>.tap</code> file into which to serialize application
58 state, should it be necessary to do so.</p>
59
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" shape="rect">smtpclient-1.tac</a> using
63 the <code>twistd</code> command line:</p>
64
65 <pre class="python"><p class="py-linenumber">1
66 </p><span class="py-src-variable">twistd</span> -<span class="py-src-variable">ny</span> <span class="py-src-variable">smtpclient</span>-<span class="py-src-number">1.</span><span class="py-src-variable">tac</span>
67 </pre>
68
69 <p>we are rewarded with the following output:</p>
70
71 <pre class="shell" xml:space="preserve">
72 exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-1.tac
73 18:31 EST [-] Log opened.
74 18:31 EST [-] twistd 2.0.0 (/usr/bin/python2.4 2.4.1) starting up
75 18:31 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
76 18:31 EST [-] Loading smtpclient-1.tac...
77 18:31 EST [-] Loaded.
78 </pre>
79
80 <p>As we expected, not much is going on.  We can shutdown this server
81 by issuing <code>^C</code>:</p>
82
83 <pre class="shell" xml:space="preserve">
84 18:34 EST [-] Received SIGINT, shutting down.
85 18:34 EST [-] Main loop terminated.
86 18:34 EST [-] Server Shut Down.
87 exarkun@boson:~/mail/tutorial/smtpclient$
88 </pre>
89
90 <h3>SMTP Client 2<a name="auto2"/></h3>
91
92 <p>The first version of our SMTP client wasn't very interesting.  It
93 didn't even establish any TCP connections!  The <a href="smtpclient-2.tac" shape="rect">second version</a> will come a little bit
94 closer to that level of complexity.  First, we need to import a few
95 more things:</p>
96
97 <pre class="python"><p class="py-linenumber">1
98 2
99 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">internet</span>
100 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">protocol</span>
101 </pre>
102
103 <p><code>twisted.application.internet</code> is
104 another <em>application service</em> module.  It provides services for
105 establishing outgoing connections (as well as creating network
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>
110
111 <p>The next line of <a href="smtpclient-2.tac" shape="rect">smtpclient-2.tac</a>
112 instantiates a new <em>client factory</em>.</p>
113
114 <pre class="python"><p class="py-linenumber">1
115 </p><span class="py-src-variable">smtpClientFactory</span> = <span class="py-src-variable">protocol</span>.<span class="py-src-variable">ClientFactory</span>()
116 </pre>
117
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>
124
125 <p>Now that we have a client factory, we'll need to hook it up to the
126 network somehow.  The next line of <code>smtpclient-2.tac</code> does
127 just that:</p>
128
129 <pre class="python"><p class="py-linenumber">1
130 </p><span class="py-src-variable">smtpClientService</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPClient</span>(<span class="py-src-variable">None</span>, <span class="py-src-variable">None</span>, <span class="py-src-variable">smtpClientFactory</span>)
131 </pre>
132
133 <p>We'll ignore the first two arguments
134 to <code>internet.TCPClient</code> for the moment and instead focus on
135 the third.  <code>TCPClient</code> is one of those <em>application
136 service</em> classes.  It creates TCP connections to a specified
137 address and then uses its third argument, a <em>client factory</em>,
138 to get a <em>protocol instance</em>.  It then associates the TCP
139 connection with the protocol instance and gets out of the way.</p>
140
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
143 disappointing:</p>
144
145 <pre class="shell" xml:space="preserve">
146 exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-2.tac
147 18:55 EST [-] Log opened.
148 18:55 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
149 18:55 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
150 18:55 EST [-] Loading smtpclient-2.tac...
151 18:55 EST [-] Loaded.
152 18:55 EST [-] Starting factory &lt;twisted.internet.protocol.ClientFactory
153               instance at 0xb791e46c&gt;
154 18:55 EST [-] Traceback (most recent call last):
155           File &quot;twisted/scripts/twistd.py&quot;, line 187, in runApp
156             app.runReactorWithLogging(config, oldstdout, oldstderr)
157           File &quot;twisted/application/app.py&quot;, line 128, in runReactorWithLogging
158             reactor.run()
159           File &quot;twisted/internet/posixbase.py&quot;, line 200, in run
160             self.mainLoop()
161           File &quot;twisted/internet/posixbase.py&quot;, line 208, in mainLoop
162             self.runUntilCurrent()
163         --- &lt;exception caught here&gt; ---
164           File &quot;twisted/internet/base.py&quot;, line 533, in runUntilCurrent
165             call.func(*call.args, **call.kw)
166           File &quot;twisted/internet/tcp.py&quot;, line 489, in resolveAddress
167             if abstract.isIPAddress(self.addr[0]):
168           File &quot;twisted/internet/abstract.py&quot;, line 315, in isIPAddress
169             parts = string.split(addr, '.')
170           File &quot;/usr/lib/python2.4/string.py&quot;, line 292, in split
171             return s.split(sep, maxsplit)
172         exceptions.AttributeError: 'NoneType' object has no attribute 'split'
173
174 18:55 EST [-] Received SIGINT, shutting down.
175 18:55 EST [-] Main loop terminated.
176 18:55 EST [-] Server Shut Down.
177 exarkun@boson:~/mail/tutorial/smtpclient$
178 </pre>
179
180 <p>What happened?  Those first two arguments to <code>TCPClient</code>
181 turned out to be important after all.  We'll get to them in the next
182 example.</p>
183
184 <h3>SMTP Client 3<a name="auto3"/></h3>
185
186 <p>Version three of our SMTP client only changes one thing.  The line
187 from version two:</p>
188
189 <pre class="python"><p class="py-linenumber">1
190 </p><span class="py-src-variable">smtpClientService</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPClient</span>(<span class="py-src-variable">None</span>, <span class="py-src-variable">None</span>, <span class="py-src-variable">smtpClientFactory</span>)
191 </pre>
192
193 <p>has its first two arguments changed from <code>None</code> to
194 something with a bit more meaning:</p>
195
196 <pre class="python"><p class="py-linenumber">1
197 </p><span class="py-src-variable">smtpClientService</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPClient</span>(<span class="py-src-string">'localhost'</span>, <span class="py-src-number">25</span>, <span class="py-src-variable">smtpClientFactory</span>)
198 </pre>
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
203 run <a href="smtpclient-3.tac" shape="rect">smtpclient-3.tac</a> and see what this
204 change gets us:</p>
205
206 <pre class="shell" xml:space="preserve">
207 exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-3.tac
208 19:10 EST [-] Log opened.
209 19:10 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
210 19:10 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
211 19:10 EST [-] Loading smtpclient-3.tac...
212 19:10 EST [-] Loaded.
213 19:10 EST [-] Starting factory &lt;twisted.internet.protocol.ClientFactory
214               instance at 0xb791e48c&gt;
215 19:10 EST [-] Enabling Multithreading.
216 19:10 EST [Uninitialized] Traceback (most recent call last):
217           File &quot;twisted/python/log.py&quot;, line 56, in callWithLogger
218             return callWithContext({&quot;system&quot;: lp}, func, *args, **kw)
219           File &quot;twisted/python/log.py&quot;, line 41, in callWithContext
220             return context.call({ILogContext: newCtx}, func, *args, **kw)
221           File &quot;twisted/python/context.py&quot;, line 52, in callWithContext
222             return self.currentContext().callWithContext(ctx, func, *args, **kw)
223           File &quot;twisted/python/context.py&quot;, line 31, in callWithContext
224             return func(*args,**kw)
225         --- &lt;exception caught here&gt; ---
226           File &quot;twisted/internet/selectreactor.py&quot;, line 139, in _doReadOrWrite
227             why = getattr(selectable, method)()
228           File &quot;twisted/internet/tcp.py&quot;, line 543, in doConnect
229             self._connectDone()
230           File &quot;twisted/internet/tcp.py&quot;, line 546, in _connectDone
231             self.protocol = self.connector.buildProtocol(self.getPeer())
232           File &quot;twisted/internet/base.py&quot;, line 641, in buildProtocol
233             return self.factory.buildProtocol(addr)
234           File &quot;twisted/internet/protocol.py&quot;, line 99, in buildProtocol
235             p = self.protocol()
236         exceptions.TypeError: 'NoneType' object is not callable
237
238 19:10 EST [Uninitialized] Stopping factory
239           &lt;twisted.internet.protocol.ClientFactory instance at
240           0xb791e48c&gt;
241 19:10 EST [-] Received SIGINT, shutting down.
242 19:10 EST [-] Main loop terminated.
243 19:10 EST [-] Server Shut Down.
244 exarkun@boson:~/mail/tutorial/smtpclient$
245 </pre>
246
247 <p>A meagre amount of progress, but the service still raises an
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>
251
252 <h3>SMTP Client 4<a name="auto4"/></h3>
253
254 <p>In the previous example, we ran into a problem because we hadn't
255 set up our <em>client factory's</em> <em>protocol</em> attribute
256 correctly (or at all).  <code>ClientFactory.buildProtocol</code> is
257 the method responsible for creating a <em>protocol instance</em>.  The
258 default implementation calls the factory's <code>protocol</code> attribute,
259 adds itself as an attribute named <code>factory</code> to the
260 resulting instance, and returns it.  In <a href="smtpclient-4.tac" shape="rect">smtpclient-4.tac</a>, we'll correct the
261 oversight that caused the traceback in smtpclient-3.tac:</p>
262
263 <pre class="python"><p class="py-linenumber">1
264 </p><span class="py-src-variable">smtpClientFactory</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-variable">protocol</span>.<span class="py-src-variable">Protocol</span>
265 </pre>
266
267 <p>Running this version of the client, we can see the output is once
268 again traceback free:</p>
269
270 <pre class="shell" xml:space="preserve">
271 exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-4.tac
272 19:29 EST [-] Log opened.
273 19:29 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
274 19:29 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
275 19:29 EST [-] Loading smtpclient-4.tac...
276 19:29 EST [-] Loaded.
277 19:29 EST [-] Starting factory &lt;twisted.internet.protocol.ClientFactory
278               instance at 0xb791e4ac&gt;
279 19:29 EST [-] Enabling Multithreading.
280 19:29 EST [-] Received SIGINT, shutting down.
281 19:29 EST [Protocol,client] Stopping factory
282           &lt;twisted.internet.protocol.ClientFactory instance at
283           0xb791e4ac&gt;
284 19:29 EST [-] Main loop terminated.
285 19:29 EST [-] Server Shut Down.
286 exarkun@boson:~/doc/mail/tutorial/smtpclient$
287 </pre>
288
289 <p>But what does this
290 mean? <code>twisted.internet.protocol.Protocol</code> is the
291 base <em>protocol</em> implementation.  For those familiar with the
292 classic UNIX network services, it is equivalent to
293 the <em>discard</em> service.  It never produces any output and it
294 discards all its input.  Not terribly useful, and certainly nothing
295 like an SMTP client.  Let's see how we can improve this in the next
296 example.</p>
297
298 <h3>SMTP Client 5<a name="auto5"/></h3>
299
300 <p>In <a href="smtpclient-5.tac" shape="rect">smtpclient-5.tac</a>, we will begin
301 to use Twisted's SMTP protocol implementation for the first time.
302 We'll make the obvious change, simply swapping
303 out <code>twisted.internet.protocol.Protocol</code> in favor
304 of <code>twisted.mail.smtp.ESMTPClient</code>.  Don't worry about
305 the <em>E</em> in <em>ESMTP</em>.  It indicates we're actually using a
306 newer version of the SMTP protocol.  There is
307 an <code>SMTPClient</code> in Twisted, but there's essentially no
308 reason to ever use it.</p>
309
310 <p>smtpclient-5.tac adds a new import:</p>
311
312 <pre class="python"><p class="py-linenumber">1
313 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">mail</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">smtp</span>
314 </pre>
315
316 <p>All of the mail related code in Twisted exists beneath
317 the <code>twisted.mail</code> package.  More specifically, everything
318 having to do with the SMTP protocol implementation is defined in
319 the <code>twisted.mail.smtp</code> module.</p>
320
321 <p>Next we remove a line we added in smtpclient-4.tac:</p>
322
323 <pre class="python"><p class="py-linenumber">1
324 </p><span class="py-src-variable">smtpClientFactory</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-variable">protocol</span>.<span class="py-src-variable">Protocol</span>
325 </pre>
326
327 <p>And add a similar one in its place:</p>
328
329 <pre class="python"><p class="py-linenumber">1
330 </p><span class="py-src-variable">smtpClientFactory</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-variable">smtp</span>.<span class="py-src-variable">ESMTPClient</span>
331 </pre>
332
333 <p>Our client factory is now using a protocol implementation which
334 behaves as an SMTP client.  What happens when we try to run this
335 version?</p>
336
337 <pre class="shell" xml:space="preserve">
338 exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-5.tac
339 19:42 EST [-] Log opened.
340 19:42 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
341 19:42 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
342 19:42 EST [-] Loading smtpclient-5.tac...
343 19:42 EST [-] Loaded.
344 19:42 EST [-] Starting factory &lt;twisted.internet.protocol.ClientFactory
345               instance at 0xb791e54c&gt;
346 19:42 EST [-] Enabling Multithreading.
347 19:42 EST [Uninitialized] Traceback (most recent call last):
348           File &quot;twisted/python/log.py&quot;, line 56, in callWithLogger
349             return callWithContext({&quot;system&quot;: lp}, func, *args, **kw)
350           File &quot;twisted/python/log.py&quot;, line 41, in callWithContext
351             return context.call({ILogContext: newCtx}, func, *args, **kw)
352           File &quot;twisted/python/context.py&quot;, line 52, in callWithContext
353             return self.currentContext().callWithContext(ctx, func, *args, **kw)
354           File &quot;twisted/python/context.py&quot;, line 31, in callWithContext
355             return func(*args,**kw)
356         --- &lt;exception caught here&gt; ---
357           File &quot;twisted/internet/selectreactor.py&quot;, line 139, in _doReadOrWrite
358             why = getattr(selectable, method)()
359           File &quot;twisted/internet/tcp.py&quot;, line 543, in doConnect
360             self._connectDone()
361           File &quot;twisted/internet/tcp.py&quot;, line 546, in _connectDone
362             self.protocol = self.connector.buildProtocol(self.getPeer())
363           File &quot;twisted/internet/base.py&quot;, line 641, in buildProtocol
364             return self.factory.buildProtocol(addr)
365           File &quot;twisted/internet/protocol.py&quot;, line 99, in buildProtocol
366             p = self.protocol()
367         exceptions.TypeError: __init__() takes at least 2 arguments (1 given)
368
369 19:42 EST [Uninitialized] Stopping factory
370           &lt;twisted.internet.protocol.ClientFactory instance at
371           0xb791e54c&gt;
372 19:43 EST [-] Received SIGINT, shutting down.
373 19:43 EST [-] Main loop terminated.
374 19:43 EST [-] Server Shut Down.
375 exarkun@boson:~/doc/mail/tutorial/smtpclient$
376 </pre>
377
378
379 <p>Oops, back to getting a traceback.  This time, the default
380 implementation of <code>buildProtocol</code> seems no longer to be
381 sufficient.  It instantiates the protocol with no arguments,
382 but <code>ESMTPClient</code> wants at least one argument.  In the next
383 version of the client, we'll override <code>buildProtocol</code> to
384 fix this problem.</p>
385
386 <h3>SMTP Client 6<a name="auto6"/></h3>
387
388 <p><a href="smtpclient-6.tac" shape="rect">smtpclient-6.tac</a> introduces
389 a <code>twisted.internet.protocol.ClientFactory</code> subclass with
390 an overridden <code>buildProtocol</code> method to overcome the
391 problem encountered in the previous example.</p>
392
393 <pre class="python"><p class="py-linenumber">1
394 2
395 3
396 4
397 5
398 </p><span class="py-src-keyword">class</span> <span class="py-src-identifier">SMTPClientFactory</span>(<span class="py-src-parameter">protocol</span>.<span class="py-src-parameter">ClientFactory</span>):
399     <span class="py-src-variable">protocol</span> = <span class="py-src-variable">smtp</span>.<span class="py-src-variable">ESMTPClient</span>
400
401     <span class="py-src-keyword">def</span> <span class="py-src-identifier">buildProtocol</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">addr</span>):
402         <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">protocol</span>(<span class="py-src-variable">secret</span>=<span class="py-src-variable">None</span>, <span class="py-src-variable">identity</span>=<span class="py-src-string">'example.com'</span>)
403 </pre>
404
405 <p>The overridden method does almost the same thing as the base
406 implementation: the only change is that it passes values for two
407 arguments to <code>twisted.mail.smtp.ESMTPClient</code>'s initializer.
408 The <code>secret</code> argument is used for SMTP authentication
409 (which we will not attempt yet).  The <code>identity</code> argument
410 is used as a to identify ourselves Another minor change to note is
411 that the <code>protocol</code> attribute is now defined in the class
412 definition, rather than tacked onto an instance after one is created.
413 This means it is a class attribute, rather than an instance attribute,
414 now, which makes no difference as far as this example is concerned.
415 There are circumstances in which the difference is important: be sure
416 you understand the implications of each approach when creating your
417 own factories.</p>
418
419 <p>One other change is required: instead of
420 instantiating <code>twisted.internet.protocol.ClientFactory</code>, we
421 will now instantiate <code>SMTPClientFactory</code>:</p>
422
423 <pre class="python"><p class="py-linenumber">1
424 </p><span class="py-src-variable">smtpClientFactory</span> = <span class="py-src-variable">SMTPClientFactory</span>()
425 </pre>
426
427 <p>Running this version of the code, we observe that the
428 code <strong>still</strong> isn't quite traceback-free.</p>
429
430 <pre class="shell" xml:space="preserve">
431 exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-6.tac
432 21:17 EST [-] Log opened.
433 21:17 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
434 21:17 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
435 21:17 EST [-] Loading smtpclient-6.tac...
436 21:17 EST [-] Loaded.
437 21:17 EST [-] Starting factory &lt;__builtin__.SMTPClientFactory instance
438               at 0xb77fd68c&gt;
439 21:17 EST [-] Enabling Multithreading.
440 21:17 EST [ESMTPClient,client] Traceback (most recent call last):
441           File &quot;twisted/python/log.py&quot;, line 56, in callWithLogger
442             return callWithContext({&quot;system&quot;: lp}, func, *args, **kw)
443           File &quot;twisted/python/log.py&quot;, line 41, in callWithContext
444             return context.call({ILogContext: newCtx}, func, *args, **kw)
445           File &quot;twisted/python/context.py&quot;, line 52, in callWithContext
446             return self.currentContext().callWithContext(ctx, func, *args, **kw)
447           File &quot;twisted/python/context.py&quot;, line 31, in callWithContext
448             return func(*args,**kw)
449         --- &lt;exception caught here&gt; ---
450           File &quot;twisted/internet/selectreactor.py&quot;, line 139, in _doReadOrWrite
451             why = getattr(selectable, method)()
452           File &quot;twisted/internet/tcp.py&quot;, line 351, in doRead
453             return self.protocol.dataReceived(data)
454           File &quot;twisted/protocols/basic.py&quot;, line 221, in dataReceived
455             why = self.lineReceived(line)
456           File &quot;twisted/mail/smtp.py&quot;, line 1039, in lineReceived
457             why = self._okresponse(self.code,'\n'.join(self.resp))
458           File &quot;twisted/mail/smtp.py&quot;, line 1281, in esmtpState_serverConfig
459             self.tryTLS(code, resp, items)
460           File &quot;twisted/mail/smtp.py&quot;, line 1294, in tryTLS
461             self.authenticate(code, resp, items)
462           File &quot;twisted/mail/smtp.py&quot;, line 1343, in authenticate
463             self.smtpState_from(code, resp)
464           File &quot;twisted/mail/smtp.py&quot;, line 1062, in smtpState_from
465             self._from = self.getMailFrom()
466           File &quot;twisted/mail/smtp.py&quot;, line 1137, in getMailFrom
467             raise NotImplementedError
468         exceptions.NotImplementedError:
469
470 21:17 EST [ESMTPClient,client] Stopping factory
471           &lt;__builtin__.SMTPClientFactory instance at 0xb77fd68c&gt;
472 21:17 EST [-] Received SIGINT, shutting down.
473 21:17 EST [-] Main loop terminated.
474 21:17 EST [-] Server Shut Down.
475 exarkun@boson:~/doc/mail/tutorial/smtpclient$
476 </pre>
477
478 <p>What we have accomplished with this iteration of the example is to
479 navigate far enough into an SMTP transaction that Twisted is now
480 interested in calling back to application-level code to determine what
481 its next step should be.  In the next example, we'll see how to
482 provide that information to it.</p>
483
484 <h3>SMTP Client 7<a name="auto7"/></h3>
485
486 <p>SMTP Client 7 is the first version of our SMTP client which
487 actually includes message data to transmit.  For simplicity's sake,
488 the message is defined as part of a new class.  In a useful program
489 which sent email, message data might be pulled in from the filesystem,
490 a database, or be generated based on
491 user-input.  <a href="smtpclient-7.tac" shape="rect">smtpclient-7.tac</a>, however,
492 defines a new class, <code>SMTPTutorialClient</code>, with three class
493 attributes (<code>mailFrom</code>, <code>mailTo</code>,
494 and <code>mailData</code>):</p>
495
496 <pre class="python"><p class="py-linenumber"> 1
497  2
498  3
499  4
500  5
501  6
502  7
503  8
504  9
505 10
506 11
507 </p><span class="py-src-keyword">class</span> <span class="py-src-identifier">SMTPTutorialClient</span>(<span class="py-src-parameter">smtp</span>.<span class="py-src-parameter">ESMTPClient</span>):
508     <span class="py-src-variable">mailFrom</span> = <span class="py-src-string">&quot;tutorial_sender@example.com&quot;</span>
509     <span class="py-src-variable">mailTo</span> = <span class="py-src-string">&quot;tutorial_recipient@example.net&quot;</span>
510     <span class="py-src-variable">mailData</span> = <span class="py-src-string">'''\
511 Date: Fri, 6 Feb 2004 10:14:39 -0800
512 From: Tutorial Guy &lt;tutorial_sender@example.com&gt;
513 To: Tutorial Gal &lt;tutorial_recipient@example.net&gt;
514 Subject: Tutorate!
515
516 Hello, how are you, goodbye.
517 '''</span>
518 </pre>
519
520 <p>This statically defined data is accessed later in the class
521 definition by three of the methods which are part of the
522  <em>SMTPClient callback API</em>.  Twisted expects each of the three
523 methods below to be defined and to return an object with a particular
524 meaning.  First, <code>getMailFrom</code>:</p>
525
526 <pre class="python"><p class="py-linenumber">1
527 2
528 3
529 4
530 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">getMailFrom</span>(<span class="py-src-parameter">self</span>):
531         <span class="py-src-variable">result</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">mailFrom</span>
532         <span class="py-src-variable">self</span>.<span class="py-src-variable">mailFrom</span> = <span class="py-src-variable">None</span>
533         <span class="py-src-keyword">return</span> <span class="py-src-variable">result</span>
534 </pre>
535
536 <p>This method is called to determine the <em>reverse-path</em>,
537 otherwise known as the <em>envelope from</em>, of the message.  This
538 value will be used when sending the <code>MAIL FROM</code> SMTP
539 command.  The method must return a string which conforms to the <a href="http://www.faqs.org/rfcs/rfc2821.html" shape="rect">RFC 2821</a> definition
540 of a <em>reverse-path</em>.  In simpler terms, it should be a string
541 like <code>&quot;alice@example.com&quot;</code>.  Only one <em>envelope
542 from</em> is allowed by the SMTP protocol, so it cannot be a list of
543 strings or a comma separated list of addresses.  Our implementation
544 of <code>getMailFrom</code> does a little bit more than just return a
545 string; we'll get back to this in a little bit.</p>
546
547 <p>The next method is <code>getMailTo</code>:</p>
548
549 <pre class="python"><p class="py-linenumber">1
550 2
551 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">getMailTo</span>(<span class="py-src-parameter">self</span>):
552         <span class="py-src-keyword">return</span> [<span class="py-src-variable">self</span>.<span class="py-src-variable">mailTo</span>]
553 </pre>
554
555 <p><code>getMailTo</code> is similar to <code>getMailFrom</code>.  It
556 returns one or more RFC 2821 addresses (this time a
557  <em>forward-path</em>, or <em>envelope to</em>).  Since SMTP allows
558 multiple recipients, <code>getMailTo</code> returns a list of these
559 addresses.  The list must contain at least one address, and even if
560 there is exactly one recipient, it must still be in a list.</p>
561
562 <p>The final callback we will define to provide information to
563 Twisted is <code>getMailData</code>:</p>
564
565 <pre class="python"><p class="py-linenumber">1
566 2
567 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">getMailData</span>(<span class="py-src-parameter">self</span>):
568         <span class="py-src-keyword">return</span> <span class="py-src-variable">StringIO</span>.<span class="py-src-variable">StringIO</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">mailData</span>)
569 </pre>
570
571 <p>This one is quite simple as well: it returns a file or a file-like
572 object which contains the message contents.  In our case, we return
573 a <code>StringIO</code> since we already have a string containing our
574 message.  If the contents of the file returned
575 by <code>getMailData</code> span multiple lines (as email messages
576 often do), the lines should be <code>\n</code> delimited (as they
577 would be when opening a text file in the <code>&quot;rt&quot;</code> mode):
578 necessary newline translation will be performed
579 by <code>SMTPClient</code> automatically.</p>
580
581 <p>There is one more new callback method defined in smtpclient-7.tac.
582 This one isn't for providing information about the messages to
583 Twisted, but for Twisted to provide information about the success or
584 failure of the message transmission to the application:</p>
585
586 <pre class="python"><p class="py-linenumber">1
587 2
588 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">sentMail</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">code</span>, <span class="py-src-parameter">resp</span>, <span class="py-src-parameter">numOk</span>, <span class="py-src-parameter">addresses</span>, <span class="py-src-parameter">log</span>):
589         <span class="py-src-keyword">print</span> <span class="py-src-string">'Sent'</span>, <span class="py-src-variable">numOk</span>, <span class="py-src-string">'messages'</span>
590 </pre>
591
592 <p>Each of the arguments to <code>sentMail</code> provides some
593 information about the success or failure of the message transmission
594 transaction.  <code>code</code> is the response code from the ultimate
595 command.  For successful transactions, it will be 250.  For transient
596 failures (those which should be retried), it will be between 400 and
597 499, inclusive.  For permanent failures (this which will never work,
598 no matter how many times you retry them), it will be between 500 and
599 599.</p>
600
601 <h3>SMTP Client 8<a name="auto8"/></h3>
602
603 <p>Thus far we have succeeded in creating a Twisted client application
604 which starts up, connects to a (possibly) remote host, transmits some
605 data, and disconnects.  Notably missing, however, is application
606 shutdown.  Hitting ^C is fine during development, but it's not exactly
607 a long-term solution.  Fortunately, programmatic shutdown is extremely
608 simple.  <a href="smtpclient-8.tac" shape="rect">smtpclient-8.tac</a>
609 extends <code>sentMail</code> with these two lines:</p>
610
611 <pre class="python"><p class="py-linenumber">1
612 2
613 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
614         <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
615 </pre>
616
617 <p>The <code>stop</code> method of the reactor causes the main event
618 loop to exit, allowing a Twisted server to shut down.  With this
619 version of the example, we see that the program actually terminates
620 after sending the message, without user-intervention:</p>
621
622 <pre class="shell" xml:space="preserve">
623 exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-8.tac
624 19:52 EST [-] Log opened.
625 19:52 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
626 19:52 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
627 19:52 EST [-] Loading smtpclient-8.tac...
628 19:52 EST [-] Loaded.
629 19:52 EST [-] Starting factory &lt;__builtin__.SMTPClientFactory instance
630               at 0xb791beec&gt;
631 19:52 EST [-] Enabling Multithreading.
632 19:52 EST [SMTPTutorialClient,client] Sent 1 messages
633 19:52 EST [SMTPTutorialClient,client] Stopping factory
634           &lt;__builtin__.SMTPClientFactory instance at 0xb791beec&gt;
635 19:52 EST [-] Main loop terminated.
636 19:52 EST [-] Server Shut Down.
637 exarkun@boson:~/doc/mail/tutorial/smtpclient$
638 </pre>
639
640 <h3>SMTP Client 9<a name="auto9"/></h3>
641
642 <p>One task remains to be completed in this tutorial SMTP client:
643 instead of always sending mail through a well-known host, we will look
644 up the mail exchange server for the recipient address and try to
645 deliver the message to that host.</p>
646
647 <p>In <a href="smtpclient-9.tac" shape="rect">smtpclient-9.tac</a>, we'll take the
648 first step towards this feature by defining a function which returns
649 the mail exchange host for a particular domain:</p>
650
651 <pre class="python"><p class="py-linenumber">1
652 2
653 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">getMailExchange</span>(<span class="py-src-parameter">host</span>):
654     <span class="py-src-keyword">return</span> <span class="py-src-string">'localhost'</span>
655 </pre>
656
657 <p>Obviously this doesn't return the correct mail exchange host yet
658 (in fact, it returns the exact same host we have been using all
659 along), but pulling out the logic for determining which host to
660 connect to into a function like this is the first step towards our
661 ultimate goal.  Now that we have <code>getMailExchange</code>, we'll
662 call it when constructing our <code>TCPClient</code> service:</p>
663
664 <pre class="python"><p class="py-linenumber">1
665 2
666 </p><span class="py-src-variable">smtpClientService</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPClient</span>(
667     <span class="py-src-variable">getMailExchange</span>(<span class="py-src-string">'example.net'</span>), <span class="py-src-number">25</span>, <span class="py-src-variable">smtpClientFactory</span>)
668 </pre>
669
670 <p>We'll expand on the definition of <code>getMailExchange</code> in
671 the next example.</p>
672
673 <h3>SMTP Client 10<a name="auto10"/></h3>
674
675 <p>In the previous example we defined <code>getMailExchange</code> to
676 return a string representing the mail exchange host for a particular
677 domain.  While this was a step in the right direction, it turns out
678 not to be a very big one.  Determining the mail exchange host for a
679 particular domain is going to involve network traffic (specifically,
680 some DNS requests).  These might take an arbitrarily large amount of
681 time, so we need to introduce a <code>Deferred</code> to represent the
682 result of <code>getMailExchange</code>.  <a href="smtpclient-10.tac" shape="rect">smtpclient-10.tac</a> redefines it
683 thusly:</p>
684
685 <pre class="python"><p class="py-linenumber">1
686 2
687 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">getMailExchange</span>(<span class="py-src-parameter">host</span>):
688     <span class="py-src-keyword">return</span> <span class="py-src-variable">defer</span>.<span class="py-src-variable">succeed</span>(<span class="py-src-string">'localhost'</span>)
689 </pre>
690
691 <p><code>defer.succeed</code> is a function which creates a
692 new <code>Deferred</code> which already has a result, in this
693 case <code>'localhost'</code>.  Now we need to adjust
694 our <code>TCPClient</code>-constructing code to expect and properly
695 handle this <code>Deferred</code>:</p>
696
697 <pre class="python"><p class="py-linenumber">1
698 2
699 3
700 4
701 5
702 6
703 7
704 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">cbMailExchange</span>(<span class="py-src-parameter">exchange</span>):
705     <span class="py-src-variable">smtpClientFactory</span> = <span class="py-src-variable">SMTPClientFactory</span>()
706
707     <span class="py-src-variable">smtpClientService</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPClient</span>(<span class="py-src-variable">exchange</span>, <span class="py-src-number">25</span>, <span class="py-src-variable">smtpClientFactory</span>)
708     <span class="py-src-variable">smtpClientService</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">application</span>)
709
710 <span class="py-src-variable">getMailExchange</span>(<span class="py-src-string">'example.net'</span>).<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">cbMailExchange</span>)
711 </pre>
712
713 <p>An in-depth exploration of <code>Deferred</code>s is beyond the
714 scope of this document.  For such a look, see
715 the <a href="../../../core/howto/defer.html" shape="rect">Deferred Reference</a>.
716 However, in brief, what this version of the code does is to delay the
717 creation of the <code>TCPClient</code> until the <code>Deferred</code>
718 returned by <code>getMailExchange</code> fires.  Once it does, we
719 proceed normally through the creation of
720 our <code>SMTPClientFactory</code> and <code>TCPClient</code>, as well
721 as set the <code>TCPClient</code>'s service parent, just as we did in
722 the previous examples.</p>
723
724 <h3>SMTP Client 11<a name="auto11"/></h3>
725
726 <p>At last we're ready to perform the mail exchange lookup.  We do
727 this by calling on an object provided specifically for this
728 task, <code>twisted.mail.relaymanager.MXCalculator</code>:</p>
729
730 <pre class="python"><p class="py-linenumber">1
731 2
732 3
733 4
734 </p><span class="py-src-keyword">def</span> <span class="py-src-identifier">getMailExchange</span>(<span class="py-src-parameter">host</span>):
735     <span class="py-src-keyword">def</span> <span class="py-src-identifier">cbMX</span>(<span class="py-src-parameter">mxRecord</span>):
736         <span class="py-src-keyword">return</span> <span class="py-src-variable">str</span>(<span class="py-src-variable">mxRecord</span>.<span class="py-src-variable">name</span>)
737     <span class="py-src-keyword">return</span> <span class="py-src-variable">relaymanager</span>.<span class="py-src-variable">MXCalculator</span>().<span class="py-src-variable">getMX</span>(<span class="py-src-variable">host</span>).<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">cbMX</span>)
738 </pre>
739
740 <p>Because <code>getMX</code> returns a <code>Record_MX</code> object
741 rather than a string, we do a little bit of post-processing to get the
742 results we want.  We have already converted the rest of the tutorial
743 application to expect a <code>Deferred</code>
744 from <code>getMailExchange</code>, so no further changes are
745 required.  <a href="smtpclient-11.tac" shape="rect">smtpclient-11.tac</a> completes
746 this tutorial by being able to both look up the mail exchange host for
747 the recipient domain, connect to it, complete an SMTP transaction,
748 report its results, and finally shut down the reactor.</p>
749
750
751
752 </div>
753
754     <p><a href="../../howto/index.html">Index</a></p>
755     <span class="version">Version: 12.1.0</span>
756   </body>
757 </html>