From d7c5a62f95c06839bda044d4bed11117219ad1a7 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Fri, 28 Feb 2014 13:49:09 -0500
Subject: [PATCH] Improve notification api by introducing the Mailing model

---
 src/main/java/com/gitblit/manager/INotificationManager.java |   38 ---
 src/main/java/com/gitblit/FederationClient.java             |   15 -
 src/main/java/com/gitblit/manager/GitblitManager.java       |   20 -
 src/main/java/com/gitblit/authority/GitblitAuthority.java   |   29 --
 src/main/java/com/gitblit/service/MailService.java          |  157 ++++++++++++-----
 src/main/java/com/gitblit/manager/NotificationManager.java  |  123 ++----------
 src/main/java/com/gitblit/models/Mailing.java               |  111 ++++++++++++
 src/test/java/com/gitblit/tests/MailTest.java               |    5 
 8 files changed, 267 insertions(+), 231 deletions(-)

diff --git a/src/main/java/com/gitblit/FederationClient.java b/src/main/java/com/gitblit/FederationClient.java
index 4f4b00b..628a05d 100644
--- a/src/main/java/com/gitblit/FederationClient.java
+++ b/src/main/java/com/gitblit/FederationClient.java
@@ -32,6 +32,7 @@
 import com.gitblit.manager.RuntimeManager;
 import com.gitblit.manager.UserManager;
 import com.gitblit.models.FederationModel;
+import com.gitblit.models.Mailing;
 import com.gitblit.service.FederationPullService;
 import com.gitblit.utils.FederationUtils;
 import com.gitblit.utils.StringUtils;
@@ -178,23 +179,11 @@
 		}
 
 		@Override
-		public void sendMail(String subject, String message, String... toAddresses) {
-		}
-
-		@Override
 		public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) {
 		}
 
 		@Override
-		public void sendHtmlMail(String subject, String message, String... toAddresses) {
-		}
-
-		@Override
-		public void sendHtmlMail(String from, String subject, String message, Collection<String> toAddresses) {
-		}
-
-		@Override
-		public void sendHtmlMail(String from, String subject, String message, String... toAddresses) {
+		public void send(Mailing mailing) {
 		}
 	}
 }
diff --git a/src/main/java/com/gitblit/authority/GitblitAuthority.java b/src/main/java/com/gitblit/authority/GitblitAuthority.java
index 51626e6..5f4a7e7 100644
--- a/src/main/java/com/gitblit/authority/GitblitAuthority.java
+++ b/src/main/java/com/gitblit/authority/GitblitAuthority.java
@@ -43,7 +43,6 @@
 import java.security.cert.X509Certificate;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Date;
@@ -51,12 +50,7 @@
 import java.util.List;
 import java.util.Map;
 
-import javax.activation.DataHandler;
-import javax.activation.FileDataSource;
 import javax.mail.Message;
-import javax.mail.Multipart;
-import javax.mail.internet.MimeBodyPart;
-import javax.mail.internet.MimeMultipart;
 import javax.swing.ImageIcon;
 import javax.swing.InputVerifier;
 import javax.swing.JButton;
@@ -93,6 +87,7 @@
 import com.gitblit.Keys;
 import com.gitblit.client.HeaderPanel;
 import com.gitblit.client.Translation;
+import com.gitblit.models.Mailing;
 import com.gitblit.models.UserModel;
 import com.gitblit.service.MailService;
 import com.gitblit.utils.ArrayUtils;
@@ -854,27 +849,17 @@
 		// send email
 		try {
 			if (mail.isReady()) {
-				Message message = mail.createMessage(Arrays.asList(user.emailAddress));
-				message.setSubject("Your Gitblit client certificate for " + metadata.serverHostname);
-
-				// body of email
+				Mailing mailing = Mailing.newPlain();
+				mailing.subject = "Your Gitblit client certificate for " + metadata.serverHostname;
+				mailing.setRecipients(user.emailAddress);
 				String body = X509Utils.processTemplate(new File(folder, X509Utils.CERTS + File.separator + "mail.tmpl"), metadata);
 				if (StringUtils.isEmpty(body)) {
 					body = MessageFormat.format("Hi {0}\n\nHere is your client certificate bundle.\nInside the zip file are installation instructions.", user.getDisplayName());
 				}
-				Multipart mp = new MimeMultipart();
-				MimeBodyPart messagePart = new MimeBodyPart();
-				messagePart.setText(body);
-				mp.addBodyPart(messagePart);
+				mailing.content = body;
+				mailing.addAttachment(zip);
 
-				// attach zip
-				MimeBodyPart filePart = new MimeBodyPart();
-				FileDataSource fds = new FileDataSource(zip);
-				filePart.setDataHandler(new DataHandler(fds));
-				filePart.setFileName(fds.getName());
-				mp.addBodyPart(filePart);
-
-				message.setContent(mp);
+				Message message = mail.createMessage(mailing);
 
 				mail.sendNow(message);
 				return true;
diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java
index 9d096dd..08d6408 100644
--- a/src/main/java/com/gitblit/manager/GitblitManager.java
+++ b/src/main/java/com/gitblit/manager/GitblitManager.java
@@ -50,6 +50,7 @@
 import com.gitblit.models.FederationSet;
 import com.gitblit.models.ForkModel;
 import com.gitblit.models.GitClientApplication;
+import com.gitblit.models.Mailing;
 import com.gitblit.models.Metric;
 import com.gitblit.models.ProjectModel;
 import com.gitblit.models.RegistrantAccessPermission;
@@ -584,28 +585,13 @@
 	}
 
 	@Override
-	public void sendMail(String subject, String message, String... toAddresses) {
-		notificationManager.sendMail(subject, message, toAddresses);
-	}
-
-	@Override
 	public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) {
 		notificationManager.sendHtmlMail(subject, message, toAddresses);
 	}
 
 	@Override
-	public void sendHtmlMail(String subject, String message, String... toAddresses) {
-		notificationManager.sendHtmlMail(subject, message, toAddresses);
-	}
-
-	@Override
-	public void sendHtmlMail(String from, String subject, String message, Collection<String> toAddresses) {
-		notificationManager.sendHtmlMail(from, subject, message, toAddresses);
-	}
-
-	@Override
-	public void sendHtmlMail(String from, String subject, String message, String... toAddresses) {
-		notificationManager.sendHtmlMail(from, subject, message, toAddresses);
+	public void send(Mailing mail) {
+		notificationManager.send(mail);
 	}
 
 	/*
diff --git a/src/main/java/com/gitblit/manager/INotificationManager.java b/src/main/java/com/gitblit/manager/INotificationManager.java
index ce5d3f7..6bee2f8 100644
--- a/src/main/java/com/gitblit/manager/INotificationManager.java
+++ b/src/main/java/com/gitblit/manager/INotificationManager.java
@@ -17,6 +17,8 @@
 
 import java.util.Collection;
 
+import com.gitblit.models.Mailing;
+
 public interface INotificationManager extends IManager {
 
 	/**
@@ -43,44 +45,14 @@
 	 * @param message
 	 * @param toAddresses
 	 */
-	void sendMail(String subject, String message, String... toAddresses);
-
-	/**
-	 * Notify users by email of something.
-	 *
-	 * @param subject
-	 * @param message
-	 * @param toAddresses
-	 */
 	void sendHtmlMail(String subject, String message, Collection<String> toAddresses);
 
 	/**
 	 * Notify users by email of something.
 	 *
-	 * @param subject
-	 * @param message
-	 * @param toAddresses
+	 * @param mailing
+	 * @return the mail message object
 	 */
-	void sendHtmlMail(String subject, String message, String... toAddresses);
-
-	/**
-	 * Notify users by email of something.
-	 *
-	 * @param from
-	 * @param subject
-	 * @param message
-	 * @param toAddresses
-	 */
-	void sendHtmlMail(String from, String subject, String message, Collection<String> toAddresses);
-
-	/**
-	 * Notify users by email of something.
-	 *
-	 * @param from
-	 * @param subject
-	 * @param message
-	 * @param toAddresses
-	 */
-	void sendHtmlMail(String from, String subject, String message, String... toAddresses);
+	void send(Mailing mailing);
 
 }
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/NotificationManager.java b/src/main/java/com/gitblit/manager/NotificationManager.java
index 22ae551..f1558b6 100644
--- a/src/main/java/com/gitblit/manager/NotificationManager.java
+++ b/src/main/java/com/gitblit/manager/NotificationManager.java
@@ -15,23 +15,19 @@
  */
 package com.gitblit.manager;
 
-import java.text.MessageFormat;
 import java.util.Collection;
-import java.util.List;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
 import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.internet.MimeBodyPart;
-import javax.mail.internet.MimeMultipart;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+import com.gitblit.models.Mailing;
 import com.gitblit.service.MailService;
 
 /**
@@ -50,19 +46,19 @@
 
 	private final IStoredSettings settings;
 
-	private final MailService mailExecutor;
+	private final MailService mailService;
 
 	public NotificationManager(IStoredSettings settings) {
 		this.settings = settings;
-		this.mailExecutor = new MailService(settings);
+		this.mailService = new MailService(settings);
 	}
 
 	@Override
 	public NotificationManager start() {
-		if (mailExecutor.isReady()) {
+		if (mailService.isReady()) {
 			int period = 2;
 			logger.info("Mail service will process the queue every {} minutes.", period);
-			scheduledExecutor.scheduleAtFixedRate(mailExecutor, 1, period, TimeUnit.MINUTES);
+			scheduledExecutor.scheduleAtFixedRate(mailService, 1, period, TimeUnit.MINUTES);
 		} else {
 			logger.warn("Mail service disabled.");
 		}
@@ -83,8 +79,11 @@
 	 */
 	@Override
 	public void sendMailToAdministrators(String subject, String message) {
-		List<String> toAddresses = settings.getStrings(Keys.mail.adminAddresses);
-		sendMail(subject, message, toAddresses);
+		Mailing mail = Mailing.newPlain();
+		mail.subject = subject;
+		mail.content = message;
+		mail.setRecipients(settings.getStrings(Keys.mail.adminAddresses));
+		send(mail);
 	}
 
 	/**
@@ -96,41 +95,11 @@
 	 */
 	@Override
 	public void sendMail(String subject, String message, Collection<String> toAddresses) {
-		this.sendMail(subject, message, toAddresses.toArray(new String[0]));
-	}
-
-	/**
-	 * Notify users by email of something.
-	 *
-	 * @param subject
-	 * @param message
-	 * @param toAddresses
-	 */
-	@Override
-	public void sendMail(String subject, String message, String... toAddresses) {
-		if (toAddresses == null || toAddresses.length == 0) {
-			logger.debug(MessageFormat.format("Dropping message {0} because there are no recipients", subject));
-			return;
-		}
-		try {
-			Message mail = mailExecutor.createMessage(toAddresses);
-			if (mail != null) {
-				mail.setSubject(subject);
-
-				MimeBodyPart messagePart = new MimeBodyPart();
-				messagePart.setText(message, "utf-8");
-				messagePart.setHeader("Content-Type", "text/plain; charset=\"utf-8\"");
-				messagePart.setHeader("Content-Transfer-Encoding", "quoted-printable");
-
-				MimeMultipart multiPart = new MimeMultipart();
-				multiPart.addBodyPart(messagePart);
-				mail.setContent(multiPart);
-
-				mailExecutor.queue(mail);
-			}
-		} catch (MessagingException e) {
-			logger.error("Messaging error", e);
-		}
+		Mailing mail = Mailing.newPlain();
+		mail.subject = subject;
+		mail.content = message;
+		mail.setRecipients(toAddresses);
+		send(mail);
 	}
 
 	/**
@@ -142,66 +111,26 @@
 	 */
 	@Override
 	public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) {
-		this.sendHtmlMail(null, subject, message, toAddresses.toArray(new String[0]));
+		Mailing mail = Mailing.newHtml();
+		mail.content = message;
+		mail.setRecipients(toAddresses);
+		send(mail);
 	}
 
 	/**
 	 * Notify users by email of something.
 	 *
-	 * @param from
-	 * @param subject
-	 * @param message
-	 * @param toAddresses
+	 * @param mailing
 	 */
 	@Override
-	public void sendHtmlMail(String from, String subject, String message, Collection<String> toAddresses) {
-		this.sendHtmlMail(from, subject, message, toAddresses.toArray(new String[0]));
-	}
-
-	/**
-	 * Notify users by email of something.
-	 *
-	 * @param subject
-	 * @param message
-	 * @param toAddresses
-	 */
-	@Override
-	public void sendHtmlMail(String subject, String message, String... toAddresses) {
-		this.sendHtmlMail(null, message, toAddresses);
-	}
-
-	/**
-	 * Notify users by email of something.
-	 *
-	 * @param from
-	 * @param subject
-	 * @param message
-	 * @param toAddresses
-	 */
-	@Override
-	public void sendHtmlMail(String from, String subject, String message, String... toAddresses) {
-		if (toAddresses == null || toAddresses.length == 0) {
-			logger.debug("Dropping message {} because there are no recipients", subject);
+	public void send(Mailing mailing) {
+		if (!mailing.hasRecipients()) {
+			logger.debug("Dropping message {} because there are no recipients", mailing.subject);
 			return;
 		}
-		try {
-			Message mail = mailExecutor.createMessage(from, toAddresses);
-			if (mail != null) {
-				mail.setSubject(subject);
-
-				MimeBodyPart messagePart = new MimeBodyPart();
-				messagePart.setText(message, "utf-8");
-				messagePart.setHeader("Content-Type", "text/html; charset=\"utf-8\"");
-				messagePart.setHeader("Content-Transfer-Encoding", "quoted-printable");
-
-				MimeMultipart multiPart = new MimeMultipart();
-				multiPart.addBodyPart(messagePart);
-				mail.setContent(multiPart);
-
-				mailExecutor.queue(mail);
-			}
-		} catch (MessagingException e) {
-			logger.error("Messaging error", e);
+		Message msg = mailService.createMessage(mailing);
+		if (msg != null) {
+			mailService.queue(msg);
 		}
 	}
 
diff --git a/src/main/java/com/gitblit/models/Mailing.java b/src/main/java/com/gitblit/models/Mailing.java
new file mode 100644
index 0000000..9c0ff90
--- /dev/null
+++ b/src/main/java/com/gitblit/models/Mailing.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2014 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.models;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Encapsulates an email notification.
+ *
+ * @author James Moger
+ *
+ */
+public class Mailing {
+
+	public enum Type {
+		plain, html
+	}
+
+	public final Type type;
+	public final Set<String> toAddresses;
+	public final Set<String> ccAddresses;
+	public final List<File> attachments;
+
+	public String from;
+	public String subject;
+	public String content;
+	public String id;
+
+	public static Mailing newHtml() {
+		return new Mailing(Type.html);
+	}
+
+	public static Mailing newPlain() {
+		return new Mailing(Type.plain);
+	}
+
+	private Mailing(Type type) {
+		this.type = type;
+		this.toAddresses = new TreeSet<String>();
+		this.ccAddresses = new TreeSet<String>();
+		this.attachments = new ArrayList<File>();
+	}
+
+	public boolean hasRecipients() {
+		return toAddresses.size() > 0;
+	}
+
+	public void setRecipients(String... addrs) {
+		setRecipients(Arrays.asList(addrs));
+	}
+
+	public void setRecipients(Collection<String> addrs) {
+		toAddresses.clear();
+		for (String addr : addrs) {
+			toAddresses.add(addr.toLowerCase());
+		}
+		cleanup();
+	}
+
+	public boolean hasCCs() {
+		return ccAddresses.size() > 0;
+	}
+
+	public void setCCs(String... addrs) {
+		setCCs(Arrays.asList(addrs));
+	}
+
+	public void setCCs(Collection<String> addrs) {
+		ccAddresses.clear();
+		for (String addr : addrs) {
+			ccAddresses.add(addr.toLowerCase());
+		}
+		cleanup();
+	}
+
+	private void cleanup() {
+		ccAddresses.removeAll(toAddresses);
+	}
+
+	public boolean hasAttachments() {
+		return attachments.size() > 0;
+	}
+
+	public void addAttachment(File file) {
+		attachments.add(file);
+	}
+
+	@Override
+	public String toString() {
+		return subject + "\n\n" + content;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/service/MailService.java b/src/main/java/com/gitblit/service/MailService.java
index 54b6c91..4759d17 100644
--- a/src/main/java/com/gitblit/service/MailService.java
+++ b/src/main/java/com/gitblit/service/MailService.java
@@ -15,30 +15,35 @@
  */
 package com.gitblit.service;
 
+import java.io.File;
 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.UUID;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.regex.Pattern;
 
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
 import javax.mail.Authenticator;
 import javax.mail.Message;
+import javax.mail.MessagingException;
 import javax.mail.PasswordAuthentication;
 import javax.mail.Session;
 import javax.mail.Transport;
 import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
 import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+import com.gitblit.models.Mailing;
 import com.gitblit.utils.StringUtils;
 
 /**
@@ -117,74 +122,108 @@
 	/**
 	 * Create a message.
 	 *
-	 * @param toAddresses
+	 * @param mailing
 	 * @return a message
 	 */
-	public Message createMessage(String... toAddresses) {
-		return createMessage(null, Arrays.asList(toAddresses));
-	}
+	public Message createMessage(Mailing mailing) {
+		if (mailing.subject == null) {
+			mailing.subject = "";
+		}
 
-	/**
-	 * Create a message.
-	 *
-	 * @param toAddresses
-	 * @return a message
-	 */
-	public Message createMessage(List<String> toAddresses) {
-		return createMessage(null, toAddresses);
-	}
+		if (mailing.content == null) {
+			mailing.content = "";
+		}
 
-	/**
-	 * Create a message.
-	 *
-	 * @param fromDisplayName
-	 * @param toAddresses
-	 * @return a message
-	 */
-	public Message createMessage(String fromDisplayName, String... toAddresses) {
-		return createMessage(fromDisplayName, Arrays.asList(toAddresses));
-	}
-
-	/**
-	 * Create a message.
-	 *
-	 * @param fromDisplayName
-	 * @param toAddresses
-	 * @return a message
-	 */
-	public Message createMessage(String fromDisplayName, List<String> toAddresses) {
-		MimeMessage message = new MimeMessage(session);
+		Message message = new MailMessageImpl(session, mailing.id);
 		try {
 			String fromAddress = settings.getString(Keys.mail.fromAddress, null);
 			if (StringUtils.isEmpty(fromAddress)) {
 				fromAddress = "gitblit@gitblit.com";
 			}
-			InternetAddress from = new InternetAddress(fromAddress, fromDisplayName == null ? "Gitblit" : fromDisplayName);
+			InternetAddress from = new InternetAddress(fromAddress, mailing.from == null ? "Gitblit" : mailing.from);
 			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) {
+
+			// validate & add TO recipients
+			List<InternetAddress> to = new ArrayList<InternetAddress>();
+			for (String address : mailing.toAddresses) {
 				if (StringUtils.isEmpty(address)) {
 					continue;
 				}
 				if (validEmail.matcher(address).find()) {
 					try {
-						tos.add(new InternetAddress(address));
+						to.add(new InternetAddress(address));
 					} catch (Throwable t) {
 					}
 				}
 			}
-			message.setRecipients(Message.RecipientType.BCC,
-					tos.toArray(new InternetAddress[tos.size()]));
+
+			// validate & add CC recipients
+			List<InternetAddress> cc = new ArrayList<InternetAddress>();
+			for (String address : mailing.ccAddresses) {
+				if (StringUtils.isEmpty(address)) {
+					continue;
+				}
+				if (validEmail.matcher(address).find()) {
+					try {
+						cc.add(new InternetAddress(address));
+					} catch (Throwable t) {
+					}
+				}
+			}
+
+			if (settings.getBoolean(Keys.web.showEmailAddresses, true)) {
+				// full disclosure of recipients
+				if (to.size() > 0) {
+					message.setRecipients(Message.RecipientType.TO,
+							to.toArray(new InternetAddress[to.size()]));
+				}
+				if (cc.size() > 0) {
+					message.setRecipients(Message.RecipientType.CC,
+							cc.toArray(new InternetAddress[cc.size()]));
+				}
+			} else {
+				// everyone is bcc'd
+				List<InternetAddress> bcc = new ArrayList<InternetAddress>();
+				bcc.addAll(to);
+				bcc.addAll(cc);
+				message.setRecipients(Message.RecipientType.BCC,
+						bcc.toArray(new InternetAddress[bcc.size()]));
+			}
+
 			message.setSentDate(new Date());
+			message.setSubject(mailing.subject);
+
+			MimeBodyPart messagePart = new MimeBodyPart();
+			messagePart.setText(mailing.content, "utf-8");
+			//messagePart.setHeader("Content-Transfer-Encoding", "quoted-printable");
+
+			if (Mailing.Type.html == mailing.type) {
+				messagePart.setHeader("Content-Type", "text/html; charset=\"utf-8\"");
+			} else {
+				messagePart.setHeader("Content-Type", "text/plain; charset=\"utf-8\"");
+			}
+
+			MimeMultipart multiPart = new MimeMultipart();
+			multiPart.addBodyPart(messagePart);
+
+			// handle attachments
+			if (mailing.hasAttachments()) {
+				for (File file : mailing.attachments) {
+					if (file.exists()) {
+						MimeBodyPart filePart = new MimeBodyPart();
+						FileDataSource fds = new FileDataSource(file);
+						filePart.setDataHandler(new DataHandler(fds));
+						filePart.setFileName(fds.getName());
+						multiPart.addBodyPart(filePart);
+					}
+				}
+			}
+
+			message.setContent(multiPart);
+
 		} catch (Exception e) {
 			logger.error("Failed to properly create message", e);
 		}
@@ -247,4 +286,26 @@
 	public void sendNow(Message message) throws Exception {
 		Transport.send(message);
 	}
+
+	private static class MailMessageImpl extends MimeMessage {
+
+		final String id;
+
+		MailMessageImpl(Session session, String id) {
+			super(session);
+			this.id = id;
+		}
+
+		@Override
+		protected void updateMessageID() throws MessagingException {
+			if (!StringUtils.isEmpty(id)) {
+				String hostname = "gitblit.com";
+				String refid = "<" + id + "@" + hostname + ">";
+				String mid = "<" + UUID.randomUUID().toString() + "@" + hostname + ">";
+				setHeader("References", refid);
+				setHeader("In-Reply-To", refid);
+				setHeader("Message-Id", mid);
+			}
+		}
+	}
 }
diff --git a/src/test/java/com/gitblit/tests/MailTest.java b/src/test/java/com/gitblit/tests/MailTest.java
index df09ca5..0224e84 100644
--- a/src/test/java/com/gitblit/tests/MailTest.java
+++ b/src/test/java/com/gitblit/tests/MailTest.java
@@ -21,6 +21,7 @@
 
 import com.gitblit.FileSettings;
 import com.gitblit.Keys;
+import com.gitblit.models.Mailing;
 import com.gitblit.service.MailService;
 
 public class MailTest extends GitblitUnitTest {
@@ -29,7 +30,9 @@
 	public void testSendMail() throws Exception {
 		FileSettings settings = new FileSettings("mailtest.properties");
 		MailService mail = new MailService(settings);
-		Message message = mail.createMessage(settings.getStrings(Keys.mail.adminAddresses));
+		Mailing mailing = Mailing.newPlain();
+		mailing.setRecipients(settings.getStrings(Keys.mail.adminAddresses));
+		Message message = mail.createMessage(mailing);
 		message.setSubject("Test");
 		message.setText("Lägger till andra stycket i ny fil. UTF-8 encoded");
 		mail.queue(message);

--
Gitblit v1.9.1