You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MailExecutor.java 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * Copyright 2011 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Date;
  20. import java.util.HashSet;
  21. import java.util.List;
  22. import java.util.Properties;
  23. import java.util.Queue;
  24. import java.util.Set;
  25. import java.util.concurrent.ConcurrentLinkedQueue;
  26. import java.util.regex.Pattern;
  27. import javax.mail.Authenticator;
  28. import javax.mail.Message;
  29. import javax.mail.PasswordAuthentication;
  30. import javax.mail.Session;
  31. import javax.mail.Transport;
  32. import javax.mail.internet.InternetAddress;
  33. import javax.mail.internet.MimeMessage;
  34. import org.slf4j.Logger;
  35. import org.slf4j.LoggerFactory;
  36. import com.gitblit.utils.StringUtils;
  37. /**
  38. * The mail executor handles sending email messages asynchronously from queue.
  39. *
  40. * @author James Moger
  41. *
  42. */
  43. public class MailExecutor implements Runnable {
  44. private final Logger logger = LoggerFactory.getLogger(MailExecutor.class);
  45. private final Queue<Message> queue = new ConcurrentLinkedQueue<Message>();
  46. private final Session session;
  47. private final IStoredSettings settings;
  48. public MailExecutor(IStoredSettings settings) {
  49. this.settings = settings;
  50. final String mailUser = settings.getString(Keys.mail.username, null);
  51. final String mailPassword = settings.getString(Keys.mail.password, null);
  52. boolean authenticate = !StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword);
  53. String server = settings.getString(Keys.mail.server, "");
  54. if (StringUtils.isEmpty(server)) {
  55. session = null;
  56. return;
  57. }
  58. int port = settings.getInteger(Keys.mail.port, 25);
  59. boolean isGMail = false;
  60. if (server.equals("smtp.gmail.com")) {
  61. port = 465;
  62. isGMail = true;
  63. }
  64. Properties props = new Properties();
  65. props.setProperty("mail.smtp.host", server);
  66. props.setProperty("mail.smtp.port", String.valueOf(port));
  67. props.setProperty("mail.smtp.auth", String.valueOf(authenticate));
  68. props.setProperty("mail.smtp.auths", String.valueOf(authenticate));
  69. if (isGMail) {
  70. props.setProperty("mail.smtp.starttls.enable", "true");
  71. props.put("mail.smtp.socketFactory.port", String.valueOf(port));
  72. props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
  73. props.put("mail.smtp.socketFactory.fallback", "false");
  74. }
  75. if (!StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword)) {
  76. // SMTP requires authentication
  77. session = Session.getInstance(props, new Authenticator() {
  78. protected PasswordAuthentication getPasswordAuthentication() {
  79. PasswordAuthentication passwordAuthentication = new PasswordAuthentication(
  80. mailUser, mailPassword);
  81. return passwordAuthentication;
  82. }
  83. });
  84. } else {
  85. // SMTP does not require authentication
  86. session = Session.getInstance(props);
  87. }
  88. }
  89. /**
  90. * Indicates if the mail executor can send emails.
  91. *
  92. * @return true if the mail executor is ready to send emails
  93. */
  94. public boolean isReady() {
  95. return session != null;
  96. }
  97. /**
  98. * Creates a message for the administrators.
  99. *
  100. * @returna message
  101. */
  102. public Message createMessageForAdministrators() {
  103. List<String> toAddresses = settings.getStrings(Keys.mail.adminAddresses);
  104. if (toAddresses.size() == 0) {
  105. logger.warn("Can not notify administrators because no email addresses are defined!");
  106. return null;
  107. }
  108. return createMessage(toAddresses);
  109. }
  110. /**
  111. * Create a message.
  112. *
  113. * @param toAddresses
  114. * @return a message
  115. */
  116. public Message createMessage(String... toAddresses) {
  117. return createMessage(Arrays.asList(toAddresses));
  118. }
  119. /**
  120. * Create a message.
  121. *
  122. * @param toAddresses
  123. * @return a message
  124. */
  125. public Message createMessage(List<String> toAddresses) {
  126. MimeMessage message = new MimeMessage(session);
  127. try {
  128. String fromAddress = settings.getString(Keys.mail.fromAddress, null);
  129. if (StringUtils.isEmpty(fromAddress)) {
  130. fromAddress = "gitblit@gitblit.com";
  131. }
  132. InternetAddress from = new InternetAddress(fromAddress, "Gitblit");
  133. message.setFrom(from);
  134. // determine unique set of addresses
  135. Set<String> uniques = new HashSet<String>();
  136. for (String address : toAddresses) {
  137. uniques.add(address.toLowerCase());
  138. }
  139. Pattern validEmail = Pattern
  140. .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})(\\]?)$");
  141. List<InternetAddress> tos = new ArrayList<InternetAddress>();
  142. for (String address : uniques) {
  143. if (StringUtils.isEmpty(address)) {
  144. continue;
  145. }
  146. if (validEmail.matcher(address).find()) {
  147. try {
  148. tos.add(new InternetAddress(address));
  149. } catch (Throwable t) {
  150. }
  151. }
  152. }
  153. message.setRecipients(Message.RecipientType.BCC,
  154. tos.toArray(new InternetAddress[tos.size()]));
  155. message.setSentDate(new Date());
  156. } catch (Exception e) {
  157. logger.error("Failed to properly create message", e);
  158. }
  159. return message;
  160. }
  161. /**
  162. * Returns the status of the mail queue.
  163. *
  164. * @return true, if the queue is empty
  165. */
  166. public boolean hasEmptyQueue() {
  167. return queue.isEmpty();
  168. }
  169. /**
  170. * Queue's an email message to be sent.
  171. *
  172. * @param message
  173. * @return true if the message was queued
  174. */
  175. public boolean queue(Message message) {
  176. if (!isReady()) {
  177. return false;
  178. }
  179. try {
  180. message.saveChanges();
  181. } catch (Throwable t) {
  182. logger.error("Failed to save changes to message!", t);
  183. }
  184. queue.add(message);
  185. return true;
  186. }
  187. @Override
  188. public void run() {
  189. if (!queue.isEmpty()) {
  190. if (session != null) {
  191. // send message via mail server
  192. List<Message> failures = new ArrayList<Message>();
  193. Message message = null;
  194. while ((message = queue.poll()) != null) {
  195. try {
  196. if (settings.getBoolean(Keys.mail.debug, false)) {
  197. logger.info("send: " + StringUtils.trimString(message.getSubject(), 60));
  198. }
  199. Transport.send(message);
  200. } catch (Throwable e) {
  201. logger.error("Failed to send message", e);
  202. failures.add(message);
  203. }
  204. }
  205. // push the failures back onto the queue for the next cycle
  206. queue.addAll(failures);
  207. }
  208. }
  209. }
  210. }