Network Programming with Perl


 
Network Programming with Perl

By Lincoln  D.  Stein

Slots : 1

Table of Contents
Chapter  7.   SMTP: Sending Mail

    Content

Net::SMTP operates at the lowest level of the e-mail access modules. It interacts directly with the SMTP daemons to transmit e-mail across the Internet. To use it effectively, you must know a bit about the innards of SMTP. The payoff for this added complexity is that Net::SMTP is completely portable, and works as well from Macintoshes and Windows machines as from UNIX systems.

The SMTP Protocol

When a client e-mail program wants to send mail, it opens a network connection to a mail server somewhere using the standard SMTP port, number 25. The client conducts a brief conversation with the server, during which time it establishes its identity, announces that it wishes to send mail to a certain party, and transmits the e-mail message. The server then takes care of seeing that the message gets where it needs to go, whether by delivering it to a local user or by transmitting the message to another server somewhere else in the world.

The language spoken by SMTP servers is a simple human-readable line-oriented protocol. Figure 7.1 shows the interaction needed to send a complete e-mail manually using Telnet as the client (the client's input is in bold).

Figure 7.1. A chat with an SMTP daemon

After connecting to the SMTP port, the server sends us a code "220" message containing a banner and greeting. We issue a HELO command, identifying the hostname of the client machine, and the server responds with a "250" message, which essentially means "OK."

After this handshake, we are ready to send some mail. We issue a MAIL command with the argument <From: sender's address> , to designate the sender. If the sender is OK, the server responds with another "250" reply. We now issue a RCPT ("recipient") command with the argument <To: recipient's address> to indicate the recipient. The server again acknowledges the command. Some SMTP servers have restrictions on the senders and recipients they will service; for example, they may refuse to relay e-mail to remote domains. Inthis case, they respond with a variety of error codes in the 500 to 599 range. It is possible to issue multiple RCPT commands for e-mail that has several recipients at the site(s) served by the SMTP server.

Having established that the sender and recipient(s) are OK, we send the DATA command. The server responds with a message prompting us for the e-mail message. The server will accept lines of input until it sees a line containing just a ".".

Internet mail has a standard format consisting of a set of header lines, ablank line, and the body of the message. Even though we have already specified the sender and recipient, we must do so again in order to create a valid e-mailmessage. A minimal mail header has a From: field, indicating the sender, a To: field, indicating the recipient, and a Subject: field. Other standard fields, such as the date, are filled in automatically by the mail daemon.

We add a blank line to separate the header from the body, enter the e-mail message text, and terminate the message with a dot. The server's code 250 acknowledgment indicates that the message was queued successfully for delivery.

We could now send additional messages by issuing further MAIL commands, but instead we disconnect politely by issuing the QUIT command. The full specification of the SMTP protocol can be found in RFC 821. The standard format for Internet mail headers is described in RFC 822.

The Net::SMTP API

Net::SMTP mirrors the SMTP protocol very closely. Net::SMTP is part of the libnet utilities and is available on CPAN. Like the other Net::* modules, it uses an object-oriented interface in which you establish a connection with a particular mailer daemon, yielding a Net::SMTP object. You then call the SMTP object's methods to send commands to the server. Like Net::FTP (but unlike Net::Telnet), Net::SMTP inherits from Net::Cmd and IO::Socket::INET, allowing you to use the Net::Cmd message() and code() methods to retrieve the most recent message and numeric status code from the server. All the low-level IO::Socket and IO::Socket::INET methods are also inherited.

To create a new Net::SMTPobject, use the new() constructor:

Table 7.1. Net::SMTP->new() Arguments

Option Description Default
Hello The domain name to use in the HELO command. Name of local host
Timeout Seconds to wait for response from server. 120
Debug Turn on verbose debugging information. undef
Port Numeric or symbolic name of port to connect to. 25

$smtp = Net::SMTP->new([$host] [,$opt1=>$val1, $opt2=>$val2...])

The new() method establishes a connection to an SMTP server and returns a new Net::SMTP object. The first optional argument is the name of the host to contact, and will default to the mail exchanger configured into Net::Config when libnet was first installed. The options are a series of named arguments. In addition to the options recognized by the IO::Socket::INET superclass, the arguments shown in Table 7.1 are possible.

If the connection is refused (or times out), new() returns false. Here's an example of contacting the mail server for the cshl.org domain with a timeout of 60 seconds.

$smtp = Net::SMTP->new('mail.cshl.org',Timeout=>60);

Once the object is created, you can send or retrieve information to the server by calling object methods. Some are quite simple:

$banner = $smtp->banner()

$domain = $smtp->domain()

Immediately after connecting to an SMTP server, you can retrieve the banner and/or domain name with which it identified by calling these two methods.

To send mail, you will first call the mail() and recipient() methods to set up the exchange:

$success = $smtp->mail($address [,\%options])

The mail() method issues a MAIL command to the server. The required first argument is the address of the sender. The optional second argument is a hash reference containing various options to be passed to servers that support the Extended Simple Mail Transport Protocol, or ESMTP. These are rarely needed; see the Net::SMTP documentation for details.

The address may be in any of the forms accepted by e-mail clients , including doe@acme.org, <doe@acme.org>, John Doe <doe@acme.org>, and doe@acme.org (John Doe).

If successful, this method returns a true value. Otherwise, it returns undef , and the inherited message() method can be used to return the text of the error message.

$success = $smtp->recipient($address1,$address2,$address3,...)

@ok_addr = $smtp-> recipient($addr1,$addr2,$addr3,...,{SkipBad=>1})

The recipient() method issues an RCPT command to the server. The arguments are a list of valid e-mail addresses to which the mail is to be delivered. The list of addresses may be followed by a hash reference containing various options.

The addresses passed to recipient() must all be acceptable to the server, or the entire call will return false. To modify this behavior, pass the option SkipBad in the options hash. The module now ignores addresses rejected by the server, and returns the list of accepted addresses as its result. For example:

@ok=$smtp->recipient('lstein@cshl.org','nobody@cshl.org',{SkipBad=>1})

Provided that the server has accepted the sender and recipient, you may now commence sending the message text using the data() , datasend() , and dataend() methods.

$success = $smtp->data([$text])

The data() method issues a DATA command to the server. If called with a scalar argument, it transmits the value of the argument as the content (header and body) of the e-mail message. If you wish to send the message one chunk at a time, call data without an argument and make a series of calls to the datasend() method. This method returns a value indicating success or failure of the command.

$success = $smtp->datasend(@data)

After calling data() without an argument, you may call datasend() one or more times to send lines of e-mail text to the server. Lines starting with a dot are automatically escaped so as not to terminate the transmission prematurely.

You may call datasend() with an array reference, if you prefer. This method and dataend() are both inherited from the Net::Cmd base class.

$success = $smtp->dataend

When your e-mail message is sent, you should call dataend() to transmit the terminal dot. If the message was accepted for delivery, the return value is true.

Two methods are useful for more complex interactions with SMTPservers:

$smtp->reset

This sends an RSET command to the server, aborting mail transmission operations in progress. You might call this if one of the desired recipients is rejected by the server; it resets the server so you can try again.

$valid = $smtp->verify($address)

@recipients = $smtp->expand($address)

The expand() and verify() methods can be used to check that a recipient address is valid prior to trying to send mail. verify() returns true if the specified address is accepted.

expand() does something more interesting. If the address is valid, it expands it into one or more aliases, if any exist. This can be used to identify forwarding addresses and mailing list recipients. The method returns a list of aliases or, if the specified address is invalid, an empty list. For security reasons, many mail administrators disable this feature, in which case, the method returns an empty list.

Finally, when you are done with the server, you will call the quit() method:

$smtp->quit

This method politely breaks the connection with the server.

Using Net::SMTP

With Net::SMTP we can write a one-shot subroutine for sending e-mail. The mail() subroutine takes two arguments: the text of an e-mail message to send (required), and the name of the SMTP host to use (optional). Call it like this:

$msg = 'END'; From: John Doe <doe@acme.org> To: L Stein <lstein@lsjs.org> Cc: jac@acme.org, vvd@acme.org Subject: hello there This is just a simple e-mail message. Nothing to get excited about. Regards, JD END mail($msg,'presto.lsjs.org') or die "arggggh!";

We create the text of the e-mail message using the here-is ( ) syntax and store it in the variable $msg . The message must contain an e-mail header with (at a minimum) the From: and To: fields. We pass the message to the mail() subroutine, which extracts the sender and recipient fields and invokes Net::SMTP to do the dirty work. Figure 7.2 shows how mail() works.

Figure 7.2. A simple subroutine for sending e-mail

Lines 1 “9: Parse the mail message We split the message into the header and the body by splitting on the first blank line. Header fields frequently contain continuation lines that begin with a blank, so we fold those into a single line.

We parse the header into a hash using a simple pattern match, and store the From: and To: fields in local variables . The To: field can contain multiple recipients, so we isolate the individual addressees by splitting on the comma character (this will fail in the unlikely case that any of the addresses contain commas). We do likewise if the header contained a Cc: field.

Lines 10 “16: Send messages We create a new Net::SMTP object and call its mail() , and recipient() methods to initiate the message. The call to recipient() uses the SkipBad option so that the method will try to deliver the mail even if the server rejects some of the recipients. We compare the number of recipients the server accepted to the number we attempted, returning from the subroutine if none were accepted, or just printing a warning if only some were rejected.

We call data() to send the complete e-mail message to the server, and quit() to terminate the connection.

Although this subroutine does its job, it lacks some features. For example, it doesn't handle the Bcc: field, which causes mail to be delivered to a recipient without that recipient appearing in the header. The MailTools module, described next , corrects the deficiencies.


   
Top

Категории