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