| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.net.smtp; |
| |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.net.InetAddress; |
| |
| import org.apache.commons.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 org.apache.commons.net.SocketClient}, you must |
| * first connect to the server with {@link org.apache.commons.net.SocketClient#connect connect } before doing anything, and finally |
| * {@link org.apache.commons.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: |
| * |
| * <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 {@link org.apache.commons.net.smtp.SMTP#getReplyCode getReplyCode } after a success or failure. |
| * </p> |
| * <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 {@link org.apache.commons.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 org.apache.commons.net.smtp.SMTPConnectionClosedException} . |
| * <code>SMTPConnectionClosedException</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 |
| * {@link org.apache.commons.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 org.apache.commons.net.smtp.SMTP#getReplyCode getReplyCode }, {@link org.apache.commons.net.smtp.SMTP#getReplyString getReplyString }, and |
| * {@link org.apache.commons.net.smtp.SMTP#getReplyStrings getReplyStrings}. |
| * </p> |
| * <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 |
| * {@link org.apache.commons.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. |
| * </p> |
| * |
| * @see SMTP |
| * @see SimpleSMTPHeader |
| * @see RelayPath |
| * @see SMTPConnectionClosedException |
| * @see org.apache.commons.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 |
| * @since 2.0 |
| */ |
| public SMTPClient(final String encoding) { |
| super(encoding); |
| } |
| |
| /** |
| * Adds 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. |
| * @throws 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. |
| * @throws 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(final RelayPath path) throws IOException { |
| return SMTPReply.isPositiveCompletion(rcpt(path.toString())); |
| } |
| |
| /** |
| * Adds 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. |
| * @throws 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. |
| * @throws 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(final String address) throws IOException { |
| return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">")); |
| } |
| |
| /** |
| * 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. |
| * <p> |
| * For example, |
| * </p> |
| * <pre> |
| * writer = client.sendMessageData(); |
| * 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> |
| * |
| * @return True if successfully completed, false if not. |
| * @throws 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. |
| * @throws 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()); |
| } |
| |
| /** |
| * 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. |
| * @throws 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. |
| * @throws 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. |
| * @throws 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. |
| * @throws 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(final String command) throws IOException { |
| if (SMTPReply.isPositiveCompletion(help(command))) { |
| return getReplyString(); |
| } |
| return null; |
| } |
| |
| /** |
| * Login to the SMTP server by sending the {@code HELO} command with the client hostname as an argument. |
| * Before performing any mail commands, you must first log in. |
| * |
| * @return True if successfully completed, false if not. |
| * @throws 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. |
| * @throws 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 { |
| final InetAddress host = getLocalAddress(); |
| final String name = host.getHostName(); |
| if (name == null) { |
| return false; |
| } |
| return SMTPReply.isPositiveCompletion(helo(name)); |
| } |
| |
| /** |
| * Login to the SMTP server by sending the {@code HELO} command with the given hostname as an argument. |
| * Before performing any mail commands, you must first log in. |
| * |
| * @param hostname The hostname with which to greet the SMTP server. |
| * @return True if successfully completed, false if not. |
| * @throws 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. |
| * @throws 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(final String hostname) throws IOException { |
| return SMTPReply.isPositiveCompletion(helo(hostname)); |
| } |
| |
| /** |
| * Logout of the SMTP server by sending the QUIT command. |
| * |
| * @return True if successfully completed, false if not. |
| * @throws 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. |
| * @throws 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, cleaning all buffers and tables. |
| * |
| * @return True if successfully completed, false if not. |
| * @throws 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. |
| * @throws 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()); |
| } |
| |
| /** |
| * Sends 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> |
| * <p> |
| * You can use the provided {@link org.apache.commons.net.smtp.SimpleSMTPHeader} 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. |
| * </p> |
| * <p> |
| * 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. |
| * </p> |
| * |
| * @return A DotTerminatedMessageWriter to which the message (including header) can be written. Returns null if the command fails. |
| * @throws 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. |
| * @throws 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 { |
| if (!SMTPReply.isPositiveIntermediate(data())) { |
| return null; |
| } |
| return new DotTerminatedMessageWriter(writer); |
| } |
| |
| /** |
| * Sends a NOOP command to the SMTP server. This is useful for preventing server timeouts. |
| * |
| * @return True if successfully completed, false if not. |
| * @throws 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. |
| * @throws 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()); |
| } |
| |
| /** |
| * Sends a 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. |
| * @throws 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. |
| * @throws 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(final String message) throws IOException { |
| try (final Writer writer = sendMessageData()) { |
| if (writer == null) { |
| return false; |
| } |
| writer.write(message); |
| } |
| return completePendingCommand(); |
| } |
| |
| /** |
| * Sends a 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. |
| * @throws 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. |
| * @throws 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(final String sender, final String recipient, final String message) throws IOException { |
| if (!setSender(sender)) { |
| return false; |
| } |
| if (!addRecipient(recipient)) { |
| return false; |
| } |
| return sendShortMessageData(message); |
| } |
| |
| /** |
| * Sends a 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}. |
| * <p> |
| * 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) |
| * </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. This must include the headers and the body, but not the trailing "." |
| * @return True if successfully completed, false if not. |
| * @throws 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. |
| * @throws 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(final String sender, final String[] recipients, final String message) throws IOException { |
| if (!setSender(sender)) { |
| return false; |
| } |
| boolean oneSuccess = false; |
| for (final String recipient : recipients) { |
| if (addRecipient(recipient)) { |
| oneSuccess = true; |
| } |
| } |
| if (!oneSuccess) { |
| return false; |
| } |
| return sendShortMessageData(message); |
| } |
| |
| /** |
| * Sets 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. |
| * @throws 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. |
| * @throws 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(final RelayPath path) throws IOException { |
| return SMTPReply.isPositiveCompletion(mail(path.toString())); |
| } |
| |
| /** |
| * Sets 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. |
| * @throws 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. |
| * @throws 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(final String address) throws IOException { |
| return SMTPReply.isPositiveCompletion(mail("<" + address + ">")); |
| } |
| |
| /** |
| * Verifies that a user or email address is valid, i.e., that mail can be delivered to that mailbox on the server. |
| * |
| * @param user The user name or email address to validate. |
| * @return True if the user name is valid, false if not. |
| * @throws 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. |
| * @throws 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(final String user) throws IOException { |
| final int result = vrfy(user); |
| return result == SMTPReply.ACTION_OK || result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD; |
| } |
| |
| } |