package net.grcomputing.opencms.webusers.lite;

import java.util.Date;
import java.security.MessageDigest;
/*
 * So I was trying to find a base64 generator for Java, and found on in
 * the ostermiller utilities jar. Then I noticed, lo and behold!, there
 * was also a very nice password generator in there.
 */
import com.Ostermiller.util.RandPass;

/**
* Handles password generation and verification.
* <p>This class uses some very nice utilities provided by 
* <a href="http://ostermiller.org">Ostermiller.org</a>.</p>
* @author Matt Butcher mbutcher@grcomputing.net
* @see <a href="http://grcomputing.net">http://grcomputing.net</a>
* @see <a href="http://ostermiller.org/utils">Ostermiller.org</a>
* <p>
* This file is part of GRC's OpenCMS module library.
* </p>
* <p>
* GRC's OpenCMS module library 
* is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* </p>
* <p>
* GRC's OpenCMS module library is distributed in the 
* hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* </p>
* <p>
* You should have received a copy of the GNU General Public License
* along with GRC's OpenCMS module library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
* Or view the license online at http://www.gnu.org/licenses/gpl.html
* </p>
* <hr>
* <p>This class provides password checking, and is used to verify passwords for
* new webusers or for webuser password modification.</p>
* <p>This class can be extended fairly easily -- just make sure that the
* child class implements the runTests() method indicating any new tests that 
* it should run.</p>
* 
*/

public class PasswordChecker {
	private String pwa = null, pwb = null;
	private String errorMessage = "";
	private static int MIN_LENGTH = 7;
	private static int MAX_LENGTH = 255;

	/**
	 * Default constructor is usually the one used.
	 */
	public PasswordChecker() {

	}

	/**
	 * This method runs all of the tests it has been instructed to run. For now,
	 * that means that each test must be hard-coded into this method (or 
	 * the child equivalent). If the password fails, using getReason() will 
	 * explain why.
	 * @return true if pw is okay, false otherwise.
	 */
	public boolean runTests(String password, String password_again) {
		this.pwa = password;
		this.pwb = password_again;
		if(!testEquality(pwa, pwb)) return false;
		if(!testLength(pwa)) return false;
		return true;
	}

	/**
	 * Returns a String detailing why a password failed the checks.
	 */
	public String getReason() {
		return errorMessage;
	}

	/**
	 * Sets the reason message for test failures. To be used by implementing
	 * tests.
	 */
	protected void setReason(String reason) {
		errorMessage = reason;
	}

	/**
	 * Tests to make sure password is neither too long (won't fit in table) or
	 * too short (security risk).
	 * @param String password
	 * @return true if ok. 
	 */
	protected boolean testLength(String password) {
		if(password.length() < MIN_LENGTH) {
			setReason("Password is too short. It must be at least " +
				MIN_LENGTH + " characters long.");
			return false;
		} else if (password.length() > MAX_LENGTH) {
			setReason("Password is too long. It can't be more than " +
				MAX_LENGTH + " characters long.");
			return false;
		}
		return true;
	}

	/**
	 * Tests if password match.
	 * @param String password
	 * @param String password_again
	 * @return true if OK.
	 */
	protected boolean testEquality (String pwa, String pwb) {
		if(pwa.equals(pwb)) return true;
		setReason("Passwords do not match.");
		return false;
	}

	/**
	 * Generates a random password. Passwords can can contain any
	 * combination of letters (upper and lower case) numbers, and 
	 * symbols. This version uses the length specified in private
	 * int MIN_LENGTH -- usually "7"
	 * @return String containing the new password.
	 * @throws Exception if the degest did not work
	 */
	public static String generatePassword() throws Exception {
		return generatePassword(MIN_LENGTH);
	}

	/**
	 * Generates a random password. Passwords can can contain any
	 * combination of letters (upper and lower case) numbers, and 
	 * symbols.
	 * <p>2003-07-31 MPB - switched generator to use 
	 * com.ostermiller.util.RandPass</p>
	 * @param int length of the password.
	 * @return String containing the new password.
	 * @throws Exception if the hash did not work
	 * @see http://ostermiller.org/utils/ Ostermiller.org Utilities
	 */
	public static String generatePassword(int pwLength) throws Exception {
		// Byte array itself isn't informative -- need characters
		// to tell actuall length.
		/*
		StringBuffer sb = new StringBuffer();
		sb.append(new String(genpw()));
		if(sb.length() == pwLength) return sb.toString();
		else if(sb.length() > pwLength) return sb.substring(0, pwLength - 1);
		else {
			while(sb.length() < pwLength ) {
				sb.append(new String(genpw()));
			}
			return sb.substring(0,pwLength - 1);
		}
		*/
		// Use default alphabet.
		RandPass pwGen = new RandPass();
		return pwGen.getPass(pwLength);

			
	}

	/** Handles creating a hash. */
	private static byte[] genpw() throws Exception {
		Date now = new Date();
		int seed = (int)(Math.random() * 100000);
		long seed2 = now.getTime();
		long rawSum = seed + seed2;

		String raw = Long.toString(rawSum);
		MessageDigest sha = MessageDigest.getInstance("SHA");
		return sha.digest(raw.getBytes());
		
	}
}
