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.tools.ant.taskdefs.email;
019:
020: import java.io.File;
021: import java.util.Iterator;
022: import java.util.StringTokenizer;
023: import java.util.Vector;
024:
025: import org.apache.tools.ant.BuildException;
026: import org.apache.tools.ant.Project;
027: import org.apache.tools.ant.Task;
028: import org.apache.tools.ant.types.EnumeratedAttribute;
029: import org.apache.tools.ant.types.FileSet;
030: import org.apache.tools.ant.types.Path;
031: import org.apache.tools.ant.types.resources.FileResource;
032: import org.apache.tools.ant.util.ClasspathUtils;
033:
034: /**
035: * A task to send SMTP email. This is a refactoring of the SendMail and
036: * MimeMail tasks such that both are within a single task.
037: *
038: * @since Ant 1.5
039: * @ant.task name="mail" category="network"
040: */
041: public class EmailTask extends Task {
042: /** Constant to show that the best available mailer should be used. */
043: public static final String AUTO = "auto";
044: /** Constant to allow the Mime mailer to be requested */
045: public static final String MIME = "mime";
046: /** Constant to allow the UU mailer to be requested */
047: public static final String UU = "uu";
048: /** Constant to allow the plaintext mailer to be requested */
049: public static final String PLAIN = "plain";
050:
051: /**
052: * Enumerates the encoding constants.
053: */
054: public static class Encoding extends EnumeratedAttribute {
055: /**
056: * finds the valid encoding values
057: *
058: * @return a list of valid entries
059: */
060: public String[] getValues() {
061: return new String[] { AUTO, MIME, UU, PLAIN };
062: }
063: }
064:
065: private String encoding = AUTO;
066: /** host running SMTP */
067: private String host = "localhost";
068: private int port = 25;
069: /** subject field */
070: private String subject = null;
071: /** any text */
072: private Message message = null;
073: /** failure flag */
074: private boolean failOnError = true;
075: private boolean includeFileNames = false;
076: private String messageMimeType = null;
077: /* special headers */
078: /** sender */
079: private EmailAddress from = null;
080: /** replyto */
081: private Vector replyToList = new Vector();
082: /** TO recipients */
083: private Vector toList = new Vector();
084: /** CC (Carbon Copy) recipients */
085: private Vector ccList = new Vector();
086: /** BCC (Blind Carbon Copy) recipients */
087: private Vector bccList = new Vector();
088:
089: /** generic headers */
090: private Vector headers = new Vector();
091:
092: /** file list */
093: private Path attachments = null;
094: /** Character set for MimeMailer*/
095: private String charset = null;
096: /** User for SMTP auth */
097: private String user = null;
098: /** Password for SMTP auth */
099: private String password = null;
100: /** indicate if the user wishes SSL-TLS */
101: private boolean ssl = false;
102:
103: /**
104: * Set the user for SMTP auth; this requires JavaMail.
105: * @param user the String username.
106: * @since Ant 1.6
107: */
108: public void setUser(String user) {
109: this .user = user;
110: }
111:
112: /**
113: * Set the password for SMTP auth; this requires JavaMail.
114: * @param password the String password.
115: * @since Ant 1.6
116: */
117: public void setPassword(String password) {
118: this .password = password;
119: }
120:
121: /**
122: * Set whether to send data over SSL.
123: * @param ssl boolean; if true SSL will be used.
124: * @since Ant 1.6
125: */
126: public void setSSL(boolean ssl) {
127: this .ssl = ssl;
128: }
129:
130: /**
131: * Set the preferred encoding method.
132: *
133: * @param encoding The encoding (one of AUTO, MIME, UU, PLAIN).
134: */
135: public void setEncoding(Encoding encoding) {
136: this .encoding = encoding.getValue();
137: }
138:
139: /**
140: * Set the mail server port.
141: *
142: * @param port The port to use.
143: */
144: public void setMailport(int port) {
145: this .port = port;
146: }
147:
148: /**
149: * Set the host.
150: *
151: * @param host The host to connect to.
152: */
153: public void setMailhost(String host) {
154: this .host = host;
155: }
156:
157: /**
158: * Set the subject line of the email.
159: *
160: * @param subject Subject of this email.
161: */
162: public void setSubject(String subject) {
163: this .subject = subject;
164: }
165:
166: /**
167: * Shorthand method to set the message.
168: *
169: * @param message Message body of this email.
170: */
171: public void setMessage(String message) {
172: if (this .message != null) {
173: throw new BuildException(
174: "Only one message can be sent in an " + "email");
175: }
176: this .message = new Message(message);
177: this .message.setProject(getProject());
178: }
179:
180: /**
181: * Shorthand method to set the message from a file.
182: *
183: * @param file The file from which to take the message.
184: */
185: public void setMessageFile(File file) {
186: if (this .message != null) {
187: throw new BuildException(
188: "Only one message can be sent in an " + "email");
189: }
190: this .message = new Message(file);
191: this .message.setProject(getProject());
192: }
193:
194: /**
195: * Shorthand method to set type of the text message, text/plain by default
196: * but text/html or text/xml is quite feasible.
197: *
198: * @param type The new MessageMimeType value.
199: */
200: public void setMessageMimeType(String type) {
201: this .messageMimeType = type;
202: }
203:
204: /**
205: * Add a message element.
206: *
207: * @param message The message object.
208: * @throws BuildException if a message has already been added.
209: */
210: public void addMessage(Message message) throws BuildException {
211: if (this .message != null) {
212: throw new BuildException(
213: "Only one message can be sent in an email");
214: }
215: this .message = message;
216: }
217:
218: /**
219: * Add a from address element.
220: *
221: * @param address The address to send from.
222: */
223: public void addFrom(EmailAddress address) {
224: if (this .from != null) {
225: throw new BuildException(
226: "Emails can only be from one address");
227: }
228: this .from = address;
229: }
230:
231: /**
232: * Shorthand to set the from address element.
233: *
234: * @param address The address to send mail from.
235: */
236: public void setFrom(String address) {
237: if (this .from != null) {
238: throw new BuildException(
239: "Emails can only be from one address");
240: }
241: this .from = new EmailAddress(address);
242: }
243:
244: /**
245: * Add a replyto address element.
246: *
247: * @param address The address to reply to.
248: * @since Ant 1.6
249: */
250: public void addReplyTo(EmailAddress address) {
251: this .replyToList.add(address);
252: }
253:
254: /**
255: * Shorthand to set the replyto address element.
256: *
257: * @param address The address to which replies should be directed.
258: * @since Ant 1.6
259: */
260: public void setReplyTo(String address) {
261: this .replyToList.add(new EmailAddress(address));
262: }
263:
264: /**
265: * Add a to address element.
266: *
267: * @param address An email address.
268: */
269: public void addTo(EmailAddress address) {
270: toList.addElement(address);
271: }
272:
273: /**
274: * Shorthand to set the "to" address element.
275: *
276: * @param list Comma-separated list of addresses.
277: */
278: public void setToList(String list) {
279: StringTokenizer tokens = new StringTokenizer(list, ",");
280:
281: while (tokens.hasMoreTokens()) {
282: toList.addElement(new EmailAddress(tokens.nextToken()));
283: }
284: }
285:
286: /**
287: * Add a "cc" address element.
288: *
289: * @param address The email address.
290: */
291: public void addCc(EmailAddress address) {
292: ccList.addElement(address);
293: }
294:
295: /**
296: * Shorthand to set the "cc" address element.
297: *
298: * @param list Comma separated list of addresses.
299: */
300: public void setCcList(String list) {
301: StringTokenizer tokens = new StringTokenizer(list, ",");
302:
303: while (tokens.hasMoreTokens()) {
304: ccList.addElement(new EmailAddress(tokens.nextToken()));
305: }
306: }
307:
308: /**
309: * Add a "bcc" address element.
310: *
311: * @param address The email address.
312: */
313: public void addBcc(EmailAddress address) {
314: bccList.addElement(address);
315: }
316:
317: /**
318: * Shorthand to set the "bcc" address element.
319: *
320: * @param list comma separated list of addresses.
321: */
322: public void setBccList(String list) {
323: StringTokenizer tokens = new StringTokenizer(list, ",");
324:
325: while (tokens.hasMoreTokens()) {
326: bccList.addElement(new EmailAddress(tokens.nextToken()));
327: }
328: }
329:
330: /**
331: * Set whether BuildExceptions should be passed back to the core.
332: *
333: * @param failOnError The new FailOnError value.
334: */
335: public void setFailOnError(boolean failOnError) {
336: this .failOnError = failOnError;
337: }
338:
339: /**
340: * Set the list of files to be attached.
341: *
342: * @param filenames Comma-separated list of files.
343: */
344: public void setFiles(String filenames) {
345: StringTokenizer t = new StringTokenizer(filenames, ", ");
346:
347: while (t.hasMoreTokens()) {
348: createAttachments().add(
349: new FileResource(getProject().resolveFile(
350: t.nextToken())));
351: }
352: }
353:
354: /**
355: * Add a set of files (nested fileset attribute).
356: *
357: * @param fs The fileset.
358: */
359: public void addFileset(FileSet fs) {
360: createAttachments().add(fs);
361: }
362:
363: /**
364: * Creates a Path as container for attachments. Supports any
365: * filesystem resource-collections that way.
366: * @return the path to be configured.
367: * @since Ant 1.7
368: */
369: public Path createAttachments() {
370: if (attachments == null) {
371: attachments = new Path(getProject());
372: }
373: return attachments.createPath();
374: }
375:
376: /**
377: * Create a nested header element.
378: * @return a Header instance.
379: */
380: public Header createHeader() {
381: Header h = new Header();
382: headers.add(h);
383: return h;
384: }
385:
386: /**
387: * Set whether to include filenames.
388: *
389: * @param includeFileNames Whether to include filenames in the text of the
390: * message.
391: */
392: public void setIncludefilenames(boolean includeFileNames) {
393: this .includeFileNames = includeFileNames;
394: }
395:
396: /**
397: * Get whether file names should be included.
398: *
399: * @return Identifies whether file names should be included.
400: */
401: public boolean getIncludeFileNames() {
402: return includeFileNames;
403: }
404:
405: /**
406: * Send an email.
407: */
408: public void execute() {
409: Message savedMessage = message;
410:
411: try {
412: Mailer mailer = null;
413:
414: // prepare for the auto select mechanism
415: boolean autoFound = false;
416: // try MIME format
417: if (encoding.equals(MIME)
418: || (encoding.equals(AUTO) && !autoFound)) {
419: try {
420: mailer = (Mailer) ClasspathUtils
421: .newInstance(
422: "org.apache.tools.ant.taskdefs.email.MimeMailer",
423: EmailTask.class.getClassLoader(),
424: Mailer.class);
425: autoFound = true;
426: log("Using MIME mail", Project.MSG_VERBOSE);
427: } catch (BuildException e) {
428: Throwable t = e.getCause() == null ? e : e
429: .getCause();
430: log("Failed to initialise MIME mail: "
431: + t.getMessage(), Project.MSG_WARN);
432: return;
433: }
434: }
435: // SMTP auth only allowed with MIME mail
436: if (!autoFound && ((user != null) || (password != null))
437: && (encoding.equals(UU) || encoding.equals(PLAIN))) {
438: throw new BuildException(
439: "SMTP auth only possible with MIME mail");
440: }
441: // SSL only allowed with MIME mail
442: if (!autoFound && (ssl)
443: && (encoding.equals(UU) || encoding.equals(PLAIN))) {
444: throw new BuildException(
445: "SSL only possible with MIME mail");
446: }
447: // try UU format
448: if (encoding.equals(UU)
449: || (encoding.equals(AUTO) && !autoFound)) {
450: try {
451: mailer = (Mailer) ClasspathUtils
452: .newInstance(
453: "org.apache.tools.ant.taskdefs.email.UUMailer",
454: EmailTask.class.getClassLoader(),
455: Mailer.class);
456: autoFound = true;
457: log("Using UU mail", Project.MSG_VERBOSE);
458: } catch (BuildException e) {
459: Throwable t = e.getCause() == null ? e : e
460: .getCause();
461: log("Failed to initialise UU mail: "
462: + t.getMessage(), Project.MSG_WARN);
463: return;
464: }
465: }
466: // try plain format
467: if (encoding.equals(PLAIN)
468: || (encoding.equals(AUTO) && !autoFound)) {
469: mailer = new PlainMailer();
470: autoFound = true;
471: log("Using plain mail", Project.MSG_VERBOSE);
472: }
473: // a valid mailer must be present by now
474: if (mailer == null) {
475: throw new BuildException(
476: "Failed to initialise encoding: " + encoding);
477: }
478: // a valid message is required
479: if (message == null) {
480: message = new Message();
481: message.setProject(getProject());
482: }
483: // an address to send from is required
484: if (from == null || from.getAddress() == null) {
485: throw new BuildException("A from element is required");
486: }
487: // at least one address to send to/cc/bcc is required
488: if (toList.isEmpty() && ccList.isEmpty()
489: && bccList.isEmpty()) {
490: throw new BuildException(
491: "At least one of to, cc or bcc must "
492: + "be supplied");
493: }
494: // set the mimetype if not done already (and required)
495: if (messageMimeType != null) {
496: if (message.isMimeTypeSpecified()) {
497: throw new BuildException(
498: "The mime type can only be "
499: + "specified in one location");
500: }
501: message.setMimeType(messageMimeType);
502: }
503: // set the character set if not done already (and required)
504: if (charset != null) {
505: if (message.getCharset() != null) {
506: throw new BuildException("The charset can only be "
507: + "specified in one location");
508: }
509: message.setCharset(charset);
510: }
511:
512: // identify which files should be attached
513: Vector files = new Vector();
514: if (attachments != null) {
515: Iterator iter = attachments.iterator();
516:
517: while (iter.hasNext()) {
518: FileResource fr = (FileResource) iter.next();
519: files.addElement(fr.getFile());
520: }
521: }
522: // let the user know what's going to happen
523: log("Sending email: " + subject, Project.MSG_INFO);
524: log("From " + from, Project.MSG_VERBOSE);
525: log("ReplyTo " + replyToList, Project.MSG_VERBOSE);
526: log("To " + toList, Project.MSG_VERBOSE);
527: log("Cc " + ccList, Project.MSG_VERBOSE);
528: log("Bcc " + bccList, Project.MSG_VERBOSE);
529:
530: // pass the params to the mailer
531: mailer.setHost(host);
532: mailer.setPort(port);
533: mailer.setUser(user);
534: mailer.setPassword(password);
535: mailer.setSSL(ssl);
536: mailer.setMessage(message);
537: mailer.setFrom(from);
538: mailer.setReplyToList(replyToList);
539: mailer.setToList(toList);
540: mailer.setCcList(ccList);
541: mailer.setBccList(bccList);
542: mailer.setFiles(files);
543: mailer.setSubject(subject);
544: mailer.setTask(this );
545: mailer.setIncludeFileNames(includeFileNames);
546: mailer.setHeaders(headers);
547:
548: // send the email
549: mailer.send();
550:
551: // let the user know what happened
552: int count = files.size();
553:
554: log("Sent email with " + count + " attachment"
555: + (count == 1 ? "" : "s"), Project.MSG_INFO);
556: } catch (BuildException e) {
557: Throwable t = e.getCause() == null ? e : e.getCause();
558: log("Failed to send email: " + t.getMessage(),
559: Project.MSG_WARN);
560: if (failOnError) {
561: throw e;
562: }
563: } catch (Exception e) {
564: log("Failed to send email: " + e.getMessage(),
565: Project.MSG_WARN);
566: if (failOnError) {
567: throw new BuildException(e);
568: }
569: } finally {
570: message = savedMessage;
571: }
572: }
573:
574: /**
575: * Sets the character set of mail message.
576: * Will be ignored if mimeType contains ....; Charset=... substring or
577: * encoding is not a <code>mime</code>.
578: * @param charset the character encoding to use.
579: * @since Ant 1.6
580: */
581: public void setCharset(String charset) {
582: this .charset = charset;
583: }
584:
585: /**
586: * Returns the character set of mail message.
587: *
588: * @return Charset of mail message.
589: * @since Ant 1.6
590: */
591: public String getCharset() {
592: return charset;
593: }
594:
595: }
|