dhbw.clippinggorilla.objects.user.UserUtils.java Source code

Java tutorial

Introduction

Here is the source code for dhbw.clippinggorilla.objects.user.UserUtils.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package dhbw.clippinggorilla.objects.user;

import com.vaadin.server.VaadinSession;
import dhbw.clippinggorilla.external.database.Columns;
import dhbw.clippinggorilla.external.database.Database;
import dhbw.clippinggorilla.external.database.Tables;
import dhbw.clippinggorilla.external.database.exceptions.PasswordChangeException;
import dhbw.clippinggorilla.external.database.exceptions.UserCreationException;
import dhbw.clippinggorilla.external.database.exceptions.UserNotFoundException;
import dhbw.clippinggorilla.external.mailserver.Mail;
import dhbw.clippinggorilla.objects.category.Category;
import dhbw.clippinggorilla.objects.clipping.ClippingUtils;
import dhbw.clippinggorilla.objects.group.Group;
import dhbw.clippinggorilla.objects.group.GroupUtils;
import dhbw.clippinggorilla.objects.interestprofile.InterestProfile;
import dhbw.clippinggorilla.objects.interestprofile.InterestProfileUtils;
import dhbw.clippinggorilla.objects.source.Source;
import dhbw.clippinggorilla.scheduler.Jobs;
import dhbw.clippinggorilla.utilities.database.SQLUtils;
import dhbw.clippinggorilla.utilities.language.Language;
import dhbw.clippinggorilla.utilities.language.Word;
import dhbw.clippinggorilla.utilities.log.Log;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.apache.commons.mail.EmailException;
import org.mindrot.jbcrypt.BCrypt;

/**
 *
 * @author frank
 */
public class UserUtils {

    private static final HashMap<VaadinSession, User> USERS = new HashMap<>();
    private static final HashMap<String, User> USERS_PER_USERNAME = new HashMap<>();
    private static final HashMap<String, User> USERS_PER_EMAIL = new HashMap<>();
    public static final HashMap<Integer, User> USERS_PER_ID = new HashMap<>();

    /**
     * Returns the current User or null if there is no User logged in
     *
     * @return The requested User or null
     */
    public static User getCurrent() {
        return USERS.get(VaadinSession.getCurrent());
    }

    private static void addUser(User user) {
        USERS_PER_USERNAME.put(user.getUsername(), user);
        USERS_PER_EMAIL.put(user.getEmail(), user);
        USERS_PER_ID.put(user.getId(), user);
    }

    public static void setCurrentUser(User u) {
        VaadinSession session = VaadinSession.getCurrent();
        if (session != null) {
            USERS.put(session, u);
        }
    }

    public static void removeCurrentUser() {
        VaadinSession session = VaadinSession.getCurrent();
        USERS.remove(session);
    }

    /**
     * Get a User via given username or email
     *
     * @param input can be an email or username
     * @return The requested User (or a exception is thrown)
     */
    public static User getUser(String input) throws UserNotFoundException {
        String sql;
        if (input.contains("@")) {
            if (USERS_PER_EMAIL.containsKey(input)) {
                User u = USERS_PER_EMAIL.get(input);
                addUser(u);
                return u;
            }
            sql = "SELECT * FROM " + Tables.USER + " WHERE " + Columns.EMAIL + " = ?";
        } else {
            if (USERS_PER_USERNAME.containsKey(input)) {
                User u = USERS_PER_USERNAME.get(input);
                addUser(u);
                return u;
            }
            sql = "SELECT * FROM " + Tables.USER + " WHERE " + Columns.USERNAME + " = ?";
        }
        User u = new User();
        try {
            PreparedStatement stat = Database.getConnection().prepareStatement(sql);
            stat.setString(1, input);
            ResultSet result = stat.executeQuery();
            if (result.next()) {
                u.setId(result.getInt(Columns.ID));
                u.setUsername(result.getString(Columns.USERNAME));
                u.setPassword(result.getString(Columns.PASSWORD));
                u.setSalt(result.getString(Columns.SALT));
                u.setEmail(result.getString(Columns.EMAIL));
                u.setFirstName(result.getString(Columns.FIRST_NAME));
                u.setLastName(result.getString(Columns.LAST_NAME));
                u.setRegistrationDate(result.getTimestamp(Columns.REGISTRATION_DATE).toLocalDateTime());
                u.setAccessLevel(result.getInt(Columns.STATUS));
                u.setActivationKey(result.getString(Columns.ACTIVATION_KEY));
                if (result.getInt(Columns.SEND_CLIPPING_MAIL) == 1) {
                    u.setSendClippingMail(true);
                } else {
                    u.setSendClippingMail(false);
                }
                u.setClippingTime(loadAllClippingSendTimes(u));
                if (u.getClippingTime().size() < 1) {
                    addClippingSendTime(u, LocalTime.of(8, 0));
                }
            } else {
                throw new UserNotFoundException();
            }
        } catch (SQLException ex) {
            throw new UserNotFoundException();
        }
        UserUtils.addUser(u);
        u.setLastClipping(ClippingUtils.getLastClipping(u));
        return u;
    }

    /**
     * Get a User via given id
     *
     * @param id The id of the requested User
     * @return The requested User (or a exception is thrown)
     */
    public static User getUser(int id) throws UserNotFoundException {
        if (USERS_PER_ID.containsKey(id)) {
            User u = USERS_PER_ID.get(id);
            addUser(u);
            return u;
        }
        String sql = "SELECT * FROM " + Tables.USER + " WHERE " + Columns.ID + " = ?";
        User u = new User();
        try {
            PreparedStatement stat = Database.getConnection().prepareStatement(sql);
            stat.setInt(1, id);
            ResultSet result = stat.executeQuery();
            if (result.next()) {
                u.setId(result.getInt(Columns.ID));
                u.setUsername(result.getString(Columns.USERNAME));
                u.setPassword(result.getString(Columns.PASSWORD));
                u.setSalt(result.getString(Columns.SALT));
                u.setEmail(result.getString(Columns.EMAIL));
                u.setFirstName(result.getString(Columns.FIRST_NAME));
                u.setLastName(result.getString(Columns.LAST_NAME));
                u.setRegistrationDate(result.getTimestamp(Columns.REGISTRATION_DATE).toLocalDateTime());
                u.setAccessLevel(result.getInt(Columns.STATUS));
                u.setActivationKey(result.getString(Columns.ACTIVATION_KEY));
                if (result.getInt(Columns.SEND_CLIPPING_MAIL) == 1) {
                    u.setSendClippingMail(true);
                } else {
                    u.setSendClippingMail(false);
                }
                u.setClippingTime(loadAllClippingSendTimes(u));
                if (u.getClippingTime().size() < 1) {
                    addClippingSendTime(u, LocalTime.of(8, 0));
                }
            } else {
                throw new UserNotFoundException();
            }
        } catch (SQLException ex) {
            throw new UserNotFoundException();
        }
        UserUtils.addUser(u);
        u.setLastClipping(ClippingUtils.getLastClipping(u));
        return u;
    }

    /**
     * Validates the user login attempt and return a user on success
     *
     * @param input email or username
     * @param password
     * @return HashMap&lt;Integer, User&gt; --&gt; Integer values: 0: ID for
     * User // -1: User not found in DB // -2: Password doesn't match to
     * user/email // -3: User not activated // -4: User banned
     */
    public static HashMap<Integer, User> loginUser(String input, String password) {
        User user = null;
        HashMap<Integer, User> returnMap = new HashMap<>();
        try {
            user = UserUtils.getUser(input);
        } catch (UserNotFoundException ex) {
            returnMap.put(-1, user);
            return returnMap;
        }
        if (user.getPassword().equals(getEncryptedPassword(password, user.getSalt()))) {
            if (user.getAccessLevel() >= 20) {
                returnMap.put(0, user);
                return returnMap;
            } else if (user.getAccessLevel() >= 10) {
                returnMap.put(-3, user);
                return returnMap;
            } else {
                returnMap.put(-4, user);
                return returnMap;
            }
        } else {
            returnMap.put(-2, user);
            return returnMap;
        }
    }

    /**
     * Changes password (requires two times the new password)
     *
     * @param user The User whose password should be changed
     *
     * @param oldPassword The old password
     * @param newPassword The new password
     * @param newPassword2 The new password again to verify integrity
     * @return true: everything is ok, false: sth. went wrong
     */
    public static synchronized boolean changePassword(User user, String oldPassword, String newPassword,
            String newPassword2) {
        if (user.getPassword().equals(getEncryptedPassword(oldPassword, user.getSalt()))
                && checkPassword(newPassword).length() == 0
                && checkSecondPassword(newPassword, newPassword2).length() == 0) {
            try {
                String sql = "UPDATE " + Tables.USER + " SET " + Columns.PASSWORD + " = ? WHERE " + Columns.ID
                        + " = ?";
                PreparedStatement statement = Database.getConnection().prepareStatement(sql);
                String pw = getEncryptedPassword(newPassword, user.getSalt());
                statement.setString(1, pw);
                statement.setInt(2, user.getId());
                statement.executeUpdate();
                user.setPassword(pw);
                return true;
            } catch (SQLException ex) {
                return false;
            }
        }
        return false;
    }

    /**
     * Change the first name of the user.
     *
     * @param u The User whose first name should be changed
     * @param newFirstName The new first name
     * @return true if change was successful, false if dberror occured or
     * requirements are not met.
     */
    public static synchronized boolean changeFirstName(User u, String newFirstName) {
        if (checkName(newFirstName).length() == 0) {
            try {
                String sql = "UPDATE " + Tables.USER + " SET " + Columns.FIRST_NAME + " = ? WHERE " + Columns.ID
                        + " = ?";
                PreparedStatement statement = Database.getConnection().prepareStatement(sql);
                statement.setString(1, newFirstName);
                statement.setInt(2, u.getId());
                statement.executeUpdate();
                u.setFirstName(newFirstName);
                return true;
            } catch (SQLException ex) {
                Log.warning("Failed to change first name!", ex);
                return false;
            }
        }
        return false;
    }

    /**
     * Change the last name of the user.
     *
     * @param u The User whose last name should be changed
     * @param newLastName The new last name
     * @return true if change was successful, false if dberror occured or
     * requirements are not met.
     */
    public static synchronized boolean changeLastName(User u, String newLastName) {
        if (checkName(newLastName).length() == 0) {
            try {
                String sql = "UPDATE " + Tables.USER + " SET " + Columns.LAST_NAME + " = ? WHERE " + Columns.ID
                        + " = ?";
                PreparedStatement statement = Database.getConnection().prepareStatement(sql);
                statement.setString(1, newLastName);
                statement.setInt(2, u.getId());
                statement.executeUpdate();
                u.setLastName(newLastName);
                return true;
            } catch (SQLException ex) {
                Log.warning("Failed to change first name!", ex);
                return false;
            }
        }
        return false;
    }

    /**
     * Changes email (requires two times the new email)
     *
     * @param u The User whose password should be changed
     * @param oldEmail The old mail
     * @param newEmail The new mail
     * @param newEmail2 The new mail again to verify integrity
     * @return true: everything is ok, false: sth. went wrong
     */
    public static synchronized boolean changeEmail(User u, String oldEmail, String newEmail, String newEmail2) {
        if (!newEmail.equals(oldEmail) && checkEmail(newEmail).length() == 0
                && checkSecondEmail(newEmail, newEmail2).length() == 0) {
            try {
                String sql = "UPDATE " + Tables.USER + " SET " + Columns.EMAIL + " = ? WHERE " + Columns.ID
                        + " = ?";
                PreparedStatement statement = Database.getConnection().prepareStatement(sql);
                statement.setString(1, newEmail);
                statement.setInt(2, u.getId());
                statement.executeUpdate();
                u.setEmail(newEmail);
                return true;
            } catch (SQLException ex) {
                Log.warning("Failed to change first name!", ex);
                return false;
            }
        }
        return false;
    }

    /**
     * Changes the username of a User
     *
     * @param u The User whose Username should be changed
     * @param newUsername The new Username
     * @return true if successful, false otherwise
     */
    public static synchronized boolean changeUsername(User u, String newUsername) {
        if (checkUsername(newUsername).length() == 0) {
            try {
                String sql = "UPDATE " + Tables.USER + " SET " + Columns.USERNAME + " = ? WHERE " + Columns.ID
                        + " = ?";
                PreparedStatement statement = Database.getConnection().prepareStatement(sql);
                statement.setString(1, newUsername);
                statement.setInt(2, u.getId());
                statement.executeUpdate();
                u.setUsername(newUsername);
                return true;
            } catch (SQLException ex) {
                Log.warning("Failed to change first name!", ex);
                return false;
            }
        }
        return false;
    }

    /**
     * Returns true if activationKey is correct and updates AccessLevel to 20
     *
     * @param u The User who should be activated
     * @param activationKey The key send via email to activate the user
     * @return true if the key is correct, false otherwise
     */
    public static synchronized boolean activateUser(User u, String activationKey) {
        if (u.getActivationKey().equals(activationKey)) {
            try {
                String sql = "UPDATE " + Tables.USER + " SET " + Columns.STATUS + " = ? WHERE " + Columns.ID
                        + " = ?";
                PreparedStatement statement = Database.getConnection().prepareStatement(sql);
                statement.setInt(1, 20);
                statement.setInt(2, u.getId());
                statement.executeUpdate();
                u.setAccessLevel(20);
                return true;
            } catch (SQLException ex) {
                Log.warning("SQLException on account activation: ", ex);
                return false;
            }
        } else {
            Log.debug("Ceck failed, not equal!");
            return false;

        }
    }

    /**
     * Sets status of user to 0, which effectively bans him.
     *
     * @param u The User sho sould be banned
     * @return true if successful, otherwise: false
     */
    public static boolean banUser(User u) {
        Log.info("Banning user " + u.getUsername());
        try {
            String sql = "UPDATE " + Tables.USER + " SET " + Columns.STATUS + " = ? WHERE " + Columns.ID + " = ?";
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, 0);
            statement.setInt(2, u.getId());
            statement.executeUpdate();
            u.setAccessLevel(0);
            return true;
        } catch (SQLException ex) {
            Log.warning("Failed banning user " + u.getUsername(), ex);
            return false;
        }
    }

    /**
     * Sets status of user to 20, which effectively unbans him.
     *
     * @param u The User whose should be unbanned
     * @return true if successful, otherwise: false
     */
    public static boolean pardonUser(User u) {
        Log.info("Pardoning user " + u.getUsername());
        try {
            String sql = "UPDATE " + Tables.USER + " SET " + Columns.STATUS + " = ? WHERE " + Columns.ID + " = ?";
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, 20);
            statement.setInt(2, u.getId());
            statement.executeUpdate();
            u.setAccessLevel(20);
            return true;
        } catch (SQLException ex) {
            Log.warning("Failed pardoning user " + u.getUsername(), ex);
            return false;
        }
    }

    /**
     * Sets status of user to 90, which effectively makes him a Admin.
     *
     * @param u The User who should be an admin
     * @return true if successful, otherwise: false
     */
    public static boolean opUser(User u) {
        Log.info("Opping user " + u.getUsername());
        try {
            String sql = "UPDATE " + Tables.USER + " SET " + Columns.STATUS + " = ? WHERE " + Columns.ID + " = ?";
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, 90);
            statement.setInt(2, u.getId());
            statement.executeUpdate();
            u.setAccessLevel(90);
            return true;
        } catch (SQLException ex) {
            Log.warning("Failed opping user " + u.getUsername(), ex);
            return false;
        }
    }

    /**
     * Inserts a new user into the database if all requirements are met. This is
     * not a full registration as the user still has to confirm his mail.
     *
     * @param username The Username
     * @param password The first password
     * @param password2 The second password
     * @param email The first email
     * @param email2 The second email
     * @param firstName The first name
     * @param lastName The second name
     * @return The newly created User
     * @throws UserCreationException If the mail could not be send or some
     * values are illegal
     */
    public static synchronized User registerUser(String username, String password, String password2, String email,
            String email2, String firstName, String lastName) throws UserCreationException {
        String salt = getSaltString();
        String activationKey = createNewActivationKey();

        //Backend check of user input to make sure that all values are within defined parameters
        if (checkUserGetBoolean(username, password, password2, email, email2, firstName, lastName)) {
            try {
                password = getEncryptedPassword(password, salt);
                String sql = "INSERT INTO " + Tables.USER + " ( " + Columns.USERNAME + ", " + Columns.SALT + ", "
                        + Columns.PASSWORD + ", " + Columns.EMAIL + ", " + Columns.FIRST_NAME + ", "
                        + Columns.LAST_NAME + ", " + Columns.ACTIVATION_KEY + ") VALUES (?, ?, ?, ?, ?, ?, ?);";
                PreparedStatement sta = Database.getConnection().prepareStatement(sql);
                sta.setString(1, username);
                sta.setString(2, salt);
                sta.setString(3, password);
                sta.setString(4, email);
                sta.setString(5, firstName);
                sta.setString(6, lastName);
                sta.setString(7, activationKey);
                sta.executeUpdate();
                Log.info("User creation: Successfully inserted new user to DB!");
                User user = getUser(username);
                if (!sendRegistrationMail(user)) {
                    throw new UserCreationException("");
                }
                return user;
            } catch (SQLException ex) {
                Log.warning("User creation: Failed due to SQL error: " + ex);
                throw new UserCreationException("" + ex);
            }
        } else {
            Log.warning("User creation: Failed due to illegal values!");
            throw new UserCreationException("User creation failed due to illegal values!");
        }
    }

    /**
     * Removes a user
     *
     * @param u The User who should be deleted
     */
    public static void removeUser(User u) {
        String sql = "DELETE FROM " + Tables.USER + " WHERE " + Columns.ID + " = ?";
        try {
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, u.getId());
            statement.executeUpdate();
        } catch (SQLException ex) {
            Log.warning("Sql failed: removeUser", ex);

        }
        USERS.values().remove(u);
        USERS_PER_EMAIL.values().remove(u);
        USERS_PER_ID.values().remove(u);
        USERS_PER_USERNAME.values().remove(u);
    }

    /**
     * Sends a registration mail to a user
     *
     * @param user The User who should receive a registration mail
     * @return true if successfully send, false otherwise
     */
    public static boolean sendRegistrationMail(User user) {
        Log.debug("Create key for user: " + user.getUsername() + " -- user.getActivationKey: "
                + user.getActivationKey());
        String mail = Language.get(Word.HELLO) + " " + user.getFirstName() + " " + user.getLastName() + ", \n"
                + Language.get(Word.REGISTRATION_VERIFICATION_MAIL_INTRO) + user.getActivationKey()
                + Language.get(Word.REGISTRATION_VERIFICATION_MAIL_OUTRO);
        try {
            Mail.send(user.getEmail(), Language.get(Word.REGISTRATION_VERIFICATION_MAIL_SUBJECT), mail);
            return true;
        } catch (EmailException e) {
            Log.error("Could not send registration Email", e);
            return false;
        }
    }

    /**
     * Sends a new activation mail for the user. This also creates a new key.
     *
     * @param u The User whose activation key should be send again
     * @throws EmailException true if send successfully, false otherwise
     */
    public static void resendActivationMail(User u) throws EmailException {
        setNewActivationKey(u);
        String mail = Language.get(Word.HELLO) + " " + u.getFirstName() + " " + u.getLastName() + ", \n"
                + Language.get(Word.REGISTRATION_VERIFICATION_MAIL_INTRO) + u.getActivationKey()
                + Language.get(Word.REGISTRATION_VERIFICATION_MAIL_OUTRO);
        try {
            Mail.send(u.getEmail(), Language.get(Word.REGISTRATION_VERIFICATION_MAIL_SUBJECT), mail);
        } catch (EmailException e) {
            Log.error("Could not resend activation email", e);
        }
    }

    private static String createNewActivationKey() {
        return getRandomIntAsString(6);
    }

    private static void setNewActivationKey(User u) {
        try {
            u.setActivationKey(getRandomIntAsString(6));
            String sql = "UPDATE " + Tables.USER + " SET " + Columns.ACTIVATION_KEY + " = ? WHERE " + Columns.ID
                    + " = ?";
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setString(1, u.getActivationKey());
            statement.setInt(2, u.getId());
            statement.executeUpdate();
        } catch (SQLException ex) {
            Log.warning("activationKey upload failed: ", ex);
        }
    }

    private static String getRandomIntAsString(int length) {
        String token = "";
        for (int i = 0; i < length; i++) {
            token += String.valueOf((int) (Math.random() * 10));
        }
        if (token.length() > length) {
            token = token.substring(1, length);
        }
        return token;
    }

    /**
     * Checks all parameters whether they contain only allowed values for i.e.
     * registration. Use checkUserGetError to recieve the ErrorMessages!
     *
     * @param username The username
     * @param password The first password
     * @param password2 The second password
     * @param email The first email
     * @param email2 The second email
     * @param firstName The first name
     * @param lastName The last name
     * @return true when everything is ok, false when there is a error.
     */
    public static boolean checkUserGetBoolean(String username, String password, String password2, String email,
            String email2, String firstName, String lastName) {
        return checkUserGetError(username, password, password2, email, email2, firstName, lastName).length() == 0;
    }

    /**
     * Checks all parameters whether they contain only allowed values for i.e.
     * registration and returns all errormessages (i.e.: username to long)
     *
     * @param username The username
     * @param password The first password
     * @param password2 The second password
     * @param email The first email
     * @param email2 The second email
     * @param firstName The first name
     * @param lastName The last name
     * @return all error messages
     */
    public static String checkUserGetError(String username, String password, String password2, String email,
            String email2, String firstName, String lastName) {
        String errorCollection = "";
        errorCollection += checkUsername(username);
        errorCollection += checkEmail(email);
        errorCollection += checkSecondEmail(email, email2);
        errorCollection += checkPassword(password);
        errorCollection += checkSecondPassword(password, password2);
        errorCollection += checkName(firstName);
        errorCollection += checkName(lastName);
        return errorCollection;
    }

    /**
     * Checks whether username is within given parameters
     *
     * @param username The username to be checked
     * @return error messages. If string is empty, everything is fine.
     */
    public static String checkUsername(String username) {
        String returnMessage = checkUsernameOnFormalCorrectness(username);
        if (checkUsernameExisting(username)) {
            returnMessage += Language.get(Word.USERNAME_ALREADY_TAKEN) + "<br>";
        }
        return returnMessage;
    }

    /**
     * Checks whether user already exists in database
     *
     * @param username The username to be checked
     * @return true if user exists
     */
    public static boolean checkUsernameExisting(String username) {
        try {
            String sql = "SELECT * FROM " + Tables.USER + " WHERE " + Columns.USERNAME + " = ?";
            PreparedStatement stat = Database.getConnection().prepareStatement(sql);
            stat.setString(1, username);
            return stat.executeQuery().next();
        } catch (SQLException ex) {
            Log.warning("SQL failed on check whether user exists", ex);
            return false;
        }
    }

    /**
     * Only checks whether username isn't too long/short enough and is formal
     * correct
     *
     * @param username The username to be checked
     * @return error Messages
     */
    public static String checkUsernameOnFormalCorrectness(String username) {
        String returnMessage = "";
        if (username.length() > 20) {
            returnMessage += Language.get(Word.USERNAME_TO_LONG) + "<br>";
        }
        if (username.length() < 3) {
            returnMessage += Language.get(Word.USERNAME_TO_SHORT) + "<br>";
        }
        if (!Pattern.compile("[a-zA-Z0-9_-]*").matcher(username).matches()) {
            returnMessage += Language.get(Word.USERNAME_FORBIDDEN_CHARACTERS) + "<br>";
        }
        return returnMessage;
    }

    /**
     * Checks whether E-Mail address is within given parameters
     *
     * @param email The email to be checked
     * @return error messages. If string is empty, everything is fine.
     */
    public static String checkEmail(String email) {
        String returnMessage = checkEmailOnFormalCorrectness(email);

        if (checkEmailExisting(email)) {
            returnMessage += Language.get(Word.EMAIL_ALREADY_TAKEN) + "<br>";
        }

        if (Mail.isChunk(email)) {
            returnMessage += Language.get(Word.JUNK_MAIL_NOT_ALLOWED);
        }
        return returnMessage;
    }

    /**
     * Only checks whether email is long enough and is formal correct
     *
     * @param email The email to be checked
     * @return error messages. If string is empty everything is fine.
     */
    public static String checkEmailOnFormalCorrectness(String email) {
        String returnMessage = "";
        String regex = "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])";
        if (!email.matches(regex)) {
            returnMessage += Language.get(Word.EMAIL_WRONG_INPUT) + "<br>";
        }
        if (email.length() > 255) {
            returnMessage += Language.get(Word.EMAIL_TO_LONG) + "<br>";
        }
        return returnMessage;
    }

    /**
     * Checks whether email already exists in database
     *
     * @param email The email to be checked
     * @return true if mail exists
     */
    public static boolean checkEmailExisting(String email) {
        try {
            String sql = "SELECT * FROM " + Tables.USER + " WHERE " + Columns.EMAIL + " = ?";
            PreparedStatement stat = Database.getConnection().prepareStatement(sql);
            stat.setString(1, email);
            return stat.executeQuery().next();
        } catch (SQLException ex) {
            Log.warning("SQL failed on check whether user exists", ex);
            return false;
        }
    }

    /**
     * Checks whether both emails are equal
     *
     * @param email The first email
     * @param email2 The second email
     * @return error messages. If string is empty, everything is fine.
     */
    public static String checkSecondEmail(String email, String email2) {
        String returnMessage = "";
        if (!email.equals(email2)) {
            returnMessage += Language.get(Word.EMAILS_DONT_MATCH) + "<br>";
        }
        return returnMessage;
    }

    /**
     * Checks password on length and characters used
     *
     * @param password The password to be checked
     * @return error messages. If string is empty, everything is fine.
     */
    public static String checkPassword(String password) {
        String returnMessage = "";
        if (password.length() > 50) {
            returnMessage += Language.get(Word.PASSWORD_TO_LONG) + "<br>";
        }
        if (password.length() < 8) {
            returnMessage += Language.get(Word.PASSWORD_TO_SHORT) + "<br>";
        }
        if (!Pattern.compile("[()@!$%&/=,-:_#.+\\w]*").matcher(password).matches()) {
            returnMessage += Language.get(Word.PASSWORD_REGEX_RULES) + "<br>";
        }
        return returnMessage;
    }

    /**
     * Checks equality of both passwords
     *
     * @param password The first password
     * @param password2 The second password
     * @return error messages. If string is empty, everything is fine.
     */
    public static String checkSecondPassword(String password, String password2) {
        String returnMessage = "";
        if (!password.equals(password2)) {
            returnMessage += Language.get(Word.PASSWORDS_DONT_MATCH) + "<br>";
        }
        return returnMessage;
    }

    /**
     * Estimates the strength of a password based on the parameters length and
     * types of used characters.
     *
     * @param password The password to be calculated
     * @return percentage of a float between 0 and 1
     */
    public static float calculatePasswordStrength(String password) {
        float strength = 0;
        if (password.length() <= 20 && password.length() >= 8) {
            strength = (float) ((password.length() - 8) / 32.0);
        } else if (password.length() > 20) {
            strength = (float) 0.5;
        }
        if (password.matches(".*[@!$%&/=,-:_#.+].*")) {
            strength += 0.2;
        }
        if (password.matches(".*[a-z].*")) {
            strength += 0.1;
        }
        if (password.matches(".*[A-Z].*")) {
            strength += 0.1;
        }
        if (password.matches(".*[0-9].*")) {
            strength += 0.1;
        }
        if (strength > 1) {
            strength = 1;
        }
        return strength;
    }

    /**
     * Initiates the recovery of a forgotten password by sending recoverymail
     * and creating a recovery token. A token is only working during a single
     * session and is not saved to the database.
     *
     * @param input email or username
     * @return authenticationToken or null if user wasn't found
     */
    public static String initiateForgottenPasswordRecovery(String input) {
        User user = getUser(input);
        String authenticationToken = getRandomIntAsString(6);
        String body = authenticationToken; //Make this better :
        Log.debug("passwordrecovery key: " + authenticationToken);
        try {
            Mail.send(user.getEmail(), Language.get(Word.FORGOTTEN_PASSWORD_MAIL_SUBJECT), body);
        } catch (EmailException ex) {
            Log.warning("Password recovery mail sending failed.", ex);
            return "";
        }
        return authenticationToken;
    }

    /**
     * Changes the password of the user specified via email. If anything fails,
     * a exception is thrown, as every error should be caught before using this
     * method!
     *
     * @param input - email or user
     * @param usersAuthenticationKey
     * @param generatedAuthenticationKey
     * @param password1 The new first password
     * @param password2 The new second password
     * @throws
     * dhbw.clippinggorilla.external.database.exceptions.PasswordChangeException
     */
    public static void executeForgottenPasswordRecovery(String input, String usersAuthenticationKey,
            String generatedAuthenticationKey, String password1, String password2) throws PasswordChangeException {
        String salt = getSaltString();
        User user = getUser(input);
        if (checkPassword(password1).length() == 0 && checkSecondPassword(password1, password2).length() == 0
                && usersAuthenticationKey.equals(generatedAuthenticationKey)) {
            try {
                password1 = getEncryptedPassword(password1, salt);
                String sql = "UPDATE " + Tables.USER + " SET " + Columns.SALT + " = ?, " + Columns.PASSWORD
                        + " = ? WHERE " + Columns.EMAIL + " = ?";
                Log.debug(sql);
                PreparedStatement sta = Database.getConnection().prepareStatement(sql);
                sta.setString(1, salt);
                sta.setString(2, password1);
                sta.setString(3, user.getEmail());
                sta.executeUpdate();
            } catch (SQLException ex) {
                Log.warning("Password recovery failed at SQL UPDATE: ", ex);
                throw new PasswordChangeException();
            }
        } else {
            throw new PasswordChangeException();
        }
    }

    /**
     * Checks if first and lastname are ok.
     *
     * @param name The name to be checked
     * @return error messages. If string is empty, everything is fine.
     */
    public static String checkName(String name) {
        String returnMessage = "";
        if (name.length() > 30) {
            returnMessage += Language.get(Word.NAME_TO_LONG) + "<br>";
        }
        if (name.length() < 2) {
            returnMessage += Language.get(Word.NAME_TO_SHORT) + "<br>";
        }
        if (!Pattern.compile("[a-zA-Z\\t\\n\\x0B\\f\\r-.]*").matcher(name).matches()) {
            returnMessage += Language.get(Word.NAME_FORBIDDEN_CHARACTERS) + "<br>";
        }
        return returnMessage;
    }

    private static String getEncryptedPassword(String passwordToHash, String salt) {
        return BCrypt.hashpw(passwordToHash, salt);
    }

    private static String getSaltString() {
        return BCrypt.gensalt();
    }

    /**
     * Creates a new User Profile and saves it to the DB
     *
     * @param u The User of the new Interestprofile
     * @param name The name of the Interestprofile
     * @param sources The selected sources
     * @param tags The selected tags
     * @param categories The selected categories
     * @return Profile, if sth. went wrong: null
     */
    public static InterestProfile createNewProfile(User u, String name, Map<Source, Boolean> sources,
            Set<String> tags, Map<Category, Boolean> categories) {
        //TODO: profile validation here!
        //Valdiation whether profile exists
        if (!InterestProfileUtils.checkNameUnique(u, name)) {
            return null;
        }
        try {
            //Insert profile data to user_profile table 
            String sql = "INSERT INTO " + Tables.USER_PROFILE + " (" + Columns.USER_ID + ", " + Columns.NAME
                    + ") VALUES (?, ?)";
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, u.getId());
            statement.setString(2, name);
            statement.executeUpdate();

            //Get profile ID from table
            sql = "SELECT " + Columns.ID + " FROM " + Tables.USER_PROFILE + " WHERE " + Columns.USER_ID
                    + " = ? AND " + Columns.NAME + " = ?";
            statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, u.getId());
            statement.setString(2, name);
            ResultSet result = statement.executeQuery();
            result.next();
            int profileId = result.getInt(Columns.ID);

            //Insert sources
            sources.forEach((lambda_source, bool) -> {
                if (bool == true) {
                    String sourceId = lambda_source.getId();
                    try {
                        String lambda_sql = "INSERT INTO " + Tables.USER_PROFILE_SOURCE + " (" + Columns.PROFILE_ID
                                + ", " + Columns.SOURCE + ") VALUES (?, ?)";
                        PreparedStatement lambda_statement = Database.getConnection().prepareStatement(lambda_sql);
                        lambda_statement.setInt(1, profileId);
                        lambda_statement.setString(2, sourceId);
                        lambda_statement.executeUpdate();
                    } catch (SQLException ex) {
                        Log.warning("Profile source insertion failed", ex);
                    }
                }
            });

            //Insert Tags
            tags.forEach((lambda_tags) -> {
                try {
                    String lambda_sql = "INSERT INTO " + Tables.USER_PROFILE_TAG + " (" + Columns.PROFILE_ID + ", "
                            + Columns.TAG + ") VALUES (?, ?)";
                    PreparedStatement lambda_statement = Database.getConnection().prepareStatement(lambda_sql);
                    lambda_statement.setInt(1, profileId);
                    lambda_statement.setString(2, lambda_tags);
                    lambda_statement.executeUpdate();
                } catch (SQLException ex) {
                    Log.warning("Profile source insertion failed", ex);
                }
            });

            //Insert categories
            categories.forEach((category, bool) -> {
                if (bool == true) {
                    String categoryId = category.getId();
                    try {
                        String lambda_sql = "INSERT INTO " + Tables.USER_PROFILE_CATEGORY + " ("
                                + Columns.PROFILE_ID + ", " + Columns.CATEGORY + ") VALUES (?, ?)";
                        PreparedStatement lambda_statement = Database.getConnection().prepareStatement(lambda_sql);
                        lambda_statement.setInt(1, profileId);
                        lambda_statement.setString(2, categoryId);
                        lambda_statement.executeUpdate();
                    } catch (SQLException ex) {
                        Log.warning("Profile source insertion failed", ex);
                    }
                }
            });
            return InterestProfileUtils.getInterestProfile(profileId);
        } catch (SQLException ex) {
            Log.warning("Insertion of new profile failed", ex);
            return null;
        }
    }

    /**
     * Returns all Userprofiles of the user.
     *
     * @param u The User whose Interestprofiles should be returned
     * @return all user profiles in a Set or null if sth. went wrong
     */
    public static Set<InterestProfile> getAllInterestProfiles(User u) {
        try {
            String sql = "SELECT " + Columns.ID + " FROM " + Tables.USER_PROFILE + " WHERE " + Columns.USER_ID
                    + " = ?";
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, u.getId());
            ResultSet result = statement.executeQuery();
            Set<InterestProfile> profileSet = new HashSet<>();
            while (result.next()) {
                profileSet.add(InterestProfileUtils.getInterestProfile(result.getInt(Columns.ID)));
            }
            return profileSet;
        } catch (SQLException ex) {
            Log.warning("Multiprofile selection failed", ex);
            return null;
        }
    }

    /**
     * Sets sendMail to true/false.
     *
     * @param u The User who should be changed
     * @param sendMail true if sending, false otherwise
     * @return true if successful
     */
    public static boolean setEmailNewsletter(User u, boolean sendMail) {
        String sql = "UPDATE " + Tables.USER + " SET " + Columns.SEND_CLIPPING_MAIL + " = ? WHERE " + Columns.ID
                + " = ?";
        try {
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            if (!sendMail) {
                statement.setInt(1, 0);
            } else {
                statement.setInt(1, 1);
            }
            statement.setInt(2, u.getId());
            statement.executeUpdate();
        } catch (SQLException ex) {
            Log.warning("Sql failed: setEmailNewsletter", ex);
            return false;
        }
        u.setSendClippingMail(sendMail);
        return true;
    }

    /**
     * Returns the value whether a clipping mail shall be send
     *
     * @param u The User to be changed
     * @return true == send; false == don't send
     */
    public static boolean getEmailNewsletter(User u) {
        String sql = "SELECT " + Columns.SEND_CLIPPING_MAIL + " FROM " + Tables.USER + " WHERE " + Columns.ID
                + " = ?";
        try {
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, u.getId());
            ResultSet result = statement.executeQuery();
            if (result.next()) {
                if (result.getInt(Columns.SEND_CLIPPING_MAIL) == 1) {
                    u.setSendClippingMail(true);
                } else {
                    u.setSendClippingMail(false);
                }
            }
            return u.isSendClippingMail();
        } catch (SQLException ex) {
            Log.warning("Sql failed: getEmailNewsletter", ex);
            return u.isSendClippingMail();
        }
    }

    /**
     * Adds a new Clipping sending time for a User
     *
     * @param u The User of the new Clipping sending time
     * @param time The Time of the clipping being send
     * @return true if successful, false otherwise
     */
    public static boolean addClippingSendTime(User u, LocalTime time) {
        try {
            String sql = "INSERT INTO " + Tables.USER_CLIPPING_TIMES + " (" + Columns.USER_ID + ", "
                    + Columns.CLIPPING_TIME + ") VALUES (?, ?)";
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, u.getId());
            statement.setString(2, time.toString());
            statement.executeUpdate();
        } catch (SQLException ex) {
            Log.warning("Sql failed: addClippingTime", ex);
            return false;
        }
        u.getClippingTime().add(time);
        Jobs.updateClippingGenerationTimes(u, u.getClippingTime());
        return true;
    }

    private static Set<LocalTime> loadAllClippingSendTimes(User u) {
        Set<LocalTime> times = new HashSet<>();
        try {
            String sql = "SELECT * FROM " + Tables.USER_CLIPPING_TIMES + " WHERE " + Columns.USER_ID + " = ?";
            PreparedStatement statement = Database.getConnection().prepareStatement(sql);
            statement.setInt(1, u.getId());
            ResultSet result = statement.executeQuery();
            while (result.next()) {
                times.add(LocalTime.parse(result.getString(Columns.CLIPPING_TIME)));
            }
        } catch (SQLException ex) {
            Log.warning("Sql failed: loadClippingTime", ex);
        }
        return times;
    }

    /**
     * removes a clipping send time from the users set.
     *
     * @param u The User which Clipping sending time should be removed
     * @param time time to remove, has to be in the users ClippingSendTimes!
     * @return true if successful
     */
    public static boolean removeClippingSendTime(User u, LocalTime time) {
        if (u.getClippingTime().contains(time)) {
            String sql = "DELETE FROM " + Tables.USER_CLIPPING_TIMES + " WHERE " + Columns.USER_ID + " = ? AND "
                    + Columns.CLIPPING_TIME + " = ?";
            try {
                PreparedStatement statement = Database.getConnection().prepareStatement(sql);
                statement.setInt(1, u.getId());
                statement.setString(2, time.toString());
                statement.executeUpdate();
            } catch (SQLException ex) {
                Log.warning("Sql failed: deleteClippingTime", ex);
                return false;
            }
            u.getClippingTime().remove(time);
            Jobs.updateClippingGenerationTimes(u, u.getClippingTime());
            return true;
        }
        return false;
    }

    /**
     * Returns all Clipping sending times for all users
     *
     * @return The Times for all users
     */
    public static Map<User, Set<LocalTime>> getAllClippingSendTimesForAllUsers() {
        String sql = "SELECT * FROM " + Tables.USER_CLIPPING_TIMES + " ORDER BY " + Columns.USER_ID;
        Map<User, Set<LocalTime>> allClippingTimes = new HashMap<>();
        try {
            Statement statement = Database.getConnection().createStatement();
            ResultSet result = statement.executeQuery(sql);
            while (result.next()) {
                User user = getUser(result.getInt(Columns.USER_ID));
                LocalTime userTime = LocalTime.parse(result.getString(Columns.CLIPPING_TIME));
                if (allClippingTimes.containsKey(user)) {
                    allClippingTimes.get(user).add(userTime);
                } else {
                    HashSet<LocalTime> userTimes = new HashSet<>();
                    userTimes.add(userTime);
                    allClippingTimes.put(user, userTimes);
                }
            }
        } catch (SQLException ex) {
            Log.warning("Sql failed: getAllClippingTime", ex);
        }
        return allClippingTimes;
    }

    /**
     * Returns all the Groups for a User
     *
     * @param user
     * @return
     */
    public static Set<Group> getAllGroups(User user) {
        Set<Group> userGroups = new LinkedHashSet<>();
        try {
            ResultSet result = SQLUtils.SELECT(Columns.GROUP_ID_COLUMN).FROM(Tables.USER_GROUPS_TABLE)
                    .WHERE(Columns.USER_ID_COLUMN, user.getId()).execute();
            while (result.next()) {
                userGroups.add(GroupUtils.getGroup(result.getInt(Columns.GROUP_ID)));
            }
        } catch (SQLException ex) {
            Log.error("Could not get Groups for User", ex);
        }
        return userGroups;
    }

}