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    }