blob: c60192d31b6c4bbf54ea14054a5729a11ef1ced0 [file]
/***
* $Id: SMTPClient.java,v 1.1 2002/04/03 01:04:37 brekke Exp $
*
* NetComponents Internet Protocol Library
* Copyright (C) 1997-2002 Daniel F. Savarese
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library in the LICENSE file; if not, write
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
***/
package com.oroinc.net.smtp;
import java.net.*;
import java.io.*;
import java.util.*;
import com.oroinc.io.*;
import com.oroinc.net.MalformedServerReplyException;
/***
* SMTPClient encapsulates all the functionality necessary to send files
* through an SMTP server. This class takes care of all
* low level details of interacting with an SMTP server and provides
* a convenient higher level interface. As with all classes derived
* from <a href="com.oroinc.net.SocketClient.html"> SocketClient </a>,
* you must first connect to the server with
* <a href="com.oroinc.net.SocketClient.html#connect"> connect </a>
* before doing anything, and finally
* <a href="com.oroinc.net.SocketClient.html#disconnect"> disconnect </a>
* after you're completely finished interacting with the server.
* Then you need to check the SMTP reply code to see if the connection
* was successful. For example:
* <pre>
* try {
* int reply;
* client.connect("mail.foobar.com");
* System.out.print(client.getReplyString());
*
* // After connection attempt, you should check the reply code to verify
* // success.
* reply = client.getReplyCode();
*
* if(!SMTPReply.isPositiveCompletion(reply)) {
* client.disconnect();
* System.err.println("SMTP server refused connection.");
* System.exit(1);
* }
*
* // Do useful stuff here.
* ...
* } catch(IOException e) {
* if(client.isConnected()) {
* try {
* client.disconnect();
* } catch(IOException f) {
* // do nothing
* }
* }
* System.err.println("Could not connect to server.");
* e.printStackTrace();
* System.exit(1);
* }
* </pre>
* <p>
* Immediately after connecting is the only real time you need to check the
* reply code (because connect is of type void). The convention for all the
* SMTP command methods in SMTPClient is such that they either return a
* boolean value or some other value.
* The boolean methods return true on a successful completion reply from
* the SMTP server and false on a reply resulting in an error condition or
* failure. The methods returning a value other than boolean return a value
* containing the higher level data produced by the SMTP command, or null if a
* reply resulted in an error condition or failure. If you want to access
* the exact SMTP reply code causing a success or failure, you must call
* <a href="com.oroinc.net.smtp.SMTP.html#getReplyCode"> getReplyCode </a> after
* a success or failure.
* <p>
* You should keep in mind that the SMTP server may choose to prematurely
* close a connection for various reasons. The SMTPClient class will detect a
* premature SMTP server connection closing when it receives a
* <a href="com.oroinc.net.smtp.SMTPReply.html#SERVICE_NOT_AVAILABLE">
* SMTPReply.SERVICE_NOT_AVAILABLE </a> response to a command.
* When that occurs, the method encountering that reply will throw
* an <a href="com.oroinc.net.smtp.SMTPConnectionClosedException.html">
* SMTPConnectionClosedException </a>.
* <code>SMTPConectionClosedException</code>
* is a subclass of <code> IOException </code> and therefore need not be
* caught separately, but if you are going to catch it separately, its
* catch block must appear before the more general <code> IOException </code>
* catch block. When you encounter an
* <a href="com.oroinc.net.smtp.SMTPConnectionClosedException.html">
* SMTPConnectionClosedException </a>, you must disconnect the connection with
* <a href="#disconnect"> disconnect() </a> to properly clean up the
* system resources used by SMTPClient. Before disconnecting, you may check
* the last reply code and text with
* <a href="com.oroinc.net.smtp.SMTP.html#getReplyCode"> getReplyCode </a>,
* <a href="com.oroinc.net.smtp.SMTP.html#getReplyString"> getReplyString </a>,
* and
* <a href="com.oroinc.net.smtp.SMTP.html#getReplyStrings">getReplyStrings</a>.
* <p>
* Rather than list it separately for each method, we mention here that
* every method communicating with the server and throwing an IOException
* can also throw a
* <a href="com.oroinc.net.MalformedServerReplyException.html">
* MalformedServerReplyException </a>, which is a subclass
* of IOException. A MalformedServerReplyException will be thrown when
* the reply received from the server deviates enough from the protocol
* specification that it cannot be interpreted in a useful manner despite
* attempts to be as lenient as possible.
* <p>
* <p>
* @author Daniel F. Savarese
* @see SMTP
* @see SimpleSMTPHeader
* @see RelayPath
* @see SMTPConnectionClosedException
* @see com.oroinc.net.MalformedServerReplyException
***/
public class SMTPClient extends SMTP {
/*
* Default SMTPClient constructor. Creates a new SMTPClient instance.
*/
//public SMTPClient() { }
/***
* At least one SMTPClient method (<a href="#sendMessage"> sendMessage </a>)
* does not complete the entire sequence of SMTP commands to complete a
* transaction. These types of commands require some action by the
* programmer after the reception of a positive intermediate command.
* After the programmer's code completes its actions, it must call this
* method to receive the completion reply from the server and verify the
* success of the entire transaction.
* <p>
* For example,
* <pre>
* writer = client.sendMessage();
* if(writer == null) // failure
* return false;
* header =
* new SimpleSMTPHeader("foobar@foo.com", "foo@foobar.com", "Re: Foo");
* writer.write(header.toString());
* writer.write("This is just a test");
* writer.close();
* if(!client.completePendingCommand()) // failure
* return false;
* </pre>
* <p>
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean completePendingCommand() throws IOException {
return SMTPReply.isPositiveCompletion(getReply());
}
/***
* Login to the SMTP server by sending the HELO command with the
* given hostname as an argument. Before performing any mail commands,
* you must first login.
* <p>
* @param hostname The hostname with which to greet the SMTP server.
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean login(String hostname) throws IOException {
return SMTPReply.isPositiveCompletion(helo(hostname));
}
/***
* Login to the SMTP server by sending the HELO command with the
* client hostname as an argument. Before performing any mail commands,
* you must first login.
* <p>
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean login() throws IOException {
String name;
InetAddress host;
host = getLocalAddress();
name = host.getHostName();
if(name == null)
return false;
return SMTPReply.isPositiveCompletion(helo(name));
}
/***
* Set the sender of a message using the SMTP MAIL command, specifying
* a reverse relay path. The sender must be set first before any
* recipients may be specified, otherwise the mail server will reject
* your commands.
* <p>
* @param path The reverse relay path pointing back to the sender.
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean setSender(RelayPath path) throws IOException {
return SMTPReply.isPositiveCompletion(mail(path.toString()));
}
/***
* Set the sender of a message using the SMTP MAIL command, specifying
* the sender's email address. The sender must be set first before any
* recipients may be specified, otherwise the mail server will reject
* your commands.
* <p>
* @param address The sender's email address.
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean setSender(String address) throws IOException {
return SMTPReply.isPositiveCompletion(mail("<" + address + ">"));
}
/***
* Add a recipient for a message using the SMTP RCPT command, specifying
* a forward relay path. The sender must be set first before any
* recipients may be specified, otherwise the mail server will reject
* your commands.
* <p>
* @param path The forward relay path pointing to the recipient.
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean addRecipient(RelayPath path) throws IOException {
return SMTPReply.isPositiveCompletion(rcpt(path.toString()));
}
/***
* Add a recipient for a message using the SMTP RCPT command, the
* recipient's email address. The sender must be set first before any
* recipients may be specified, otherwise the mail server will reject
* your commands.
* <p>
* @param address The recipient's email address.
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean addRecipient(String address) throws IOException {
return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">"));
}
/***
* Send the SMTP DATA command in preparation to send an email message.
* This method returns a DotTerminatedMessageWriter instance to which
* the message can be written. Null is returned if the DATA command
* fails.
* <p>
* You must not issue any commands to the SMTP server (i.e., call any
* (other methods) until you finish writing to the returned Writer
* instance and close it. The SMTP protocol uses the same stream for
* issuing commands as it does for returning results. Therefore the
* returned Writer actually writes directly to the SMTP connection.
* After you close the writer, you can execute new commands. If you
* do not follow these requirements your program will not work properly.
* <p>
* You can use the provided
* <a href="com.oroinc.net.smtp.SimpleSMTPHeader.html"> SimpleSMTPHeader </a>
* class to construct a bare minimum header.
* To construct more complicated headers you should
* refer to RFC 822. When the Java Mail API is finalized, you will be
* able to use it to compose fully compliant Internet text messages.
* The DotTerminatedMessageWriter takes care of doubling line-leading
* dots and ending the message with a single dot upon closing, so all
* you have to worry about is writing the header and the message.
* <p>
* Upon closing the returned Writer, you need to call
* <a href="#completePendingCommand"> completePendingCommand() </a>
* to finalize the transaction and verify its success or failure from
* the server reply.
* <p>
* @return A DotTerminatedMessageWriter to which the message (including
* header) can be written. Returns null if the command fails.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public Writer sendMessageData() throws IOException {
if(!SMTPReply.isPositiveIntermediate(data()))
return null;
return new DotTerminatedMessageWriter(_writer);
}
/***
* A convenience method for sending short messages. This method fetches
* the Writer returned by <a href="#sendMessageData"> sendMessageData() </a>
* and writes the specified String to it. After writing the message,
* this method calls <a href="#completePendingCommand">
* completePendingCommand() </a> to finalize the transaction and returns
* its success or failure.
* <p>
* @param message The short email message to send.
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean sendShortMessageData(String message) throws IOException {
Writer writer;
writer = sendMessageData();
if(writer == null)
return false;
writer.write(message);
writer.close();
return completePendingCommand();
}
/***
* A convenience method for a sending short email without having to
* explicitly set the sender and recipient(s). This method
* sets the sender and recipient using
* <a href="#setSender"> setSender </a> and
* <a href="#addRecipient"> addRecipient </a>, and then sends the
* message using <a href="#sendShortMessageData"> sendShortMessageData </a>.
* <p>
* @param sender The email address of the sender.
* @param recipient The email address of the recipient.
* @param message The short email message to send.
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean sendSimpleMessage(String sender, String recipient,
String message)
throws IOException
{
if(!setSender(sender))
return false;
if(!addRecipient(recipient))
return false;
return sendShortMessageData(message);
}
/***
* A convenience method for a sending short email without having to
* explicitly set the sender and recipient(s). This method
* sets the sender and recipients using
* <a href="#setSender"> setSender </a> and
* <a href="#addRecipient"> addRecipient </a>, and then sends the
* message using <a href="#sendShortMessageData"> sendShortMessageData </a>.
* <p>
* @param sender The email address of the sender.
* @param recipients An array of recipient email addresses.
* @param message The short email message to send.
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean sendSimpleMessage(String sender, String[] recipients,
String message)
throws IOException
{
boolean oneSuccess = false;
int count;
if(!setSender(sender))
return false;
for(count = 0; count < recipients.length; count++) {
if(addRecipient(recipients[count]))
oneSuccess = true;
}
if(!oneSuccess)
return false;
return sendShortMessageData(message);
}
/***
* Logout of the SMTP server by sending the QUIT command.
* <p>
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean logout() throws IOException {
return SMTPReply.isPositiveCompletion(quit());
}
/***
* Aborts the current mail transaction, resetting all server stored
* sender, recipient, and mail data, cleaing all buffers and tables.
* <p>
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean reset() throws IOException {
return SMTPReply.isPositiveCompletion(rset());
}
/***
* Verify that a username or email address is valid, i.e., that mail
* can be delivered to that mailbox on the server.
* <p>
* @param username The username or email address to validate.
* @return True if the username is valid, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean verify(String username) throws IOException {
int result;
result = vrfy(username);
return (result == SMTPReply.ACTION_OK ||
result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD);
}
/***
* Fetches the system help information from the server and returns the
* full string.
* <p>
* @return The system help string obtained from the server. null if the
* information could not be obtained.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public String listHelp() throws IOException {
if(SMTPReply.isPositiveCompletion(help()))
return getReplyString();
return null;
}
/***
* Fetches the help information for a given command from the server and
* returns the full string.
* <p>
* @param The command on which to ask for help.
* @return The command help string obtained from the server. null if the
* information could not be obtained.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public String listHelp(String command) throws IOException {
if(SMTPReply.isPositiveCompletion(help(command)))
return getReplyString();
return null;
}
/***
* Sends a NOOP command to the SMTP server. This is useful for preventing
* server timeouts.
* <p>
* @return True if successfully completed, false if not.
* @exception SMTPConnectionClosedException
* If the SMTP server prematurely closes the connection as a result
* of the client being idle or some other reason causing the server
* to send SMTP reply code 421. This exception may be caught either
* as an IOException or independently as itself.
* @exception IOException If an I/O error occurs while either sending a
* command to the server or receiving a reply from the server.
***/
public boolean sendNoOp() throws IOException {
return SMTPReply.isPositiveCompletion(noop());
}
}