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 }