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.io.InputStream;
022: import java.io.IOException;
023: import java.io.PrintStream;
024: import java.io.OutputStream;
025: import java.io.ByteArrayInputStream;
026: import java.io.ByteArrayOutputStream;
027: import java.io.UnsupportedEncodingException;
028:
029: import java.util.Vector;
030: import java.util.Iterator;
031: import java.util.Properties;
032: import java.util.Enumeration;
033: import java.util.StringTokenizer;
034:
035: import java.security.Provider;
036: import java.security.Security;
037:
038: import javax.activation.DataHandler;
039: import javax.activation.FileDataSource;
040:
041: import javax.mail.Message;
042: import javax.mail.Session;
043: import javax.mail.Transport;
044: import javax.mail.Authenticator;
045: import javax.mail.MessagingException;
046: import javax.mail.PasswordAuthentication;
047: import javax.mail.internet.MimeMessage;
048: import javax.mail.internet.MimeBodyPart;
049: import javax.mail.internet.MimeMultipart;
050: import javax.mail.internet.InternetAddress;
051: import javax.mail.internet.AddressException;
052:
053: import org.apache.tools.ant.BuildException;
054:
055: /**
056: * Uses the JavaMail classes to send Mime format email.
057: *
058: * @since Ant 1.5
059: */
060: public class MimeMailer extends Mailer {
061: private static final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
062:
063: /** Default character set */
064: private static final String DEFAULT_CHARSET = System
065: .getProperty("file.encoding");
066:
067: // To work properly with national charsets we have to use
068: // implementation of interface javax.activation.DataSource
069: /**
070: * String data source implementation.
071: * @since Ant 1.6
072: */
073: class StringDataSource implements javax.activation.DataSource {
074: private String data = null;
075: private String type = null;
076: private String charset = null;
077: private ByteArrayOutputStream out;
078:
079: public InputStream getInputStream() throws IOException {
080: if (data == null && out == null) {
081: throw new IOException("No data");
082: }
083: if (out != null) {
084: String encodedOut = out.toString(charset);
085: data = (data != null) ? data.concat(encodedOut)
086: : encodedOut;
087: out = null;
088: }
089: return new ByteArrayInputStream(data.getBytes(charset));
090: }
091:
092: public OutputStream getOutputStream() throws IOException {
093: out = (out == null) ? new ByteArrayOutputStream() : out;
094: return out;
095: }
096:
097: public void setContentType(String type) {
098: this .type = type.toLowerCase();
099: }
100:
101: public String getContentType() {
102: if (type != null && type.indexOf("charset") > 0
103: && type.startsWith("text/")) {
104: return type;
105: }
106: // Must be like "text/plain; charset=windows-1251"
107: return new StringBuffer(type != null ? type : "text/plain")
108: .append("; charset=").append(charset).toString();
109: }
110:
111: public String getName() {
112: return "StringDataSource";
113: }
114:
115: public void setCharset(String charset) {
116: this .charset = charset;
117: }
118:
119: public String getCharset() {
120: return charset;
121: }
122: }
123:
124: /**
125: * Send the email.
126: *
127: * @throws BuildException if the email can't be sent.
128: */
129: public void send() {
130: try {
131: Properties props = new Properties();
132:
133: props.put("mail.smtp.host", host);
134: props.put("mail.smtp.port", String.valueOf(port));
135:
136: // Aside, the JDK is clearly unaware of the Scottish
137: // 'session', which involves excessive quantities of
138: // alcohol :-)
139: Session sesh;
140: Authenticator auth;
141: if (SSL) {
142: try {
143: Provider p = (Provider) Class.forName(
144: "com.sun.net.ssl.internal.ssl.Provider")
145: .newInstance();
146: Security.addProvider(p);
147: } catch (Exception e) {
148: throw new BuildException(
149: "could not instantiate ssl "
150: + "security provider, check that you have JSSE in "
151: + "your classpath");
152: }
153: // SMTP provider
154: props.put("mail.smtp.socketFactory.class", SSL_FACTORY);
155: props.put("mail.smtp.socketFactory.fallback", "false");
156: }
157: if (user == null && password == null) {
158: sesh = Session.getDefaultInstance(props, null);
159: } else {
160: props.put("mail.smtp.auth", "true");
161: auth = new SimpleAuthenticator(user, password);
162: sesh = Session.getInstance(props, auth);
163: }
164: //create the message
165: MimeMessage msg = new MimeMessage(sesh);
166: MimeMultipart attachments = new MimeMultipart();
167:
168: //set the sender
169: if (from.getName() == null) {
170: msg.setFrom(new InternetAddress(from.getAddress()));
171: } else {
172: msg.setFrom(new InternetAddress(from.getAddress(), from
173: .getName()));
174: }
175: // set the reply to addresses
176: msg.setReplyTo(internetAddresses(replyToList));
177: msg.setRecipients(Message.RecipientType.TO,
178: internetAddresses(toList));
179: msg.setRecipients(Message.RecipientType.CC,
180: internetAddresses(ccList));
181: msg.setRecipients(Message.RecipientType.BCC,
182: internetAddresses(bccList));
183:
184: // Choosing character set of the mail message
185: // First: looking it from MimeType
186: String charset = parseCharSetFromMimeType(message
187: .getMimeType());
188: if (charset != null) {
189: // Assign/reassign message charset from MimeType
190: message.setCharset(charset);
191: } else {
192: // Next: looking if charset having explicit definition
193: charset = message.getCharset();
194: if (charset == null) {
195: // Using default
196: charset = DEFAULT_CHARSET;
197: message.setCharset(charset);
198: }
199: }
200: // Using javax.activation.DataSource paradigm
201: StringDataSource sds = new StringDataSource();
202: sds.setContentType(message.getMimeType());
203: sds.setCharset(charset);
204:
205: if (subject != null) {
206: msg.setSubject(subject, charset);
207: }
208: msg.addHeader("Date", getDate());
209:
210: for (Iterator iter = headers.iterator(); iter.hasNext();) {
211: Header h = (Header) iter.next();
212: msg.addHeader(h.getName(), h.getValue());
213: }
214: PrintStream out = new PrintStream(sds.getOutputStream());
215: message.print(out);
216: out.close();
217:
218: MimeBodyPart textbody = new MimeBodyPart();
219: textbody.setDataHandler(new DataHandler(sds));
220: attachments.addBodyPart(textbody);
221:
222: Enumeration e = files.elements();
223:
224: while (e.hasMoreElements()) {
225: File file = (File) e.nextElement();
226:
227: MimeBodyPart body;
228:
229: body = new MimeBodyPart();
230: if (!file.exists() || !file.canRead()) {
231: throw new BuildException("File \""
232: + file.getAbsolutePath()
233: + "\" does not exist or is not "
234: + "readable.");
235: }
236: FileDataSource fileData = new FileDataSource(file);
237: DataHandler fileDataHandler = new DataHandler(fileData);
238:
239: body.setDataHandler(fileDataHandler);
240: body.setFileName(file.getName());
241: attachments.addBodyPart(body);
242: }
243: msg.setContent(attachments);
244: Transport.send(msg);
245: } catch (MessagingException e) {
246: throw new BuildException(
247: "Problem while sending mime mail:", e);
248: } catch (IOException e) {
249: throw new BuildException(
250: "Problem while sending mime mail:", e);
251: }
252: }
253:
254: private static InternetAddress[] internetAddresses(Vector list)
255: throws AddressException, UnsupportedEncodingException {
256: InternetAddress[] addrs = new InternetAddress[list.size()];
257:
258: for (int i = 0; i < list.size(); ++i) {
259: EmailAddress addr = (EmailAddress) list.elementAt(i);
260:
261: String name = addr.getName();
262: addrs[i] = (name == null) ? new InternetAddress(addr
263: .getAddress()) : new InternetAddress(addr
264: .getAddress(), name);
265: }
266: return addrs;
267: }
268:
269: private String parseCharSetFromMimeType(String type) {
270: int pos;
271: if (type == null || (pos = type.indexOf("charset")) < 0) {
272: return null;
273: }
274: // Assuming mime type in form "text/XXXX; charset=XXXXXX"
275: StringTokenizer token = new StringTokenizer(
276: type.substring(pos), "=; ");
277: token.nextToken(); // Skip 'charset='
278: return token.nextToken();
279: }
280:
281: static class SimpleAuthenticator extends Authenticator {
282: private String user = null;
283: private String password = null;
284:
285: public SimpleAuthenticator(String user, String password) {
286: this .user = user;
287: this .password = password;
288: }
289:
290: public PasswordAuthentication getPasswordAuthentication() {
291:
292: return new PasswordAuthentication(user, password);
293: }
294: }
295: }
|