1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.net.smtp;
  17. import java.io.IOException;
  18. import java.io.Writer;
  19. import java.net.InetAddress;
  20. import org.apache.commons.net.io.DotTerminatedMessageWriter;
  21. /***
  22. * SMTPClient encapsulates all the functionality necessary to send files
  23. * through an SMTP server. This class takes care of all
  24. * low level details of interacting with an SMTP server and provides
  25. * a convenient higher level interface. As with all classes derived
  26. * from <a href="org.apache.commons.net.SocketClient.html"> SocketClient </a>,
  27. * you must first connect to the server with
  28. * <a href="org.apache.commons.net.SocketClient.html#connect"> connect </a>
  29. * before doing anything, and finally
  30. * <a href="org.apache.commons.net.SocketClient.html#disconnect"> disconnect </a>
  31. * after you're completely finished interacting with the server.
  32. * Then you need to check the SMTP reply code to see if the connection
  33. * was successful. For example:
  34. * <pre>
  35. * try {
  36. * int reply;
  37. * client.connect("mail.foobar.com");
  38. * System.out.print(client.getReplyString());
  39. *
  40. * // After connection attempt, you should check the reply code to verify
  41. * // success.
  42. * reply = client.getReplyCode();
  43. *
  44. * if(!SMTPReply.isPositiveCompletion(reply)) {
  45. * client.disconnect();
  46. * System.err.println("SMTP server refused connection.");
  47. * System.exit(1);
  48. * }
  49. *
  50. * // Do useful stuff here.
  51. * ...
  52. * } catch(IOException e) {
  53. * if(client.isConnected()) {
  54. * try {
  55. * client.disconnect();
  56. * } catch(IOException f) {
  57. * // do nothing
  58. * }
  59. * }
  60. * System.err.println("Could not connect to server.");
  61. * e.printStackTrace();
  62. * System.exit(1);
  63. * }
  64. * </pre>
  65. * <p>
  66. * Immediately after connecting is the only real time you need to check the
  67. * reply code (because connect is of type void). The convention for all the
  68. * SMTP command methods in SMTPClient is such that they either return a
  69. * boolean value or some other value.
  70. * The boolean methods return true on a successful completion reply from
  71. * the SMTP server and false on a reply resulting in an error condition or
  72. * failure. The methods returning a value other than boolean return a value
  73. * containing the higher level data produced by the SMTP command, or null if a
  74. * reply resulted in an error condition or failure. If you want to access
  75. * the exact SMTP reply code causing a success or failure, you must call
  76. * <a href="org.apache.commons.net.smtp.SMTP.html#getReplyCode"> getReplyCode </a> after
  77. * a success or failure.
  78. * <p>
  79. * You should keep in mind that the SMTP server may choose to prematurely
  80. * close a connection for various reasons. The SMTPClient class will detect a
  81. * premature SMTP server connection closing when it receives a
  82. * <a href="org.apache.commons.net.smtp.SMTPReply.html#SERVICE_NOT_AVAILABLE">
  83. * SMTPReply.SERVICE_NOT_AVAILABLE </a> response to a command.
  84. * When that occurs, the method encountering that reply will throw
  85. * an <a href="org.apache.commons.net.smtp.SMTPConnectionClosedException.html">
  86. * SMTPConnectionClosedException </a>.
  87. * <code>SMTPConectionClosedException</code>
  88. * is a subclass of <code> IOException </code> and therefore need not be
  89. * caught separately, but if you are going to catch it separately, its
  90. * catch block must appear before the more general <code> IOException </code>
  91. * catch block. When you encounter an
  92. * <a href="org.apache.commons.net.smtp.SMTPConnectionClosedException.html">
  93. * SMTPConnectionClosedException </a>, you must disconnect the connection with
  94. * <a href="#disconnect"> disconnect() </a> to properly clean up the
  95. * system resources used by SMTPClient. Before disconnecting, you may check
  96. * the last reply code and text with
  97. * <a href="org.apache.commons.net.smtp.SMTP.html#getReplyCode"> getReplyCode </a>,
  98. * <a href="org.apache.commons.net.smtp.SMTP.html#getReplyString"> getReplyString </a>,
  99. * and
  100. * <a href="org.apache.commons.net.smtp.SMTP.html#getReplyStrings">getReplyStrings</a>.
  101. * <p>
  102. * Rather than list it separately for each method, we mention here that
  103. * every method communicating with the server and throwing an IOException
  104. * can also throw a
  105. * <a href="org.apache.commons.net.MalformedServerReplyException.html">
  106. * MalformedServerReplyException </a>, which is a subclass
  107. * of IOException. A MalformedServerReplyException will be thrown when
  108. * the reply received from the server deviates enough from the protocol
  109. * specification that it cannot be interpreted in a useful manner despite
  110. * attempts to be as lenient as possible.
  111. * <p>
  112. * <p>
  113. * @author Daniel F. Savarese
  114. * @see SMTP
  115. * @see SimpleSMTPHeader
  116. * @see RelayPath
  117. * @see SMTPConnectionClosedException
  118. * @see org.apache.commons.net.MalformedServerReplyException
  119. ***/
  120. public class SMTPClient extends SMTP
  121. {
  122. /*
  123. * Default SMTPClient constructor. Creates a new SMTPClient instance.
  124. */
  125. //public SMTPClient() { }
  126. /***
  127. * At least one SMTPClient method (<a href="#sendMessage"> sendMessage </a>)
  128. * does not complete the entire sequence of SMTP commands to complete a
  129. * transaction. These types of commands require some action by the
  130. * programmer after the reception of a positive intermediate command.
  131. * After the programmer's code completes its actions, it must call this
  132. * method to receive the completion reply from the server and verify the
  133. * success of the entire transaction.
  134. * <p>
  135. * For example,
  136. * <pre>
  137. * writer = client.sendMessage();
  138. * if(writer == null) // failure
  139. * return false;
  140. * header =
  141. * new SimpleSMTPHeader("foobar@foo.com", "foo@foobar.com", "Re: Foo");
  142. * writer.write(header.toString());
  143. * writer.write("This is just a test");
  144. * writer.close();
  145. * if(!client.completePendingCommand()) // failure
  146. * return false;
  147. * </pre>
  148. * <p>
  149. * @return True if successfully completed, false if not.
  150. * @exception SMTPConnectionClosedException
  151. * If the SMTP server prematurely closes the connection as a result
  152. * of the client being idle or some other reason causing the server
  153. * to send SMTP reply code 421. This exception may be caught either
  154. * as an IOException or independently as itself.
  155. * @exception IOException If an I/O error occurs while either sending a
  156. * command to the server or receiving a reply from the server.
  157. ***/
  158. public boolean completePendingCommand() throws IOException
  159. {
  160. return SMTPReply.isPositiveCompletion(getReply());
  161. }
  162. /***
  163. * Login to the SMTP server by sending the HELO command with the
  164. * given hostname as an argument. Before performing any mail commands,
  165. * you must first login.
  166. * <p>
  167. * @param hostname The hostname with which to greet the SMTP server.
  168. * @return True if successfully completed, false if not.
  169. * @exception SMTPConnectionClosedException
  170. * If the SMTP server prematurely closes the connection as a result
  171. * of the client being idle or some other reason causing the server
  172. * to send SMTP reply code 421. This exception may be caught either
  173. * as an IOException or independently as itself.
  174. * @exception IOException If an I/O error occurs while either sending a
  175. * command to the server or receiving a reply from the server.
  176. ***/
  177. public boolean login(String hostname) throws IOException
  178. {
  179. return SMTPReply.isPositiveCompletion(helo(hostname));
  180. }
  181. /***
  182. * Login to the SMTP server by sending the HELO command with the
  183. * client hostname as an argument. Before performing any mail commands,
  184. * you must first login.
  185. * <p>
  186. * @return True if successfully completed, false if not.
  187. * @exception SMTPConnectionClosedException
  188. * If the SMTP server prematurely closes the connection as a result
  189. * of the client being idle or some other reason causing the server
  190. * to send SMTP reply code 421. This exception may be caught either
  191. * as an IOException or independently as itself.
  192. * @exception IOException If an I/O error occurs while either sending a
  193. * command to the server or receiving a reply from the server.
  194. ***/
  195. public boolean login() throws IOException
  196. {
  197. String name;
  198. InetAddress host;
  199. host = getLocalAddress();
  200. name = host.getHostName();
  201. if (name == null)
  202. return false;
  203. return SMTPReply.isPositiveCompletion(helo(name));
  204. }
  205. /***
  206. * Set the sender of a message using the SMTP MAIL command, specifying
  207. * a reverse relay path. The sender must be set first before any
  208. * recipients may be specified, otherwise the mail server will reject
  209. * your commands.
  210. * <p>
  211. * @param path The reverse relay path pointing back to the sender.
  212. * @return True if successfully completed, false if not.
  213. * @exception SMTPConnectionClosedException
  214. * If the SMTP server prematurely closes the connection as a result
  215. * of the client being idle or some other reason causing the server
  216. * to send SMTP reply code 421. This exception may be caught either
  217. * as an IOException or independently as itself.
  218. * @exception IOException If an I/O error occurs while either sending a
  219. * command to the server or receiving a reply from the server.
  220. ***/
  221. public boolean setSender(RelayPath path) throws IOException
  222. {
  223. return SMTPReply.isPositiveCompletion(mail(path.toString()));
  224. }
  225. /***
  226. * Set the sender of a message using the SMTP MAIL command, specifying
  227. * the sender's email address. The sender must be set first before any
  228. * recipients may be specified, otherwise the mail server will reject
  229. * your commands.
  230. * <p>
  231. * @param address The sender's email address.
  232. * @return True if successfully completed, false if not.
  233. * @exception SMTPConnectionClosedException
  234. * If the SMTP server prematurely closes the connection as a result
  235. * of the client being idle or some other reason causing the server
  236. * to send SMTP reply code 421. This exception may be caught either
  237. * as an IOException or independently as itself.
  238. * @exception IOException If an I/O error occurs while either sending a
  239. * command to the server or receiving a reply from the server.
  240. ***/
  241. public boolean setSender(String address) throws IOException
  242. {
  243. return SMTPReply.isPositiveCompletion(mail("<" + address + ">"));
  244. }
  245. /***
  246. * Add a recipient for a message using the SMTP RCPT command, specifying
  247. * a forward relay path. The sender must be set first before any
  248. * recipients may be specified, otherwise the mail server will reject
  249. * your commands.
  250. * <p>
  251. * @param path The forward relay path pointing to the recipient.
  252. * @return True if successfully completed, false if not.
  253. * @exception SMTPConnectionClosedException
  254. * If the SMTP server prematurely closes the connection as a result
  255. * of the client being idle or some other reason causing the server
  256. * to send SMTP reply code 421. This exception may be caught either
  257. * as an IOException or independently as itself.
  258. * @exception IOException If an I/O error occurs while either sending a
  259. * command to the server or receiving a reply from the server.
  260. ***/
  261. public boolean addRecipient(RelayPath path) throws IOException
  262. {
  263. return SMTPReply.isPositiveCompletion(rcpt(path.toString()));
  264. }
  265. /***
  266. * Add a recipient for a message using the SMTP RCPT command, the
  267. * recipient's email address. The sender must be set first before any
  268. * recipients may be specified, otherwise the mail server will reject
  269. * your commands.
  270. * <p>
  271. * @param address The recipient's email address.
  272. * @return True if successfully completed, false if not.
  273. * @exception SMTPConnectionClosedException
  274. * If the SMTP server prematurely closes the connection as a result
  275. * of the client being idle or some other reason causing the server
  276. * to send SMTP reply code 421. This exception may be caught either
  277. * as an IOException or independently as itself.
  278. * @exception IOException If an I/O error occurs while either sending a
  279. * command to the server or receiving a reply from the server.
  280. ***/
  281. public boolean addRecipient(String address) throws IOException
  282. {
  283. return SMTPReply.isPositiveCompletion(rcpt("<" + address + ">"));
  284. }
  285. /***
  286. * Send the SMTP DATA command in preparation to send an email message.
  287. * This method returns a DotTerminatedMessageWriter instance to which
  288. * the message can be written. Null is returned if the DATA command
  289. * fails.
  290. * <p>
  291. * You must not issue any commands to the SMTP server (i.e., call any
  292. * (other methods) until you finish writing to the returned Writer
  293. * instance and close it. The SMTP protocol uses the same stream for
  294. * issuing commands as it does for returning results. Therefore the
  295. * returned Writer actually writes directly to the SMTP connection.
  296. * After you close the writer, you can execute new commands. If you
  297. * do not follow these requirements your program will not work properly.
  298. * <p>
  299. * You can use the provided
  300. * <a href="org.apache.commons.net.smtp.SimpleSMTPHeader.html"> SimpleSMTPHeader </a>
  301. * class to construct a bare minimum header.
  302. * To construct more complicated headers you should
  303. * refer to RFC 822. When the Java Mail API is finalized, you will be
  304. * able to use it to compose fully compliant Internet text messages.
  305. * The DotTerminatedMessageWriter takes care of doubling line-leading
  306. * dots and ending the message with a single dot upon closing, so all
  307. * you have to worry about is writing the header and the message.
  308. * <p>
  309. * Upon closing the returned Writer, you need to call
  310. * <a href="#completePendingCommand"> completePendingCommand() </a>
  311. * to finalize the transaction and verify its success or failure from
  312. * the server reply.
  313. * <p>
  314. * @return A DotTerminatedMessageWriter to which the message (including
  315. * header) can be written. Returns null if the command fails.
  316. * @exception SMTPConnectionClosedException
  317. * If the SMTP server prematurely closes the connection as a result
  318. * of the client being idle or some other reason causing the server
  319. * to send SMTP reply code 421. This exception may be caught either
  320. * as an IOException or independently as itself.
  321. * @exception IOException If an I/O error occurs while either sending a
  322. * command to the server or receiving a reply from the server.
  323. ***/
  324. public Writer sendMessageData() throws IOException
  325. {
  326. if (!SMTPReply.isPositiveIntermediate(data()))
  327. return null;
  328. return new DotTerminatedMessageWriter(_writer);
  329. }
  330. /***
  331. * A convenience method for sending short messages. This method fetches
  332. * the Writer returned by <a href="#sendMessageData"> sendMessageData() </a>
  333. * and writes the specified String to it. After writing the message,
  334. * this method calls <a href="#completePendingCommand">
  335. * completePendingCommand() </a> to finalize the transaction and returns
  336. * its success or failure.
  337. * <p>
  338. * @param message The short email message to send.
  339. * @return True if successfully completed, false if not.
  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 boolean sendShortMessageData(String message) throws IOException
  349. {
  350. Writer writer;
  351. writer = sendMessageData();
  352. if (writer == null)
  353. return false;
  354. writer.write(message);
  355. writer.close();
  356. return completePendingCommand();
  357. }
  358. /***
  359. * A convenience method for a sending short email without having to
  360. * explicitly set the sender and recipient(s). This method
  361. * sets the sender and recipient using
  362. * <a href="#setSender"> setSender </a> and
  363. * <a href="#addRecipient"> addRecipient </a>, and then sends the
  364. * message using <a href="#sendShortMessageData"> sendShortMessageData </a>.
  365. * <p>
  366. * @param sender The email address of the sender.
  367. * @param recipient The email address of the recipient.
  368. * @param message The short email message to send.
  369. * @return True if successfully completed, false if not.
  370. * @exception SMTPConnectionClosedException
  371. * If the SMTP server prematurely closes the connection as a result
  372. * of the client being idle or some other reason causing the server
  373. * to send SMTP reply code 421. This exception may be caught either
  374. * as an IOException or independently as itself.
  375. * @exception IOException If an I/O error occurs while either sending a
  376. * command to the server or receiving a reply from the server.
  377. ***/
  378. public boolean sendSimpleMessage(String sender, String recipient,
  379. String message)
  380. throws IOException
  381. {
  382. if (!setSender(sender))
  383. return false;
  384. if (!addRecipient(recipient))
  385. return false;
  386. return sendShortMessageData(message);
  387. }
  388. /***
  389. * A convenience method for a sending short email without having to
  390. * explicitly set the sender and recipient(s). This method
  391. * sets the sender and recipients using
  392. * <a href="#setSender"> setSender </a> and
  393. * <a href="#addRecipient"> addRecipient </a>, and then sends the
  394. * message using <a href="#sendShortMessageData"> sendShortMessageData </a>.
  395. * <p>
  396. * @param sender The email address of the sender.
  397. * @param recipients An array of recipient email addresses.
  398. * @param message The short email message to send.
  399. * @return True if successfully completed, false if not.
  400. * @exception SMTPConnectionClosedException
  401. * If the SMTP server prematurely closes the connection as a result
  402. * of the client being idle or some other reason causing the server
  403. * to send SMTP reply code 421. This exception may be caught either
  404. * as an IOException or independently as itself.
  405. * @exception IOException If an I/O error occurs while either sending a
  406. * command to the server or receiving a reply from the server.
  407. ***/
  408. public boolean sendSimpleMessage(String sender, String[] recipients,
  409. String message)
  410. throws IOException
  411. {
  412. boolean oneSuccess = false;
  413. int count;
  414. if (!setSender(sender))
  415. return false;
  416. for (count = 0; count < recipients.length; count++)
  417. {
  418. if (addRecipient(recipients[count]))
  419. oneSuccess = true;
  420. }
  421. if (!oneSuccess)
  422. return false;
  423. return sendShortMessageData(message);
  424. }
  425. /***
  426. * Logout of the SMTP server by sending the QUIT command.
  427. * <p>
  428. * @return True if successfully completed, false if not.
  429. * @exception SMTPConnectionClosedException
  430. * If the SMTP server prematurely closes the connection as a result
  431. * of the client being idle or some other reason causing the server
  432. * to send SMTP reply code 421. This exception may be caught either
  433. * as an IOException or independently as itself.
  434. * @exception IOException If an I/O error occurs while either sending a
  435. * command to the server or receiving a reply from the server.
  436. ***/
  437. public boolean logout() throws IOException
  438. {
  439. return SMTPReply.isPositiveCompletion(quit());
  440. }
  441. /***
  442. * Aborts the current mail transaction, resetting all server stored
  443. * sender, recipient, and mail data, cleaing all buffers and tables.
  444. * <p>
  445. * @return True if successfully completed, false if not.
  446. * @exception SMTPConnectionClosedException
  447. * If the SMTP server prematurely closes the connection as a result
  448. * of the client being idle or some other reason causing the server
  449. * to send SMTP reply code 421. This exception may be caught either
  450. * as an IOException or independently as itself.
  451. * @exception IOException If an I/O error occurs while either sending a
  452. * command to the server or receiving a reply from the server.
  453. ***/
  454. public boolean reset() throws IOException
  455. {
  456. return SMTPReply.isPositiveCompletion(rset());
  457. }
  458. /***
  459. * Verify that a username or email address is valid, i.e., that mail
  460. * can be delivered to that mailbox on the server.
  461. * <p>
  462. * @param username The username or email address to validate.
  463. * @return True if the username is valid, false if not.
  464. * @exception SMTPConnectionClosedException
  465. * If the SMTP server prematurely closes the connection as a result
  466. * of the client being idle or some other reason causing the server
  467. * to send SMTP reply code 421. This exception may be caught either
  468. * as an IOException or independently as itself.
  469. * @exception IOException If an I/O error occurs while either sending a
  470. * command to the server or receiving a reply from the server.
  471. ***/
  472. public boolean verify(String username) throws IOException
  473. {
  474. int result;
  475. result = vrfy(username);
  476. return (result == SMTPReply.ACTION_OK ||
  477. result == SMTPReply.USER_NOT_LOCAL_WILL_FORWARD);
  478. }
  479. /***
  480. * Fetches the system help information from the server and returns the
  481. * full string.
  482. * <p>
  483. * @return The system help string obtained from the server. null if the
  484. * information could not be obtained.
  485. * @exception SMTPConnectionClosedException
  486. * If the SMTP server prematurely closes the connection as a result
  487. * of the client being idle or some other reason causing the server
  488. * to send SMTP reply code 421. This exception may be caught either
  489. * as an IOException or independently as itself.
  490. * @exception IOException If an I/O error occurs while either sending a
  491. * command to the server or receiving a reply from the server.
  492. ***/
  493. public String listHelp() throws IOException
  494. {
  495. if (SMTPReply.isPositiveCompletion(help()))
  496. return getReplyString();
  497. return null;
  498. }
  499. /***
  500. * Fetches the help information for a given command from the server and
  501. * returns the full string.
  502. * <p>
  503. * @param command The command on which to ask for help.
  504. * @return The command help string obtained from the server. null if the
  505. * information could not be obtained.
  506. * @exception SMTPConnectionClosedException
  507. * If the SMTP server prematurely closes the connection as a result
  508. * of the client being idle or some other reason causing the server
  509. * to send SMTP reply code 421. This exception may be caught either
  510. * as an IOException or independently as itself.
  511. * @exception IOException If an I/O error occurs while either sending a
  512. * command to the server or receiving a reply from the server.
  513. ***/
  514. public String listHelp(String command) throws IOException
  515. {
  516. if (SMTPReply.isPositiveCompletion(help(command)))
  517. return getReplyString();
  518. return null;
  519. }
  520. /***
  521. * Sends a NOOP command to the SMTP server. This is useful for preventing
  522. * server timeouts.
  523. * <p>
  524. * @return True if successfully completed, false if not.
  525. * @exception SMTPConnectionClosedException
  526. * If the SMTP server prematurely closes the connection as a result
  527. * of the client being idle or some other reason causing the server
  528. * to send SMTP reply code 421. This exception may be caught either
  529. * as an IOException or independently as itself.
  530. * @exception IOException If an I/O error occurs while either sending a
  531. * command to the server or receiving a reply from the server.
  532. ***/
  533. public boolean sendNoOp() throws IOException
  534. {
  535. return SMTPReply.isPositiveCompletion(noop());
  536. }
  537. }