summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/gitblit/service/MailService.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/gitblit/service/MailService.java')
-rw-r--r--src/main/java/com/gitblit/service/MailService.java229
1 files changed, 229 insertions, 0 deletions
diff --git a/src/main/java/com/gitblit/service/MailService.java b/src/main/java/com/gitblit/service/MailService.java
new file mode 100644
index 00000000..1d5e91f5
--- /dev/null
+++ b/src/main/java/com/gitblit/service/MailService.java
@@ -0,0 +1,229 @@
+/*
+ * 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.service;
+
+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.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * The mail service handles sending email messages asynchronously from a queue.
+ *
+ * @author James Moger
+ *
+ */
+public class MailService implements Runnable {
+
+ private final Logger logger = LoggerFactory.getLogger(MailService.class);
+
+ private final Queue<Message> queue = new ConcurrentLinkedQueue<Message>();
+
+ private final Session session;
+
+ private final IStoredSettings settings;
+
+ public MailService(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<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);
+ }
+}