Simple SMTP Client

 < Day Day Up > 



Now, let’s look at a simple SMTP client written in Ruby. 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 20.12).

Four methods are provided by the Mailer class for e-mail transport. These are init, dialog, send, and finish. Only method dialog is hidden in the class (a protected method), and is utilized by the Mailer class to implement the actual SMTP protocol. We investigate each of these 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 nil. We know these to be instance variables because each is preceded by a ‘@’ character. These variables are global to the class, and retain their value statically between method calls. This is important because we want to be able to define the socket variable within the init method, and still have it available when we later call the send method. Other types of Ruby variables include class variables, preceded by “@@” and pure global variables, preceded by “$”.

Listing 20.12 Ruby language SMTP client source.

start example
  1   require ‘socket’   2   3   class Mailer   4   5     #   6     # Globals defined within the init method.   7     #   8     @subject = nil   9     @sender = nil  10     @recipient = nil  11     @content_type = nil  12     @contents = nil  13     @sock = 0  14  15  16     #  17     # Initialize the internal mail variables  18     #  19     def init( subject, sender, recipient,  20               content_type, contents )  21  22       @subject = subject  23       @sender = sender  24       @recipient = recipient  25       @content_type = content_type  26       @contents = contents  27  28     end  29  30  31     #  32     # Perform a dialog with the SMTP server  33     #  34     def dialog( command, expected_response )  35  36       # Only send the command if the caller provided one.  37       if command != nil  38  39         @sock.write( command )  40  41       end  42  43       # Only check for a response if the user defined the  44       # expected response code.  45       if expected_response != nil  46  47         # Get a single line from the connection  48         line = @sock.gets  49  50         # Check the received line with the expected  51         # response code  52         if line[0..expected_response.length-1] !=  53            expected_response  54  55           # Not what was expected, raise an exception.  56           raise  57  58         end  59  60       end  61  62     end  63     protected:dialog  64  65  66     #  67     # Send the mail based upon the initialized parameters  68     #  69     def send  70  71       mail_server = @recipient.split(‘@’)[1]  72  73       @sock = TCPSocket::open(mail_server, "mail")  74  75       # Look for initial e-mail salutation  76       self::dialog( nil, "220" )  77  78       # Send HELO and await response  79       self::dialog( "HELO thisdomain.com\n", "250" )  80  81       # Send "MAIL FROM" command and await response  82       string = "MAIL FROM: " + @sender + "\n"  83       self::dialog( string, "250" )  84  85       # Send "RCPT TO" command and await response  86       string = "RCPT TO: " + @recipient + "\n"  87       self::dialog( string, "250" )  88  89       # Send "DATA" to start the message body  90       self::dialog( "DATA\n", "354" )  91  92       # Send out the mail headers (from/to/subject)  93       string = "From: " + @sender + "\n"  94       self::dialog( string, nil )  95  96       string = "To: " + @recipient + "\n"  97       self::dialog( string, nil )  98  99       string = "Subject: " + @subject + "\n" 100       self::dialog( string, nil ) 101 102       # Send the content type 103       string = "Content-Type: " + @content_type + "\n" 104       self::dialog( string, nil ) 105 106       # Send the actual message body 107       self::dialog( @contents, nil ) 108 109       # Send the end-of-email indicator 110       self::dialog( "\n.\n", "250" ) 111 112       # Finally, close out the session 113       self::dialog( "QUIT\n", "221" ) 114 115       rescue 116 117         puts "Couldn’t send mail" 118         @sock.close 119 120     end 121 122 123     def finish 124 125       @sock.close 126 127     end 128 129 130   end
end example

The first function used by mail users is the init method. The application will create an instance of the Mailer class using Mailer::new. With the new instance, the init method is called to specify the subject, sender, recipient, content_type, and message body (contents). As shown in lines 19–28, these variables are copied from the application into the class instance variables and are bound to this instance of the class.

The send method is the next method invoked by the user (lines 69–120). This method implements SMTP and performs all communication with the SMTP server. The first step, at line 71, is to extract the domain name to which we’re connecting (the target mail server). This is taken from the recipient mail address using the split method, using the ‘@’ character to split the recipient username from the target domain name. For example, “mtj@mtjones.com” is split into an array of two elements, “mtj” and “mtjones.com”. Note that at line 71, we use only the second element, and store this to the mail_server local variable. Variable mail_server is then used at line 73 to create the TCP socket and connect it to the server using TCPSocket::open. The remainder of the send method is SMTP-specific communication to the mail server. This was discussed previously in Chapter 15, Software Patterns Introduction.

One item to note in the send method is the use of the rescue statement. If a method within the send method’s calls-chain raises an error (using the raise statement), then execution continues immediately at the rescue section. In this section (at line 115), we close the socket to end the session using the close method.

The dialog method (lines 34–63) is a protected method that performs the communication transactions with the SMTP server. Recall that SMTP commanding is command/response-based. If the caller provides a command, it is communicated to the SMTP server using the write method (lines 37–41). If a response is expected (lines 45–60), then it is retrieved using the gets method. The dialog method also checks the response from the expected numeric code (lines 52 and 53) as defined by the caller (expected_response), and if a different code is found, an exception is raised (line 56). Otherwise, the dialog method returns and the send method continues with the SMTP transactions.

The final method in the Mailer class (see Listing 20.12) is the finish method. This method is a cleanup method that closes the client socket using the close method. The finish method is the final method to be called once the SMTP session is complete.

Let’s now look at a sample usage of the Mailer class. In Listing 20.13, a simple application that utilizes the Mailer class to send an e-mail is shown. At line 4, we create a new instance of the Mailer class using Mailer::new. We store this new instance in a variable called mail. At lines 6–10, we call init and initialize the instance variables with our set of values to prepare for sending a new e-mail. We specify (in order) the subject, sender, recipient, content_type, and contents. Note that in this instance, we specify the content_type as “text/html”. Note at line 10, we specify an HTML-encoded e-mail. Therefore, the content_type must be “text/html” to permit the receiving mail client to properly render the e-mail. If we were sending pure text rather than HTML, we’d use “text/plain” as the content_type. At line 11, we call the send method to send the e-mail, and, finally, at line 13, we close out the SMTP session by calling the finish method.

Listing 20.13 Instantiating a new SMTP client.

start example
 1   #  2   # Create a new mailer and send an e-mail  3   #  4   mail = Mailer::new  5  6   mail.init( "The Subject",  7              "tim@mtjones.com",  8              "mtj@mtjones.com",  9              "text/html", 10              "<HTML><BODY><H1>:Your mail</H1></BODY></HTML>" ) 11   mail.send 12 13   mail.finish
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