package games.strategy.engine.pbem; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Date; import java.util.Properties; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; 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.util.ByteArrayDataSource; import games.strategy.engine.framework.startup.ui.editors.EditorPanel; import games.strategy.engine.framework.startup.ui.editors.EmailSenderEditor; import games.strategy.engine.framework.startup.ui.editors.IBean; import games.strategy.triplea.help.HelpSupport; /** * A PBEM (play by email) sender that will email turn summary and save game * This class is saved as a property as part of a save game * This class has two password fields, one is transitive and used while the game is running, the other is 'cleared' when * the game starts. This is done for security reasons so save games will not include passwords. * The non-transitive password is used when the object is stored in the local cache */ public class GenericEmailSender implements IEmailSender { private static final long serialVersionUID = 4644748856027574157L; /** * a value to assign to the non-transitive password, as we can see that is was cleared. */ private static final String USE_TRANSITIVE_PASSWORD = "d0a11f0f-96d3-4303-8875-4965aefb2ce4"; /** * Currently only message encryption is allowed. Later connect based encryption through SSL may be implementes */ public enum Encryption { NONE, TLS } private long m_timeout = TimeUnit.SECONDS.toMillis(60); private String m_subjectPrefix; private String m_userName; private String m_password; private transient String m_transPassword; private String m_toAddress; private String m_host = "smptserver.example.com"; private int m_port = 25; private Encryption m_encryption; private boolean m_alsoPostAfterCombatMove = false; @Override public void sendEmail(final String subject, final String htmlMessage, final File saveGame, final String saveGameName) throws IOException { // this is the last step and we create the email to send if (m_toAddress == null) { throw new IOException("Could not send email, no To address configured"); } final Properties props = new Properties(); if (getUserName() != null) { props.put("mail.smtp.auth", "true"); } if (m_encryption == Encryption.TLS) { props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.starttls.required", "true"); } props.put("mail.smtp.host", getHost()); props.put("mail.smtp.port", getPort()); props.put("mail.smtp.connectiontimeout", m_timeout); props.put("mail.smtp.timeout", m_timeout); final String to = m_toAddress; final String from = "noreply@triplea-game.org"; // todo get the turn and player number from the game data try { final Session session = Session.getInstance(props, null); final MimeMessage mimeMessage = new MimeMessage(session); // Build the message fields one by one: // priority mimeMessage.setHeader("X-Priority", "3 (Normal)"); // from mimeMessage.setFrom(new InternetAddress(from)); // to address final StringTokenizer toAddresses = new StringTokenizer(to, " ", false); while (toAddresses.hasMoreTokens()) { mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(toAddresses.nextToken().trim())); } // subject mimeMessage.setSubject(m_subjectPrefix + " " + subject); final MimeBodyPart bodypart = new MimeBodyPart(); bodypart.setText(htmlMessage, "UTF-8"); bodypart.setHeader("Content-Type", "text/html"); if (saveGame != null) { final Multipart multipart = new MimeMultipart(); multipart.addBodyPart(bodypart); // add save game final FileInputStream fin = new FileInputStream(saveGame); final DataSource source = new ByteArrayDataSource(fin, "application/triplea"); final BodyPart messageBodyPart = new MimeBodyPart(); messageBodyPart.setDataHandler(new DataHandler(source)); messageBodyPart.setFileName(saveGameName); multipart.addBodyPart(messageBodyPart); mimeMessage.setContent(multipart); } // date try { mimeMessage.setSentDate(new Date()); } catch (final Exception e) { // NoOp - the Date field is simply ignored in this case } final Transport transport = session.getTransport("smtp"); if (getUserName() != null) { transport.connect(getHost(), getPort(), getUserName(), getPassword()); } else { transport.connect(); } mimeMessage.saveChanges(); transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients()); transport.close(); } catch (final MessagingException e) { throw new IOException(e.getMessage()); } } /** * Get the user name used to login to the smtp server to send the email. * * @return the userName or null if no authentication is required */ @Override public String getUserName() { return m_userName; } /** * Set the userName used for authentication with the smtp server. * * @param userName * the userName or null if no authentication is required */ @Override public void setUserName(final String userName) { m_userName = userName; } /** * Get the password used to authenticate. * * @return the password or null */ @Override public String getPassword() { if (USE_TRANSITIVE_PASSWORD.equals(m_password)) { return m_transPassword; } return m_password; } /** * Set the password to authenticate with. * * @param password * the password or null */ @Override public void setPassword(final String password) { m_password = password; m_transPassword = password; } /** * Get the timeout (in milliseconds) before the send operation should be aborted. * * @return the timeout */ public long getTimeout() { return m_timeout; } /** * Set the send timeout, after the Email sender is connected to the SMTP server this is the maximum amount of time * it will wait before aborting the send operation * * @param timeout * the timeout in milli seconds. The default is 60 seconds (60000 milli seconds) */ public void setTimeout(final long timeout) { m_timeout = timeout; } /** * Get the SMTP host. * * @return the host to send to */ public String getHost() { return m_host; } /** * Set the smtp server host or IP address. * * @param host * the host */ public void setHost(final String host) { m_host = host; } /** * Get the smtp server post. * * @return the port */ public int getPort() { return m_port; } /** * Set the SMTP server port. * * @param port * the port */ public void setPort(final int port) { m_port = port; } /** * Get the message encryption. * * @return the selected encryption */ public Encryption getEncryption() { return m_encryption; } /** * Sets the message encryption. * * @param encryption * the encryption */ public void setEncryption(final Encryption encryption) { m_encryption = encryption; } /** * Sets the to address field, if multiple email addresses are given they must be separated by space. * * @param to * the to addresses */ public void setToAddress(final String to) { m_toAddress = to; } /** * Get the To address configured. * * @return the to address, or multiple separated by space */ @Override public String getToAddress() { return m_toAddress; } @Override public void clearSensitiveInfo() { m_password = USE_TRANSITIVE_PASSWORD; } @Override public IEmailSender doClone() { final GenericEmailSender sender = new GenericEmailSender(); sender.setSubjectPrefix(getSubjectPrefix()); sender.setEncryption(getEncryption()); sender.setHost(getHost()); sender.setPassword(getPassword()); sender.setPort(getPort()); sender.setTimeout(getTimeout()); sender.setToAddress(getToAddress()); sender.setUserName(getUserName()); sender.setAlsoPostAfterCombatMove(getAlsoPostAfterCombatMove()); return sender; } @Override public boolean getAlsoPostAfterCombatMove() { return m_alsoPostAfterCombatMove; } @Override public void setAlsoPostAfterCombatMove(final boolean postAlso) { m_alsoPostAfterCombatMove = postAlso; } public String getSubjectPrefix() { return m_subjectPrefix; } public void setSubjectPrefix(final String subjectPrefix) { m_subjectPrefix = subjectPrefix; } @Override public String getDisplayName() { return "Generic SMTP"; } @Override public EditorPanel getEditor() { return new EmailSenderEditor(this, new EmailSenderEditor.EditorConfiguration(true, true, true)); } @Override public boolean sameType(final IBean other) { return other.getClass() == GenericEmailSender.class; } @Override public String getHelpText() { return HelpSupport.loadHelp("genericEmailSender.html"); } @Override public String toString() { return "GenericEmailSender{" + "m_toAddress='" + m_toAddress + '\'' + ", m_userName='" + m_userName + '\'' + ", m_host='" + m_host + '\'' + ", m_port=" + m_port + ", m_encryption=" + m_encryption + '}'; } }