/* * Copyright 2011 gitblit.com. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gitblit; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.regex.Pattern; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitblit.utils.StringUtils; /** * The mail executor handles sending email messages asynchronously from queue. * * @author James Moger * */ public class MailExecutor implements Runnable { private final Logger logger = LoggerFactory.getLogger(MailExecutor.class); private final Queue queue = new ConcurrentLinkedQueue(); private final Session session; private final IStoredSettings settings; public MailExecutor(IStoredSettings settings) { this.settings = settings; final String mailUser = settings.getString(Keys.mail.username, null); final String mailPassword = settings.getString(Keys.mail.password, null); final boolean smtps = settings.getBoolean(Keys.mail.smtps, false); boolean authenticate = !StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword); String server = settings.getString(Keys.mail.server, ""); if (StringUtils.isEmpty(server)) { session = null; return; } int port = settings.getInteger(Keys.mail.port, 25); boolean isGMail = false; if (server.equals("smtp.gmail.com")) { port = 465; isGMail = true; } Properties props = new Properties(); props.setProperty("mail.smtp.host", server); props.setProperty("mail.smtp.port", String.valueOf(port)); props.setProperty("mail.smtp.auth", String.valueOf(authenticate)); props.setProperty("mail.smtp.auths", String.valueOf(authenticate)); if (isGMail || smtps) { props.setProperty("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.socketFactory.port", String.valueOf(port)); props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.put("mail.smtp.socketFactory.fallback", "false"); } if (!StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword)) { // SMTP requires authentication session = Session.getInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { PasswordAuthentication passwordAuthentication = new PasswordAuthentication( mailUser, mailPassword); return passwordAuthentication; } }); } else { // SMTP does not require authentication session = Session.getInstance(props); } } /** * Indicates if the mail executor can send emails. * * @return true if the mail executor is ready to send emails */ public boolean isReady() { return session != null; } /** * Create a message. * * @param toAddresses * @return a message */ public Message createMessage(String... toAddresses) { return createMessage(Arrays.asList(toAddresses)); } /** * Create a message. * * @param toAddresses * @return a message */ public Message createMessage(List toAddresses) { MimeMessage message = new MimeMessage(session); try { String fromAddress = settings.getString(Keys.mail.fromAddress, null); if (StringUtils.isEmpty(fromAddress)) { fromAddress = "gitblit@gitblit.com"; } InternetAddress from = new InternetAddress(fromAddress, "Gitblit"); message.setFrom(from); // determine unique set of addresses Set uniques = new HashSet(); for (String address : toAddresses) { uniques.add(address.toLowerCase()); } Pattern validEmail = Pattern .compile("^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$"); List tos = new ArrayList(); for (String address : uniques) { if (StringUtils.isEmpty(address)) { continue; } if (validEmail.matcher(address).find()) { try { tos.add(new InternetAddress(address)); } catch (Throwable t) { } } } message.setRecipients(Message.RecipientType.BCC, tos.toArray(new InternetAddress[tos.size()])); message.setSentDate(new Date()); } catch (Exception e) { logger.error("Failed to properly create message", e); } return message; } /** * Returns the status of the mail queue. * * @return true, if the queue is empty */ public boolean hasEmptyQueue() { return queue.isEmpty(); } /** * Queue's an email message to be sent. * * @param message * @return true if the message was queued */ public boolean queue(Message message) { if (!isReady()) { return false; } try { message.saveChanges(); } catch (Throwable t) { logger.error("Failed to save changes to message!", t); } queue.add(message); return true; } @Override public void run() { if (!queue.isEmpty()) { if (session != null) { // send message via mail server List failures = new ArrayList(); Message message = null; while ((message = queue.poll()) != null) { try { if (settings.getBoolean(Keys.mail.debug, false)) { logger.info("send: " + StringUtils.trimString(message.getSubject(), 60)); } Transport.send(message); } catch (Throwable e) { logger.error("Failed to send message", e); failures.add(message); } } // push the failures back onto the queue for the next cycle queue.addAll(failures); } } } public void sendNow(Message message) throws Exception { Transport.send(message); } }