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 }