001    /*
002     * Copyright 2001-2005 The Apache Software Foundation
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.apache.commons.net.smtp;
017    
018    import java.io.IOException;
019    import java.io.Writer;
020    import java.net.InetAddress;
021    import org.apache.commons.net.io.DotTerminatedMessageWriter;
022    
023    /***
024     * SMTPClient encapsulates all the functionality necessary to send files
025     * through an SMTP server.  This class takes care of all
026     * low level details of interacting with an SMTP server and provides
027     * a convenient higher level interface.  As with all classes derived
028     * from {@link org.apache.commons.net.SocketClient},
029     * you must first connect to the server with
030     * {@link org.apache.commons.net.SocketClient#connect  connect }
031     * before doing anything, and finally
032     * {@link org.apache.commons.net.SocketClient#disconnect  disconnect }
033     * after you're completely finished interacting with the server.
034     * Then you need to check the SMTP reply code to see if the connection
035     * was successful.  For example:
036     * <pre>
037     *    try {
038     *      int reply;
039     *      client.connect("mail.foobar.com");
040     *      System.out.print(client.getReplyString());
041     *
042     *      // After connection attempt, you should check the reply code to verify
043     *      // success.
044     *      reply = client.getReplyCode();
045     *
046     *      if(!SMTPReply.isPositiveCompletion(reply)) {
047     *        client.disconnect();
048     *        System.err.println("SMTP server refused connection.");
049     *        System.exit(1);
050     *      }
051     *
052     *      // Do useful stuff here.
053     *      ...
054     *    } catch(IOException e) {
055     *      if(client.isConnected()) {
056     *        try {
057     *          client.disconnect();
058     *        } catch(IOException f) {
059     *          // do nothing
060     *        }
061     *      }
062     *      System.err.println("Could not connect to server.");
063     *      e.printStackTrace();
064     *      System.exit(1);
065     *    }
066     * </pre>
067     * <p>
068     * Immediately after connecting is the only real time you need to check the
069     * reply code (because connect is of type void).  The convention for all the
070     * SMTP command methods in SMTPClient is such that they either return a
071     * boolean value or some other value.
072     * The boolean methods return true on a successful completion reply from
073     * the SMTP server and false on a reply resulting in an error condition or
074     * failure.  The methods returning a value other than boolean return a value
075     * containing the higher level data produced by the SMTP command, or null if a
076     * reply resulted in an error condition or failure.  If you want to access
077     * the exact SMTP reply code causing a success or failure, you must call
078     * {@link org.apache.commons.net.smtp.SMTP#getReplyCode  getReplyCode } after
079     * a success or failure.
080     * <p>
081     * You should keep in mind that the SMTP server may choose to prematurely
082     * close a connection for various reasons.  The SMTPClient class will detect a
083     * premature SMTP server connection closing when it receives a
084     * {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE }
085     *  response to a command.
086     * When that occurs, the method encountering that reply will throw
087     * an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
088     * .
089     * <code>SMTPConectionClosedException</code>
090     * is a subclass of <code> IOException </code> and therefore need not be
091     * caught separately, but if you are going to catch it separately, its
092     * catch block must appear before the more general <code> IOException </code>
093     * catch block.  When you encounter an
094     * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
095     * , you must disconnect the connection with
096     * {@link #disconnect  disconnect() } to properly clean up the
097     * system resources used by SMTPClient.  Before disconnecting, you may check
098     * the last reply code and text with
099     * {@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    }