/* * Copyright (c) JForum Team. All rights reserved. * * The software in this package is published under the terms of the LGPL * license a copy of which has been included with this distribution in the * license.txt file. * * The JForum Project * http://www.jforum.net */ package net.jforum.util.mail; import groovy.text.SimpleTemplateEngine; import groovy.text.TemplateEngine; import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.NoSuchProviderException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import net.jforum.core.exceptions.MailException; import net.jforum.entities.User; import net.jforum.util.ConfigKeys; import net.jforum.util.JForumConfig; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; /** * Dispatch emails to the world. * * @author Rafael Steil */ public abstract class Spammer { private static final Logger logger = Logger.getLogger(Spammer.class); private static final int MESSAGE_HTML = 0; private static final int MESSAGE_TEXT = 1; private Session session; private String username; private String password; private String messageId; private String inReplyTo; private int messageFormat; private boolean needCustomization; private MimeMessage message; private JForumConfig config; private File templateFile; private List<User> users = new ArrayList<User>(); private Properties mailProperties = new Properties(); private Map<String, Object> templateParams = new HashMap<String, Object>(); private TemplateEngine templateEngine = new SimpleTemplateEngine(); public Spammer(JForumConfig config) throws MailException { this.config = config; boolean ssl = config.getBoolean(ConfigKeys.MAIL_SMTP_SSL); String hostProperty = this.hostProperty(ssl); String portProperty = this.portProperty(ssl); String authProperty = this.authProperty(ssl); String localhostProperty = this.localhostProperty(ssl); this.mailProperties.put(hostProperty, config.getValue(ConfigKeys.MAIL_SMTP_HOST)); this.mailProperties.put(portProperty, config.getValue(ConfigKeys.MAIL_SMTP_PORT)); String localhost = this.config.getValue(ConfigKeys.MAIL_SMTP_LOCALHOST); if (!StringUtils.isEmpty(localhost)) { this.mailProperties.put(localhostProperty, localhost); } this.mailProperties.put("mail.mime.address.strict", "false"); this.mailProperties.put("mail.mime.charset", this.config.getValue(ConfigKeys.MAIL_CHARSET)); this.mailProperties.put(authProperty, this.config.getValue(ConfigKeys.MAIL_SMTP_AUTH)); this.username = this.config.getValue(ConfigKeys.MAIL_SMTP_USERNAME); this.password = this.config.getValue(ConfigKeys.MAIL_SMTP_PASSWORD); messageFormat = this.config.getValue(ConfigKeys.MAIL_MESSSAGE_FORMAT).equals("html") ? MESSAGE_HTML : MESSAGE_TEXT; this.session = Session.getInstance(mailProperties); } public boolean dispatchMessages() { try { if (this.config.getBoolean(ConfigKeys.MAIL_SMTP_AUTH)) { this.dispatchAuthenticatedMessage(); } else { this.dispatchAnonymousMessage(); } } catch (MessagingException e) { logger.error("Error while dispatching the message." + e, e); } return true; } protected JForumConfig getConfig() { return this.config; } private void dispatchAnonymousMessage() throws AddressException, MessagingException { int sendDelay = this.config.getInt(ConfigKeys.MAIL_SMTP_DELAY); for (User user : this.users) { if (StringUtils.isEmpty(user.getEmail())) { continue; } if (this.needCustomization) { this.defineUserMessage(user); } Address address = new InternetAddress(user.getEmail()); if (logger.isTraceEnabled()) { logger.trace("Sending mail to: " + user.getEmail()); } this.message.setRecipient(Message.RecipientType.TO, address); Transport.send(this.message, new Address[] { address }); if (sendDelay > 0) { this.waitUntilNextMessage(sendDelay); } } } private void dispatchAuthenticatedMessage() throws NoSuchProviderException { if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) { int batchSize = this.config.getInt(ConfigKeys.MAIL_BATCH_SIZE); int total = (int)Math.ceil((double)this.users.size() / (double)batchSize); Iterator<User> iterator = this.users.iterator(); for (int i = 0; i < total; i++) { this.dispatchNoMoreThanBatchSize(iterator, batchSize); } } } private void dispatchNoMoreThanBatchSize(Iterator<User> iterator, int batchSize) throws NoSuchProviderException { boolean ssl = this.config.getBoolean(ConfigKeys.MAIL_SMTP_SSL); Transport transport = this.session.getTransport(ssl ? "smtps" : "smtp"); try { String host = this.config.getValue(ConfigKeys.MAIL_SMTP_HOST); int sendDelay = this.config.getInt(ConfigKeys.MAIL_SMTP_DELAY); transport.connect(host, username, password); if (transport.isConnected()) { for (int counter = 0; counter < batchSize && iterator.hasNext(); counter++) { User user = iterator.next(); if (StringUtils.isEmpty(user.getEmail())) { continue; } if (this.needCustomization) { this.defineUserMessage(user); } Address address = new InternetAddress(user.getEmail()); if (logger.isDebugEnabled()) { logger.debug("Sending mail to: " + user.getEmail()); } this.message.setRecipient(Message.RecipientType.TO, address); transport.sendMessage(this.message, new Address[] { address }); if (sendDelay > 0) { this.waitUntilNextMessage(sendDelay); } } } } catch (Exception e) { logger.error("Errow while sending emails: " + e, e); throw new MailException(e); } finally { try { transport.close(); } catch (Exception e) { } } } private void defineUserMessage(User user) { try { this.templateParams.put("user", user); String text = this.processTemplate(); this.defineMessageText(text); } catch (Exception e) { throw new MailException(e); } } private void waitUntilNextMessage(int sendDelay) { try { Thread.sleep(sendDelay); } catch (InterruptedException ie) { logger.error("Error while Thread.sleep." + ie, ie); } } /** * Prepares the mail message for sending. * * @param subject the subject of the email * @param messageFile the path to the mail message template * @throws MailException */ protected void prepareMessage(String subject, String messageFile) throws MailException { if (this.messageId == null) { this.message = new MimeMessage(session); } else { this.message = new IdentifiableMimeMessage(session); ((IdentifiableMimeMessage) this.message).setMessageId(this.messageId); } this.templateParams.put("forumName", this.config.getValue(ConfigKeys.FORUM_NAME)); try { this.message.setSentDate(new Date()); this.message.setFrom(new InternetAddress(this.config.getValue(ConfigKeys.MAIL_SENDER))); this.message.setSubject(subject, this.config.getValue(ConfigKeys.MAIL_CHARSET)); if (this.inReplyTo != null) { this.message.addHeader("In-Reply-To", this.inReplyTo); } this.createTemplate(messageFile); this.needCustomization = this.isCustomizationNeeded(); // If we don't need to customize any part of the message, // then build the generic text right now if (!this.needCustomization) { String text = this.processTemplate(); this.defineMessageText(text); } } catch (Exception e) { throw new MailException(e); } } /** * Set the text contents of the email we're sending * * @param text the text to set * @throws MessagingException */ private void defineMessageText(String text) throws MessagingException { if (messageFormat == MESSAGE_TEXT) { this.message.setText(text); } else { String charset = this.config.getValue(ConfigKeys.MAIL_CHARSET); this.message.setContent(text.replaceAll("\n", "<br />"), "text/html; charset=" + charset); } } /** * Gets the message text to send in the email. * * @param templateName The file with the email template, relative to the application root * @return The email message text * @throws Exception */ protected void createTemplate(String templateName) throws Exception { this.templateFile = new File(this.config.getValue(ConfigKeys.APPLICATION_PATH) + templateName); } /** * Merge the template data, creating the final content. T * his method should only be called after {@link #createTemplate(String)} and * {@link #setTemplateParams(SimpleHash)} * * @return the generated content */ protected String processTemplate() throws Exception { return this.templateEngine.createTemplate(this.templateFile) .make(this.templateParams).toString(); } /** * Set the parameters for the template being processed * * @param params the parameters to the template */ protected void setTemplateParams(Map<String, Object> params) { this.templateParams = params; } /** * Check if we have to send customized emails * * @return true if there is a need for customized emails */ private boolean isCustomizationNeeded() { for (User user : this.users) { if (user.getNotifyText()) { return true; } } return false; } protected String buildForumLink() { String forumLink = this.getConfig().getValue(ConfigKeys.FORUM_LINK); if (forumLink.charAt(forumLink.length() - 1) != '/') { forumLink += "/"; } return forumLink; } protected void setMessageId(String messageId) { this.messageId = messageId; } protected void setInReplyTo(String inReplyTo) { this.inReplyTo = inReplyTo; } protected void setUsers(List<User> users) { this.users = users; } private String localhostProperty(boolean ssl) { return ssl ? ConfigKeys.MAIL_SMTP_SSL_LOCALHOST : ConfigKeys.MAIL_SMTP_LOCALHOST; } private String authProperty(boolean ssl) { return ssl ? ConfigKeys.MAIL_SMTP_SSL_AUTH : ConfigKeys.MAIL_SMTP_AUTH; } private String portProperty(boolean ssl) { return ssl ? ConfigKeys.MAIL_SMTP_SSL_PORT : ConfigKeys.MAIL_SMTP_PORT; } private String hostProperty(boolean ssl) { return ssl ? ConfigKeys.MAIL_SMTP_SSL_HOST : ConfigKeys.MAIL_SMTP_HOST; } }