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
228 return SMTPReply.isPositiveCompletion(helo(name));
229 }
230
231
232 /***
233 * Set the sender of a message using the SMTP MAIL command, specifying
234 * a reverse relay path. The sender must be set first before any
235 * recipients may be specified, otherwise the mail server will reject
236 * your commands.
237 * <p>
238 * @param path The reverse relay path pointing back to the sender.
239 * @return True if successfully completed, false if not.
240 * @exception SMTPConnectionClosedException
241 * If the SMTP server prematurely closes the connection as a result
242 * of the client being idle or some other reason causing the server
243 * to send SMTP reply code 421. This exception may be caught either
244 * as an IOException or independently as itself.
245 * @exception IOException If an I/O error occurs while either sending a
246 * command to the server or receiving a reply from the server.
247 ***/
248 public boolean setSender(RelayPath path) throws IOException
249 {
250 return SMTPReply.isPositiveCompletion(mail(path.toString()));
251 }
252
253
254 /***
255 * Set the sender of a message using the SMTP MAIL command, specifying
256 * the sender's email address. The sender must be set first before any
257 * recipients may be specified, otherwise the mail server will reject
258 * your commands.
259 * <p>
260 * @param address The sender's email address.
261 * @return True if successfully completed, false if not.
262 * @exception SMTPConnectionClosedException
263 * If the SMTP server prematurely closes the connection as a result
264 * of the client being idle or some other reason causing the server
265 * to send SMTP reply code 421. This exception may be caught either
266 * as an IOException or independently as itself.
267 * @exception IOException If an I/O error occurs while either sending a
268 * command to the server or receiving a reply from the server.
269 ***/
270 public boolean setSender(String address) throws IOException
271 {
272 return SMTPReply.isPositiveCompletion(mail("<" + address + ">"));
273 }
274
275
276 /***
277 * Add a recipient for a message using the SMTP RCPT command, specifying
278 * a forward relay path. The sender must be set first before any
279 * recipients may be specified, otherwise the mail server will reject
280 * your commands.
281 * <p>
282 * @param path The forward relay path pointing to the recipient.
283 * @return True if successfully completed, false if not.
284 * @exception SMTPConnectionClosedException
285 * If the SMTP server prematurely closes the connection as a result
286 * of the client being idle or some other reason causing the server
287 * to send SMTP reply code 421. This exception may be caught either
288 * as an IOException or independently as itself.
289 * @exception IOException If an I/O error occurs while either sending a
290 * command to the server or receiving a reply from the server.
291 ***/
292 public boolean addRecipient(RelayPath path) throws IOException
293 {
294 return SMTPReply.isPositiveCompletion(rcpt(path.toString()));
295 }
296
297
298 /***
299 * Add a recipient for a message using the SMTP RCPT command, the
300 * recipient's email address. The sender must be set first before any
301 * recipients may be specified, otherwise the mail server will reject
302 * your commands.
303 * <p>
304 * @param address The recipient's email address.
305 * @return True if successfully completed, false if not.
306 * @exception SMTPConnectionClosedException
307 * If the SMTP server prematurely closes the connection as a result
308 * of the client being idle or some other reason causing the server
309 * to send SMTP reply code 421. This exception may be caught either
310 * as an IOException or independently as itself.
311 * @exception IOException If an I/O error occurs while either sending a
312 * command to the server or receiving a reply from the server.
313 ***/
314 public boolean addRecipient(String address) throws IOException
315 {
316 return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">"));
317 }
318
319
320
321 /***
322 * Send the SMTP DATA command in preparation to send an email message.
323 * This method returns a DotTerminatedMessageWriter instance to which
324 * the message can be written. Null is returned if the DATA command
325 * fails.
326 * <p>
327 * You must not issue any commands to the SMTP server (i.e., call any
328 * (other methods) until you finish writing to the returned Writer
329 * instance and close it. The SMTP protocol uses the same stream for
330 * issuing commands as it does for returning results. Therefore the
331 * returned Writer actually writes directly to the SMTP connection.
332 * After you close the writer, you can execute new commands. If you
333 * do not follow these requirements your program will not work properly.
334 * <p>
335 * You can use the provided
336 * {@link org.apache.commons.net.smtp.SimpleSMTPHeader}
337 * class to construct a bare minimum header.
338 * To construct more complicated headers you should
339 * refer to RFC 822. When the Java Mail API is finalized, you will be
340 * able to use it to compose fully compliant Internet text messages.
341 * The DotTerminatedMessageWriter takes care of doubling line-leading
342 * dots and ending the message with a single dot upon closing, so all
343 * you have to worry about is writing the header and the message.
344 * <p>
345 * Upon closing the returned Writer, you need to call
346 * {@link #completePendingCommand completePendingCommand() }
347 * to finalize the transaction and verify its success or failure from
348 * the server reply.
349 * <p>
350 * @return A DotTerminatedMessageWriter to which the message (including
351 * header) can be written. Returns null if the command fails.
352 * @exception SMTPConnectionClosedException
353 * If the SMTP server prematurely closes the connection as a result
354 * of the client being idle or some other reason causing the server
355 * to send SMTP reply code 421. This exception may be caught either
356 * as an IOException or independently as itself.
357 * @exception IOException If an I/O error occurs while either sending a
358 * command to the server or receiving a reply from the server.
359 ***/
360 public Writer sendMessageData() throws IOException
361 {
362 if (!SMTPReply.isPositiveIntermediate(data())) {
363 return null;
364 }
365
366 return new DotTerminatedMessageWriter(_writer);
367 }
368
369
370 /***
371 * A convenience method for sending short messages. This method fetches
372 * the Writer returned by {@link #sendMessageData sendMessageData() }
373 * and writes the specified String to it. After writing the message,
374 * this method calls {@link #completePendingCommand completePendingCommand() }
375 * to finalize the transaction and returns
376 * its success or failure.
377 * <p>
378 * @param message The short email message to send.
379 * @return True if successfully completed, false if not.
380 * @exception SMTPConnectionClosedException
381 * If the SMTP server prematurely closes the connection as a result
382 * of the client being idle or some other reason causing the server
383 * to send SMTP reply code 421. This exception may be caught either
384 * as an IOException or independently as itself.
385 * @exception IOException If an I/O error occurs while either sending a
386 * command to the server or receiving a reply from the server.
387 ***/
388 public boolean sendShortMessageData(String message) throws IOException
389 {
390 Writer writer;
391
392 writer = sendMessageData();
393
394 if (writer == null) {
395 return false;
396 }
397
398 writer.write(message);
399 writer.close();
400
401 return completePendingCommand();
402 }
403
404
405 /***
406 * A convenience method for a sending short email without having to
407 * explicitly set the sender and recipient(s). This method
408 * sets the sender and recipient using
409 * {@link #setSender setSender } and
410 * {@link #addRecipient addRecipient }, and then sends the
411 * message using {@link #sendShortMessageData sendShortMessageData }.
412 * <p>
413 * @param sender The email address of the sender.
414 * @param recipient The email address of the recipient.
415 * @param message The short email message to send.
416 * @return True if successfully completed, false if not.
417 * @exception SMTPConnectionClosedException
418 * If the SMTP server prematurely closes the connection as a result
419 * of the client being idle or some other reason causing the server
420 * to send SMTP reply code 421. This exception may be caught either
421 * as an IOException or independently as itself.
422 * @exception IOException If an I/O error occurs while either sending a
423 * command to the server or receiving a reply from the server.
424 ***/
425 public boolean sendSimpleMessage(String sender, String recipient,
426 String message)
427 throws IOException
428 {
429 if (!setSender(sender)) {
430 return false;
431 }
432
433 if (!addRecipient(recipient)) {
434 return false;
435 }
436
437 return sendShortMessageData(message);
438 }
439
440
441
442 /***
443 * A convenience method for a sending short email without having to
444 * explicitly set the sender and recipient(s). This method
445 * sets the sender and recipients using
446 * {@link #setSender setSender } and
447 * {@link #addRecipient addRecipient }, and then sends the
448 * message using {@link #sendShortMessageData sendShortMessageData }.
449 * <p>
450 * @param sender The email address of the sender.
451 * @param recipients An array of recipient email addresses.
452 * @param message The short email message to send.
453 * @return True if successfully completed, false if not.
454 * @exception SMTPConnectionClosedException
455 * If the SMTP server prematurely closes the connection as a result
456 * of the client being idle or some other reason causing the server
457 * to send SMTP reply code 421. This exception may be caught either
458 * as an IOException or independently as itself.
459 * @exception IOException If an I/O error occurs while either sending a
460 * command to the server or receiving a reply from the server.
461 ***/
462 public boolean sendSimpleMessage(String sender, String[] recipients,
463 String message)
464 throws IOException
465 {
466 boolean oneSuccess = false;
467 int count;
468
469 if (!setSender(sender)) {
470 return false;
471 }
472
473 for (count = 0; count < recipients.length; count++)
474 {
475 if (addRecipient(recipients[count])) {
476 oneSuccess = true;
477 }
478 }
479
480 if (!oneSuccess) {
481 return false;
482 }
483
484 return sendShortMessageData(message);
485 }
486
487
488 /***
489 * Logout of the SMTP server by sending the QUIT command.
490 * <p>
491 * @return True if successfully completed, false if not.
492 * @exception SMTPConnectionClosedException
493 * If the SMTP server prematurely closes the connection as a result
494 * of the client being idle or some other reason causing the server
495 * to send SMTP reply code 421. This exception may be caught either
496 * as an IOException or independently as itself.
497 * @exception IOException If an I/O error occurs while either sending a
498 * command to the server or receiving a reply from the server.
499 ***/
500 public boolean logout() throws IOException
501 {
502 return SMTPReply.isPositiveCompletion(quit());
503 }
504
505
506
507 /***
508 * Aborts the current mail transaction, resetting all server stored
509 * sender, recipient, and mail data, cleaing all buffers and tables.
510 * <p>
511 * @return True if successfully completed, false if not.
512 * @exception SMTPConnectionClosedException
513 * If the SMTP server prematurely closes the connection as a result
514 * of the client being idle or some other reason causing the server
515 * to send SMTP reply code 421. This exception may be caught either
516 * as an IOException or independently as itself.
517 * @exception IOException If an I/O error occurs while either sending a
518 * command to the server or receiving a reply from the server.
519 ***/
520 public boolean reset() throws IOException
521 {
522 return SMTPReply.isPositiveCompletion(rset());
523 }
524
525
526 /***
527 * Verify that a username or email address is valid, i.e., that mail
528 * can be delivered to that mailbox on the server.
529 * <p>
530 * @param username The username or email address to validate.
531 * @return True if the username is valid, false if not.
532 * @exception SMTPConnectionClosedException
533 * If the SMTP server prematurely closes the connection as a result
534 * of the client being idle or some other reason causing the server
535 * to send SMTP reply code 421. This exception may be caught either
536 * as an IOException or independently as itself.
537 * @exception IOException If an I/O error occurs while either sending a
538 * command to the server or receiving a reply from the server.
539 ***/
540 public boolean verify(String username) throws IOException
541 {
542 int result;
543
544 result = vrfy(username);
545
546 return (result == SMTPReply.ACTION_OK ||
547 result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD);
548 }
549
550
551 /***
552 * Fetches the system help information from the server and returns the
553 * full string.
554 * <p>
555 * @return The system help string obtained from the server. null if the
556 * information could not be obtained.
557 * @exception SMTPConnectionClosedException
558 * If the SMTP server prematurely closes the connection as a result
559 * of the client being idle or some other reason causing the server
560 * to send SMTP reply code 421. This exception may be caught either
561 * as an IOException or independently as itself.
562 * @exception IOException If an I/O error occurs while either sending a
563 * command to the server or receiving a reply from the server.
564 ***/
565 public String listHelp() throws IOException
566 {
567 if (SMTPReply.isPositiveCompletion(help())) {
568 return getReplyString();
569 }
570 return null;
571 }
572
573
574 /***
575 * Fetches the help information for a given command from the server and
576 * returns the full string.
577 * <p>
578 * @param command The command on which to ask for help.
579 * @return The command help string obtained from the server. null if the
580 * information could not be obtained.
581 * @exception SMTPConnectionClosedException
582 * If the SMTP server prematurely closes the connection as a result
583 * of the client being idle or some other reason causing the server
584 * to send SMTP reply code 421. This exception may be caught either
585 * as an IOException or independently as itself.
586 * @exception IOException If an I/O error occurs while either sending a
587 * command to the server or receiving a reply from the server.
588 ***/
589 public String listHelp(String command) throws IOException
590 {
591 if (SMTPReply.isPositiveCompletion(help(command))) {
592 return getReplyString();
593 }
594 return null;
595 }
596
597
598 /***
599 * Sends a NOOP command to the SMTP server. This is useful for preventing
600 * server timeouts.
601 * <p>
602 * @return True if successfully completed, false if not.
603 * @exception SMTPConnectionClosedException
604 * If the SMTP server prematurely closes the connection as a result
605 * of the client being idle or some other reason causing the server
606 * to send SMTP reply code 421. This exception may be caught either
607 * as an IOException or independently as itself.
608 * @exception IOException If an I/O error occurs while either sending a
609 * command to the server or receiving a reply from the server.
610 ***/
611 public boolean sendNoOp() throws IOException
612 {
613 return SMTPReply.isPositiveCompletion(noop());
614 }
615
616 }