1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.net.smtp;
17
18 import java.io.IOException;
19 import java.io.Writer;
20 import java.net.InetAddress;
21 import org.apache.commons.net.io.DotTerminatedMessageWriter;
22
23 /***
24 * SMTPClient encapsulates all the functionality necessary to send files
25 * through an SMTP server. This class takes care of all
26 * low level details of interacting with an SMTP server and provides
27 * a convenient higher level interface. As with all classes derived
28 * from {@link org.apache.commons.net.SocketClient},
29 * you must first connect to the server with
30 * {@link org.apache.commons.net.SocketClient#connect connect }
31 * before doing anything, and finally
32 * {@link org.apache.commons.net.SocketClient#disconnect disconnect }
33 * after you're completely finished interacting with the server.
34 * Then you need to check the SMTP reply code to see if the connection
35 * was successful. For example:
36 * <pre>
37 * try {
38 * int reply;
39 * client.connect("mail.foobar.com");
40 * System.out.print(client.getReplyString());
41 *
42 * // After connection attempt, you should check the reply code to verify
43 * // success.
44 * reply = client.getReplyCode();
45 *
46 * if(!SMTPReply.isPositiveCompletion(reply)) {
47 * client.disconnect();
48 * System.err.println("SMTP server refused connection.");
49 * System.exit(1);
50 * }
51 *
52 * // Do useful stuff here.
53 * ...
54 * } catch(IOException e) {
55 * if(client.isConnected()) {
56 * try {
57 * client.disconnect();
58 * } catch(IOException f) {
59 * // do nothing
60 * }
61 * }
62 * System.err.println("Could not connect to server.");
63 * e.printStackTrace();
64 * System.exit(1);
65 * }
66 * </pre>
67 * <p>
68 * Immediately after connecting is the only real time you need to check the
69 * reply code (because connect is of type void). The convention for all the
70 * SMTP command methods in SMTPClient is such that they either return a
71 * boolean value or some other value.
72 * The boolean methods return true on a successful completion reply from
73 * the SMTP server and false on a reply resulting in an error condition or
74 * failure. The methods returning a value other than boolean return a value
75 * containing the higher level data produced by the SMTP command, or null if a
76 * reply resulted in an error condition or failure. If you want to access
77 * the exact SMTP reply code causing a success or failure, you must call
78 * {@link org.apache.commons.net.smtp.SMTP#getReplyCode getReplyCode } after
79 * a success or failure.
80 * <p>
81 * You should keep in mind that the SMTP server may choose to prematurely
82 * close a connection for various reasons. The SMTPClient class will detect a
83 * premature SMTP server connection closing when it receives a
84 * {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE }
85 * response to a command.
86 * When that occurs, the method encountering that reply will throw
87 * an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
88 * .
89 * <code>SMTPConectionClosedException</code>
90 * is a subclass of <code> IOException </code> and therefore need not be
91 * caught separately, but if you are going to catch it separately, its
92 * catch block must appear before the more general <code> IOException </code>
93 * catch block. When you encounter an
94 * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException}
95 * , you must disconnect the connection with
96 * {@link #disconnect disconnect() } to properly clean up the
97 * system resources used by SMTPClient. Before disconnecting, you may check
98 * the last reply code and text with
99 * {@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
128
129
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 }