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 * <p>
117 * <p>
118 * @see SMTP
119 * @see SimpleSMTPHeader
120 * @see RelayPath
121 * @see SMTPConnectionClosedException
122 * @see org.apache.commons.net.MalformedServerReplyException
123 ***/
124
125public 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}