From f66e89662c091e082bd1d2feb6ac91513ccff273 Mon Sep 17 00:00:00 2001
From: Rafael Cavazin <rafaelcavazin@gmail.com>
Date: Sun, 21 Jul 2013 09:59:00 -0400
Subject: [PATCH] Merge branch 'master' of https://github.com/gitblit/gitblit

---
 src/main/java/com/gitblit/MailExecutor.java |  226 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 226 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/gitblit/MailExecutor.java b/src/main/java/com/gitblit/MailExecutor.java
new file mode 100644
index 0000000..c4e776a
--- /dev/null
+++ b/src/main/java/com/gitblit/MailExecutor.java
@@ -0,0 +1,226 @@
+/*
+ * 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<Message> queue = new ConcurrentLinkedQueue<Message>();
+
+	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() {
+				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<String> 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<String> uniques = new HashSet<String>();
+			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<InternetAddress> tos = new ArrayList<InternetAddress>();
+			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<Message> failures = new ArrayList<Message>();
+				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);
+	}
+}

--
Gitblit v1.9.1