View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.smtp;
19  
20  import java.io.IOException;
21  import java.io.Writer;
22  import java.net.InetAddress;
23  
24  import org.apache.commons.net.io.DotTerminatedMessageWriter;
25  
26  /***
27   * SMTPClient encapsulates all the functionality necessary to send files
28   * through an SMTP server.  This class takes care of all
29   * low level details of interacting with an SMTP server and provides
30   * a convenient higher level interface.  As with all classes derived
31   * from {@link org.apache.commons.net.SocketClient},
32   * you must first connect to the server with
33   * {@link org.apache.commons.net.SocketClient#connect  connect }
34   * before doing anything, and finally
35   * {@link org.apache.commons.net.SocketClient#disconnect  disconnect }
36   * after you're completely finished interacting with the server.
37   * Then you need to check the SMTP reply code to see if the connection
38   * was successful.  For example:
39   * <pre>
40   *    try {
41   *      int reply;
42   *      client.connect("mail.foobar.com");
43   *      System.out.print(client.getReplyString());
44   *
45   *      // After connection attempt, you should check the reply code to verify
46   *      // success.
47   *      reply = client.getReplyCode();
48   *
49   *      if(!SMTPReply.isPositiveCompletion(reply)) {
50   *        client.disconnect();
51   *        System.err.println("SMTP server refused connection.");
52   *        System.exit(1);
53   *      }
54   *
55   *      // Do useful stuff here.
56   *      ...
57   *    } catch(IOException e) {
58   *      if(client.isConnected()) {
59   *        try {
60   *          client.disconnect();
61   *        } catch(IOException f) {
62   *          // do nothing
63   *        }
64   *      }
65   *      System.err.println("Could not connect to server.");
66   *      e.printStackTrace();
67   *      System.exit(1);
68   *    }
69   * </pre>
70   * <p>
71   * Immediately after connecting is the only real time you need to check the
72   * reply code (because connect is of type void).  The convention for all the
73   * SMTP command methods in SMTPClient is such that they either return a
74   * boolean value or some other value.
75   * The boolean methods return true on a successful completion reply from
76   * the SMTP server and false on a reply resulting in an error condition or
77   * failure.  The methods returning a value other than boolean return a value
78   * containing the higher level data produced by the SMTP command, or null if a
79   * reply resulted in an error condition or failure.  If you want to access
80   * the exact SMTP reply code causing a success or failure, you must call
81   * {@link org.apache.commons.net.smtp.SMTP#getReplyCode  getReplyCode } after
82   * a success or failure.
83   * <p>
84   * You should keep in mind that the SMTP server may choose to prematurely
85   * close a connection for various reasons.  The SMTPClient class will detect a
86   * premature SMTP server connection closing when it receives a
87   * {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE }
88   *  response to a command.
89   * When that occurs, the method encountering that reply will throw
90   * an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
91   * .
92   * <code>SMTPConectionClosedException</code>
93   * is a subclass of <code> IOException </code> and therefore need not be
94   * caught separately, but if you are going to catch it separately, its
95   * catch block must appear before the more general <code> IOException </code>
96   * catch block.  When you encounter an
97   * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
98   * , you must disconnect the connection with
99   * {@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 
125 public 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 }