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 }