#!/usr/bin/perl -Tw -I. -I/usr/local/positive/Perl

#
# This software is copyright (c) 2001-2002 Positive Networks, Inc.  All rights reserved.
# 
# This software is the proprietary and confidential property of Positive Networks, Inc.
# Possession, transmission, publication, or use of this software is prohibited except by prior written consent.
#  

# reg.pl - handles the user registration proces

use strict;
use Pos::Webtop::Web::ModuleLoader;
use Pos::Webtop::Web::OEMLoader;
use Digest::MD5;

my $tb = Pos::Webtop::Toolbox->new();
my $client = $tb->getClient();
my $cgi = $tb->getCgi();
my $authdb = $tb->getPolicyDb();

my $nostep = $cgi->param('nostep');
my $mode = $cgi->param('mode');
my $username = "";
my $userId;

$nostep = 0 if (not defined($nostep));
$mode   = "" if (not defined($mode));

if ($mode eq 'login')
{
	$username = $cgi->param('username');

	($userId) = $authdb->query(sql => "
		select
			user_id
		from
			users
		where
			user_name_key = upper('" . $authdb->escape(dirty => $username) . "')
		");
}
else
{
	$client->checkSession(ignoreAuxAuth=> 1);

	$userId = $client->getUserId();
}

$client->repersistValues(values=>['startUrl']);

my ($authType) = $authdb->query(sql =>  "select auth_type_id from user_auth_types where user_id = $userId");

if (defined($cgi->param('initialPassword'))) 
{
	my $initialPassword = $cgi->param('initialPassword');
	my $confirmPassword = $cgi->param('confirmPassword');
	my $validity = validatePassword(userId => $userId, mode => $mode, password=>$initialPassword, tb=>$tb);

	if ($initialPassword ne $confirmPassword)
	{
		setUserPassword(tb=>$tb, , errorType=>'2', error=>"Passwords did not match.", validity=>$validity, mode => $mode, username => $username, userId => $userId );
	}
	elsif($validity)
	{
		setUserPassword(tb=>$tb, , errorType=>'3', error=>"Password does not meet Complexity Standards", validity=>$validity, mode => $mode, username => $username, userId => $userId);
	}
	else
	{
		#
		# TODO: Call an external binary to change the user's NTLM password
		#

		# do the password input
		my $md5pw = MD5->hexhash($initialPassword);
		my $sql = "
						UPDATE 
							users
						SET 
							password = '$md5pw',
							last_password_reset = " . $db->getCurrentDateString() . "
						WHERE 
							user_id = $userId
					";

		my $db = $tb->getPolicyDb();
		$db->do( sql => $sql );
		$db->commit;

		# If we were called from the login page, refresh back to the 
		# login page using the user's new credentials
		if ($mode eq 'login')
		{
			my $username = $cgi->param('username');

			print "Content-type: text/html\n\n
				<html><head>
				<META HTTP-EQUIV=REFRESH CONTENT=\"0; URL=login.pl?un=" . HTML::Entities::encode($username) . "&pw=" . HTML::Entities::encode($confirmPassword) . "\">
				</head></html>
				";
		}
		# Otherwise, redirect internally with the already logged in session
		else
		{
			my $refreshModule = $client->getPersistedValue( name => "refreshModule" );

			print "Content-type: text/html\n\n
				<html>
				<body>
				<form method='POST' action='main.pl' name='refreshForm'>
				<input type='hidden' name='module' value='$refreshModule'/>
			";

			print $client->getFormInputs();

			print "
				</form>

				<script language='JavaScript1.2' type='text/javascript'>

					document.refreshForm.submit();

				</script>
				</body>
				</html>
			";
		}
	}
}
else
{
	setUserPassword(tb=>$tb, authType=>$authType, mode => $mode, username => $username, userId => $userId);
}

sub setUserPassword
{
	my ($tb, $errorType, $error, $validity, $mode, $username, $userId) = @{{@_}}{qw/tb errorType error validity mode username userId/};
	my $client = $tb->getClient();
	my $cgi = $tb->getCgi();
	my $db = $tb->getPolicyDb();
	my $requireOldPassword = $cgi->param('reqoldpass');
	my $nostep = $cgi->param('nostep');

	my $errorValue;
	my $errorTypeValue;

	$validity = "" unless defined($validity);
	$errorType = 0 unless defined($errorType);

	$requireOldPassword = 1 if ($mode eq 'login');

	my ($eightChars, $numReq, $upperLowerReq, $nonAlphaNumeric, $passMustDiffer) = @{ getPasswordComplexity(tb => $tb, userId => $userId) };

	# Pop up the password setting page

	my $oem = $tb->getOem;
	my $oemId = $tb->getOemId;
	my $title = "Registration";

	if ($mode eq 'login')
	{
		$title = "Password Expired";
	}

	$oem->printHtmlHeader( title => $title);
	$oem->printBodyHeader();

	my $oemStep2Text;
	my $oemStep2Info;

	if ( $oemId == 20 )
	{
		$oemStep2Text = 'Create your new EarthLink VPN password';
		$oemStep2Info = '<b>You\'ll use this password to sign on to the EarthLink VPN service.</b>
								<br><i>Note: This will be your password for both VPNLink and WebLink access.';
	}
	else
	{
		$oemStep2Text = 'Choose your Sign On password';
		$oemStep2Info = 'You\'ll use this password to sign on to the ' . $oem->getSetting(setting => 'rebrandName') . ' service.';
	}

	# If we're in login mode, set the title and info to convey that the user's password has expired
	if ($mode eq 'login')
	{
		$oemStep2Text = 'Your password has expired';
		$oemStep2Info = 'Please specify a new password.';
	}

	print "
		<div style='text-align: left' class='BOXTD1'><span style='font-size: 18px;'><B>$oemStep2Text" .
		((!$nostep) ? ": <font size='2'>(Step 2 of 3)</font> " : "") . "</b></span></div>
			<form method=POST action='main.pl' name='refreshForm'>
			";

	my $refreshModule = $client->getPersistedValue( name => "refreshModule" );

   print
		"

			<form method='POST' action='main.pl' name='refreshForm'>

				<input type='hidden' name='module' value='$refreshModule'/>
		";

	print  $client->getFormInputs();

	print 
		"
			</form>
			
			</form>
			
			
			<form autocomplete='off' method=POST action='reg3.pl' name='finishForm'>
				" . ((length($mode))?"<input type='hidden' name='mode' value=\"" . HTML::Entities::encode($mode) . "\"/>":"") . "
				" . ((length($username))?"<input type='hidden' name='username' value=\"" . HTML::Entities::encode($username) . "\"/>":"") . "
				" . ((defined($nostep))?"<input type='hidden' name='nostep' value=\"" . HTML::Entities::encode($nostep) . "\"/>":"") . "
				" . ((defined($requireOldPassword))?"<input type='hidden' name='reqoldpass' value=\"" . HTML::Entities::encode($requireOldPassword) . "\"/>":"") . "
				<table align='center' border='0' cellpadding='5' cellspacing='0'>
					<tr>
						<td colspan='2' class='BOXTD1' align='center'><br/>$oemStep2Info<br><br></td>
					</tr>
					<tr>
						<td>
							<table cellspacing=1 cellpadding=4 bgcolor'#000000'>
								<tr>
									<td class='title' colspan=2>
										Change Password
									</td>
								</tr>
				";
				
				if ($requireOldPassword)
				{
					print
						"
								<tr>
									<td bgcolor='#FFFFFF' width='50' class='BOXTD1' align='right'><b>Old password:</b></td>
									<td bgcolor='#FFFFFF' width ='150'><input type='password' name='oldpass' maxlength='79' id='oldPassword'></td>
								</tr>
						";
				}

				print "
								<tr>
									<td bgcolor='#FFFFFF' width='50' class='BOXTD1' align='right'><b>" . (($requireOldPassword)?"New ":"") . "password:</b></td>
									<td bgcolor='#FFFFFF' width ='150'><input type ='password' name='initialPassword' maxlength='79' id='initialPassword'></td>
								</tr>
								<tr>
									<td bgcolor='#FFFFFF' width='50' class='BOXTD1'><nobr><b>Confirm" . (($requireOldPassword)?" new ":"") . "password:</b></nobr></td>
									<td bgcolor='#FFFFFF'> <input type ='password' name='confirmPassword' maxlength='79'><br> </td>
								</tr>
							</table>
							<center>
							<table border='0' cellspacing='10' style='height: 32px'>
								<tr>
									<td><input type=submit value='Continue' onClick='javascript:document.finishForm.submit();'></td>
									<td><input type=button value='Cancel' onClick='javascript:document.refreshForm.submit();'></td>
								</tr>
							</table>
							</center>
					";
	$client->repersistValues( values=> [ "refreshModule" ] );

	print $client->getFormInputs();
				print"
						</td>
						<td valign='top'>
							
							<table border='0' cellspacing=\"1\" cellpadding=\"3\" bgcolor=\"#000000\" width=\"100%\">
								<tr>
									<td class='NOTE'>Password Complexity Guidelines</td>
							</tr>
							<tr>
								<td align='left' bgcolor='#FFFFFF'><ul>
	";

	if ($requireOldPassword)
	{
		if ($validity =~ /oldpass/)
		{
			print "
										<li class='notes'><font color ='red'><b>Old password correct</b></font></li>";
		}
		else
		{
			print " 
										<li class='notes'>Old password correct</li>";
		}

	}

	my @info = @{ getPasswordComplexity(tb=>$tb, userId => $userId) };

	if ($info[0]->[0])
	{
		if ($validity =~ /lessthan/)
		{
			print "
										<li class='notes'><font color ='red'><b>At least 8 characters</b></font></li>";
		}
		else
		{
			print " 
										<li class='notes'>At least 8 characters</li>";
		}
	}

	if ($info[0]->[1])
	{
		if ($validity =~ /digit/)
		{
			print " 
										<li class='notes'><font color = 'red'><b>At least 1 number</b></font> </li>";
		}
		else
		{
			print " 
										<li class='notes'>At least 1 number </li>";
		}
	}

	if ($info[0]->[2])
	{
		if ($validity =~ /alpha/)
		{
			print " 
										<li class='notes'><font color = 'red'><b>Upper and lower case</b></font> </li>";
		}
		else
		{
			print " 
										<li class='notes'>Upper and lower case</li>";
		}
	}

	if ($info[0]->[3])
	{
		if ($validity =~ /nonalpha/)
		{
			print " 
										<li class='notes'><font color = 'red'><b>At least 1 non-alpha numeric character</b></font></li>";
		}
		else
		{
			print " 
										<li class='notes'>At least 1 non-alpha numeric character</li>";
		}
	}

	if ($info[0]->[4])
	{
		if ($validity =~ /equal/)
		{
			print " 
										<li class='notes'><font color = 'red'><b>Password must differ from old password</b></font></li>";
		}
		else
		{
			print "
										<li class='notes'>Password must differ from old password</li>";
		}
	}

	print "
										<li class='notes'>Case sensitive</li>";

	if ($errorType == '2')
	{
		print " 
										<li class='notes'><font color='red'><b>Passwords must match</b></font></li>";
	}
	else
	{
		print " 
										<li class='notes'>Passwords must match</li>";
 	}

	if ($validity =~ /onechar/ and !$info[0]->[0])
	{
		print "
										<li class='notes'><font color='red'><b>Password must be at least one character</b></font></li>";
	}
	elsif(!$info[0]->[0])
	{
		print "
										<li class='notes'>Password must be at least one character</li>";
	}

	print "
									</ul>
								</td>
							</tr>
						</table>
					</tr>
		";

	print "
						</td>
					</tr>
				</table>
			</form>

		<script language='JavaScript1.3' TYPE='text/javascript'>
			" . (($requireOldPassword)?"
			document.getElementById('oldPassword').focus();
			":"
			document.getElementById('initialPassword').focus();
			") . "
		</script>
	";


	$oem->printBodyFooter();
	$oem->printHtmlFooter();

}

sub validatePassword 
{
	my ($tb, $userId, $mode, $password) = @{{@_}}{qw/tb userId mode password/};
	my $client = $tb->getClient();
	my $cgi = $tb->getCgi();
	my $db = $tb->getPolicyDb();

	my $pc = getPasswordComplexity(tb => $tb, userId => $userId);
	my $eightChars = $pc->[0];

	my $hexedPass = MD5->hexhash($password);

	my ($oldPassword) = $db->query(sql=>"
			SELECT password 
			FROM users 
			WHERE user_id = $userId
		");

	my $error = 0;	

	# If we need to validate the old password, do so now
	# This needs to be investigated for security issues.  A malicious user could
	# not supply reqoldpass.
	my $requireOldPassword = $cgi->param('reqoldpass');

	if ($requireOldPassword or $mode eq 'login')
	{
		my $oldPass = $cgi->param('oldpass');
		my $oldPassHash = MD5->hexhash($oldPass);

		if (uc($oldPassHash) ne uc($oldPassword))
		{
			$error .= 'oldpass';
		}
	}

	if($eightChars->[0])
	{
		if ( length($password) < 8) 
		{
			$error .= 'lessthan';
		}
	}

	if($eightChars->[1])
	{
		if ($password !~ /\d/)
		{
			$error .= 'digit';
		}
	}

	if($eightChars->[2])
	{
		if ($password !~ /[A-Z]/ or $password !~ /[a-z]/)
		{
			$error .= 'alpha';
		}
	}

	if($eightChars->[3])
	{
		if ($password !~ /\W/)
		{
			$error .= 'nonalpha';
		}
	}

	if($eightChars->[4])
	{
		if (uc($hexedPass) eq uc($oldPassword))
		{
			$error .= 'equal';
		}
	}

	if (!length $password)
	{
		$error .= 'onechar';
	}

	return $error;
}

sub getPasswordComplexity 
{
	my ($tb, $userId) = @{{@_}}{qw/tb userId/};
	my $db = $tb->getPolicyDb();
	my $client = $tb->getClient();

	my $sql = "
					SELECT eight_characters, one_number, upper_lower_case, non_alpha_numeric, password_must_differ
					FROM user_auth_requirements
					WHERE user_id IN 
					(
						SELECT user_id
						FROM users
						WHERE user_id = $userId
					)
				";

	my @passwordComplexity = $db->query(sql=>$sql);
	return \@passwordComplexity;
}
