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(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(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(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(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(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(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(String message) throws IOException
390    {
391        Writer writer;
392
393        writer = sendMessageData();
394
395        if (writer == null) {
396            return false;
397        }
398
399        writer.write(message);
400        writer.close();
401
402        return completePendingCommand();
403    }
404
405
406    /***
407     * A convenience method for a sending short email without having to
408     * explicitly set the sender and recipient(s).  This method
409     * sets the sender and recipient using
410     * {@link #setSender  setSender } and
411     * {@link #addRecipient  addRecipient }, and then sends the
412     * message using {@link #sendShortMessageData  sendShortMessageData }.
413     * <p>
414     * @param sender  The email address of the sender.
415     * @param recipient  The email address of the recipient.
416     * @param message  The short email message to send.
417     * This must include the headers and the body, but not the trailing "."
418     * @return True if successfully completed, false if not.
419     * @throws SMTPConnectionClosedException
420     *      If the SMTP server prematurely closes the connection as a result
421     *      of the client being idle or some other reason causing the server
422     *      to send SMTP reply code 421.  This exception may be caught either
423     *      as an IOException or independently as itself.
424     * @throws IOException  If an I/O error occurs while either sending a
425     *      command to the server or receiving a reply from the server.
426     ***/
427    public boolean sendSimpleMessage(String sender, String recipient,
428                                     String message)
429    throws IOException
430    {
431        if (!setSender(sender)) {
432            return false;
433        }
434
435        if (!addRecipient(recipient)) {
436            return false;
437        }
438
439        return sendShortMessageData(message);
440    }
441
442
443
444    /***
445     * A convenience method for a sending short email without having to
446     * explicitly set the sender and recipient(s).  This method
447     * sets the sender and recipients using
448     * {@link #setSender(String)  setSender} and
449     * {@link #addRecipient(String)  addRecipient}, and then sends the
450     * message using {@link #sendShortMessageData(String)  sendShortMessageData}.
451     * <p>
452     * Note that the method ignores failures when calling
453     * {@link #addRecipient(String)  addRecipient} so long as
454     * at least one call succeeds. If no recipients can be successfully
455     * added then the method will fail (and does not attempt to
456     * send the message)
457     * <p>
458     * @param sender  The email address of the sender.
459     * @param recipients  An array of recipient email addresses.
460     * @param message  The short email message to send.
461     * This must include the headers and the body, but not the trailing "."
462     * @return True if successfully completed, false if not.
463     * @throws SMTPConnectionClosedException
464     *      If the SMTP server prematurely closes the connection as a result
465     *      of the client being idle or some other reason causing the server
466     *      to send SMTP reply code 421.  This exception may be caught either
467     *      as an IOException or independently as itself.
468     * @throws IOException  If an I/O error occurs while either sending a
469     *      command to the server or receiving a reply from the server.
470     ***/
471    public boolean sendSimpleMessage(String sender, String[] recipients,
472                                     String message)
473    throws IOException
474    {
475        boolean oneSuccess = false;
476        int count;
477
478        if (!setSender(sender)) {
479            return false;
480        }
481
482        for (count = 0; count < recipients.length; count++)
483        {
484            if (addRecipient(recipients[count])) {
485                oneSuccess = true;
486            }
487        }
488
489        if (!oneSuccess) {
490            return false;
491        }
492
493        return sendShortMessageData(message);
494    }
495
496
497    /***
498     * Logout of the SMTP server by sending the QUIT command.
499     * <p>
500     * @return True if successfully completed, false if not.
501     * @throws SMTPConnectionClosedException
502     *      If the SMTP server prematurely closes the connection as a result
503     *      of the client being idle or some other reason causing the server
504     *      to send SMTP reply code 421.  This exception may be caught either
505     *      as an IOException or independently as itself.
506     * @throws IOException  If an I/O error occurs while either sending a
507     *      command to the server or receiving a reply from the server.
508     ***/
509    public boolean logout() throws IOException
510    {
511        return SMTPReply.isPositiveCompletion(quit());
512    }
513
514
515
516    /***
517     * Aborts the current mail transaction, resetting all server stored
518     * sender, recipient, and mail data, cleaing all buffers and tables.
519     * <p>
520     * @return True if successfully completed, false if not.
521     * @throws SMTPConnectionClosedException
522     *      If the SMTP server prematurely closes the connection as a result
523     *      of the client being idle or some other reason causing the server
524     *      to send SMTP reply code 421.  This exception may be caught either
525     *      as an IOException or independently as itself.
526     * @throws IOException  If an I/O error occurs while either sending a
527     *      command to the server or receiving a reply from the server.
528     ***/
529    public boolean reset() throws IOException
530    {
531        return SMTPReply.isPositiveCompletion(rset());
532    }
533
534
535    /***
536     * Verify that a username or email address is valid, i.e., that mail
537     * can be delivered to that mailbox on the server.
538     * <p>
539     * @param username  The username or email address to validate.
540     * @return True if the username is valid, false if not.
541     * @throws SMTPConnectionClosedException
542     *      If the SMTP server prematurely closes the connection as a result
543     *      of the client being idle or some other reason causing the server
544     *      to send SMTP reply code 421.  This exception may be caught either
545     *      as an IOException or independently as itself.
546     * @throws IOException  If an I/O error occurs while either sending a
547     *      command to the server or receiving a reply from the server.
548     ***/
549    public boolean verify(String username) throws IOException
550    {
551        int result;
552
553        result = vrfy(username);
554
555        return (result == SMTPReply.ACTION_OK ||
556                result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD);
557    }
558
559
560    /***
561     * Fetches the system help information from the server and returns the
562     * full string.
563     * <p>
564     * @return The system help string obtained from the server.  null if the
565     *       information could not be obtained.
566     * @throws SMTPConnectionClosedException
567     *      If the SMTP server prematurely closes the connection as a result
568     *      of the client being idle or some other reason causing the server
569     *      to send SMTP reply code 421.  This exception may be caught either
570     *      as an IOException or independently as itself.
571     * @throws IOException  If an I/O error occurs while either sending a
572     *  command to the server or receiving a reply from the server.
573     ***/
574    public String listHelp() throws IOException
575    {
576        if (SMTPReply.isPositiveCompletion(help())) {
577            return getReplyString();
578        }
579        return null;
580    }
581
582
583    /***
584     * Fetches the help information for a given command from the server and
585     * returns the full string.
586     * <p>
587     * @param command The command on which to ask for help.
588     * @return The command help string obtained from the server.  null if the
589     *       information could not be obtained.
590     * @throws SMTPConnectionClosedException
591     *      If the SMTP server prematurely closes the connection as a result
592     *      of the client being idle or some other reason causing the server
593     *      to send SMTP reply code 421.  This exception may be caught either
594     *      as an IOException or independently as itself.
595     * @throws IOException  If an I/O error occurs while either sending a
596     *  command to the server or receiving a reply from the server.
597     ***/
598    public String listHelp(String command) throws IOException
599    {
600        if (SMTPReply.isPositiveCompletion(help(command))) {
601            return getReplyString();
602        }
603        return null;
604    }
605
606
607    /***
608     * Sends a NOOP command to the SMTP server.  This is useful for preventing
609     * server timeouts.
610     * <p>
611     * @return True if successfully completed, false if not.
612     * @throws SMTPConnectionClosedException
613     *      If the SMTP server prematurely closes the connection as a result
614     *      of the client being idle or some other reason causing the server
615     *      to send SMTP reply code 421.  This exception may be caught either
616     *      as an IOException or independently as itself.
617     * @throws IOException  If an I/O error occurs while either sending a
618     *      command to the server or receiving a reply from the server.
619     ***/
620    public boolean sendNoOp() throws IOException
621    {
622        return SMTPReply.isPositiveCompletion(noop());
623    }
624
625}