package org.openmhealth.reference.request; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Properties; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.NoSuchProviderException; import javax.mail.SendFailedException; import javax.mail.Session; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import org.openmhealth.reference.data.UserBin; import org.openmhealth.reference.domain.User; import org.openmhealth.reference.exception.OmhException; import org.openmhealth.reference.servlet.Version1; import com.sun.mail.smtp.SMTPTransport; /** * <p> * Creates a new registration for a new user. * </p> * * @author John Jenkins */ public class UserRegistrationRequest extends Request<Object> { /** * The path to this API after the mandatory path and the version, e.g. * /omh/v1. */ public static final String PATH = "/users/registration"; /** * The logger for this request. */ private static final Logger LOGGER = Logger.getLogger(UserRegistrationRequest.class.toString()); /** * The algorithm to use to create the random registration ID. */ private static final String DIGEST_ALGORITHM = "SHA-512"; /** * The mail protocol to use when sending mail. */ private static final String MAIL_PROTOCOL = "smtp"; /** * The mail property key for SSL. */ // private static final String MAIL_PROPERTY_SSL_ENABLED = // "mail." + MAIL_PROTOCOL + ".ssl.enable"; /** * The mail session properties to use for every session. */ private static final Properties MAIL_SESSION_PROPERTIES = new Properties(); // static { // // Always use SSL. // MAIL_SESSION_PROPERTIES.put(MAIL_PROPERTY_SSL_ENABLED, true); // } /** * The email address of the sender of the registration emails. */ private static final String MAIL_SENDER_EMAIL_STRING = "no-reply@openmhealth.org"; /** * The InternetAddress object to use for the sender of the registration * emails. */ private static final InternetAddress MAIL_SENDER_EMAIL; static { try { MAIL_SENDER_EMAIL = new InternetAddress(MAIL_SENDER_EMAIL_STRING); } catch(AddressException e) { throw new IllegalStateException( "The sender email address is invalid.", e); } } /** * The subject line for the registration email. */ private static final String REGISTRATION_SUBJECT = "Open mHealth Registration"; /** * The specialized text to use as a placeholder for the activation link * within the {@link #REGISTRATION_TEXT}. */ private static final String ACTIVATION_LINK_PLACEHOLDER = "<ACTIVATION_LINK>"; /** * The text to send in the registration email. */ private static final String REGISTRATION_TEXT = "<h3>Registration Activation</h3>" + "<p>Thank you for creating an account. To activate your account, " + "follow the link.</p>" + "<br/>" + ACTIVATION_LINK_PLACEHOLDER + "<br/>" + "<h6>If you are not attempting to create an account, please " + "disregard this email.</h6>"; /** * The new user. */ private final User user; /** * The base URL to use when building the link in the activation email, * e.g. http://localhost:8080/omh/v1 */ private final String activationUrl; /** * Creates a registration request. * * @param username * The new user's user-name. * * @param password * The new user's plain-text password. * * @param email * The new user's email address. * * @param rootUrl * The root URL for our domain, e.g. http://localhost:8080/omh. * * @throws OmhException * A parameter was invalid. */ public UserRegistrationRequest( final String username, final String password, final String email, final String rootUrl) throws OmhException { if(rootUrl == null) { throw new OmhException("The root URL is null."); } // Create the new user from the parameters and a random registration // ID. this.user = new User( username, User.hashPassword(password), email, createRegistrationId(username, email), System.currentTimeMillis(), null); // Build the rest of the base URL for these requests. this.activationUrl = rootUrl + Version1.PATH + UserActivationRequest.ACTIVATION_PAGE; } /* * (non-Javadoc) * @see org.openmhealth.reference.request.Request#service() */ @Override public void service() throws OmhException { // First, short-circuit if this request has already been serviced. if(isServiced()) { return; } else { setServiced(); } // Create the registration entry in the database. UserBin.getInstance().createUser(user); // Create a mail session. Session smtpSession = Session.getDefaultInstance(MAIL_SESSION_PROPERTIES); // Create the message. MimeMessage message = new MimeMessage(smtpSession); // Add the recipient. try { message.setRecipient(Message.RecipientType.TO, user.getEmail()); } catch(MessagingException e) { throw new OmhException( "There was an error setting the recipient of the message.", e); } // Add the sender. try { message.setFrom(MAIL_SENDER_EMAIL); } catch(MessagingException e) { throw new OmhException( "There was an error setting the sender's email address.", e); } // Set the subject. try { message.setSubject(REGISTRATION_SUBJECT); } catch(MessagingException e) { throw new OmhException( "There was an error setting the subject on the message.", e); } // Set the content of the message. try { message.setContent(createRegistrationText(), "text/html"); } catch(MessagingException e) { throw new OmhException( "There was an error constructing the message.", e); } // Prepare the message to be sent. try { message.saveChanges(); } catch(MessagingException e) { throw new OmhException( "There was an error saving the changes to the message.", e); } // Send the registration email. sendMailMessage(smtpSession, message); } /** * Creates a random registration ID. * * @param username * The user's user-name. * * @param email * The user's email address. * * @return A random value that can be used as a registration ID. * * @throws OmhException * There was a problem creating the registration ID. */ private String createRegistrationId( final String username, final String email) throws OmhException { // Generate the digest. MessageDigest digest; try { digest = MessageDigest.getInstance(DIGEST_ALGORITHM); } catch(NoSuchAlgorithmException e) { throw new OmhException("The SHA-512 algorithm is unknown.", e); } digest.update(username.getBytes()); digest.update(email.getBytes()); digest .update( new Long(System.currentTimeMillis()).toString().getBytes()); digest.update(UUID.randomUUID().toString().getBytes()); byte[] digestBytes = digest.digest(); StringBuffer buffer = new StringBuffer(); for(int i = 0; i < digestBytes.length; i++) { buffer.append( Integer.toString( (digestBytes[i] & 0xff) + 0x100, 16) .substring(1)); } return buffer.toString(); } /** * Creates the registration text. * * @return The registration text. * * @throws OmhException * There was a problem creating the registration text. */ private String createRegistrationText() throws OmhException { return REGISTRATION_TEXT .replace( ACTIVATION_LINK_PLACEHOLDER, "<a href=\"" + activationUrl + "?" + User.JSON_KEY_REGISTRATION_KEY + "=" + user.getRegistratioKey() + "\">Click here to activate your account.</a>"); } /** * Sends a mail message. * * @param smtpSession * The session used to create the message. * * @param message * The message to be sent. * * @throws OmhException * There was a problem creating the connection to the mail server * or sending the message. */ private void sendMailMessage( final Session smtpSession, final Message message) throws OmhException { // Get the transport from the session. SMTPTransport transport; try { transport = (SMTPTransport) smtpSession.getTransport(MAIL_PROTOCOL); } catch(NoSuchProviderException e) { throw new OmhException( "There is no provider for " + MAIL_PROTOCOL + ".", e); } // Connect to the transport. try { transport.connect(); } catch(MessagingException e) { throw new OmhException("Could not connect to the mail server.", e); } // Send the message. try { transport.sendMessage(message, message.getAllRecipients()); } catch(SendFailedException e) { throw new OmhException("Failed to send the message.", e); } catch(MessagingException e) { throw new OmhException( "There was a problem while sending the message.", e); } finally { // Close the connection to the transport. try { transport.close(); } catch(MessagingException e) { LOGGER .log( Level.WARNING, "After sending the message there was an error " + "closing the connection.", e); } } } }