panda.net.smtp.SMTPClient Maven / Gradle / Ivy
Show all versions of panda-core Show documentation
package panda.net.smtp;
import java.io.IOException;
import java.io.Writer;
import java.net.InetAddress;
import panda.io.stream.MultiWriter;
import panda.net.io.DotTerminatedMessageWriter;
/***
* 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
* {@link panda.net.SocketClient}, you must first connect to the server with
* {@link panda.net.SocketClient#connect connect } before doing anything, and finally
* {@link panda.net.SocketClient#disconnect disconnect } 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:
*
*
* 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);
* }
*
*
* 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 {@link panda.net.smtp.SMTP#getReplyCode getReplyCode } after a success or
* failure.
*
* 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 {@link panda.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE
* SMTPReply.SERVICE_NOT_AVAILABLE } response to a command. When that occurs, the method
* encountering that reply will throw an {@link panda.net.smtp.SMTPConnectionClosedException} .
* SMTPConectionClosedException
is a subclass of IOException
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 IOException
catch block. When you
* encounter an {@link panda.net.smtp.SMTPConnectionClosedException} , you must disconnect the
* connection with {@link #disconnect disconnect() } to properly clean up the system resources used
* by SMTPClient. Before disconnecting, you may check the last reply code and text with
* {@link panda.net.smtp.SMTP#getReplyCode getReplyCode },
* {@link panda.net.smtp.SMTP#getReplyString getReplyString }, and
* {@link panda.net.smtp.SMTP#getReplyStrings getReplyStrings}.
*
* 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
* {@link panda.net.MalformedServerReplyException} , 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.
*
* @see SMTP
* @see SMTPHeader
* @see RelayPath
* @see SMTPConnectionClosedException
* @see panda.net.MalformedServerReplyException
***/
public class SMTPClient extends SMTP {
/**
* Default SMTPClient constructor. Creates a new SMTPClient instance.
*/
public SMTPClient() {
}
/**
* Overloaded constructor that takes an encoding specification
*
* @param encoding The encoding to use
*/
public SMTPClient(String encoding) {
super(encoding);
}
/***
* At least one SMTPClient method ({@link #sendMessageData sendMessageData }) 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.
*
* For example,
*
*
* writer = client.sendMessageData();
* if (writer == null) // failure
* return false;
* header = new SimpleSMTPHeader("[email protected]", "[email protected]", "Re: Foo");
* writer.write(header.toString());
* writer.write("This is just a test");
* writer.close();
* if (!client.completePendingCommand()) // failure
* return false;
*
*
*
* @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.
*
*
* @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.
*
*
* @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.
*
*
* @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.
*
*
* @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.
*
*
* @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.
*
*
* @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.
*
* 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.
*
* You can use the provided {@link panda.net.smtp.SMTPHeader} class to construct a bare
* minimum header. To construct more complicated headers you should refer to RFC 5322. 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.
*
* Upon closing the returned Writer, you need to call {@link #completePendingCommand
* completePendingCommand() } to finalize the transaction and verify its success or failure from
* the server reply.
*
*
* @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.
* @see #sendShortMessageData(String)
***/
public Writer sendMessageData() throws IOException {
return sendMessageData(null);
}
public Writer sendMessageData(Writer dbg) throws IOException {
if (!SMTPReply.isPositiveIntermediate(data())) {
return null;
}
if (dbg != null) {
return new DotTerminatedMessageWriter(new MultiWriter(dbg, _writer));
}
return new DotTerminatedMessageWriter(_writer);
}
/***
* A convenience method for sending short messages. This method fetches the Writer returned by
* {@link #sendMessageData sendMessageData() } and writes the specified String to it. After
* writing the message, this method calls {@link #completePendingCommand
* completePendingCommand() } to finalize the transaction and returns its success or failure.
*
*
* @param message The short email message to send. This must include the headers and the body,
* but not the trailing "."
* @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 {@link #setSender setSender
* } and {@link #addRecipient addRecipient }, and then sends the message using
* {@link #sendShortMessageData sendShortMessageData }.
*
*
* @param sender The email address of the sender.
* @param recipient The email address of the recipient.
* @param message The short email message to send. This must include the headers and the body,
* but not the trailing "."
* @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 {@link #setSender(String)
* setSender} and {@link #addRecipient(String) addRecipient}, and then sends the message using
* {@link #sendShortMessageData(String) sendShortMessageData}.
*
* Note that the method ignores failures when calling {@link #addRecipient(String) addRecipient}
* so long as at least one call succeeds. If no recipients can be successfully added then the
* method will fail (and does not attempt to send the message)
*
*
* @param sender The email address of the sender.
* @param recipients An array of recipient email addresses.
* @param message The short email message to send. This must include the headers and the body,
* but not the trailing "."
* @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.
*
*
* @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.
*
*
* @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.
*
*
* @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.
*
*
* @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.
*
*
* @param command 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.
*
*
* @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());
}
}