001 /*
002 *
003 * $Revision: 15111 $ $Date: 2009-04-28 11:50:42 +0200 (Tue, 28 Apr 2009) $
004 *
005 * This file is part of *** M y C o R e ***
006 * See http://www.mycore.de/ for details.
007 *
008 * This program is free software; you can use it, redistribute it
009 * and / or modify it under the terms of the GNU General Public License
010 * (GPL) as published by the Free Software Foundation; either version 2
011 * of the License or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program, in a file called gpl.txt or license.txt.
020 * If not, write to the Free Software Foundation Inc.,
021 * 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
022 */
023
024 package org.mycore.common;
025
026 import java.net.URL;
027 import java.util.ArrayList;
028 import java.util.Date;
029 import java.util.List;
030 import java.util.Properties;
031
032 import javax.activation.DataHandler;
033 import javax.activation.DataSource;
034 import javax.activation.URLDataSource;
035 import javax.mail.Message;
036 import javax.mail.Multipart;
037 import javax.mail.Session;
038 import javax.mail.Transport;
039 import javax.mail.internet.InternetAddress;
040 import javax.mail.internet.MimeBodyPart;
041 import javax.mail.internet.MimeMessage;
042 import javax.mail.internet.MimeMultipart;
043
044 import org.apache.log4j.Logger;
045 import org.jdom.Element;
046
047 /**
048 * This class provides methods to send emails from within a MyCoRe application.
049 *
050 * @author Marc Schluepmann
051 * @author Frank Lützenkirchen
052 * @author Werner Greßhoff
053 *
054 * @version $Revision: 15111 $ $Date: 2009-04-28 11:50:42 +0200 (Tue, 28 Apr 2009) $
055 */
056 public class MCRMailer {
057 /** Logger */
058 static Logger logger = Logger.getLogger(MCRMailer.class);
059
060 protected static Session mailSession;
061
062 protected static String encoding;
063
064 /** How often should MCRMailer try to send mail? */
065 protected static int numTries;
066
067 /** Initializes the class */
068 static {
069 MCRConfiguration config = MCRConfiguration.instance();
070 encoding = config.getString("MCR.Mail.Encoding", "ISO-8859-1");
071
072 Properties mailProperties = new Properties();
073
074 try {
075 numTries = config.getInt("MCR.Mail.NumTries", 1);
076 mailProperties.setProperty("mail.smtp.host", config.getString("MCR.Mail.Server"));
077 mailProperties.setProperty("mail.transport.protocol", config.getString("MCR.Mail.Protocol", "smtp"));
078 mailSession = Session.getDefaultInstance(mailProperties, null);
079 mailSession.setDebug(config.getBoolean("MCR.Mail.Debug", false));
080 } catch (MCRConfigurationException mcrx) {
081 String msg = "Missing email configuration data.";
082 logger.fatal(msg, mcrx);
083 }
084 }
085
086 /**
087 * This method sends a simple plaintext email with the given parameters.
088 *
089 * @param sender
090 * the sender of the email
091 * @param recipient
092 * the recipient of the email
093 * @param subject
094 * the subject of the email
095 * @param body
096 * the textbody of the email
097 */
098 public static void send(String sender, String recipient, String subject, String body) {
099 logger.debug("Called plaintext send method with single recipient.");
100
101 ArrayList<String> recipients = new ArrayList<String>();
102 recipients.add(recipient);
103 send(sender, null, recipients, null, subject, body, null);
104 }
105
106 /**
107 * This method sends a simple plaintext email to more than one recipient. If
108 * flag BCC is true, the sender will also get the email as BCC recipient.
109 *
110 * @param sender
111 * the sender of the email
112 * @param recipients
113 * the recipients of the email as a List of Strings
114 * @param subject
115 * the subject of the email
116 * @param body
117 * the textbody of the email
118 * @param bcc
119 * if true, sender will also get a copy as cc recipient
120 */
121 public static void send(String sender, List<String> recipients, String subject, String body, boolean bcc) {
122 logger.debug("Called plaintext send method with multiple recipients.");
123
124 List<String> bccList = null;
125
126 if (bcc) {
127 bccList = new ArrayList<String>();
128 bccList.add(sender);
129 }
130
131 send(sender, null, recipients, bccList, subject, body, null);
132 }
133
134 /**
135 * This method sends a multipart email with the given parameters.
136 *
137 * @param sender
138 * the sender of the email
139 * @param recipient
140 * the recipient of the email
141 * @param subject
142 * the subject of the email
143 * @param parts
144 * a List of URL strings which should be added as parts
145 * @param body
146 * the textbody of the email
147 */
148 public static void send(String sender, String recipient, String subject, String body, List<String> parts) {
149 logger.debug("Called multipart send method with single recipient.");
150
151 ArrayList<String> recipients = new ArrayList<String>();
152 recipients.add(recipient);
153 send(sender, null, recipients, null, subject, body, parts);
154 }
155
156 /**
157 * This method sends a multipart email to more than one recipient. If flag
158 * BCC is true, the sender will also get the email as BCC recipient.
159 *
160 * @param sender
161 * the sender of the email
162 * @param recipients
163 * the recipients of the email as a List of Strings
164 * @param subject
165 * the subject of the email
166 * @param body
167 * the textbody of the email
168 * @param parts
169 * a List of URL strings which should be added as parts
170 * @param bcc
171 * if true, sender will also get a copy as bcc recipient
172 */
173 public static void send(String sender, List<String> recipients, String subject, String body, List<String> parts, boolean bcc) {
174 logger.debug("Called multipart send method with multiple recipients.");
175
176 List<String> bccList = null;
177
178 if (bcc) {
179 bccList = new ArrayList<String>();
180 bccList.add(sender);
181 }
182
183 send(sender, null, recipients, bccList, subject, body, parts);
184 }
185
186 /**
187 * Send email from a given XML document. See the sample mail below:
188 *
189 * <email><from>bingo@bongo.com</from>
190 * <to>jim.knopf@lummerland.de</to>
191 * <bcc>frau.waas@lummerland.de</bcc>
192 * <subject>Grüße aus der Stadt der Drachen</subject>
193 * <body>Es ist recht bewölkt. Alles Gute, Jim.</body>
194 * <part>http://upload.wikimedia.org/wikipedia/de/f/f7/JimKnopf.jpg</part>
195 * </email>
196 *
197 * @param email the email as JDOM element.
198 */
199 public static void send(Element email) {
200 String from = email.getChildTextTrim("from");
201
202 @SuppressWarnings("unchecked")
203 List<Element> rptList = email.getChildren("replyTo");
204 List<String> replyTo = new ArrayList<String>();
205
206 for (Element reply : rptList)
207 replyTo.add(reply.getTextTrim());
208
209 @SuppressWarnings("unchecked")
210 List<Element> toList = email.getChildren("to");
211 List<String> to = new ArrayList<String>();
212
213 for (Element toElement : toList)
214 to.add(toElement.getTextTrim());
215
216 @SuppressWarnings("unchecked")
217 List<Element> bccList = email.getChildren("bcc");
218 List<String> bcc = new ArrayList<String>();
219
220 for (Element bccElement : bccList)
221 bcc.add(bccElement.getTextTrim());
222
223 String subject = email.getChildTextTrim("subject");
224 String body = email.getChildTextTrim("body");
225
226 @SuppressWarnings("unchecked")
227 List<Element> partsList = email.getChildren("part");
228 List<String> parts = new ArrayList<String>();
229
230 for (Element partsElement : partsList)
231 parts.add(partsElement.getTextTrim());
232
233 send(from, replyTo, to, bcc, subject, body, parts);
234 }
235
236 /**
237 * Sends email. When sending email fails (for example, outgoing mail server
238 * is not responding), sending will be retried after five minutes. This is
239 * done up to 10 times.
240 *
241 *
242 * @param from
243 * the sender of the email
244 * @param replyTo
245 * the reply-to addresses as a List of Strings, may be null
246 * @param to
247 * the recipients of the email as a List of Strings
248 * @param bcc
249 * the bcc recipients of the email as a List of Strings, may be
250 * null
251 * @param subject
252 * the subject of the email
253 * @param body
254 * the text of the email
255 * @param parts
256 * a List of URL strings which should be added as parts, may be
257 * null
258 */
259 public static void send(final String from, final List<String> replyTo, final List<String> to, final List<String> bcc,
260 final String subject, final String body, final List<String> parts) {
261 if (to == null || to.size() == 0) {
262 StringBuilder sb = new StringBuilder("No receiver defined for mail\n");
263 sb.append("Subject: ").append(subject).append('\n');
264 sb.append("Body:\n").append(body).append('\n');
265 sb.append("Parts: ").append(parts).append('\n');
266 throw new MCRException(sb.toString());
267 }
268 try {
269 if (numTries > 0)
270 trySending(from, replyTo, to, bcc, subject, body, parts);
271 } catch (Exception ex) {
272 logger.info("Sending email failed: ", ex);
273 if (numTries < 2)
274 return;
275
276 Thread t = new Thread(new Runnable() {
277 public void run() {
278 for (int i = numTries - 1; i > 0; i--) {
279 logger.info("Retrying in 5 minutes...");
280 Object obj = new Object();
281 try {
282 synchronized (obj) {
283 obj.wait(300000);
284 }
285 } // wait 5 minutes
286 catch (InterruptedException ignored) {
287 }
288
289 try {
290 trySending(from, replyTo, to, bcc, subject, body, parts);
291 logger.info("Successfully resended email.");
292 break;
293 } catch (Exception ex) {
294 logger.info("Sending email failed: ", ex);
295 }
296 }
297 }
298 });
299 t.start(); // Try to resend mail in separate thread
300 }
301 }
302
303 private static void trySending(String from, List<String> replyTo, List<String> to, List<String> bcc, String subject, String body,
304 List<String> parts) throws Exception {
305 MimeMessage msg = new MimeMessage(mailSession);
306 msg.setFrom(buildAddress(from));
307
308 if (replyTo != null) {
309 InternetAddress[] adrs = new InternetAddress[replyTo.size()];
310
311 for (int i = 0; i < replyTo.size(); i++)
312 adrs[i] = buildAddress((replyTo.get(i)));
313
314 msg.setReplyTo(adrs);
315 }
316
317 for (int i = 0; i < to.size(); i++)
318 msg.addRecipient(Message.RecipientType.TO, buildAddress(to.get(i)));
319
320 if (bcc != null) {
321 for (int i = 0; i < bcc.size(); i++)
322 msg.addRecipient(Message.RecipientType.BCC, buildAddress(bcc.get(i)));
323 }
324
325 msg.setSentDate(new Date());
326 msg.setSubject(subject, encoding);
327
328 if ((parts == null) || (parts.size() == 0)) {
329 msg.setText(body, encoding);
330 } else {
331 // Create the message part
332 MimeBodyPart messagePart = new MimeBodyPart();
333
334 // Fill the message
335 messagePart.setText(body, encoding);
336
337 Multipart multipart = new MimeMultipart();
338 multipart.addBodyPart(messagePart);
339
340 for (int i = 0; i < parts.size(); i++) {
341 messagePart = new MimeBodyPart();
342
343 URL url = new URL(parts.get(i));
344 DataSource source = new URLDataSource(url);
345 messagePart.setDataHandler(new DataHandler(source));
346
347 String fileName = url.getPath();
348 if (fileName.contains("\\"))
349 fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
350 else if (fileName.contains("/"))
351 fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
352 messagePart.setFileName(fileName);
353
354 multipart.addBodyPart(messagePart);
355 }
356
357 // Put parts in message
358 msg.setContent(multipart);
359 }
360
361 logger.info("Sending email to " + to.get(0));
362 Transport.send(msg);
363 }
364
365 /**
366 * Builds email address from a string. The string may be a single email
367 * address or a combination of a personal name and address, like "John Doe"
368 * <john@doe.com>
369 */
370 private static InternetAddress buildAddress(String s) throws Exception {
371 if (!s.endsWith(">")) {
372 return new InternetAddress(s.trim());
373 }
374
375 String name = s.substring(0, s.lastIndexOf("<")).trim();
376 String addr = s.substring(s.lastIndexOf("<") + 1, s.length() - 1).trim();
377
378 // Name must be quoted if it contains umlauts or special characters
379 if (!name.startsWith("\""))
380 name = "\"" + name;
381 if (!name.endsWith("\""))
382 name = name + "\"";
383
384 return new InternetAddress(addr, name);
385 }
386 }