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