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 018package org.apache.commons.net.smtp; 019 020import java.io.IOException; 021import java.io.Writer; 022import java.net.InetAddress; 023 024import org.apache.commons.net.io.DotTerminatedMessageWriter; 025 026/** 027 * SMTPClient encapsulates all the functionality necessary to send files through an SMTP server. This class takes care of all low level details of interacting 028 * with an SMTP server and provides a convenient higher level interface. As with all classes derived from {@link org.apache.commons.net.SocketClient}, you must 029 * first connect to the server with {@link org.apache.commons.net.SocketClient#connect connect } before doing anything, and finally 030 * {@link org.apache.commons.net.SocketClient#disconnect disconnect } after you're completely finished interacting with the server. Then you need to check the 031 * SMTP reply code to see if the connection was successful. For example: 032 * 033 * <pre> 034 * try { 035 * int reply; 036 * client.connect("mail.foobar.com"); 037 * System.out.print(client.getReplyString()); 038 * 039 * // After connection attempt, you should check the reply code to verify 040 * // success. 041 * reply = client.getReplyCode(); 042 * 043 * if(!SMTPReply.isPositiveCompletion(reply)) { 044 * client.disconnect(); 045 * System.err.println("SMTP server refused connection."); 046 * System.exit(1); 047 * } 048 * 049 * // Do useful stuff here. 050 * ... 051 * } catch(IOException e) { 052 * if(client.isConnected()) { 053 * try { 054 * client.disconnect(); 055 * } catch(IOException f) { 056 * // do nothing 057 * } 058 * } 059 * System.err.println("Could not connect to server."); 060 * e.printStackTrace(); 061 * System.exit(1); 062 * } 063 * </pre> 064 * <p> 065 * Immediately after connecting is the only real time you need to check the reply code (because connect is of type void). The convention for all the SMTP 066 * command methods in SMTPClient is such that they either return a boolean value or some other value. The boolean methods return true on a successful completion 067 * reply from the SMTP server and false on a reply resulting in an error condition or failure. The methods returning a value other than boolean return a value 068 * containing the higher level data produced by the SMTP command, or null if a reply resulted in an error condition or failure. If you want to access the exact 069 * SMTP reply code causing a success or failure, you must call {@link org.apache.commons.net.smtp.SMTP#getReplyCode getReplyCode } after a success or failure. 070 * <p> 071 * You should keep in mind that the SMTP server may choose to prematurely close a connection for various reasons. The SMTPClient class will detect a premature 072 * SMTP server connection closing when it receives a {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE } 073 * response to a command. When that occurs, the method encountering that reply will throw an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} . 074 * <code>SMTPConectionClosedException</code> is a subclass of <code> IOException </code> and therefore need not be caught separately, but if you are going to 075 * catch it separately, its catch block must appear before the more general <code> IOException </code> catch block. When you encounter an 076 * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} , you must disconnect the connection with {@link #disconnect disconnect() } to properly 077 * clean up the system resources used by SMTPClient. Before disconnecting, you may check the last reply code and text with 078 * {@link org.apache.commons.net.smtp.SMTP#getReplyCode getReplyCode }, {@link org.apache.commons.net.smtp.SMTP#getReplyString getReplyString }, and 079 * {@link org.apache.commons.net.smtp.SMTP#getReplyStrings getReplyStrings}. 080 * <p> 081 * Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a 082 * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the 083 * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as 084 * lenient as possible. 085 * 086 * @see SMTP 087 * @see SimpleSMTPHeader 088 * @see RelayPath 089 * @see SMTPConnectionClosedException 090 * @see org.apache.commons.net.MalformedServerReplyException 091 */ 092 093public class SMTPClient extends SMTP { 094 095 /** 096 * Default SMTPClient constructor. Creates a new SMTPClient instance. 097 */ 098 public SMTPClient() { 099 } 100 101 /** 102 * Overloaded constructor that takes an encoding specification 103 * 104 * @param encoding The encoding to use 105 * @since 2.0 106 */ 107 public SMTPClient(final String encoding) { 108 super(encoding); 109 } 110 111 /** 112 * Add a recipient for a message using the SMTP RCPT command, specifying a forward relay path. The sender must be set first before any recipients may be 113 * specified, otherwise the mail server will reject your commands. 114 * <p> 115 * 116 * @param path The forward relay path pointing to the recipient. 117 * @return True if successfully completed, false if not. 118 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 119 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 120 * independently as itself. 121 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 122 */ 123 public boolean addRecipient(final RelayPath path) throws IOException { 124 return SMTPReply.isPositiveCompletion(rcpt(path.toString())); 125 } 126 127 /** 128 * Add a recipient for a message using the SMTP RCPT command, the recipient's email address. The sender must be set first before any recipients may be 129 * specified, otherwise the mail server will reject your commands. 130 * <p> 131 * 132 * @param address The recipient's email address. 133 * @return True if successfully completed, false if not. 134 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 135 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 136 * independently as itself. 137 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 138 */ 139 public boolean addRecipient(final String address) throws IOException { 140 return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">")); 141 } 142 143 /** 144 * At least one SMTPClient method ({@link #sendMessageData sendMessageData }) does not complete the entire sequence of SMTP commands to complete a 145 * transaction. These types of commands require some action by the programmer after the reception of a positive intermediate command. After the programmer's 146 * code completes its actions, it must call this method to receive the completion reply from the server and verify the success of the entire transaction. 147 * <p> 148 * For example, 149 * 150 * <pre> 151 * writer = client.sendMessageData(); 152 * if (writer == null) // failure 153 * return false; 154 * header = new SimpleSMTPHeader("foobar@foo.com", "foo@foobar.com", "Re: Foo"); 155 * writer.write(header.toString()); 156 * writer.write("This is just a test"); 157 * writer.close(); 158 * if (!client.completePendingCommand()) // failure 159 * return false; 160 * </pre> 161 * <p> 162 * 163 * @return True if successfully completed, false if not. 164 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 165 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 166 * independently as itself. 167 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 168 */ 169 public boolean completePendingCommand() throws IOException { 170 return SMTPReply.isPositiveCompletion(getReply()); 171 } 172 173 /** 174 * Fetches the system help information from the server and returns the full string. 175 * <p> 176 * 177 * @return The system help string obtained from the server. null if the information could not be obtained. 178 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 179 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 180 * independently as itself. 181 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 182 */ 183 public String listHelp() throws IOException { 184 if (SMTPReply.isPositiveCompletion(help())) { 185 return getReplyString(); 186 } 187 return null; 188 } 189 190 /** 191 * Fetches the help information for a given command from the server and returns the full string. 192 * <p> 193 * 194 * @param command The command on which to ask for help. 195 * @return The command help string obtained from the server. null if the information could not be obtained. 196 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 197 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 198 * independently as itself. 199 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 200 */ 201 public String listHelp(final String command) throws IOException { 202 if (SMTPReply.isPositiveCompletion(help(command))) { 203 return getReplyString(); 204 } 205 return null; 206 } 207 208 /** 209 * Login to the SMTP server by sending the HELO command with the client hostname as an argument. Before performing any mail commands, you must first login. 210 * <p> 211 * 212 * @return True if successfully completed, false if not. 213 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 214 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 215 * independently as itself. 216 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 217 */ 218 public boolean login() throws IOException { 219 final String name; 220 final InetAddress host; 221 222 host = getLocalAddress(); 223 name = host.getHostName(); 224 225 if (name == null) { 226 return false; 227 } 228 229 return SMTPReply.isPositiveCompletion(helo(name)); 230 } 231 232 /** 233 * Login to the SMTP server by sending the HELO command with the given hostname as an argument. Before performing any mail commands, you must first login. 234 * <p> 235 * 236 * @param hostname The hostname with which to greet the SMTP server. 237 * @return True if successfully completed, false if not. 238 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 239 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 240 * independently as itself. 241 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 242 */ 243 public boolean login(final String hostname) throws IOException { 244 return SMTPReply.isPositiveCompletion(helo(hostname)); 245 } 246 247 /** 248 * Logout of the SMTP server by sending the QUIT command. 249 * <p> 250 * 251 * @return True if successfully completed, false if not. 252 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 253 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 254 * independently as itself. 255 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 256 */ 257 public boolean logout() throws IOException { 258 return SMTPReply.isPositiveCompletion(quit()); 259 } 260 261 /** 262 * Aborts the current mail transaction, resetting all server stored sender, recipient, and mail data, cleaing all buffers and tables. 263 * <p> 264 * 265 * @return True if successfully completed, false if not. 266 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 267 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 268 * independently as itself. 269 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 270 */ 271 public boolean reset() throws IOException { 272 return SMTPReply.isPositiveCompletion(rset()); 273 } 274 275 /** 276 * Send the SMTP DATA command in preparation to send an email message. This method returns a DotTerminatedMessageWriter instance to which the message can be 277 * written. Null is returned if the DATA command fails. 278 * <p> 279 * You must not issue any commands to the SMTP server (i.e., call any (other methods) until you finish writing to the returned Writer instance and close it. 280 * The SMTP protocol uses the same stream for issuing commands as it does for returning results. Therefore the returned Writer actually writes directly to 281 * the SMTP connection. After you close the writer, you can execute new commands. If you do not follow these requirements your program will not work 282 * properly. 283 * <p> 284 * You can use the provided {@link org.apache.commons.net.smtp.SimpleSMTPHeader} class to construct a bare minimum header. To construct more complicated 285 * headers you should refer to RFC 5322. When the Java Mail API is finalized, you will be able to use it to compose fully compliant Internet text messages. 286 * The DotTerminatedMessageWriter takes care of doubling line-leading dots and ending the message with a single dot upon closing, so all you have to worry 287 * about is writing the header and the message. 288 * <p> 289 * Upon closing the returned Writer, you need to call {@link #completePendingCommand completePendingCommand() } to finalize the transaction and verify its 290 * success or failure from the server reply. 291 * <p> 292 * 293 * @return A DotTerminatedMessageWriter to which the message (including header) can be written. Returns null if the command fails. 294 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 295 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 296 * independently as itself. 297 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 298 * @see #sendShortMessageData(String) 299 */ 300 public Writer sendMessageData() throws IOException { 301 if (!SMTPReply.isPositiveIntermediate(data())) { 302 return null; 303 } 304 305 return new DotTerminatedMessageWriter(writer); 306 } 307 308 /** 309 * Sends a NOOP command to the SMTP server. This is useful for preventing server timeouts. 310 * <p> 311 * 312 * @return True if successfully completed, false if not. 313 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 314 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 315 * independently as itself. 316 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 317 */ 318 public boolean sendNoOp() throws IOException { 319 return SMTPReply.isPositiveCompletion(noop()); 320 } 321 322 /** 323 * A convenience method for sending short messages. This method fetches the Writer returned by {@link #sendMessageData sendMessageData() } and writes the 324 * specified String to it. After writing the message, this method calls {@link #completePendingCommand completePendingCommand() } to finalize the 325 * transaction and returns its success or failure. 326 * <p> 327 * 328 * @param message The short email message to send. This must include the headers and the body, but not the trailing "." 329 * @return True if successfully completed, false if not. 330 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 331 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 332 * independently as itself. 333 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 334 */ 335 public boolean sendShortMessageData(final String message) throws IOException { 336 try (final Writer writer = sendMessageData()) { 337 338 if (writer == null) { 339 return false; 340 } 341 342 writer.write(message); 343 } 344 345 return completePendingCommand(); 346 } 347 348 /** 349 * A convenience method for a sending short email without having to explicitly set the sender and recipient(s). This method sets the sender and recipient 350 * using {@link #setSender setSender } and {@link #addRecipient addRecipient }, and then sends the message using {@link #sendShortMessageData 351 * sendShortMessageData }. 352 * <p> 353 * 354 * @param sender The email address of the sender. 355 * @param recipient The email address of the recipient. 356 * @param message The short email message to send. This must include the headers and the body, but not the trailing "." 357 * @return True if successfully completed, false if not. 358 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 359 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 360 * independently as itself. 361 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 362 */ 363 public boolean sendSimpleMessage(final String sender, final String recipient, final String message) throws IOException { 364 if (!setSender(sender)) { 365 return false; 366 } 367 368 if (!addRecipient(recipient)) { 369 return false; 370 } 371 372 return sendShortMessageData(message); 373 } 374 375 /** 376 * A convenience method for a sending short email without having to explicitly set the sender and recipient(s). This method sets the sender and recipients 377 * using {@link #setSender(String) setSender} and {@link #addRecipient(String) addRecipient}, and then sends the message using 378 * {@link #sendShortMessageData(String) sendShortMessageData}. 379 * <p> 380 * Note that the method ignores failures when calling {@link #addRecipient(String) addRecipient} so long as at least one call succeeds. If no recipients can 381 * be successfully added then the method will fail (and does not attempt to send the message) 382 * <p> 383 * 384 * @param sender The email address of the sender. 385 * @param recipients An array of recipient email addresses. 386 * @param message The short email message to send. This must include the headers and the body, but not the trailing "." 387 * @return True if successfully completed, false if not. 388 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 389 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 390 * independently as itself. 391 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 392 */ 393 public boolean sendSimpleMessage(final String sender, final String[] recipients, final String message) throws IOException { 394 boolean oneSuccess = false; 395 int count; 396 397 if (!setSender(sender)) { 398 return false; 399 } 400 401 for (count = 0; count < recipients.length; count++) { 402 if (addRecipient(recipients[count])) { 403 oneSuccess = true; 404 } 405 } 406 407 if (!oneSuccess) { 408 return false; 409 } 410 411 return sendShortMessageData(message); 412 } 413 414 /** 415 * Set the sender of a message using the SMTP MAIL command, specifying a reverse relay path. The sender must be set first before any recipients may be 416 * specified, otherwise the mail server will reject your commands. 417 * <p> 418 * 419 * @param path The reverse relay path pointing back to the sender. 420 * @return True if successfully completed, false if not. 421 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 422 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 423 * independently as itself. 424 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 425 */ 426 public boolean setSender(final RelayPath path) throws IOException { 427 return SMTPReply.isPositiveCompletion(mail(path.toString())); 428 } 429 430 /** 431 * Set the sender of a message using the SMTP MAIL command, specifying the sender's email address. The sender must be set first before any recipients may be 432 * specified, otherwise the mail server will reject your commands. 433 * <p> 434 * 435 * @param address The sender's email address. 436 * @return True if successfully completed, false if not. 437 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 438 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 439 * independently as itself. 440 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 441 */ 442 public boolean setSender(final String address) throws IOException { 443 return SMTPReply.isPositiveCompletion(mail("<" + address + ">")); 444 } 445 446 /** 447 * Verify that a username or email address is valid, i.e., that mail can be delivered to that mailbox on the server. 448 * <p> 449 * 450 * @param username The username or email address to validate. 451 * @return True if the username is valid, false if not. 452 * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason 453 * causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or 454 * independently as itself. 455 * @throws IOException If an I/O error occurs while either sending a command to the server or receiving a reply from the server. 456 */ 457 public boolean verify(final String username) throws IOException { 458 final int result; 459 460 result = vrfy(username); 461 462 return result == SMTPReply.ACTION_OK || result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD; 463 } 464 465}