1. /*
  2. * Copyright 2000-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. */
  17. package org.apache.tools.ant.taskdefs.email;
  18. // Ant imports
  19. import java.io.File;
  20. import java.util.Enumeration;
  21. import java.util.StringTokenizer;
  22. import java.util.Vector;
  23. import org.apache.tools.ant.BuildException;
  24. import org.apache.tools.ant.DirectoryScanner;
  25. import org.apache.tools.ant.Project;
  26. import org.apache.tools.ant.Task;
  27. import org.apache.tools.ant.types.EnumeratedAttribute;
  28. import org.apache.tools.ant.types.FileSet;
  29. /**
  30. * A task to send SMTP email. This is a refactoring of the SendMail and
  31. * MimeMail tasks such that both are within a single task.
  32. *
  33. * @since Ant 1.5
  34. * @ant.task name="mail" category="network"
  35. */
  36. public class EmailTask
  37. extends Task {
  38. /** Constant to show that the best available mailer should be used. */
  39. public static final String AUTO = "auto";
  40. /** Constant to allow the Mime mailer to be requested */
  41. public static final String MIME = "mime";
  42. /** Constant to allow the UU mailer to be requested */
  43. public static final String UU = "uu";
  44. /** Constant to allow the plaintext mailer to be requested */
  45. public static final String PLAIN = "plain";
  46. /**
  47. * Enumerates the encoding constants
  48. */
  49. public static class Encoding extends EnumeratedAttribute {
  50. /**
  51. * finds the valid encoding values
  52. *
  53. * @return a list of valid entries
  54. */
  55. public String[] getValues() {
  56. return new String[] {AUTO, MIME, UU, PLAIN};
  57. }
  58. }
  59. private String encoding = AUTO;
  60. /** host running SMTP */
  61. private String host = "localhost";
  62. private int port = 25;
  63. /** subject field */
  64. private String subject = null;
  65. /** any text */
  66. private Message message = null;
  67. /** failure flag */
  68. private boolean failOnError = true;
  69. private boolean includeFileNames = false;
  70. private String messageMimeType = null;
  71. /** special headers */
  72. /** sender */
  73. private EmailAddress from = null;
  74. /** replyto */
  75. private Vector replyToList = new Vector();
  76. /** TO recipients */
  77. private Vector toList = new Vector();
  78. /** CC (Carbon Copy) recipients */
  79. private Vector ccList = new Vector();
  80. /** BCC (Blind Carbon Copy) recipients */
  81. private Vector bccList = new Vector();
  82. /** file list */
  83. private Vector files = new Vector();
  84. private Vector filesets = new Vector();
  85. /** Character set for MimeMailer*/
  86. private String charset = null;
  87. /** User for SMTP auth */
  88. private String user = null;
  89. /** Password for SMTP auth */
  90. private String password = null;
  91. /** indicate if the user wishes SSL-TLS */
  92. private boolean SSL = false;
  93. /**
  94. * sets the user for SMTP auth; this requires JavaMail
  95. * @param user
  96. * @since ant 1.6
  97. */
  98. public void setUser(String user) {
  99. this.user = user;
  100. }
  101. /**
  102. * sets the password for SMTP auth; this requires JavaMail
  103. * @param password
  104. * @since ant 1.6
  105. */
  106. public void setPassword(String password) {
  107. this.password = password;
  108. }
  109. /**
  110. * tells if the user needs to send his data over SSL
  111. * @param SSL
  112. * @since ant 1.6
  113. */
  114. public void setSSL(boolean SSL) {
  115. this.SSL = SSL;
  116. }
  117. /**
  118. * Allows the build writer to choose the preferred encoding method
  119. *
  120. * @param encoding The encoding (one of AUTO,MIME,UU,PLAIN)
  121. */
  122. public void setEncoding(Encoding encoding) {
  123. this.encoding = encoding.getValue();
  124. }
  125. /**
  126. * Sets the mail server port
  127. *
  128. * @param port The port to use
  129. */
  130. public void setMailport(int port) {
  131. this.port = port;
  132. }
  133. /**
  134. * Sets the host
  135. *
  136. * @param host The host to connect to
  137. */
  138. public void setMailhost(String host) {
  139. this.host = host;
  140. }
  141. /**
  142. * Sets the subject line of the email
  143. *
  144. * @param subject Subject of this email.
  145. */
  146. public void setSubject(String subject) {
  147. this.subject = subject;
  148. }
  149. /**
  150. * Shorthand method to set the message
  151. *
  152. * @param message Message body of this email.
  153. */
  154. public void setMessage(String message) {
  155. if (this.message != null) {
  156. throw new BuildException("Only one message can be sent in an "
  157. + "email");
  158. }
  159. this.message = new Message(message);
  160. this.message.setProject(getProject());
  161. }
  162. /**
  163. * Shorthand method to set the message from a file
  164. *
  165. * @param file The file from which to take the message
  166. */
  167. public void setMessageFile(File file) {
  168. if (this.message != null) {
  169. throw new BuildException("Only one message can be sent in an "
  170. + "email");
  171. }
  172. this.message = new Message(file);
  173. this.message.setProject(getProject());
  174. }
  175. /**
  176. * Shorthand method to set type of the text message, text/plain by default
  177. * but text/html or text/xml is quite feasible.
  178. *
  179. * @param type The new MessageMimeType value
  180. */
  181. public void setMessageMimeType(String type) {
  182. this.messageMimeType = type;
  183. }
  184. /**
  185. * Add a message element
  186. *
  187. * @param message The message object
  188. * @throws BuildException if a message has already been added
  189. */
  190. public void addMessage(Message message)
  191. throws BuildException {
  192. if (this.message != null) {
  193. throw new BuildException("Only one message can be sent in an "
  194. + "email");
  195. }
  196. this.message = message;
  197. }
  198. /**
  199. * Adds a from address element
  200. *
  201. * @param address The address to send from
  202. */
  203. public void addFrom(EmailAddress address) {
  204. if (this.from != null) {
  205. throw new BuildException("Emails can only be from one address");
  206. }
  207. this.from = address;
  208. }
  209. /**
  210. * Shorthand to set the from address element
  211. *
  212. * @param address The address to send mail from
  213. */
  214. public void setFrom(String address) {
  215. if (this.from != null) {
  216. throw new BuildException("Emails can only be from one address");
  217. }
  218. this.from = new EmailAddress(address);
  219. }
  220. /**
  221. * Adds a replyto address element
  222. *
  223. * @param address The address to reply to
  224. * @since ant 1.6
  225. */
  226. public void addReplyTo(EmailAddress address) {
  227. this.replyToList.add(address);
  228. }
  229. /**
  230. * Shorthand to set the replyto address element
  231. *
  232. * @param address The address to which replies should be directed
  233. * @since ant 1.6
  234. */
  235. public void setReplyTo(String address) {
  236. this.replyToList.add(new EmailAddress(address));
  237. }
  238. /**
  239. * Adds a to address element
  240. *
  241. * @param address An email address
  242. */
  243. public void addTo(EmailAddress address) {
  244. toList.addElement(address);
  245. }
  246. /**
  247. * Adds "to" address elements
  248. *
  249. * @param list Comma separated list of addresses
  250. */
  251. public void setToList(String list) {
  252. StringTokenizer tokens = new StringTokenizer(list, ",");
  253. while (tokens.hasMoreTokens()) {
  254. toList.addElement(new EmailAddress(tokens.nextToken()));
  255. }
  256. }
  257. /**
  258. * Adds "cc" address element
  259. *
  260. * @param address The email address
  261. */
  262. public void addCc(EmailAddress address) {
  263. ccList.addElement(address);
  264. }
  265. /**
  266. * Adds "cc" address elements
  267. *
  268. * @param list Comma separated list of addresses
  269. */
  270. public void setCcList(String list) {
  271. StringTokenizer tokens = new StringTokenizer(list, ",");
  272. while (tokens.hasMoreTokens()) {
  273. ccList.addElement(new EmailAddress(tokens.nextToken()));
  274. }
  275. }
  276. /**
  277. * Adds "bcc" address elements
  278. *
  279. * @param address The email address
  280. */
  281. public void addBcc(EmailAddress address) {
  282. bccList.addElement(address);
  283. }
  284. /**
  285. * Adds "bcc" address elements
  286. *
  287. * @param list comma separated list of addresses
  288. */
  289. public void setBccList(String list) {
  290. StringTokenizer tokens = new StringTokenizer(list, ",");
  291. while (tokens.hasMoreTokens()) {
  292. bccList.addElement(new EmailAddress(tokens.nextToken()));
  293. }
  294. }
  295. /**
  296. * Indicates whether BuildExceptions should be passed back to the core
  297. *
  298. * @param failOnError The new FailOnError value
  299. */
  300. public void setFailOnError(boolean failOnError) {
  301. this.failOnError = failOnError;
  302. }
  303. /**
  304. * Adds a list of files to be attached
  305. *
  306. * @param filenames Comma separated list of files
  307. */
  308. public void setFiles(String filenames) {
  309. StringTokenizer t = new StringTokenizer(filenames, ", ");
  310. while (t.hasMoreTokens()) {
  311. files.addElement(getProject().resolveFile(t.nextToken()));
  312. }
  313. }
  314. /**
  315. * Adds a set of files (nested fileset attribute).
  316. *
  317. * @param fs The fileset
  318. */
  319. public void addFileset(FileSet fs) {
  320. filesets.addElement(fs);
  321. }
  322. /**
  323. * Sets Includefilenames attribute
  324. *
  325. * @param includeFileNames Whether to include filenames in the text of the
  326. * message
  327. */
  328. public void setIncludefilenames(boolean includeFileNames) {
  329. this.includeFileNames = includeFileNames;
  330. }
  331. /**
  332. * Identifies whether file names should be included
  333. *
  334. * @return Identifies whether file names should be included
  335. */
  336. public boolean getIncludeFileNames() {
  337. return includeFileNames;
  338. }
  339. /** Sends an email */
  340. public void execute() {
  341. Message savedMessage = message;
  342. Vector savedFiles = (Vector) files.clone();
  343. try {
  344. Mailer mailer = null;
  345. // prepare for the auto select mechanism
  346. boolean autoFound = false;
  347. // try MIME format
  348. if (encoding.equals(MIME)
  349. || (encoding.equals(AUTO) && !autoFound)) {
  350. try {
  351. mailer =
  352. (Mailer) Class.forName("org.apache.tools.ant.taskdefs.email.MimeMailer")
  353. .newInstance();
  354. autoFound = true;
  355. log("Using MIME mail", Project.MSG_VERBOSE);
  356. } catch (Throwable e) {
  357. log("Failed to initialise MIME mail: "
  358. + e.getMessage(), Project.MSG_WARN);
  359. }
  360. }
  361. // SMTP auth only allowed with MIME mail
  362. if (autoFound == false && ((user != null) || (password != null))
  363. && (encoding.equals(UU) || encoding.equals(PLAIN))) {
  364. throw new BuildException("SMTP auth only possible with MIME mail");
  365. }
  366. // SSL only allowed with MIME mail
  367. if (autoFound == false && (SSL)
  368. && (encoding.equals(UU) || encoding.equals(PLAIN))) {
  369. throw new BuildException("SSL only possible with MIME mail");
  370. }
  371. // try UU format
  372. if (encoding.equals(UU)
  373. || (encoding.equals(AUTO) && !autoFound)) {
  374. try {
  375. mailer =
  376. (Mailer) Class.forName("org.apache.tools.ant.taskdefs.email.UUMailer")
  377. .newInstance();
  378. autoFound = true;
  379. log("Using UU mail", Project.MSG_VERBOSE);
  380. } catch (Throwable e) {
  381. log("Failed to initialise UU mail", Project.MSG_WARN);
  382. }
  383. }
  384. // try plain format
  385. if (encoding.equals(PLAIN)
  386. || (encoding.equals(AUTO) && !autoFound)) {
  387. mailer = new PlainMailer();
  388. autoFound = true;
  389. log("Using plain mail", Project.MSG_VERBOSE);
  390. }
  391. // a valid mailer must be present by now
  392. if (mailer == null) {
  393. throw new BuildException("Failed to initialise encoding: "
  394. + encoding);
  395. }
  396. // a valid message is required
  397. if (message == null) {
  398. message = new Message();
  399. message.setProject(getProject());
  400. }
  401. // an address to send from is required
  402. if (from == null || from.getAddress() == null) {
  403. throw new BuildException("A from element is required");
  404. }
  405. // at least one address to send to/cc/bcc is required
  406. if (toList.isEmpty() && ccList.isEmpty() && bccList.isEmpty()) {
  407. throw new BuildException("At least one of to,cc or bcc must "
  408. + "be supplied");
  409. }
  410. // set the mimetype if not done already (and required)
  411. if (messageMimeType != null) {
  412. if (message.isMimeTypeSpecified()) {
  413. throw new BuildException("The mime type can only be "
  414. + "specified in one location");
  415. } else {
  416. message.setMimeType(messageMimeType);
  417. }
  418. }
  419. // set the character set if not done already (and required)
  420. if (charset != null) {
  421. if (message.getCharset() != null) {
  422. throw new BuildException("The charset can only be "
  423. + "specified in one location");
  424. } else {
  425. message.setCharset(charset);
  426. }
  427. }
  428. // identify which files should be attached
  429. Enumeration e = filesets.elements();
  430. while (e.hasMoreElements()) {
  431. FileSet fs = (FileSet) e.nextElement();
  432. DirectoryScanner ds = fs.getDirectoryScanner(getProject());
  433. String[] includedFiles = ds.getIncludedFiles();
  434. File baseDir = ds.getBasedir();
  435. for (int j = 0; j < includedFiles.length; ++j) {
  436. File file = new File(baseDir, includedFiles[j]);
  437. files.addElement(file);
  438. }
  439. }
  440. // let the user know what's going to happen
  441. log("Sending email: " + subject, Project.MSG_INFO);
  442. log("From " + from, Project.MSG_VERBOSE);
  443. log("ReplyTo " + replyToList, Project.MSG_VERBOSE);
  444. log("To " + toList, Project.MSG_VERBOSE);
  445. log("Cc " + ccList, Project.MSG_VERBOSE);
  446. log("Bcc " + bccList, Project.MSG_VERBOSE);
  447. // pass the params to the mailer
  448. mailer.setHost(host);
  449. mailer.setPort(port);
  450. mailer.setUser(user);
  451. mailer.setPassword(password);
  452. mailer.setSSL(SSL);
  453. mailer.setMessage(message);
  454. mailer.setFrom(from);
  455. mailer.setReplyToList(replyToList);
  456. mailer.setToList(toList);
  457. mailer.setCcList(ccList);
  458. mailer.setBccList(bccList);
  459. mailer.setFiles(files);
  460. mailer.setSubject(subject);
  461. mailer.setTask(this);
  462. mailer.setIncludeFileNames(includeFileNames);
  463. // send the email
  464. mailer.send();
  465. // let the user know what happened
  466. int count = files.size();
  467. log("Sent email with " + count + " attachment"
  468. + (count == 1 ? "" : "s"), Project.MSG_INFO);
  469. } catch (BuildException e) {
  470. log("Failed to send email", Project.MSG_WARN);
  471. if (failOnError) {
  472. throw e;
  473. }
  474. } catch (Exception e) {
  475. log("Failed to send email", Project.MSG_WARN);
  476. if (failOnError) {
  477. throw new BuildException(e);
  478. }
  479. } finally {
  480. message = savedMessage;
  481. files = savedFiles;
  482. }
  483. }
  484. /**
  485. * Sets the character set of mail message.
  486. * Will be ignored if mimeType contains ....; Charset=... substring or
  487. * encoding is not a <code>mime</code>
  488. * @since Ant 1.6
  489. */
  490. public void setCharset(String charset) {
  491. this.charset = charset;
  492. }
  493. /**
  494. * Returns the character set of mail message.
  495. *
  496. * @return Charset of mail message.
  497. * @since Ant 1.6
  498. */
  499. public String getCharset() {
  500. return charset;
  501. }
  502. }