    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
    028 * through an SMTP server.  This class takes care of all
    029 * low level details of interacting with an SMTP server and provides
    030 * a convenient higher level interface.  As with all classes derived
    031 * from {@link org.apache.commons.net.SocketClient},
    032 * you must first connect to the server with
    033 * {@link org.apache.commons.net.SocketClient#connect  connect }
    034 * before doing anything, and finally
    035 * {@link org.apache.commons.net.SocketClient#disconnect  disconnect }
    036 * after you're completely finished interacting with the server.
    037 * Then you need to check the SMTP reply code to see if the connection
    038 * was successful.  For example:
    039 * <pre>
    040 *    try {
    041 *      int reply;
    042 *      client.connect("mail.foobar.com");
    043 *      System.out.print(client.getReplyString());
    044 *
    045 *      // After connection attempt, you should check the reply code to verify
    046 *      // success.
    047 *      reply = client.getReplyCode();
    048 *
    049 *      if(!SMTPReply.isPositiveCompletion(reply)) {
    050 *        client.disconnect();
    051 *        System.err.println("SMTP server refused connection.");
    052 *        System.exit(1);
    053 *      }
    054 *
    055 *      // Do useful stuff here.
    056 *      ...
    057 *    } catch(IOException e) {
    058 *      if(client.isConnected()) {
    059 *        try {
    060 *          client.disconnect();
    061 *        } catch(IOException f) {
    062 *          // do nothing
    063 *        }
    064 *      }
    065 *      System.err.println("Could not connect to server.");
    066 *      e.printStackTrace();
    067 *      System.exit(1);
    068 *    }
    069 * </pre>
    070 * <p>
    071 * Immediately after connecting is the only real time you need to check the
    072 * reply code (because connect is of type void).  The convention for all the
    073 * SMTP command methods in SMTPClient is such that they either return a
    074 * boolean value or some other value.
    075 * The boolean methods return true on a successful completion reply from
    076 * the SMTP server and false on a reply resulting in an error condition or
    077 * failure.  The methods returning a value other than boolean return a value
    078 * containing the higher level data produced by the SMTP command, or null if a
    079 * reply resulted in an error condition or failure.  If you want to access
    080 * the exact SMTP reply code causing a success or failure, you must call
    081 * {@link org.apache.commons.net.smtp.SMTP#getReplyCode  getReplyCode } after
    082 * a success or failure.
    083 * <p>
    084 * You should keep in mind that the SMTP server may choose to prematurely
    085 * close a connection for various reasons.  The SMTPClient class will detect a
    086 * premature SMTP server connection closing when it receives a
    087 * {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE }
    088 *  response to a command.
    089 * When that occurs, the method encountering that reply will throw
    090 * an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
    091 * .
    092 * <code>SMTPConectionClosedException</code>
    093 * is a subclass of <code> IOException </code> and therefore need not be
    094 * caught separately, but if you are going to catch it separately, its
    095 * catch block must appear before the more general <code> IOException </code>
    096 * catch block.  When you encounter an
    097 * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
    098 * , you must disconnect the connection with
    099 * {@link #disconnect  disconnect() } to properly clean up the
    100 * system resources used by SMTPClient.  Before disconnecting, you may check
    101 * the last reply code and text with
    102 * {@link org.apache.commons.net.smtp.SMTP#getReplyCode  getReplyCode },
    103 * {@link org.apache.commons.net.smtp.SMTP#getReplyString  getReplyString },
    104 * and
    105 * {@link org.apache.commons.net.smtp.SMTP#getReplyStrings getReplyStrings}.
    106 * <p>
    107 * Rather than list it separately for each method, we mention here that
    108 * every method communicating with the server and throwing an IOException
    109 * can also throw a
    110 * {@link org.apache.commons.net.MalformedServerReplyException}
    111 * , which is a subclass
    112 * of IOException.  A MalformedServerReplyException will be thrown when
    113 * the reply received from the server deviates enough from the protocol
    114 * specification that it cannot be interpreted in a useful manner despite
    115 * attempts to be as lenient as possible.
    116 *
    117 * @see SMTP
    118 * @see SimpleSMTPHeader
    119 * @see RelayPath
    120 * @see SMTPConnectionClosedException
    121 * @see org.apache.commons.net.MalformedServerReplyException
    122 ***/
    123
    124public class SMTPClient extends SMTP
    125{
    126
    127    /**
    128     * Default SMTPClient constructor.  Creates a new SMTPClient instance.
    129     */
    130    public SMTPClient() {  }
    131
    132    /**
    133     * Overloaded constructor that takes an encoding specification
    134     * @param encoding The encoding to use
    135     * @since 2.0
    136     */
    137    public SMTPClient(final String encoding) {
    138        super(encoding);
    139    }
    140
    141
    142    /***
    143     * At least one SMTPClient method ({@link #sendMessageData  sendMessageData })
    144     * does not complete the entire sequence of SMTP commands to complete a
    145     * transaction.  These types of commands require some action by the
    146     * programmer after the reception of a positive intermediate command.
    147     * After the programmer's code completes its actions, it must call this
    148     * method to receive the completion reply from the server and verify the
    149     * success of the entire transaction.
    150     * <p>
    151     * For example,
    152     * <pre>
    153     * writer = client.sendMessageData();
    154     * if(writer == null) // failure
    155     *   return false;
    156     * header =
    157     *  new SimpleSMTPHeader("foobar@foo.com", "foo@foobar.com", "Re: Foo");
    158     * writer.write(header.toString());
    159     * writer.write("This is just a test");
    160     * writer.close();
    161     * if(!client.completePendingCommand()) // failure
    162     *   return false;
    163     * </pre>
    164     * <p>
    165     * @return True if successfully completed, false if not.
    166     * @throws SMTPConnectionClosedException
    167     *      If the SMTP server prematurely closes the connection as a result
    168     *      of the client being idle or some other reason causing the server
    169     *      to send SMTP reply code 421.  This exception may be caught either
    170     *      as an IOException or independently as itself.
    171     * @throws IOException  If an I/O error occurs while either sending a
    172     *      command to the server or receiving a reply from the server.
    173     ***/
    174    public boolean completePendingCommand() throws IOException
    175    {
    176        return SMTPReply.isPositiveCompletion(getReply());
    177    }
    178
    179
    180    /***
    181     * Login to the SMTP server by sending the HELO command with the
    182     * given hostname as an argument.  Before performing any mail commands,
    183     * you must first login.
    184     * <p>
    185     * @param hostname  The hostname with which to greet the SMTP server.
    186     * @return True if successfully completed, false if not.
    187     * @throws SMTPConnectionClosedException
    188     *      If the SMTP server prematurely closes the connection as a result
    189     *      of the client being idle or some other reason causing the server
    190     *      to send SMTP reply code 421.  This exception may be caught either
    191     *      as an IOException or independently as itself.
    192     * @throws IOException  If an I/O error occurs while either sending a
    193     *      command to the server or receiving a reply from the server.
    194     ***/
    195    public boolean login(final String hostname) throws IOException
    196    {
    197        return SMTPReply.isPositiveCompletion(helo(hostname));
    198    }
    199
    200
    201    /***
    202     * Login to the SMTP server by sending the HELO command with the
    203     * client hostname as an argument.  Before performing any mail commands,
    204     * you must first login.
    205     * <p>
    206     * @return True if successfully completed, false if not.
    207     * @throws SMTPConnectionClosedException
    208     *      If the SMTP server prematurely closes the connection as a result
    209     *      of the client being idle or some other reason causing the server
    210     *      to send SMTP reply code 421.  This exception may be caught either
    211     *      as an IOException or independently as itself.
    212     * @throws IOException  If an I/O error occurs while either sending a
    213     *      command to the server or receiving a reply from the server.
    214     ***/
    215    public boolean login() throws IOException
    216    {
    217        String name;
    218        InetAddress host;
    219
    220        host = getLocalAddress();
    221        name = host.getHostName();
    222
    223        if (name == null) {
    224            return false;
    225        }
    226
    227        return SMTPReply.isPositiveCompletion(helo(name));
    228    }
    229
    230
    231    /***
    232     * Set the sender of a message using the SMTP MAIL command, specifying
    233     * a reverse relay path.  The sender must be set first before any
    234     * recipients may be specified, otherwise the mail server will reject
    235     * your commands.
    236     * <p>
    237     * @param path  The reverse relay path pointing back to the sender.
    238     * @return True if successfully completed, false if not.
    239     * @throws SMTPConnectionClosedException
    240     *      If the SMTP server prematurely closes the connection as a result
    241     *      of the client being idle or some other reason causing the server
    242     *      to send SMTP reply code 421.  This exception may be caught either
    243     *      as an IOException or independently as itself.
    244     * @throws IOException  If an I/O error occurs while either sending a
    245     *      command to the server or receiving a reply from the server.
    246     ***/
    247    public boolean setSender(final RelayPath path) throws IOException
    248    {
    249        return SMTPReply.isPositiveCompletion(mail(path.toString()));
    250    }
    251
    252
    253    /***
    254     * Set the sender of a message using the SMTP MAIL command, specifying
    255     * the sender's email address. The sender must be set first before any
    256     * recipients may be specified, otherwise the mail server will reject
    257     * your commands.
    258     * <p>
    259     * @param address  The sender's email address.
    260     * @return True if successfully completed, false if not.
    261     * @throws SMTPConnectionClosedException
    262     *      If the SMTP server prematurely closes the connection as a result
    263     *      of the client being idle or some other reason causing the server
    264     *      to send SMTP reply code 421.  This exception may be caught either
    265     *      as an IOException or independently as itself.
    266     * @throws IOException  If an I/O error occurs while either sending a
    267     *      command to the server or receiving a reply from the server.
    268     ***/
    269    public boolean setSender(final String address) throws IOException
    270    {
    271        return SMTPReply.isPositiveCompletion(mail("<" + address + ">"));
    272    }
    273
    274
    275    /***
    276     * Add a recipient for a message using the SMTP RCPT command, specifying
    277     * a forward relay path.  The sender must be set first before any
    278     * recipients may be specified, otherwise the mail server will reject
    279     * your commands.
    280     * <p>
    281     * @param path  The forward relay path pointing to the recipient.
    282     * @return True if successfully completed, false if not.
    283     * @throws SMTPConnectionClosedException
    284     *      If the SMTP server prematurely closes the connection as a result
    285     *      of the client being idle or some other reason causing the server
    286     *      to send SMTP reply code 421.  This exception may be caught either
    287     *      as an IOException or independently as itself.
    288     * @throws IOException  If an I/O error occurs while either sending a
    289     *      command to the server or receiving a reply from the server.
    290     ***/
    291    public boolean addRecipient(final RelayPath path) throws IOException
    292    {
    293        return SMTPReply.isPositiveCompletion(rcpt(path.toString()));
    294    }
    295
    296
    297    /***
    298     * Add a recipient for a message using the SMTP RCPT command, the
    299     * recipient's email address.  The sender must be set first before any
    300     * recipients may be specified, otherwise the mail server will reject
    301     * your commands.
    302     * <p>
    303     * @param address  The recipient's email address.
    304     * @return True if successfully completed, false if not.
    305     * @throws SMTPConnectionClosedException
    306     *      If the SMTP server prematurely closes the connection as a result
    307     *      of the client being idle or some other reason causing the server
    308     *      to send SMTP reply code 421.  This exception may be caught either
    309     *      as an IOException or independently as itself.
    310     * @throws IOException  If an I/O error occurs while either sending a
    311     *      command to the server or receiving a reply from the server.
    312     ***/
    313    public boolean addRecipient(final String address) throws IOException
    314    {
    315        return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">"));
    316    }
    317
    318
    319
    320    /***
    321     * Send the SMTP DATA command in preparation to send an email message.
    322     * This method returns a DotTerminatedMessageWriter instance to which
    323     * the message can be written.  Null is returned if the DATA command
    324     * fails.
    325     * <p>
    326     * You must not issue any commands to the SMTP server (i.e., call any
    327     * (other methods) until you finish writing to the returned Writer
    328     * instance and close it.  The SMTP protocol uses the same stream for
    329     * issuing commands as it does for returning results.  Therefore the
    330     * returned Writer actually writes directly to the SMTP connection.
    331     * After you close the writer, you can execute new commands.  If you
    332     * do not follow these requirements your program will not work properly.
    333     * <p>
    334     * You can use the provided
    335     * {@link org.apache.commons.net.smtp.SimpleSMTPHeader}
    336     * class to construct a bare minimum header.
    337     * To construct more complicated headers you should
    338     * refer to RFC 5322.  When the Java Mail API is finalized, you will be
    339     * able to use it to compose fully compliant Internet text messages.
    340     * The DotTerminatedMessageWriter takes care of doubling line-leading
    341     * dots and ending the message with a single dot upon closing, so all
    342     * you have to worry about is writing the header and the message.
    343     * <p>
    344     * Upon closing the returned Writer, you need to call
    345     * {@link #completePendingCommand  completePendingCommand() }
    346     * to finalize the transaction and verify its success or failure from
    347     * the server reply.
    348     * <p>
    349     * @return A DotTerminatedMessageWriter to which the message (including
    350     *      header) can be written.  Returns null if the command fails.
    351     * @throws SMTPConnectionClosedException
    352     *      If the SMTP server prematurely closes the connection as a result
    353     *      of the client being idle or some other reason causing the server
    354     *      to send SMTP reply code 421.  This exception may be caught either
    355     *      as an IOException or independently as itself.
    356     * @throws IOException  If an I/O error occurs while either sending a
    357     *      command to the server or receiving a reply from the server.
    358     * @see #sendShortMessageData(String)
    359     ***/
    360    public Writer sendMessageData() throws IOException
    361    {
    362        if (!SMTPReply.isPositiveIntermediate(data())) {
    363            return null;
    364        }
    365
    366        return new DotTerminatedMessageWriter(writer);
    367    }
    368
    369
    370    /***
    371     * A convenience method for sending short messages.  This method fetches
    372     * the Writer returned by {@link #sendMessageData  sendMessageData() }
    373     * and writes the specified String to it.  After writing the message,
    374     * this method calls {@link #completePendingCommand completePendingCommand() }
    375     *  to finalize the transaction and returns
    376     * its success or failure.
    377     * <p>
    378     * @param message  The short email message to send.
    379     * This must include the headers and the body, but not the trailing "."
    380     * @return True if successfully completed, false if not.
    381     * @throws SMTPConnectionClosedException
    382     *      If the SMTP server prematurely closes the connection as a result
    383     *      of the client being idle or some other reason causing the server
    384     *      to send SMTP reply code 421.  This exception may be caught either
    385     *      as an IOException or independently as itself.
    386     * @throws IOException  If an I/O error occurs while either sending a
    387     *      command to the server or receiving a reply from the server.
    388     ***/
    389    public boolean sendShortMessageData(final String message) throws IOException
    390    {
    391        try (Writer writer = sendMessageData()) {
    392
    393            if (writer == null) {
    394                return false;
    395            }
    396
    397            writer.write(message);
    398        }
    399
    400        return completePendingCommand();
    401    }
    402
    403
    404    /***
    405     * A convenience method for a sending short email without having to
    406     * explicitly set the sender and recipient(s).  This method
    407     * sets the sender and recipient using
    408     * {@link #setSender  setSender } and
    409     * {@link #addRecipient  addRecipient }, and then sends the
    410     * message using {@link #sendShortMessageData  sendShortMessageData }.
    411     * <p>
    412     * @param sender  The email address of the sender.
    413     * @param recipient  The email address of the recipient.
    414     * @param message  The short email message to send.
    415     * This must include the headers and the body, but not the trailing "."
    416     * @return True if successfully completed, false if not.
    417     * @throws SMTPConnectionClosedException
    418     *      If the SMTP server prematurely closes the connection as a result
    419     *      of the client being idle or some other reason causing the server
    420     *      to send SMTP reply code 421.  This exception may be caught either
    421     *      as an IOException or independently as itself.
    422     * @throws IOException  If an I/O error occurs while either sending a
    423     *      command to the server or receiving a reply from the server.
    424     ***/
    425    public boolean sendSimpleMessage(final String sender, final String recipient,
    426                                     final String message)
    427    throws IOException
    428    {
    429        if (!setSender(sender)) {
    430            return false;
    431        }
    432
    433        if (!addRecipient(recipient)) {
    434            return false;
    435        }
    436
    437        return sendShortMessageData(message);
    438    }
    439
    440
    441
    442    /***
    443     * A convenience method for a sending short email without having to
    444     * explicitly set the sender and recipient(s).  This method
    445     * sets the sender and recipients using
    446     * {@link #setSender(String)  setSender} and
    447     * {@link #addRecipient(String)  addRecipient}, and then sends the
    448     * message using {@link #sendShortMessageData(String)  sendShortMessageData}.
    449     * <p>
    450     * Note that the method ignores failures when calling
    451     * {@link #addRecipient(String)  addRecipient} so long as
    452     * at least one call succeeds. If no recipients can be successfully
    453     * added then the method will fail (and does not attempt to
    454     * send the message)
    455     * <p>
    456     * @param sender  The email address of the sender.
    457     * @param recipients  An array of recipient email addresses.
    458     * @param message  The short email message to send.
    459     * This must include the headers and the body, but not the trailing "."
    460     * @return True if successfully completed, false if not.
    461     * @throws SMTPConnectionClosedException
    462     *      If the SMTP server prematurely closes the connection as a result
    463     *      of the client being idle or some other reason causing the server
    464     *      to send SMTP reply code 421.  This exception may be caught either
    465     *      as an IOException or independently as itself.
    466     * @throws IOException  If an I/O error occurs while either sending a
    467     *      command to the server or receiving a reply from the server.
    468     ***/
    469    public boolean sendSimpleMessage(final String sender, final String[] recipients,
    470                                     final String message)
    471    throws IOException
    472    {
    473        boolean oneSuccess = false;
    474        int count;
    475
    476        if (!setSender(sender)) {
    477            return false;
    478        }
    479
    480        for (count = 0; count < recipients.length; count++)
    481        {
    482            if (addRecipient(recipients[count])) {
    483                oneSuccess = true;
    484            }
    485        }
    486
    487        if (!oneSuccess) {
    488            return false;
    489        }
    490
    491        return sendShortMessageData(message);
    492    }
    493
    494
    495    /***
    496     * Logout of the SMTP server by sending the QUIT command.
    497     * <p>
    498     * @return True if successfully completed, false if not.
    499     * @throws SMTPConnectionClosedException
    500     *      If the SMTP server prematurely closes the connection as a result
    501     *      of the client being idle or some other reason causing the server
    502     *      to send SMTP reply code 421.  This exception may be caught either
    503     *      as an IOException or independently as itself.
    504     * @throws IOException  If an I/O error occurs while either sending a
    505     *      command to the server or receiving a reply from the server.
    506     ***/
    507    public boolean logout() throws IOException
    508    {
    509        return SMTPReply.isPositiveCompletion(quit());
    510    }
    511
    512
    513
    514    /***
    515     * Aborts the current mail transaction, resetting all server stored
    516     * sender, recipient, and mail data, cleaing all buffers and tables.
    517     * <p>
    518     * @return True if successfully completed, false if not.
    519     * @throws SMTPConnectionClosedException
    520     *      If the SMTP server prematurely closes the connection as a result
    521     *      of the client being idle or some other reason causing the server
    522     *      to send SMTP reply code 421.  This exception may be caught either
    523     *      as an IOException or independently as itself.
    524     * @throws IOException  If an I/O error occurs while either sending a
    525     *      command to the server or receiving a reply from the server.
    526     ***/
    527    public boolean reset() throws IOException
    528    {
    529        return SMTPReply.isPositiveCompletion(rset());
    530    }
    531
    532
    533    /***
    534     * Verify that a username or email address is valid, i.e., that mail
    535     * can be delivered to that mailbox on the server.
    536     * <p>
    537     * @param username  The username or email address to validate.
    538     * @return True if the username is valid, false if not.
    539     * @throws SMTPConnectionClosedException
    540     *      If the SMTP server prematurely closes the connection as a result
    541     *      of the client being idle or some other reason causing the server
    542     *      to send SMTP reply code 421.  This exception may be caught either
    543     *      as an IOException or independently as itself.
    544     * @throws IOException  If an I/O error occurs while either sending a
    545     *      command to the server or receiving a reply from the server.
    546     ***/
    547    public boolean verify(final String username) throws IOException
    548    {
    549        int result;
    550
    551        result = vrfy(username);
    552
    553        return result == SMTPReply.ACTION_OK ||
    554                result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD;
    555    }
    556
    557
    558    /***
    559     * Fetches the system help information from the server and returns the
    560     * full string.
    561     * <p>
    562     * @return The system help string obtained from the server.  null if the
    563     *       information could not be obtained.
    564     * @throws SMTPConnectionClosedException
    565     *      If the SMTP server prematurely closes the connection as a result
    566     *      of the client being idle or some other reason causing the server
    567     *      to send SMTP reply code 421.  This exception may be caught either
    568     *      as an IOException or independently as itself.
    569     * @throws IOException  If an I/O error occurs while either sending a
    570     *  command to the server or receiving a reply from the server.
    571     ***/
    572    public String listHelp() throws IOException
    573    {
    574        if (SMTPReply.isPositiveCompletion(help())) {
    575            return getReplyString();
    576        }
    577        return null;
    578    }
    579
    580
    581    /***
    582     * Fetches the help information for a given command from the server and
    583     * returns the full string.
    584     * <p>
    585     * @param command The command on which to ask for help.
    586     * @return The command help string obtained from the server.  null if the
    587     *       information could not be obtained.
    588     * @throws SMTPConnectionClosedException
    589     *      If the SMTP server prematurely closes the connection as a result
    590     *      of the client being idle or some other reason causing the server
    591     *      to send SMTP reply code 421.  This exception may be caught either
    592     *      as an IOException or independently as itself.
    593     * @throws IOException  If an I/O error occurs while either sending a
    594     *  command to the server or receiving a reply from the server.
    595     ***/
    596    public String listHelp(final String command) throws IOException
    597    {
    598        if (SMTPReply.isPositiveCompletion(help(command))) {
    599            return getReplyString();
    600        }
    601        return null;
    602    }
    603
    604
    605    /***
    606     * Sends a NOOP command to the SMTP server.  This is useful for preventing
    607     * server timeouts.
    608     * <p>
    609     * @return True if successfully completed, false if not.
    610     * @throws SMTPConnectionClosedException
    611     *      If the SMTP server prematurely closes the connection as a result
    612     *      of the client being idle or some other reason causing the server
    613     *      to send SMTP reply code 421.  This exception may be caught either
    614     *      as an IOException or independently as itself.
    615     * @throws IOException  If an I/O error occurs while either sending a
    616     *      command to the server or receiving a reply from the server.
    617     ***/
    618    public boolean sendNoOp() throws IOException
    619    {
    620        return SMTPReply.isPositiveCompletion(noop());
    621    }
    622
    623}