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     * @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            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 822.  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         ***/
359        public Writer sendMessageData() throws IOException
360        {
361            if (!SMTPReply.isPositiveIntermediate(data()))
362                return null;
363    
364            return new DotTerminatedMessageWriter(_writer);
365        }
366    
367    
368        /***
369         * A convenience method for sending short messages.  This method fetches
370         * the Writer returned by {@link #sendMessageData  sendMessageData() }
371         * and writes the specified String to it.  After writing the message,
372         * this method calls {@link #completePendingCommand completePendingCommand() }
373         *  to finalize the transaction and returns
374         * its success or failure.
375         * <p>
376         * @param message  The short email message to send.
377         * @return True if successfully completed, false if not.
378         * @exception SMTPConnectionClosedException
379         *      If the SMTP server prematurely closes the connection as a result
380         *      of the client being idle or some other reason causing the server
381         *      to send SMTP reply code 421.  This exception may be caught either
382         *      as an IOException or independently as itself.
383         * @exception IOException  If an I/O error occurs while either sending a
384         *      command to the server or receiving a reply from the server.
385         ***/
386        public boolean sendShortMessageData(String message) throws IOException
387        {
388            Writer writer;
389    
390            writer = sendMessageData();
391    
392            if (writer == null)
393                return false;
394    
395            writer.write(message);
396            writer.close();
397    
398            return completePendingCommand();
399        }
400    
401    
402        /***
403         * A convenience method for a sending short email without having to
404         * explicitly set the sender and recipient(s).  This method
405         * sets the sender and recipient using
406         * {@link #setSender  setSender } and
407         * {@link #addRecipient  addRecipient }, and then sends the
408         * message using {@link #sendShortMessageData  sendShortMessageData }.
409         * <p>
410         * @param sender  The email address of the sender.
411         * @param recipient  The email address of the recipient.
412         * @param message  The short email message to send.
413         * @return True if successfully completed, false if not.
414         * @exception SMTPConnectionClosedException
415         *      If the SMTP server prematurely closes the connection as a result
416         *      of the client being idle or some other reason causing the server
417         *      to send SMTP reply code 421.  This exception may be caught either
418         *      as an IOException or independently as itself.
419         * @exception IOException  If an I/O error occurs while either sending a
420         *      command to the server or receiving a reply from the server.
421         ***/
422        public boolean sendSimpleMessage(String sender, String recipient,
423                                         String message)
424        throws IOException
425        {
426            if (!setSender(sender))
427                return false;
428    
429            if (!addRecipient(recipient))
430                return false;
431    
432            return sendShortMessageData(message);
433        }
434    
435    
436    
437        /***
438         * A convenience method for a sending short email without having to
439         * explicitly set the sender and recipient(s).  This method
440         * sets the sender and recipients using
441         * {@link #setSender  setSender } and
442         * {@link #addRecipient  addRecipient }, and then sends the
443         * message using {@link #sendShortMessageData  sendShortMessageData }.
444         * <p>
445         * @param sender  The email address of the sender.
446         * @param recipients  An array of recipient email addresses.
447         * @param message  The short email message to send.
448         * @return True if successfully completed, false if not.
449         * @exception SMTPConnectionClosedException
450         *      If the SMTP server prematurely closes the connection as a result
451         *      of the client being idle or some other reason causing the server
452         *      to send SMTP reply code 421.  This exception may be caught either
453         *      as an IOException or independently as itself.
454         * @exception IOException  If an I/O error occurs while either sending a
455         *      command to the server or receiving a reply from the server.
456         ***/
457        public boolean sendSimpleMessage(String sender, String[] recipients,
458                                         String message)
459        throws IOException
460        {
461            boolean oneSuccess = false;
462            int count;
463    
464            if (!setSender(sender))
465                return false;
466    
467            for (count = 0; count < recipients.length; count++)
468            {
469                if (addRecipient(recipients[count]))
470                    oneSuccess = true;
471            }
472    
473            if (!oneSuccess)
474                return false;
475    
476            return sendShortMessageData(message);
477        }
478    
479    
480        /***
481         * Logout of the SMTP server by sending the QUIT command.
482         * <p>
483         * @return True if successfully completed, false if not.
484         * @exception SMTPConnectionClosedException
485         *      If the SMTP server prematurely closes the connection as a result
486         *      of the client being idle or some other reason causing the server
487         *      to send SMTP reply code 421.  This exception may be caught either
488         *      as an IOException or independently as itself.
489         * @exception IOException  If an I/O error occurs while either sending a
490         *      command to the server or receiving a reply from the server.
491         ***/
492        public boolean logout() throws IOException
493        {
494            return SMTPReply.isPositiveCompletion(quit());
495        }
496    
497    
498    
499        /***
500         * Aborts the current mail transaction, resetting all server stored
501         * sender, recipient, and mail data, cleaing all buffers and tables.
502         * <p>
503         * @return True if successfully completed, false if not.
504         * @exception SMTPConnectionClosedException
505         *      If the SMTP server prematurely closes the connection as a result
506         *      of the client being idle or some other reason causing the server
507         *      to send SMTP reply code 421.  This exception may be caught either
508         *      as an IOException or independently as itself.
509         * @exception IOException  If an I/O error occurs while either sending a
510         *      command to the server or receiving a reply from the server.
511         ***/
512        public boolean reset() throws IOException
513        {
514            return SMTPReply.isPositiveCompletion(rset());
515        }
516    
517    
518        /***
519         * Verify that a username or email address is valid, i.e., that mail
520         * can be delivered to that mailbox on the server.
521         * <p>
522         * @param username  The username or email address to validate.
523         * @return True if the username is valid, false if not.
524         * @exception SMTPConnectionClosedException
525         *      If the SMTP server prematurely closes the connection as a result
526         *      of the client being idle or some other reason causing the server
527         *      to send SMTP reply code 421.  This exception may be caught either
528         *      as an IOException or independently as itself.
529         * @exception IOException  If an I/O error occurs while either sending a
530         *      command to the server or receiving a reply from the server.
531         ***/
532        public boolean verify(String username) throws IOException
533        {
534            int result;
535    
536            result = vrfy(username);
537    
538            return (result == SMTPReply.ACTION_OK ||
539                    result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD);
540        }
541    
542    
543        /***
544         * Fetches the system help information from the server and returns the
545         * full string.
546         * <p>
547         * @return The system help string obtained from the server.  null if the
548         *       information could not be obtained.
549         * @exception SMTPConnectionClosedException
550         *      If the SMTP server prematurely closes the connection as a result
551         *      of the client being idle or some other reason causing the server
552         *      to send SMTP reply code 421.  This exception may be caught either
553         *      as an IOException or independently as itself.
554         * @exception IOException  If an I/O error occurs while either sending a
555         *  command to the server or receiving a reply from the server.
556         ***/
557        public String listHelp() throws IOException
558        {
559            if (SMTPReply.isPositiveCompletion(help()))
560                return getReplyString();
561            return null;
562        }
563    
564    
565        /***
566         * Fetches the help information for a given command from the server and
567         * returns the full string.
568         * <p>
569         * @param command The command on which to ask for help.
570         * @return The command help string obtained from the server.  null if the
571         *       information could not be obtained.
572         * @exception SMTPConnectionClosedException
573         *      If the SMTP server prematurely closes the connection as a result
574         *      of the client being idle or some other reason causing the server
575         *      to send SMTP reply code 421.  This exception may be caught either
576         *      as an IOException or independently as itself.
577         * @exception IOException  If an I/O error occurs while either sending a
578         *  command to the server or receiving a reply from the server.
579         ***/
580        public String listHelp(String command) throws IOException
581        {
582            if (SMTPReply.isPositiveCompletion(help(command)))
583                return getReplyString();
584            return null;
585        }
586    
587    
588        /***
589         * Sends a NOOP command to the SMTP server.  This is useful for preventing
590         * server timeouts.
591         * <p>
592         * @return True if successfully completed, false if not.
593         * @exception SMTPConnectionClosedException
594         *      If the SMTP server prematurely closes the connection as a result
595         *      of the client being idle or some other reason causing the server
596         *      to send SMTP reply code 421.  This exception may be caught either
597         *      as an IOException or independently as itself.
598         * @exception IOException  If an I/O error occurs while either sending a
599         *      command to the server or receiving a reply from the server.
600         ***/
601        public boolean sendNoOp() throws IOException
602        {
603            return SMTPReply.isPositiveCompletion(noop());
604        }
605    
606    }