| < Day Day Up > |
|
The simple Tcl SMTP client (lines 1-99) in Listing 21.10 is made up of only two procedures, the main procedure for sending the e-mail (Mail_Send, lines 42-99) and the dialog procedure (dialog, lines 4-36) for communicating dialog transactions with the SMTP server.
Let's look at the SMTP client in the reverse order. We first discuss the dialog procedure and then its user, Mail_Send. The dialog procedure (lines 4-36) provides the ability to send a command and receive a response from the SMTP server. The caller may optionally specify no command, or no response. If a response is specified, the response is expected from the server and is verified with what the server actually returned.
Line 4 begins with the definition of the dialog procedure. The dialog procedure expects three arguments from the caller: the client socket, string command, and string response. At line 7, we check to see if the caller specified a command to send by testing the length of the command using the string command with the length option. If the length is greater than zero, a command is present and is then sent through the socket at line 9 using the puts command. Note that the -nonewline option is specified. We'll allow the caller to add its newline if needed for the command.
At line 15, we begin checking for a response. If a response is expected (again identified by the string length of the exp_response argument), we get a line of text from the socket using the gets command at line 18. Recall that the first three characters of an SMTP server response represent the response status code. At line 21, the first three characters are extracted from the line read through the socket and stored in stscode. We then compare the response code extracted from the server response to the response code that the caller expected at line 25 using the string compare command. The result is stored in check, and then tested at line 27 (non-zero means that the strings did not compare successfully). If the response codes were not equal, we issue an error, otherwise we simply return. The error command is a mechanism to raise errors to calling procedures, which we discuss in the Mail_Send procedure.
The Mail_Send procedure (Listing 21.10, lines 40-99) performs the actual SMTP client protocol. It uses the dialog procedure to perform the actual command transactions with the server, and is, therefore, very simple because the input and output operations are abstracted away.
The Mail_Send procedure accepts a number of arguments for defining the location of the outgoing mail server (mail_server) as well as the e-mail to be sent. The e-mail is defined by the subject (the subject to be shown in the e-mail), the sender (who this e-mail will be shown as arriving from), the recipient (the e-mail address for whom this e-mail is directed), the content-type (how the body of the e-mail should be rendered by the receiving client), and, finally, the e-mail body itself (contents).
We begin at line 46 by creating the client socket (using the socket command) and connecting it to the mail_server defined by the caller and the standard SMTP server port (25). Once the socket is connected, we configure the socket for line buffering (line 49) using the fconfigure command. At line 52, we use the catch command. The catch command catches errors that may occur in the script block within the catch command. If an error is raised in the script, the script is exited and a 1 is returned to indicate that an error occurred. Otherwise, a 0 is returned indicating that no error occurred. The catch block, therefore, allows us to catch any errors that are raised for any of the dialogs, and immediately exit the script within the catch block. The error is stored within variable err (as shown at line 52).
Within the catch block (lines 55-86), we perform our dialogs with the SMTP server. These won't be detailed here because they are discussed in detail in Chapter 15, Software Patterns Introduction. Each dialog potentially issues a command and in most cases awaits a valid response from the server. Upon completion of the dialogs, we check our error status (line 91) and if an error occurred, we simply issue a warning to standard-out using the puts command to notify the user. The socket is then closed at line 97 using the close command.
The final step in Listing 21.10 is calling the Mail_Send procedure at line 105 to send an e-mail. This step specifies the outgoing mail server, subject line, sender and recipient, content type, and, finally, the message body.
Listing 21.10 Simple Tcl SMTP client source.
1 # 2 # Perform a dialog with the SMTP server 3 # 4 proc dialog { sock command exp_response } { 5 6 # Send the command if the user provided one 7 if { [string length $command] > 0 } { 8 9 puts -nonewline $sock $command 10 11 } 12 13 # Only check for a response if the user defined the 14 # expected response code 15 if { [string length $exp_response] > 0 } { 16 17 # Get a single line from the connection 18 set line [ gets $sock ] 19 20 # Parse the status code from the line 21 set stscode [ string range $line 0 2 ] 22 23 # Check the status code from the server with the 24 # response code that's expected 25 set check [ string compare $stscode $exp_response ] 26 27 if { $check != 0 } { 28 29 # Not what was expected, raise an error 30 error "bad dialog" 31 32 } 33 34 } 35 36 } 37 38 39 # 40 # Send the mail based upon the defined parameters 41 # 42 proc Mail_Send { mail_server subject sender recipient 43 content_type contents } { 44 45 # Create and connect the client socket 46 set sock [ socket $mail_server 25 ] 47 48 # Configure for immediate receipt 49 fconfigure $sock -buffering line 50 51 # Catch any errors that occur 52 set err [ catch { 53 54 # Look for the initial e-mail salutation 55 dialog $sock "" "220" 56 57 # Send HELO and await response 58 dialog $sock "HELO thisdomain.com\n" "250" 59 60 # Send "MAIL FROM" command and await response 61 dialog $sock "MAIL FROM: $sender\n" "250" 62 63 # Send "RCPT TO" command and await response 64 dialog $sock "RCPT TO: $recipient\n" "250" 65 66 # Send "DATA" to start the message body 67 dialog $sock "DATA\n" "354" 68 69 # Send out the mail headers (from/to/subject) 70 dialog $sock "From: $sender\n" "" 71 72 dialog $sock "To: $recipient\n" "" 73 74 dialog $sock "Subject: $subject\n" "" 75 76 # Send the content type 77 dialog $sock "Content-Type: $content_type\n\n" "" 78 79 # Send the actual message body 80 dialog $sock "$contents" "" 81 82 # Send the end-of-email indicator 83 dialog $sock "\n.\n" "250" 84 85 # Finally, close out the session 86 dialog $sock "QUIT\n" "221" 87 88 } ] 89 90 # Emit to standard-out if an error occurred 91 if { $err } { 92 93 puts "Couldn't send mail" 94 95 } 96 97 close $sock 98 99 } 100 101 102 # 103 # Send a test e-mail 104 # 105 Mail_Send "mtjones.com" "The Subject" 106 "tim@mtjones.com" "mtj@mtjones.com" 107 "text/html" 108 "<HTML><BODY><H1>This is the mail</H1></BODY></HTML>" 109
| < Day Day Up > |
|