Simple SMTP Client

 < Day Day Up > 



Now, let’s look at a simple SMTP client written in Python. The SMTP client is implemented as a class that provides a set of methods to send an e-mail to a specified recipient (see Listing 18.11).

Four methods are provided by the Mailer class for e-mail transport. These are the __init__ method (though abstracted by the class name Mailer), dialog, send, and finish. Only method dialog is used internally by the Mailer class to implement the actual SMTP protocol, all others are used as API functions for transporting e-mail. We’ll investigate each of the methods in their order of appearance within a sample use. One item to note before the methods is the creation of global class variables. In this case (lines 8–13), we define a set of instance variables and initialize them each to 0. As we’ll see in Listing 18.11, referencing these class variables requires the use of the self modifier.

The Python language Mailer class is shown in Listing 18.11. It begins by making the socket module visible using the import statement at line 1. We define the Mailer class at line 3, and then, as defined in the previous paragraph, we create our class global variables.

Listing 18.11 Python language SMTP client source.

start example
  1   import socket   2   3   class Mailer:   4   5     #   6     # Define the class variables (host and server socket)   7     #   8     subject = 0   9     sender = 0  10     recipient = 0  11     content_type = 0  12     contents = 0  13     sock = 0  14  15  16     #  17     # Class initialization method  18     #  19     def __init__( self, subject, sender, recipient,   20                   content_type, contents ):  21  22       self.subject = subject  23       self.sender = sender  24       self.recipient = recipient  25       self.content_type = content_type  26       self.contents = contents  27       self.sock = 0  28  29  30     #  31     # Perform a dialog with the SMTP server  32     #  33     def dialog( self, command, expected_response ):  34  35       if command != 0:  36  37         self.sock.send( command )  38  39       if expected_response != 0:  40  41         line = self.sock.recv( 100 )  42  43         if line.startswith( expected_response ) == 0:  44  45           raise IOError  46  47  48     #  49     # Send the mail based upon the initialized parameters  50     #  51     def send( self ):  52  53       mail_elements = self.recipient.split('@')  54  55       self.sock = socket.socket( socket.AF_INET,   56                                  socket.SOCK_STREAM )  57  58       self.sock.connect( (mail_elements[1], 25) )  59  60       # Look for initial e-mail salutation  61       self.dialog( 0, "220" )  62  63       # Send HELO and await response  64       self.dialog( "HELO thisdomain.com\n", "250" )  65  66       # Send "MAIL FROM" command and await response  67       string = "MAIL FROM: " + self.sender + "\n"  68       self.dialog( string, "250" )  69  70       # Send "RCPT TO" command and await response  71       string = "RCPT TO: " + self.recipient + "\n"  72       self.dialog( string, "250" )  73  74       # Send "DATA" to start the message body  75       self.dialog( "DATA\n", "354" )  76  77       # Send out the mail headers (from/to/subject)  78       string = "From: " + self.sender + "\n"  79       self.dialog( string, 0 )  80  81       string = "To: " + self.recipient + "\n"  82       self.dialog( string, 0 )  83  84       string = "Subject: " + self.subject + "\n"  85       self.dialog( string, 0 )  86  87       # Send the Content Type  88       string = "Content-Type: " + self.content_type + "\n"  89       self.dialog( string, 0 )  90  91       # Send the actual message body  92       self.dialog( self.contents, 0 )  93  94       # Send the end-of-email indicator  95       self.dialog( "\n.\n", "250" )  96  97       # Finally, close out the session  98       self.dialog( "QUIT\n", "221" ) 100 101 102     # 103     # Close out the smtp session by closing the socket 104     # 105     def finish( self ): 106 107       self.sock.shutdown(1)
end example

Let’s now look at the methods created within the Mailer class. The first method used by mail users is the __init__ method that is accessed by the class name as a method (abstracted by Mailer). The application creates an instance of the Mailer class using the Mailer method. With the new instance, the Mailer (__init__) method is called to specify the subject, sender, recipient, content_type, and message body (contents). As shown in lines 22–26, these variables are copied from the application into the class instance variables and are bound to this instance of the class. The final __init__ method step is initializing the local sock variable, in this case to zero (line 27).

Next, we look at the send method (lines 51–98). The first step at line 53 is to identify the mail server to which we’ll connect to send our e-mail. We can identify this mail server by taking the FQDN from the recipient (such as mtjones.com from the e-mail address mtj@mtjones.com). The split method is used to separate the username from the domain name, with the resulting two-element array stored in mail_elements (line 53). A stream socket is created next at lines 55–56, which will be used as our conduit to the mail server. At line 58, we use the connect method to connect to the mail server. We utilize the second element of the mail_elements array as the mail server to which we’re connecting, and specify the standard SMTP port number, 25.

Lines 61–98 of the send method from Listing 18.11 implement the basics of the SMTP client side. Using the dialog method (provided within this class), transactions with the SMTP server are performed. We do not discuss the details of the SMTP protocol here; however, a discussion of SMTP and the design pattern is provided in Chapter 15, Software Patterns Introduction.

The dialog method (shown in lines 33–45 of Listing 18.11) provides the ability for the client to perform communicative transactions with the SMTP server. The dialog method accepts three arguments, the self variable, the command (the string to send to the server), and the expected_response (the string error code that is expected back for a successful transaction). The dialog function can send a command and receive a response, send a command without expecting a response, or send no command but receive a response. This behavior is implemented simply by checking the command/expected_response for their presence and acting accordingly. At line 35, we check the presence of a command (command is not zero), and if present, the command is sent using the send method at line 37. At line 39, we check whether the caller provided an expected_response. If so, a response is expected, so we read up to 100 octets from the socket using the recv method at line 41. Using the startswith method, we check to see if the expected_response is the first set of characters in the server response (stored in line). If the expected response is not found, we raise an IOError. This error causes us to abort the dialog and raises the exception back to the user application, which in this case deals with the exception.

Finally, to complete the SMTP session, the finish method must be called (lines 105–107). This method simply closes the client socket of the Mailer object using the shutdown method.

Now, let’s look at a sample application of the Mailer class. Listing 18.12 shows a sample usage of the Mailer class. At line 6, a new Mailer object is created by calling the __init__ method of the Mailer class (implicit in the Mailer call). We specify the subject (line 6), source and destination e-mail addresses (lines 7 and 8), the content type (line 9), and finally the mail message (shown as two split strings at lines 10 and 11). Note that, in this case, we’re showing an HTML encoded e-mail being sent, as is illustrated by the HTML content in the mail message.

Now that the mail object has been created and initialized, we can send the e-mail using the send method (at line 15). Note the try statement at line 14 that bounds the send method. For methods that extend the possibility of raising an exception, the try method is used to note this. If an exception is raised within the bounds of the try block, the except statement identifies which exceptions to handle and how to handle them. In this case (line 16), we’re handling the IOError exception, and we handle it by simply emitting a debug message using the print statement (line 17). If an exception was not raised within the try block, execution continues at line 20 where we use the finish method to complete the SMTP transaction and close the socket. Finally at line 23, the mail object is deleted using the del statement.

Listing 18.12 Instantiating a new SMTP client.

start example
 1   #  2   # Create a new mailer and send an e-mail  3   #  4  5   # Create a new Mailer instance  6   mail = Mailer( "The Subject",  7                  "tim@mtjones.com",  8                  "mtj@mtjones.com",  9                  "text/html", 10                  "<HTML><BODY><H1>This is the mail" 11                  "</H1></BODY></HTML" ) 12 13   # Try to send the mail 14   try: 15     mail.send() 16   except IOError: 17     print "Couldn't send mail" 18 19   # Finish up the mail session 20   mail.finish() 21 22   # Remove the instance 23   del mail
end example



 < Day Day Up > 



BSD Sockets Programming from a Multi-Language Perspective
Network Programming for Microsoft Windows , Second Edition (Microsoft Programming Series)
ISBN: 1584502681
EAN: 2147483647
Year: 2003
Pages: 225
Authors: Jim Ohlund

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net