001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.net.smtp;
019    
020    import java.io.IOException;
021    import java.io.Writer;
022    import java.net.InetAddress;
023    
024    import org.apache.commons.net.io.DotTerminatedMessageWriter;
025    
026    /***
027     * SMTPClient encapsulates all the functionality necessary to send files
028     * through an SMTP server.  This class takes care of all
029     * low level details of interacting with an SMTP server and provides
030     * a convenient higher level interface.  As with all classes derived
031     * from {@link org.apache.commons.net.SocketClient},
032     * you must first connect to the server with
033     * {@link org.apache.commons.net.SocketClient#connect  connect }
034     * before doing anything, and finally
035     * {@link org.apache.commons.net.SocketClient#disconnect  disconnect }
036     * after you're completely finished interacting with the server.
037     * Then you need to check the SMTP reply code to see if the connection
038     * was successful.  For example:
039     * <pre>
040     *    try {
041     *      int reply;
042     *      client.connect("mail.foobar.com");
043     *      System.out.print(client.getReplyString());
044     *
045     *      // After connection attempt, you should check the reply code to verify
046     *      // success.
047     *      reply = client.getReplyCode();
048     *
049     *      if(!SMTPReply.isPositiveCompletion(reply)) {
050     *        client.disconnect();
051     *        System.err.println("SMTP server refused connection.");
052     *        System.exit(1);
053     *      }
054     *
055     *      // Do useful stuff here.
056     *      ...
057     *    } catch(IOException e) {
058     *      if(client.isConnected()) {
059     *        try {
060     *          client.disconnect();
061     *        } catch(IOException f) {
062     *          // do nothing
063     *        }
064     *      }
065     *      System.err.println("Could not connect to server.");
066     *      e.printStackTrace();
067     *      System.exit(1);
068     *    }
069     * </pre>
070     * <p>
071     * Immediately after connecting is the only real time you need to check the
072     * reply code (because connect is of type void).  The convention for all the
073     * SMTP command methods in SMTPClient is such that they either return a
074     * boolean value or some other value.
075     * The boolean methods return true on a successful completion reply from
076     * the SMTP server and false on a reply resulting in an error condition or
077     * failure.  The methods returning a value other than boolean return a value
078     * containing the higher level data produced by the SMTP command, or null if a
079     * reply resulted in an error condition or failure.  If you want to access
080     * the exact SMTP reply code causing a success or failure, you must call
081     * {@link org.apache.commons.net.smtp.SMTP#getReplyCode  getReplyCode } after
082     * a success or failure.
083     * <p>
084     * You should keep in mind that the SMTP server may choose to prematurely
085     * close a connection for various reasons.  The SMTPClient class will detect a
086     * premature SMTP server connection closing when it receives a
087     * {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE }
088     *  response to a command.
089     * When that occurs, the method encountering that reply will throw
090     * an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
091     * .
092     * <code>SMTPConectionClosedException</code>
093     * is a subclass of <code> IOException </code> and therefore need not be
094     * caught separately, but if you are going to catch it separately, its
095     * catch block must appear before the more general <code> IOException </code>
096     * catch block.  When you encounter an
097     * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
098     * , you must disconnect the connection with
099     * {@link #disconnect  disconnect() } to properly clean up the
100     * system resources used by SMTPClient.  Before disconnecting, you may check
101     * the last reply code and text with
102     * {@link org.apache.commons.net.smtp.SMTP#getReplyCode  getReplyCode },
103     * {@link org.apache.commons.net.smtp.SMTP#getReplyString  getReplyString },
104     * and
105     * {@link org.apache.commons.net.smtp.SMTP#getReplyStrings getReplyStrings}.
106     * <p>
107     * Rather than list it separately for each method, we mention here that
108     * every method communicating with the server and throwing an IOException
109     * can also throw a
110     * {@link org.apache.commons.net.MalformedServerReplyException}
111     * , which is a subclass
112     * of IOException.  A MalformedServerReplyException will be thrown when
113     * the reply received from the server deviates enough from the protocol
114     * specification that it cannot be interpreted in a useful manner despite
115     * attempts to be as lenient as possible.
116     * <p>
117     * <p>
118     * @author Daniel F. Savarese
119     * @see SMTP
120     * @see SimpleSMTPHeader
121     * @see RelayPath
122     * @see SMTPConnectionClosedException
123     * @see org.apache.commons.net.MalformedServerReplyException
124     ***/
125    
126    public class SMTPClient extends SMTP
127    {
128    
129        /**
130         * Default SMTPClient constructor.  Creates a new SMTPClient instance.
131         */
132        public SMTPClient() {  }
133        
134        /**
135         * Overloaded constructor that takes an encoding specification
136         * @param encoding The encoding to use
137         * @since 2.0
138         */
139        public SMTPClient(String encoding) {
140            super(encoding);
141        }
142    
143    
144        /***
145         * At least one SMTPClient method ({@link #sendMessageData  sendMessageData })
146         * does not complete the entire sequence of SMTP commands to complete a
147         * transaction.  These types of commands require some action by the
148         * programmer after the reception of a positive intermediate command.
149         * After the programmer's code completes its actions, it must call this
150         * method to receive the completion reply from the server and verify the
151         * success of the entire transaction.
152         * <p>
153         * For example,
154         * <pre>
155         * writer = client.sendMessage();
156         * if(writer == null) // failure
157         *   return false;
158         * header =
159         *  new SimpleSMTPHeader("foobar@foo.com", "foo@foobar.com", "Re: Foo");
160         * writer.write(header.toString());
161         * writer.write("This is just a test");
162         * writer.close();
163         * if(!client.completePendingCommand()) // failure
164         *   return false;
165         * </pre>
166         * <p>
167         * @return True if successfully completed, false if not.
168         * @exception SMTPConnectionClosedException
169         *      If the SMTP server prematurely closes the connection as a result
170         *      of the client being idle or some other reason causing the server
171         *      to send SMTP reply code 421.  This exception may be caught either
172         *      as an IOException or independently as itself.
173         * @exception IOException  If an I/O error occurs while either sending a
174         *      command to the server or receiving a reply from the server.
175         ***/
176        public boolean completePendingCommand() throws IOException
177        {
178            return SMTPReply.isPositiveCompletion(getReply());
179        }
180    
181    
182        /***
183         * Login to the SMTP server by sending the HELO command with the
184         * given hostname as an argument.  Before performing any mail commands,
185         * you must first login.
186         * <p>
187         * @param hostname  The hostname with which to greet the SMTP server.
188         * @return True if successfully completed, false if not.
189         * @exception SMTPConnectionClosedException
190         *      If the SMTP server prematurely closes the connection as a result
191         *      of the client being idle or some other reason causing the server
192         *      to send SMTP reply code 421.  This exception may be caught either
193         *      as an IOException or independently as itself.
194         * @exception IOException  If an I/O error occurs while either sending a
195         *      command to the server or receiving a reply from the server.
196         ***/
197        public boolean login(String hostname) throws IOException
198        {
199            return SMTPReply.isPositiveCompletion(helo(hostname));
200        }
201    
202    
203        /***
204         * Login to the SMTP server by sending the HELO command with the
205         * client hostname as an argument.  Before performing any mail commands,
206         * you must first login.
207         * <p>
208         * @return True if successfully completed, false if not.
209         * @exception SMTPConnectionClosedException
210         *      If the SMTP server prematurely closes the connection as a result
211         *      of the client being idle or some other reason causing the server
212         *      to send SMTP reply code 421.  This exception may be caught either
213         *      as an IOException or independently as itself.
214         * @exception IOException  If an I/O error occurs while either sending a
215         *      command to the server or receiving a reply from the server.
216         ***/
217        public boolean login() throws IOException
218        {
219            String name;
220            InetAddress host;
221    
222            host = getLocalAddress();
223            name = host.getHostName();
224    
225            if (name == null)
226                return false;
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            return new DotTerminatedMessageWriter(_writer);
366        }
367    
368    
369        /***
370         * A convenience method for sending short messages.  This method fetches
371         * the Writer returned by {@link #sendMessageData  sendMessageData() }
372         * and writes the specified String to it.  After writing the message,
373         * this method calls {@link #completePendingCommand completePendingCommand() }
374         *  to finalize the transaction and returns
375         * its success or failure.
376         * <p>
377         * @param message  The short email message to send.
378         * @return True if successfully completed, false if not.
379         * @exception SMTPConnectionClosedException
380         *      If the SMTP server prematurely closes the connection as a result
381         *      of the client being idle or some other reason causing the server
382         *      to send SMTP reply code 421.  This exception may be caught either
383         *      as an IOException or independently as itself.
384         * @exception IOException  If an I/O error occurs while either sending a
385         *      command to the server or receiving a reply from the server.
386         ***/
387        public boolean sendShortMessageData(String message) throws IOException
388        {
389            Writer writer;
390    
391            writer = sendMessageData();
392    
393            if (writer == null)
394                return false;
395    
396            writer.write(message);
397            writer.close();
398    
399            return completePendingCommand();
400        }
401    
402    
403        /***
404         * A convenience method for a sending short email without having to
405         * explicitly set the sender and recipient(s).  This method
406         * sets the sender and recipient using
407         * {@link #setSender  setSender } and
408         * {@link #addRecipient  addRecipient }, and then sends the
409         * message using {@link #sendShortMessageData  sendShortMessageData }.
410         * <p>
411         * @param sender  The email address of the sender.
412         * @param recipient  The email address of the recipient.
413         * @param message  The short email message to send.
414         * @return True if successfully completed, false if not.
415         * @exception SMTPConnectionClosedException
416         *      If the SMTP server prematurely closes the connection as a result
417         *      of the client being idle or some other reason causing the server
418         *      to send SMTP reply code 421.  This exception may be caught either
419         *      as an IOException or independently as itself.
420         * @exception IOException  If an I/O error occurs while either sending a
421         *      command to the server or receiving a reply from the server.
422         ***/
423        public boolean sendSimpleMessage(String sender, String recipient,
424                                         String message)
425        throws IOException
426        {
427            if (!setSender(sender))
428                return false;
429    
430            if (!addRecipient(recipient))
431                return false;
432    
433            return sendShortMessageData(message);
434        }
435    
436    
437    
438        /***
439         * A convenience method for a sending short email without having to
440         * explicitly set the sender and recipient(s).  This method
441         * sets the sender and recipients using
442         * {@link #setSender  setSender } and
443         * {@link #addRecipient  addRecipient }, and then sends the
444         * message using {@link #sendShortMessageData  sendShortMessageData }.
445         * <p>
446         * @param sender  The email address of the sender.
447         * @param recipients  An array of recipient email addresses.
448         * @param message  The short email message to send.
449         * @return True if successfully completed, false if not.
450         * @exception SMTPConnectionClosedException
451         *      If the SMTP server prematurely closes the connection as a result
452         *      of the client being idle or some other reason causing the server
453         *      to send SMTP reply code 421.  This exception may be caught either
454         *      as an IOException or independently as itself.
455         * @exception IOException  If an I/O error occurs while either sending a
456         *      command to the server or receiving a reply from the server.
457         ***/
458        public boolean sendSimpleMessage(String sender, String[] recipients,
459                                         String message)
460        throws IOException
461        {
462            boolean oneSuccess = false;
463            int count;
464    
465            if (!setSender(sender))
466                return false;
467    
468            for (count = 0; count < recipients.length; count++)
469            {
470                if (addRecipient(recipients[count]))
471                    oneSuccess = true;
472            }
473    
474            if (!oneSuccess)
475                return false;
476    
477            return sendShortMessageData(message);
478        }
479    
480    
481        /***
482         * Logout of the SMTP server by sending the QUIT command.
483         * <p>
484         * @return True if successfully completed, false if not.
485         * @exception SMTPConnectionClosedException
486         *      If the SMTP server prematurely closes the connection as a result
487         *      of the client being idle or some other reason causing the server
488         *      to send SMTP reply code 421.  This exception may be caught either
489         *      as an IOException or independently as itself.
490         * @exception IOException  If an I/O error occurs while either sending a
491         *      command to the server or receiving a reply from the server.
492         ***/
493        public boolean logout() throws IOException
494        {
495            return SMTPReply.isPositiveCompletion(quit());
496        }
497    
498    
499    
500        /***
501         * Aborts the current mail transaction, resetting all server stored
502         * sender, recipient, and mail data, cleaing all buffers and tables.
503         * <p>
504         * @return True if successfully completed, false if not.
505         * @exception SMTPConnectionClosedException
506         *      If the SMTP server prematurely closes the connection as a result
507         *      of the client being idle or some other reason causing the server
508         *      to send SMTP reply code 421.  This exception may be caught either
509         *      as an IOException or independently as itself.
510         * @exception IOException  If an I/O error occurs while either sending a
511         *      command to the server or receiving a reply from the server.
512         ***/
513        public boolean reset() throws IOException
514        {
515            return SMTPReply.isPositiveCompletion(rset());
516        }
517    
518    
519        /***
520         * Verify that a username or email address is valid, i.e., that mail
521         * can be delivered to that mailbox on the server.
522         * <p>
523         * @param username  The username or email address to validate.
524         * @return True if the username is valid, false if not.
525         * @exception SMTPConnectionClosedException
526         *      If the SMTP server prematurely closes the connection as a result
527         *      of the client being idle or some other reason causing the server
528         *      to send SMTP reply code 421.  This exception may be caught either
529         *      as an IOException or independently as itself.
530         * @exception IOException  If an I/O error occurs while either sending a
531         *      command to the server or receiving a reply from the server.
532         ***/
533        public boolean verify(String username) throws IOException
534        {
535            int result;
536    
537            result = vrfy(username);
538    
539            return (result == SMTPReply.ACTION_OK ||
540                    result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD);
541        }
542    
543    
544        /***
545         * Fetches the system help information from the server and returns the
546         * full string.
547         * <p>
548         * @return The system help string obtained from the server.  null if the
549         *       information could not be obtained.
550         * @exception SMTPConnectionClosedException
551         *      If the SMTP server prematurely closes the connection as a result
552         *      of the client being idle or some other reason causing the server
553         *      to send SMTP reply code 421.  This exception may be caught either
554         *      as an IOException or independently as itself.
555         * @exception IOException  If an I/O error occurs while either sending a
556         *  command to the server or receiving a reply from the server.
557         ***/
558        public String listHelp() throws IOException
559        {
560            if (SMTPReply.isPositiveCompletion(help()))
561                return getReplyString();
562            return null;
563        }
564    
565    
566        /***
567         * Fetches the help information for a given command from the server and
568         * returns the full string.
569         * <p>
570         * @param command The command on which to ask for help.
571         * @return The command help string obtained from the server.  null if the
572         *       information could not be obtained.
573         * @exception SMTPConnectionClosedException
574         *      If the SMTP server prematurely closes the connection as a result
575         *      of the client being idle or some other reason causing the server
576         *      to send SMTP reply code 421.  This exception may be caught either
577         *      as an IOException or independently as itself.
578         * @exception IOException  If an I/O error occurs while either sending a
579         *  command to the server or receiving a reply from the server.
580         ***/
581        public String listHelp(String command) throws IOException
582        {
583            if (SMTPReply.isPositiveCompletion(help(command)))
584                return getReplyString();
585            return null;
586        }
587    
588    
589        /***
590         * Sends a NOOP command to the SMTP server.  This is useful for preventing
591         * server timeouts.
592         * <p>
593         * @return True if successfully completed, false if not.
594         * @exception SMTPConnectionClosedException
595         *      If the SMTP server prematurely closes the connection as a result
596         *      of the client being idle or some other reason causing the server
597         *      to send SMTP reply code 421.  This exception may be caught either
598         *      as an IOException or independently as itself.
599         * @exception IOException  If an I/O error occurs while either sending a
600         *      command to the server or receiving a reply from the server.
601         ***/
602        public boolean sendNoOp() throws IOException
603        {
604            return SMTPReply.isPositiveCompletion(noop());
605        }
606    
607    }