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 }