View Javadoc

    1   /*
    2    * Copyright 2001-2005 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.net.smtp;
    17
    18  import java.io.IOException;
    19  import java.io.Writer;
    20  import java.net.InetAddress;
    21  import org.apache.commons.net.io.DotTerminatedMessageWriter;
    22
    23  /****
    24   * SMTPClient encapsulates all the functionality necessary to send files
    25   * through an SMTP server.  This class takes care of all
    26   * low level details of interacting with an SMTP server and provides
    27   * a convenient higher level interface.  As with all classes derived
    28   * from {@link org.apache.commons.net.SocketClient},
    29   * you must first connect to the server with
    30   * {@link org.apache.commons.net.SocketClient#connect  connect }
    31   * before doing anything, and finally
    32   * {@link org.apache.commons.net.SocketClient#disconnect  disconnect }
    33   * after you're completely finished interacting with the server.
    34   * Then you need to check the SMTP reply code to see if the connection
    35   * was successful.  For example:
    36   * <pre>
    37   *    try {
    38   *      int reply;
    39   *      client.connect("mail.foobar.com");
    40   *      System.out.print(client.getReplyString());
    41   *
    42   *      // After connection attempt, you should check the reply code to verify
    43   *      // success.
    44   *      reply = client.getReplyCode();
    45   *
    46   *      if(!SMTPReply.isPositiveCompletion(reply)) {
    47   *        client.disconnect();
    48   *        System.err.println("SMTP server refused connection.");
    49   *        System.exit(1);
    50   *      }
    51   *
    52   *      // Do useful stuff here.
    53   *      ...
    54   *    } catch(IOException e) {
    55   *      if(client.isConnected()) {
    56   *        try {
    57   *          client.disconnect();
    58   *        } catch(IOException f) {
    59   *          // do nothing
    60   *        }
    61   *      }
    62   *      System.err.println("Could not connect to server.");
    63   *      e.printStackTrace();
    64   *      System.exit(1);
    65   *    }
    66   * </pre>
    67   * <p>
    68   * Immediately after connecting is the only real time you need to check the
    69   * reply code (because connect is of type void).  The convention for all the
    70   * SMTP command methods in SMTPClient is such that they either return a
    71   * boolean value or some other value.
    72   * The boolean methods return true on a successful completion reply from
    73   * the SMTP server and false on a reply resulting in an error condition or
    74   * failure.  The methods returning a value other than boolean return a value
    75   * containing the higher level data produced by the SMTP command, or null if a
    76   * reply resulted in an error condition or failure.  If you want to access
    77   * the exact SMTP reply code causing a success or failure, you must call
    78   * {@link org.apache.commons.net.smtp.SMTP#getReplyCode  getReplyCode } after
    79   * a success or failure.
    80   * <p>
    81   * You should keep in mind that the SMTP server may choose to prematurely
    82   * close a connection for various reasons.  The SMTPClient class will detect a
    83   * premature SMTP server connection closing when it receives a
    84   * {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE }
    85   *  response to a command.
    86   * When that occurs, the method encountering that reply will throw
    87   * an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
    88   * .
    89   * <code>SMTPConectionClosedException</code>
    90   * is a subclass of <code> IOException </code> and therefore need not be
    91   * caught separately, but if you are going to catch it separately, its
    92   * catch block must appear before the more general <code> IOException </code>
    93   * catch block.  When you encounter an
    94   * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
    95   * , you must disconnect the connection with
    96   * {@link #disconnect  disconnect() } to properly clean up the
    97   * system resources used by SMTPClient.  Before disconnecting, you may check
    98   * the last reply code and text with
    99   * {@link org.apache.commons.net.smtp.SMTP#getReplyCode  getReplyCode },
    100  * {@link org.apache.commons.net.smtp.SMTP#getReplyString  getReplyString },
    101  * and
    102  * {@link org.apache.commons.net.smtp.SMTP#getReplyStrings getReplyStrings}.
    103  * <p>
    104  * Rather than list it separately for each method, we mention here that
    105  * every method communicating with the server and throwing an IOException
    106  * can also throw a
    107  * {@link org.apache.commons.net.MalformedServerReplyException}
    108  * , which is a subclass
    109  * of IOException.  A MalformedServerReplyException will be thrown when
    110  * the reply received from the server deviates enough from the protocol
    111  * specification that it cannot be interpreted in a useful manner despite
    112  * attempts to be as lenient as possible.
    113  * <p>
    114  * <p>
    115  * @author Daniel F. Savarese
    116  * @see SMTP
    117  * @see SimpleSMTPHeader
    118  * @see RelayPath
    119  * @see SMTPConnectionClosedException
    120  * @see org.apache.commons.net.MalformedServerReplyException
    121  ***/
    122
    123 public class SMTPClient extends SMTP
    124 {
    125
    126     /*
    127      * Default SMTPClient constructor.  Creates a new SMTPClient instance.
    128      */
    129     //public SMTPClient() {  }
    130
    131
    132     /****
    133      * At least one SMTPClient method ({@link #sendMessageData  sendMessageData })
    134      * does not complete the entire sequence of SMTP commands to complete a
    135      * transaction.  These types of commands require some action by the
    136      * programmer after the reception of a positive intermediate command.
    137      * After the programmer's code completes its actions, it must call this
    138      * method to receive the completion reply from the server and verify the
    139      * success of the entire transaction.
    140      * <p>
    141      * For example,
    142      * <pre>
    143      * writer = client.sendMessage();
    144      * if(writer == null) // failure
    145      *   return false;
    146      * header =
    147      *  new SimpleSMTPHeader("foobar@foo.com", "foo@foobar.com", "Re: Foo");
    148      * writer.write(header.toString());
    149      * writer.write("This is just a test");
    150      * writer.close();
    151      * if(!client.completePendingCommand()) // failure
    152      *   return false;
    153      * </pre>
    154      * <p>
    155      * @return True if successfully completed, false if not.
    156      * @exception SMTPConnectionClosedException
    157      *      If the SMTP server prematurely closes the connection as a result
    158      *      of the client being idle or some other reason causing the server
    159      *      to send SMTP reply code 421.  This exception may be caught either
    160      *      as an IOException or independently as itself.
    161      * @exception IOException  If an I/O error occurs while either sending a
    162      *      command to the server or receiving a reply from the server.
    163      ***/
    164     public boolean completePendingCommand() throws IOException
    165     {
    166         return SMTPReply.isPositiveCompletion(getReply());
    167     }
    168
    169
    170     /****
    171      * Login to the SMTP server by sending the HELO command with the
    172      * given hostname as an argument.  Before performing any mail commands,
    173      * you must first login.
    174      * <p>
    175      * @param hostname  The hostname with which to greet the SMTP server.
    176      * @return True if successfully completed, false if not.
    177      * @exception SMTPConnectionClosedException
    178      *      If the SMTP server prematurely closes the connection as a result
    179      *      of the client being idle or some other reason causing the server
    180      *      to send SMTP reply code 421.  This exception may be caught either
    181      *      as an IOException or independently as itself.
    182      * @exception IOException  If an I/O error occurs while either sending a
    183      *      command to the server or receiving a reply from the server.
    184      ***/
    185     public boolean login(String hostname) throws IOException
    186     {
    187         return SMTPReply.isPositiveCompletion(helo(hostname));
    188     }
    189
    190
    191     /****
    192      * Login to the SMTP server by sending the HELO command with the
    193      * client hostname as an argument.  Before performing any mail commands,
    194      * you must first login.
    195      * <p>
    196      * @return True if successfully completed, false if not.
    197      * @exception SMTPConnectionClosedException
    198      *      If the SMTP server prematurely closes the connection as a result
    199      *      of the client being idle or some other reason causing the server
    200      *      to send SMTP reply code 421.  This exception may be caught either
    201      *      as an IOException or independently as itself.
    202      * @exception IOException  If an I/O error occurs while either sending a
    203      *      command to the server or receiving a reply from the server.
    204      ***/
    205     public boolean login() throws IOException
    206     {
    207         String name;
    208         InetAddress host;
    209
    210         host = getLocalAddress();
    211         name = host.getHostName();
    212
    213         if (name == null)
    214             return false;
    215
    216         return SMTPReply.isPositiveCompletion(helo(name));
    217     }
    218
    219
    220     /****
    221      * Set the sender of a message using the SMTP MAIL command, specifying
    222      * a reverse relay path.  The sender must be set first before any
    223      * recipients may be specified, otherwise the mail server will reject
    224      * your commands.
    225      * <p>
    226      * @param path  The reverse relay path pointing back to the sender.
    227      * @return True if successfully completed, false if not.
    228      * @exception SMTPConnectionClosedException
    229      *      If the SMTP server prematurely closes the connection as a result
    230      *      of the client being idle or some other reason causing the server
    231      *      to send SMTP reply code 421.  This exception may be caught either
    232      *      as an IOException or independently as itself.
    233      * @exception IOException  If an I/O error occurs while either sending a
    234      *      command to the server or receiving a reply from the server.
    235      ***/
    236     public boolean setSender(RelayPath path) throws IOException
    237     {
    238         return SMTPReply.isPositiveCompletion(mail(path.toString()));
    239     }
    240
    241
    242     /****
    243      * Set the sender of a message using the SMTP MAIL command, specifying
    244      * the sender's email address. The sender must be set first before any
    245      * recipients may be specified, otherwise the mail server will reject
    246      * your commands.
    247      * <p>
    248      * @param address  The sender's email address.
    249      * @return True if successfully completed, false if not.
    250      * @exception SMTPConnectionClosedException
    251      *      If the SMTP server prematurely closes the connection as a result
    252      *      of the client being idle or some other reason causing the server
    253      *      to send SMTP reply code 421.  This exception may be caught either
    254      *      as an IOException or independently as itself.
    255      * @exception IOException  If an I/O error occurs while either sending a
    256      *      command to the server or receiving a reply from the server.
    257      ***/
    258     public boolean setSender(String address) throws IOException
    259     {
    260         return SMTPReply.isPositiveCompletion(mail("<" + address + ">"));
    261     }
    262
    263
    264     /****
    265      * Add a recipient for a message using the SMTP RCPT command, specifying
    266      * a forward relay path.  The sender must be set first before any
    267      * recipients may be specified, otherwise the mail server will reject
    268      * your commands.
    269      * <p>
    270      * @param path  The forward relay path pointing to the recipient.
    271      * @return True if successfully completed, false if not.
    272      * @exception SMTPConnectionClosedException
    273      *      If the SMTP server prematurely closes the connection as a result
    274      *      of the client being idle or some other reason causing the server
    275      *      to send SMTP reply code 421.  This exception may be caught either
    276      *      as an IOException or independently as itself.
    277      * @exception IOException  If an I/O error occurs while either sending a
    278      *      command to the server or receiving a reply from the server.
    279      ***/
    280     public boolean addRecipient(RelayPath path) throws IOException
    281     {
    282         return SMTPReply.isPositiveCompletion(rcpt(path.toString()));
    283     }
    284
    285
    286     /****
    287      * Add a recipient for a message using the SMTP RCPT command, the
    288      * recipient's email address.  The sender must be set first before any
    289      * recipients may be specified, otherwise the mail server will reject
    290      * your commands.
    291      * <p>
    292      * @param address  The recipient's email address.
    293      * @return True if successfully completed, false if not.
    294      * @exception SMTPConnectionClosedException
    295      *      If the SMTP server prematurely closes the connection as a result
    296      *      of the client being idle or some other reason causing the server
    297      *      to send SMTP reply code 421.  This exception may be caught either
    298      *      as an IOException or independently as itself.
    299      * @exception IOException  If an I/O error occurs while either sending a
    300      *      command to the server or receiving a reply from the server.
    301      ***/
    302     public boolean addRecipient(String address) throws IOException
    303     {
    304         return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">"));
    305     }
    306
    307
    308
    309     /****
    310      * Send the SMTP DATA command in preparation to send an email message.
    311      * This method returns a DotTerminatedMessageWriter instance to which
    312      * the message can be written.  Null is returned if the DATA command
    313      * fails.
    314      * <p>
    315      * You must not issue any commands to the SMTP server (i.e., call any
    316      * (other methods) until you finish writing to the returned Writer
    317      * instance and close it.  The SMTP protocol uses the same stream for
    318      * issuing commands as it does for returning results.  Therefore the
    319      * returned Writer actually writes directly to the SMTP connection.
    320      * After you close the writer, you can execute new commands.  If you
    321      * do not follow these requirements your program will not work properly.
    322      * <p>
    323      * You can use the provided
    324      * {@link org.apache.commons.net.smtp.SimpleSMTPHeader}
    325      * class to construct a bare minimum header.
    326      * To construct more complicated headers you should
    327      * refer to RFC 822.  When the Java Mail API is finalized, you will be
    328      * able to use it to compose fully compliant Internet text messages.
    329      * The DotTerminatedMessageWriter takes care of doubling line-leading
    330      * dots and ending the message with a single dot upon closing, so all
    331      * you have to worry about is writing the header and the message.
    332      * <p>
    333      * Upon closing the returned Writer, you need to call
    334      * {@link #completePendingCommand  completePendingCommand() }
    335      * to finalize the transaction and verify its success or failure from
    336      * the server reply.
    337      * <p>
    338      * @return A DotTerminatedMessageWriter to which the message (including
    339      *      header) can be written.  Returns null if the command fails.
    340      * @exception SMTPConnectionClosedException
    341      *      If the SMTP server prematurely closes the connection as a result
    342      *      of the client being idle or some other reason causing the server
    343      *      to send SMTP reply code 421.  This exception may be caught either
    344      *      as an IOException or independently as itself.
    345      * @exception IOException  If an I/O error occurs while either sending a
    346      *      command to the server or receiving a reply from the server.
    347      ***/
    348     public Writer sendMessageData() throws IOException
    349     {
    350         if (!SMTPReply.isPositiveIntermediate(data()))
    351             return null;
    352
    353         return new DotTerminatedMessageWriter(_writer);
    354     }
    355
    356
    357     /****
    358      * A convenience method for sending short messages.  This method fetches
    359      * the Writer returned by {@link #sendMessageData  sendMessageData() }
    360      * and writes the specified String to it.  After writing the message,
    361      * this method calls {@link #completePendingCommand completePendingCommand() }
    362      *  to finalize the transaction and returns
    363      * its success or failure.
    364      * <p>
    365      * @param message  The short email message to send.
    366      * @return True if successfully completed, false if not.
    367      * @exception SMTPConnectionClosedException
    368      *      If the SMTP server prematurely closes the connection as a result
    369      *      of the client being idle or some other reason causing the server
    370      *      to send SMTP reply code 421.  This exception may be caught either
    371      *      as an IOException or independently as itself.
    372      * @exception IOException  If an I/O error occurs while either sending a
    373      *      command to the server or receiving a reply from the server.
    374      ***/
    375     public boolean sendShortMessageData(String message) throws IOException
    376     {
    377         Writer writer;
    378
    379         writer = sendMessageData();
    380
    381         if (writer == null)
    382             return false;
    383
    384         writer.write(message);
    385         writer.close();
    386
    387         return completePendingCommand();
    388     }
    389
    390
    391     /****
    392      * A convenience method for a sending short email without having to
    393      * explicitly set the sender and recipient(s).  This method
    394      * sets the sender and recipient using
    395      * {@link #setSender  setSender } and
    396      * {@link #addRecipient  addRecipient }, and then sends the
    397      * message using {@link #sendShortMessageData  sendShortMessageData }.
    398      * <p>
    399      * @param sender  The email address of the sender.
    400      * @param recipient  The email address of the recipient.
    401      * @param message  The short email message to send.
    402      * @return True if successfully completed, false if not.
    403      * @exception SMTPConnectionClosedException
    404      *      If the SMTP server prematurely closes the connection as a result
    405      *      of the client being idle or some other reason causing the server
    406      *      to send SMTP reply code 421.  This exception may be caught either
    407      *      as an IOException or independently as itself.
    408      * @exception IOException  If an I/O error occurs while either sending a
    409      *      command to the server or receiving a reply from the server.
    410      ***/
    411     public boolean sendSimpleMessage(String sender, String recipient,
    412                                      String message)
    413     throws IOException
    414     {
    415         if (!setSender(sender))
    416             return false;
    417
    418         if (!addRecipient(recipient))
    419             return false;
    420
    421         return sendShortMessageData(message);
    422     }
    423
    424
    425
    426     /****
    427      * A convenience method for a sending short email without having to
    428      * explicitly set the sender and recipient(s).  This method
    429      * sets the sender and recipients using
    430      * {@link #setSender  setSender } and
    431      * {@link #addRecipient  addRecipient }, and then sends the
    432      * message using {@link #sendShortMessageData  sendShortMessageData }.
    433      * <p>
    434      * @param sender  The email address of the sender.
    435      * @param recipients  An array of recipient email addresses.
    436      * @param message  The short email message to send.
    437      * @return True if successfully completed, false if not.
    438      * @exception SMTPConnectionClosedException
    439      *      If the SMTP server prematurely closes the connection as a result
    440      *      of the client being idle or some other reason causing the server
    441      *      to send SMTP reply code 421.  This exception may be caught either
    442      *      as an IOException or independently as itself.
    443      * @exception IOException  If an I/O error occurs while either sending a
    444      *      command to the server or receiving a reply from the server.
    445      ***/
    446     public boolean sendSimpleMessage(String sender, String[] recipients,
    447                                      String message)
    448     throws IOException
    449     {
    450         boolean oneSuccess = false;
    451         int count;
    452
    453         if (!setSender(sender))
    454             return false;
    455
    456         for (count = 0; count < recipients.length; count++)
    457         {
    458             if (addRecipient(recipients[count]))
    459                 oneSuccess = true;
    460         }
    461
    462         if (!oneSuccess)
    463             return false;
    464
    465         return sendShortMessageData(message);
    466     }
    467
    468
    469     /****
    470      * Logout of the SMTP server by sending the QUIT command.
    471      * <p>
    472      * @return True if successfully completed, false if not.
    473      * @exception SMTPConnectionClosedException
    474      *      If the SMTP server prematurely closes the connection as a result
    475      *      of the client being idle or some other reason causing the server
    476      *      to send SMTP reply code 421.  This exception may be caught either
    477      *      as an IOException or independently as itself.
    478      * @exception IOException  If an I/O error occurs while either sending a
    479      *      command to the server or receiving a reply from the server.
    480      ***/
    481     public boolean logout() throws IOException
    482     {
    483         return SMTPReply.isPositiveCompletion(quit());
    484     }
    485
    486
    487
    488     /****
    489      * Aborts the current mail transaction, resetting all server stored
    490      * sender, recipient, and mail data, cleaing all buffers and tables.
    491      * <p>
    492      * @return True if successfully completed, false if not.
    493      * @exception SMTPConnectionClosedException
    494      *      If the SMTP server prematurely closes the connection as a result
    495      *      of the client being idle or some other reason causing the server
    496      *      to send SMTP reply code 421.  This exception may be caught either
    497      *      as an IOException or independently as itself.
    498      * @exception IOException  If an I/O error occurs while either sending a
    499      *      command to the server or receiving a reply from the server.
    500      ***/
    501     public boolean reset() throws IOException
    502     {
    503         return SMTPReply.isPositiveCompletion(rset());
    504     }
    505
    506
    507     /****
    508      * Verify that a username or email address is valid, i.e., that mail
    509      * can be delivered to that mailbox on the server.
    510      * <p>
    511      * @param username  The username or email address to validate.
    512      * @return True if the username is valid, false if not.
    513      * @exception SMTPConnectionClosedException
    514      *      If the SMTP server prematurely closes the connection as a result
    515      *      of the client being idle or some other reason causing the server
    516      *      to send SMTP reply code 421.  This exception may be caught either
    517      *      as an IOException or independently as itself.
    518      * @exception IOException  If an I/O error occurs while either sending a
    519      *      command to the server or receiving a reply from the server.
    520      ***/
    521     public boolean verify(String username) throws IOException
    522     {
    523         int result;
    524
    525         result = vrfy(username);
    526
    527         return (result == SMTPReply.ACTION_OK ||
    528                 result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD);
    529     }
    530
    531
    532     /****
    533      * Fetches the system help information from the server and returns the
    534      * full string.
    535      * <p>
    536      * @return The system help string obtained from the server.  null if the
    537      *       information could not be obtained.
    538      * @exception SMTPConnectionClosedException
    539      *      If the SMTP server prematurely closes the connection as a result
    540      *      of the client being idle or some other reason causing the server
    541      *      to send SMTP reply code 421.  This exception may be caught either
    542      *      as an IOException or independently as itself.
    543      * @exception IOException  If an I/O error occurs while either sending a
    544      *  command to the server or receiving a reply from the server.
    545      ***/
    546     public String listHelp() throws IOException
    547     {
    548         if (SMTPReply.isPositiveCompletion(help()))
    549             return getReplyString();
    550         return null;
    551     }
    552
    553
    554     /****
    555      * Fetches the help information for a given command from the server and
    556      * returns the full string.
    557      * <p>
    558      * @param command The command on which to ask for help.
    559      * @return The command help string obtained from the server.  null if the
    560      *       information could not be obtained.
    561      * @exception SMTPConnectionClosedException
    562      *      If the SMTP server prematurely closes the connection as a result
    563      *      of the client being idle or some other reason causing the server
    564      *      to send SMTP reply code 421.  This exception may be caught either
    565      *      as an IOException or independently as itself.
    566      * @exception IOException  If an I/O error occurs while either sending a
    567      *  command to the server or receiving a reply from the server.
    568      ***/
    569     public String listHelp(String command) throws IOException
    570     {
    571         if (SMTPReply.isPositiveCompletion(help(command)))
    572             return getReplyString();
    573         return null;
    574     }
    575
    576
    577     /****
    578      * Sends a NOOP command to the SMTP server.  This is useful for preventing
    579      * server timeouts.
    580      * <p>
    581      * @return True if successfully completed, false if not.
    582      * @exception SMTPConnectionClosedException
    583      *      If the SMTP server prematurely closes the connection as a result
    584      *      of the client being idle or some other reason causing the server
    585      *      to send SMTP reply code 421.  This exception may be caught either
    586      *      as an IOException or independently as itself.
    587      * @exception IOException  If an I/O error occurs while either sending a
    588      *      command to the server or receiving a reply from the server.
    589      ***/
    590     public boolean sendNoOp() throws IOException
    591     {
    592         return SMTPReply.isPositiveCompletion(noop());
    593     }
    594
    595 }