    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.net.smtp;
    019
    020import java.io.IOException;
    021import java.io.Writer;
    022import java.net.InetAddress;
    023
    024import org.apache.commons.net.io.DotTerminatedMessageWriter;
    025
    026/**
    027 * SMTPClient encapsulates all the functionality necessary to send files through an SMTP server. This class takes care of all low level details of interacting
    028 * 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
    029 * first connect to the server with {@link org.apache.commons.net.SocketClient#connect connect } before doing anything, and finally
    030 * {@link org.apache.commons.net.SocketClient#disconnect disconnect } after you're completely finished interacting with the server. Then you need to check the
    031 * SMTP reply code to see if the connection was successful. For example:
    032 *
    033 * <pre>
    034 *    try {
    035 *      int reply;
    036 *      client.connect("mail.foobar.com");
    037 *      System.out.print(client.getReplyString());
    038 *
    039 *      // After connection attempt, you should check the reply code to verify
    040 *      // success.
    041 *      reply = client.getReplyCode();
    042 *
    043 *      if (!SMTPReply.isPositiveCompletion(reply)) {
    044 *        client.disconnect();
    045 *        System.err.println("SMTP server refused connection.");
    046 *        System.exit(1);
    047 *      }
    048 *
    049 *      // Do useful stuff here.
    050 *      ...
    051 *    } catch (IOException e) {
    052 *      if (client.isConnected()) {
    053 *        try {
    054 *          client.disconnect();
    055 *        } catch (IOException f) {
    056 *          // do nothing
    057 *        }
    058 *      }
    059 *      System.err.println("Could not connect to server.");
    060 *      e.printStackTrace();
    061 *      System.exit(1);
    062 *    }
    063 * </pre>
    064 * <p>
    065 * 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
    066 * 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
    067 * 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
    068 * 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
    069 * 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.
    070 * </p>
    071 * <p>
    072 * 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
    073 * SMTP server connection closing when it receives a {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE }
    074 * response to a command. When that occurs, the method encountering that reply will throw an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} .
    075 * <code>SMTPConnectionClosedException</code> is a subclass of <code> IOException </code> and therefore need not be caught separately, but if you are going to
    076 * catch it separately, its catch block must appear before the more general <code> IOException </code> catch block. When you encounter an
    077 * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} , you must disconnect the connection with {@link #disconnect disconnect() } to properly
    078 * clean up the system resources used by SMTPClient. Before disconnecting, you may check the last reply code and text with
    079 * {@link org.apache.commons.net.smtp.SMTP#getReplyCode getReplyCode }, {@link org.apache.commons.net.smtp.SMTP#getReplyString getReplyString }, and
    080 * {@link org.apache.commons.net.smtp.SMTP#getReplyStrings getReplyStrings}.
    081 * </p>
    082 * <p>
    083 * 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
    084 * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the
    085 * 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
    086 * lenient as possible.
    087 * </p>
    088 *
    089 * @see SMTP
    090 * @see SimpleSMTPHeader
    091 * @see RelayPath
    092 * @see SMTPConnectionClosedException
    093 * @see org.apache.commons.net.MalformedServerReplyException
    094 */
    095public class SMTPClient extends SMTP {
    096
    097    /**
    098     * Default SMTPClient constructor. Creates a new SMTPClient instance.
    099     */
    100    public SMTPClient() {
    101    }
    102
    103    /**
    104     * Overloaded constructor that takes an encoding specification
    105     *
    106     * @param encoding The encoding to use
    107     * @since 2.0
    108     */
    109    public SMTPClient(final String encoding) {
    110        super(encoding);
    111    }
    112
    113    /**
    114     * 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
    115     * specified, otherwise the mail server will reject your commands.
    116     *
    117     * @param path The forward relay path pointing to the recipient.
    118     * @return True if successfully completed, false if not.
    119     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    120     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    121     *                                       independently as itself.
    122     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    123     */
    124    public boolean addRecipient(final RelayPath path) throws IOException {
    125        return SMTPReply.isPositiveCompletion(rcpt(path.toString()));
    126    }
    127
    128    /**
    129     * 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
    130     * specified, otherwise the mail server will reject your commands.
    131     *
    132     * @param address The recipient's email address.
    133     * @return True if successfully completed, false if not.
    134     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    135     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    136     *                                       independently as itself.
    137     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    138     */
    139    public boolean addRecipient(final String address) throws IOException {
    140        return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">"));
    141    }
    142
    143    /**
    144     * At least one SMTPClient method ({@link #sendMessageData sendMessageData }) does not complete the entire sequence of SMTP commands to complete a
    145     * transaction. These types of commands require some action by the programmer after the reception of a positive intermediate command. After the programmer's
    146     * 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.
    147     * <p>
    148     * For example,
    149     * </p>
    150     * <pre>
    151     * writer = client.sendMessageData();
    152     * if (writer == null) // failure
    153     *     return false;
    154     * header = new SimpleSMTPHeader("foobar@foo.com", "foo@foobar.com", "Re: Foo");
    155     * writer.write(header.toString());
    156     * writer.write("This is just a test");
    157     * writer.close();
    158     * if (!client.completePendingCommand()) // failure
    159     *     return false;
    160     * </pre>
    161     *
    162     * @return True if successfully completed, false if not.
    163     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    164     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    165     *                                       independently as itself.
    166     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    167     */
    168    public boolean completePendingCommand() throws IOException {
    169        return SMTPReply.isPositiveCompletion(getReply());
    170    }
    171
    172    /**
    173     * Fetches the system help information from the server and returns the full string.
    174     *
    175     * @return The system help string obtained from the server. null if the information could not be obtained.
    176     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    177     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    178     *                                       independently as itself.
    179     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    180     */
    181    public String listHelp() throws IOException {
    182        if (SMTPReply.isPositiveCompletion(help())) {
    183            return getReplyString();
    184        }
    185        return null;
    186    }
    187
    188    /**
    189     * Fetches the help information for a given command from the server and returns the full string.
    190     *
    191     * @param command The command on which to ask for help.
    192     * @return The command help string obtained from the server. null if the information could not be obtained.
    193     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    194     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    195     *                                       independently as itself.
    196     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    197     */
    198    public String listHelp(final String command) throws IOException {
    199        if (SMTPReply.isPositiveCompletion(help(command))) {
    200            return getReplyString();
    201        }
    202        return null;
    203    }
    204
    205    /**
    206     * Login to the SMTP server by sending the {@code HELO} command with the client hostname as an argument.
    207     * Before performing any mail commands, you must first log in.
    208     *
    209     * @return True if successfully completed, false if not.
    210     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    211     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    212     *                                       independently as itself.
    213     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    214     */
    215    public boolean login() throws IOException {
    216        final String name;
    217        final InetAddress host;
    218
    219        host = getLocalAddress();
    220        name = host.getHostName();
    221
    222        if (name == null) {
    223            return false;
    224        }
    225
    226        return SMTPReply.isPositiveCompletion(helo(name));
    227    }
    228
    229    /**
    230     * Login to the SMTP server by sending the {@code HELO} command with the given hostname as an argument.
    231     * Before performing any mail commands, you must first log in.
    232     *
    233     * @param hostname The hostname with which to greet the SMTP server.
    234     * @return True if successfully completed, false if not.
    235     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    236     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    237     *                                       independently as itself.
    238     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    239     */
    240    public boolean login(final String hostname) throws IOException {
    241        return SMTPReply.isPositiveCompletion(helo(hostname));
    242    }
    243
    244    /**
    245     * Logout of the SMTP server by sending the QUIT command.
    246     *
    247     * @return True if successfully completed, false if not.
    248     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    249     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    250     *                                       independently as itself.
    251     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    252     */
    253    public boolean logout() throws IOException {
    254        return SMTPReply.isPositiveCompletion(quit());
    255    }
    256
    257    /**
    258     * Aborts the current mail transaction, resetting all server stored sender, recipient, and mail data, cleaning all buffers and tables.
    259     *
    260     * @return True if successfully completed, false if not.
    261     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    262     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    263     *                                       independently as itself.
    264     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    265     */
    266    public boolean reset() throws IOException {
    267        return SMTPReply.isPositiveCompletion(rset());
    268    }
    269
    270    /**
    271     * Send the SMTP DATA command in preparation to send an email message. This method returns a DotTerminatedMessageWriter instance to which the message can be
    272     * written. Null is returned if the DATA command fails.
    273     * <p>
    274     * 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.
    275     * The SMTP protocol uses the same stream for issuing commands as it does for returning results. Therefore, the returned Writer actually writes directly to
    276     * 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
    277     * properly.
    278     * </p>
    279     * <p>
    280     * You can use the provided {@link org.apache.commons.net.smtp.SimpleSMTPHeader} class to construct a bare minimum header. To construct more complicated
    281     * 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.
    282     * 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
    283     * about is writing the header and the message.
    284     * </p>
    285     * <p>
    286     * Upon closing the returned Writer, you need to call {@link #completePendingCommand completePendingCommand() } to finalize the transaction and verify its
    287     * success or failure from the server reply.
    288     * </p>
    289     *
    290     * @return A DotTerminatedMessageWriter to which the message (including header) can be written. Returns null if the command fails.
    291     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    292     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    293     *                                       independently as itself.
    294     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    295     * @see #sendShortMessageData(String)
    296     */
    297    public Writer sendMessageData() throws IOException {
    298        if (!SMTPReply.isPositiveIntermediate(data())) {
    299            return null;
    300        }
    301
    302        return new DotTerminatedMessageWriter(writer);
    303    }
    304
    305    /**
    306     * Sends a NOOP command to the SMTP server. This is useful for preventing server timeouts.
    307     *
    308     * @return True if successfully completed, false if not.
    309     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    310     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    311     *                                       independently as itself.
    312     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    313     */
    314    public boolean sendNoOp() throws IOException {
    315        return SMTPReply.isPositiveCompletion(noop());
    316    }
    317
    318    /**
    319     * A convenience method for sending short messages. This method fetches the Writer returned by {@link #sendMessageData sendMessageData() } and writes the
    320     * specified String to it. After writing the message, this method calls {@link #completePendingCommand completePendingCommand() } to finalize the
    321     * transaction and returns its success or failure.
    322     *
    323     * @param message The short email message to send. This must include the headers and the body, but not the trailing "."
    324     * @return True if successfully completed, false if not.
    325     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    326     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    327     *                                       independently as itself.
    328     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    329     */
    330    public boolean sendShortMessageData(final String message) throws IOException {
    331        try (final Writer writer = sendMessageData()) {
    332
    333            if (writer == null) {
    334                return false;
    335            }
    336
    337            writer.write(message);
    338        }
    339
    340        return completePendingCommand();
    341    }
    342
    343    /**
    344     * 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
    345     * using {@link #setSender setSender } and {@link #addRecipient addRecipient }, and then sends the message using {@link #sendShortMessageData
    346     * sendShortMessageData }.
    347     *
    348     * @param sender    The email address of the sender.
    349     * @param recipient The email address of the recipient.
    350     * @param message   The short email message to send. This must include the headers and the body, but not the trailing "."
    351     * @return True if successfully completed, false if not.
    352     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    353     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    354     *                                       independently as itself.
    355     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    356     */
    357    public boolean sendSimpleMessage(final String sender, final String recipient, final String message) throws IOException {
    358        if (!setSender(sender)) {
    359            return false;
    360        }
    361
    362        if (!addRecipient(recipient)) {
    363            return false;
    364        }
    365
    366        return sendShortMessageData(message);
    367    }
    368
    369    /**
    370     * 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
    371     * using {@link #setSender(String) setSender} and {@link #addRecipient(String) addRecipient}, and then sends the message using
    372     * {@link #sendShortMessageData(String) sendShortMessageData}.
    373     * <p>
    374     * Note that the method ignores failures when calling {@link #addRecipient(String) addRecipient} so long as at least one call succeeds. If no recipients can
    375     * be successfully added then the method will fail (and does not attempt to send the message)
    376     * </p>
    377     *
    378     * @param sender     The email address of the sender.
    379     * @param recipients An array of recipient email addresses.
    380     * @param message    The short email message to send. This must include the headers and the body, but not the trailing "."
    381     * @return True if successfully completed, false if not.
    382     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    383     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    384     *                                       independently as itself.
    385     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    386     */
    387    public boolean sendSimpleMessage(final String sender, final String[] recipients, final String message) throws IOException {
    388        boolean oneSuccess = false;
    389        int count;
    390
    391        if (!setSender(sender)) {
    392            return false;
    393        }
    394
    395        for (count = 0; count < recipients.length; count++) {
    396            if (addRecipient(recipients[count])) {
    397                oneSuccess = true;
    398            }
    399        }
    400
    401        if (!oneSuccess) {
    402            return false;
    403        }
    404
    405        return sendShortMessageData(message);
    406    }
    407
    408    /**
    409     * 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
    410     * specified, otherwise the mail server will reject your commands.
    411     *
    412     * @param path The reverse relay path pointing back to the sender.
    413     * @return True if successfully completed, false if not.
    414     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    415     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    416     *                                       independently as itself.
    417     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    418     */
    419    public boolean setSender(final RelayPath path) throws IOException {
    420        return SMTPReply.isPositiveCompletion(mail(path.toString()));
    421    }
    422
    423    /**
    424     * 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
    425     * specified, otherwise the mail server will reject your commands.
    426     *
    427     * @param address The sender's email address.
    428     * @return True if successfully completed, false if not.
    429     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    430     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    431     *                                       independently as itself.
    432     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    433     */
    434    public boolean setSender(final String address) throws IOException {
    435        return SMTPReply.isPositiveCompletion(mail("<" + address + ">"));
    436    }
    437
    438    /**
    439     * Verify that a user or email address is valid, i.e., that mail can be delivered to that mailbox on the server.
    440     *
    441     * @param user The user name or email address to validate.
    442     * @return True if the user name is valid, false if not.
    443     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
    444     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
    445     *                                       independently as itself.
    446     * @throws IOException                   If an I/O error occurs while either sending a command to the server or receiving a reply from the server.
    447     */
    448    public boolean verify(final String user) throws IOException {
    449        final int result;
    450
    451        result = vrfy(user);
    452
    453        return result == SMTPReply.ACTION_OK || result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD;
    454    }
    455
    456}