Paul Martin
2016-04-27 c2188a840bc4153ae92112b04b2e06a90d3944aa
src/main/java/com/gitblit/service/MailService.java
@@ -15,30 +15,36 @@
 */
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 javax.mail.internet.MimeUtility;
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;
/**
@@ -63,6 +69,7 @@
      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);
      final boolean starttls = settings.getBoolean(Keys.mail.starttls, false);
      boolean authenticate = !StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword);
      String server = settings.getString(Keys.mail.server, "");
      if (StringUtils.isEmpty(server)) {
@@ -81,6 +88,7 @@
      props.setProperty("mail.smtp.port", String.valueOf(port));
      props.setProperty("mail.smtp.auth", String.valueOf(authenticate));
      props.setProperty("mail.smtp.auths", String.valueOf(authenticate));
      props.setProperty("mail.smtp.starttls.enable", String.valueOf(starttls));
      if (isGMail || smtps) {
         props.setProperty("mail.smtp.starttls.enable", "true");
@@ -114,56 +122,112 @@
      return session != null;
   }
   /**
    * Create a message.
    *
    * @param toAddresses
    * @param mailing
    * @return a message
    */
   public Message createMessage(String... toAddresses) {
      return createMessage(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) {
      MimeMessage message = new MimeMessage(session);
      if (mailing.content == null) {
         mailing.content = "";
      }
      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, "Gitblit");
         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());
         // UTF-8 encode
         message.setSubject(MimeUtility.encodeText(mailing.subject, "utf-8", "B"));
         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);
      }
@@ -226,4 +290,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);
         }
      }
   }
}