    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
    018    package org.apache.commons.net.smtp;
    019
    020    import java.io.IOException;
    021    import java.io.Writer;
    022    import java.net.InetAddress;
    023
    024    import 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     * <p>
    117     * <p>
    118     * @see SMTP
    119     * @see SimpleSMTPHeader
    120     * @see RelayPath
    121     * @see SMTPConnectionClosedException
    122     * @see org.apache.commons.net.MalformedServerReplyException
    123     ***/
    124
    125    public class SMTPClient extends SMTP
    126    {
    127
    128        /**
    129         * Default SMTPClient constructor.  Creates a new SMTPClient instance.
    130         */
    131        public SMTPClient() {  }
    132
    133        /**
    134         * Overloaded constructor that takes an encoding specification
    135         * @param encoding The encoding to use
    136         * @since 2.0
    137         */
    138        public SMTPClient(String encoding) {
    139            super(encoding);
    140        }
    141
    142
    143        /***
    144         * At least one SMTPClient method ({@link #sendMessageData  sendMessageData })
    145         * does not complete the entire sequence of SMTP commands to complete a
    146         * transaction.  These types of commands require some action by the
    147         * programmer after the reception of a positive intermediate command.
    148         * After the programmer's code completes its actions, it must call this
    149         * method to receive the completion reply from the server and verify the
    150         * success of the entire transaction.
    151         * <p>
    152         * For example,
    153         * <pre>
    154         * writer = client.sendMessage();
    155         * if(writer == null) // failure
    156         *   return false;
    157         * header =
    158         *  new SimpleSMTPHeader("foobar@foo.com", "foo@foobar.com", "Re: Foo");
    159         * writer.write(header.toString());
    160         * writer.write("This is just a test");
    161         * writer.close();
    162         * if(!client.completePendingCommand()) // failure
    163         *   return false;
    164         * </pre>
    165         * <p>
    166         * @return True if successfully completed, false if not.
    167         * @exception SMTPConnectionClosedException
    168         *      If the SMTP server prematurely closes the connection as a result
    169         *      of the client being idle or some other reason causing the server
    170         *      to send SMTP reply code 421.  This exception may be caught either
    171         *      as an IOException or independently as itself.
    172         * @exception IOException  If an I/O error occurs while either sending a
    173         *      command to the server or receiving a reply from the server.
    174         ***/
    175        public boolean completePendingCommand() throws IOException
    176        {
    177            return SMTPReply.isPositiveCompletion(getReply());
    178        }
    179
    180
    181        /***
    182         * Login to the SMTP server by sending the HELO command with the
    183         * given hostname as an argument.  Before performing any mail commands,
    184         * you must first login.
    185         * <p>
    186         * @param hostname  The hostname with which to greet the SMTP server.
    187         * @return True if successfully completed, false if not.
    188         * @exception SMTPConnectionClosedException
    189         *      If the SMTP server prematurely closes the connection as a result
    190         *      of the client being idle or some other reason causing the server
    191         *      to send SMTP reply code 421.  This exception may be caught either
    192         *      as an IOException or independently as itself.
    193         * @exception IOException  If an I/O error occurs while either sending a
    194         *      command to the server or receiving a reply from the server.
    195         ***/
    196        public boolean login(String hostname) throws IOException
    197        {
    198            return SMTPReply.isPositiveCompletion(helo(hostname));
    199        }
    200
    201
    202        /***
    203         * Login to the SMTP server by sending the HELO command with the
    204         * client hostname as an argument.  Before performing any mail commands,
    205         * you must first login.
    206         * <p>
    207         * @return True if successfully completed, false if not.
    208         * @exception SMTPConnectionClosedException
    209         *      If the SMTP server prematurely closes the connection as a result
    210         *      of the client being idle or some other reason causing the server
    211         *      to send SMTP reply code 421.  This exception may be caught either
    212         *      as an IOException or independently as itself.
    213         * @exception IOException  If an I/O error occurs while either sending a
    214         *      command to the server or receiving a reply from the server.
    215         ***/
    216        public boolean login() throws IOException
    217        {
    218            String name;
    219            InetAddress host;
    220
    221            host = getLocalAddress();
    222            name = host.getHostName();
    223
    224            if (name == null) {
    225                return false;
    226            }
    227
    228            return SMTPReply.isPositiveCompletion(helo(name));
    229        }
    230
    231
    232        /***
    233         * Set the sender of a message using the SMTP MAIL command, specifying
    234         * a reverse relay path.  The sender must be set first before any
    235         * recipients may be specified, otherwise the mail server will reject
    236         * your commands.
    237         * <p>
    238         * @param path  The reverse relay path pointing back to the sender.
    239         * @return True if successfully completed, false if not.
    240         * @exception SMTPConnectionClosedException
    241         *      If the SMTP server prematurely closes the connection as a result
    242         *      of the client being idle or some other reason causing the server
    243         *      to send SMTP reply code 421.  This exception may be caught either
    244         *      as an IOException or independently as itself.
    245         * @exception IOException  If an I/O error occurs while either sending a
    246         *      command to the server or receiving a reply from the server.
    247         ***/
    248        public boolean setSender(RelayPath path) throws IOException
    249        {
    250            return SMTPReply.isPositiveCompletion(mail(path.toString()));
    251        }
    252
    253
    254        /***
    255         * Set the sender of a message using the SMTP MAIL command, specifying
    256         * the sender's email address. The sender must be set first before any
    257         * recipients may be specified, otherwise the mail server will reject
    258         * your commands.
    259         * <p>
    260         * @param address  The sender's email address.
    261         * @return True if successfully completed, false if not.
    262         * @exception SMTPConnectionClosedException
    263         *      If the SMTP server prematurely closes the connection as a result
    264         *      of the client being idle or some other reason causing the server
    265         *      to send SMTP reply code 421.  This exception may be caught either
    266         *      as an IOException or independently as itself.
    267         * @exception IOException  If an I/O error occurs while either sending a
    268         *      command to the server or receiving a reply from the server.
    269         ***/
    270        public boolean setSender(String address) throws IOException
    271        {
    272            return SMTPReply.isPositiveCompletion(mail("<" + address + ">"));
    273        }
    274
    275
    276        /***
    277         * Add a recipient for a message using the SMTP RCPT command, specifying
    278         * a forward relay path.  The sender must be set first before any
    279         * recipients may be specified, otherwise the mail server will reject
    280         * your commands.
    281         * <p>
    282         * @param path  The forward relay path pointing to the recipient.
    283         * @return True if successfully completed, false if not.
    284         * @exception SMTPConnectionClosedException
    285         *      If the SMTP server prematurely closes the connection as a result
    286         *      of the client being idle or some other reason causing the server
    287         *      to send SMTP reply code 421.  This exception may be caught either
    288         *      as an IOException or independently as itself.
    289         * @exception IOException  If an I/O error occurs while either sending a
    290         *      command to the server or receiving a reply from the server.
    291         ***/
    292        public boolean addRecipient(RelayPath path) throws IOException
    293        {
    294            return SMTPReply.isPositiveCompletion(rcpt(path.toString()));
    295        }
    296
    297
    298        /***
    299         * Add a recipient for a message using the SMTP RCPT command, the
    300         * recipient's email address.  The sender must be set first before any
    301         * recipients may be specified, otherwise the mail server will reject
    302         * your commands.
    303         * <p>
    304         * @param address  The recipient's email address.
    305         * @return True if successfully completed, false if not.
    306         * @exception SMTPConnectionClosedException
    307         *      If the SMTP server prematurely closes the connection as a result
    308         *      of the client being idle or some other reason causing the server
    309         *      to send SMTP reply code 421.  This exception may be caught either
    310         *      as an IOException or independently as itself.
    311         * @exception IOException  If an I/O error occurs while either sending a
    312         *      command to the server or receiving a reply from the server.
    313         ***/
    314        public boolean addRecipient(String address) throws IOException
    315        {
    316            return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">"));
    317        }
    318
    319
    320
    321        /***
    322         * Send the SMTP DATA command in preparation to send an email message.
    323         * This method returns a DotTerminatedMessageWriter instance to which
    324         * the message can be written.  Null is returned if the DATA command
    325         * fails.
    326         * <p>
    327         * You must not issue any commands to the SMTP server (i.e., call any
    328         * (other methods) until you finish writing to the returned Writer
    329         * instance and close it.  The SMTP protocol uses the same stream for
    330         * issuing commands as it does for returning results.  Therefore the
    331         * returned Writer actually writes directly to the SMTP connection.
    332         * After you close the writer, you can execute new commands.  If you
    333         * do not follow these requirements your program will not work properly.
    334         * <p>
    335         * You can use the provided
    336         * {@link org.apache.commons.net.smtp.SimpleSMTPHeader}
    337         * class to construct a bare minimum header.
    338         * To construct more complicated headers you should
    339         * refer to RFC 822.  When the Java Mail API is finalized, you will be
    340         * able to use it to compose fully compliant Internet text messages.
    341         * The DotTerminatedMessageWriter takes care of doubling line-leading
    342         * dots and ending the message with a single dot upon closing, so all
    343         * you have to worry about is writing the header and the message.
    344         * <p>
    345         * Upon closing the returned Writer, you need to call
    346         * {@link #completePendingCommand  completePendingCommand() }
    347         * to finalize the transaction and verify its success or failure from
    348         * the server reply.
    349         * <p>
    350         * @return A DotTerminatedMessageWriter to which the message (including
    351         *      header) can be written.  Returns null if the command fails.
    352         * @exception SMTPConnectionClosedException
    353         *      If the SMTP server prematurely closes the connection as a result
    354         *      of the client being idle or some other reason causing the server
    355         *      to send SMTP reply code 421.  This exception may be caught either
    356         *      as an IOException or independently as itself.
    357         * @exception IOException  If an I/O error occurs while either sending a
    358         *      command to the server or receiving a reply from the server.
    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         * @return True if successfully completed, false if not.
    380         * @exception SMTPConnectionClosedException
    381         *      If the SMTP server prematurely closes the connection as a result
    382         *      of the client being idle or some other reason causing the server
    383         *      to send SMTP reply code 421.  This exception may be caught either
    384         *      as an IOException or independently as itself.
    385         * @exception IOException  If an I/O error occurs while either sending a
    386         *      command to the server or receiving a reply from the server.
    387         ***/
    388        public boolean sendShortMessageData(String message) throws IOException
    389        {
    390            Writer writer;
    391
    392            writer = sendMessageData();
    393
    394            if (writer == null) {
    395                return false;
    396            }
    397
    398            writer.write(message);
    399            writer.close();
    400
    401            return completePendingCommand();
    402        }
    403
    404
    405        /***
    406         * A convenience method for a sending short email without having to
    407         * explicitly set the sender and recipient(s).  This method
    408         * sets the sender and recipient using
    409         * {@link #setSender  setSender } and
    410         * {@link #addRecipient  addRecipient }, and then sends the
    411         * message using {@link #sendShortMessageData  sendShortMessageData }.
    412         * <p>
    413         * @param sender  The email address of the sender.
    414         * @param recipient  The email address of the recipient.
    415         * @param message  The short email message to send.
    416         * @return True if successfully completed, false if not.
    417         * @exception 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         * @exception 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(String sender, String recipient,
    426                                         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  setSender } and
    447         * {@link #addRecipient  addRecipient }, and then sends the
    448         * message using {@link #sendShortMessageData  sendShortMessageData }.
    449         * <p>
    450         * @param sender  The email address of the sender.
    451         * @param recipients  An array of recipient email addresses.
    452         * @param message  The short email message to send.
    453         * @return True if successfully completed, false if not.
    454         * @exception SMTPConnectionClosedException
    455         *      If the SMTP server prematurely closes the connection as a result
    456         *      of the client being idle or some other reason causing the server
    457         *      to send SMTP reply code 421.  This exception may be caught either
    458         *      as an IOException or independently as itself.
    459         * @exception IOException  If an I/O error occurs while either sending a
    460         *      command to the server or receiving a reply from the server.
    461         ***/
    462        public boolean sendSimpleMessage(String sender, String[] recipients,
    463                                         String message)
    464        throws IOException
    465        {
    466            boolean oneSuccess = false;
    467            int count;
    468
    469            if (!setSender(sender)) {
    470                return false;
    471            }
    472
    473            for (count = 0; count < recipients.length; count++)
    474            {
    475                if (addRecipient(recipients[count])) {
    476                    oneSuccess = true;
    477                }
    478            }
    479
    480            if (!oneSuccess) {
    481                return false;
    482            }
    483
    484            return sendShortMessageData(message);
    485        }
    486
    487
    488        /***
    489         * Logout of the SMTP server by sending the QUIT command.
    490         * <p>
    491         * @return True if successfully completed, false if not.
    492         * @exception SMTPConnectionClosedException
    493         *      If the SMTP server prematurely closes the connection as a result
    494         *      of the client being idle or some other reason causing the server
    495         *      to send SMTP reply code 421.  This exception may be caught either
    496         *      as an IOException or independently as itself.
    497         * @exception IOException  If an I/O error occurs while either sending a
    498         *      command to the server or receiving a reply from the server.
    499         ***/
    500        public boolean logout() throws IOException
    501        {
    502            return SMTPReply.isPositiveCompletion(quit());
    503        }
    504
    505
    506
    507        /***
    508         * Aborts the current mail transaction, resetting all server stored
    509         * sender, recipient, and mail data, cleaing all buffers and tables.
    510         * <p>
    511         * @return True if successfully completed, false if not.
    512         * @exception SMTPConnectionClosedException
    513         *      If the SMTP server prematurely closes the connection as a result
    514         *      of the client being idle or some other reason causing the server
    515         *      to send SMTP reply code 421.  This exception may be caught either
    516         *      as an IOException or independently as itself.
    517         * @exception IOException  If an I/O error occurs while either sending a
    518         *      command to the server or receiving a reply from the server.
    519         ***/
    520        public boolean reset() throws IOException
    521        {
    522            return SMTPReply.isPositiveCompletion(rset());
    523        }
    524
    525
    526        /***
    527         * Verify that a username or email address is valid, i.e., that mail
    528         * can be delivered to that mailbox on the server.
    529         * <p>
    530         * @param username  The username or email address to validate.
    531         * @return True if the username is valid, false if not.
    532         * @exception SMTPConnectionClosedException
    533         *      If the SMTP server prematurely closes the connection as a result
    534         *      of the client being idle or some other reason causing the server
    535         *      to send SMTP reply code 421.  This exception may be caught either
    536         *      as an IOException or independently as itself.
    537         * @exception IOException  If an I/O error occurs while either sending a
    538         *      command to the server or receiving a reply from the server.
    539         ***/
    540        public boolean verify(String username) throws IOException
    541        {
    542            int result;
    543
    544            result = vrfy(username);
    545
    546            return (result == SMTPReply.ACTION_OK ||
    547                    result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD);
    548        }
    549
    550
    551        /***
    552         * Fetches the system help information from the server and returns the
    553         * full string.
    554         * <p>
    555         * @return The system help string obtained from the server.  null if the
    556         *       information could not be obtained.
    557         * @exception SMTPConnectionClosedException
    558         *      If the SMTP server prematurely closes the connection as a result
    559         *      of the client being idle or some other reason causing the server
    560         *      to send SMTP reply code 421.  This exception may be caught either
    561         *      as an IOException or independently as itself.
    562         * @exception IOException  If an I/O error occurs while either sending a
    563         *  command to the server or receiving a reply from the server.
    564         ***/
    565        public String listHelp() throws IOException
    566        {
    567            if (SMTPReply.isPositiveCompletion(help())) {
    568                return getReplyString();
    569            }
    570            return null;
    571        }
    572
    573
    574        /***
    575         * Fetches the help information for a given command from the server and
    576         * returns the full string.
    577         * <p>
    578         * @param command The command on which to ask for help.
    579         * @return The command help string obtained from the server.  null if the
    580         *       information could not be obtained.
    581         * @exception SMTPConnectionClosedException
    582         *      If the SMTP server prematurely closes the connection as a result
    583         *      of the client being idle or some other reason causing the server
    584         *      to send SMTP reply code 421.  This exception may be caught either
    585         *      as an IOException or independently as itself.
    586         * @exception IOException  If an I/O error occurs while either sending a
    587         *  command to the server or receiving a reply from the server.
    588         ***/
    589        public String listHelp(String command) throws IOException
    590        {
    591            if (SMTPReply.isPositiveCompletion(help(command))) {
    592                return getReplyString();
    593            }
    594            return null;
    595        }
    596
    597
    598        /***
    599         * Sends a NOOP command to the SMTP server.  This is useful for preventing
    600         * server timeouts.
    601         * <p>
    602         * @return True if successfully completed, false if not.
    603         * @exception SMTPConnectionClosedException
    604         *      If the SMTP server prematurely closes the connection as a result
    605         *      of the client being idle or some other reason causing the server
    606         *      to send SMTP reply code 421.  This exception may be caught either
    607         *      as an IOException or independently as itself.
    608         * @exception IOException  If an I/O error occurs while either sending a
    609         *      command to the server or receiving a reply from the server.
    610         ***/
    611        public boolean sendNoOp() throws IOException
    612        {
    613            return SMTPReply.isPositiveCompletion(noop());
    614        }
    615
    616    }