<?php 
/**
* @version		$Id: users.db.php 2568 2024-05-01 19:52:15Z IOS $
* @package		Elxis
* @subpackage	Database
* @copyright	Copyright (c) 2006-2025 Elxis CMS (https://www.elxis.org). All rights reserved.
* @license		Elxis Public License ( https://www.elxis.org/elxis-public-license.html )
* @author		Elxis Team ( https://www.elxis.org )
* @description 	Elxis CMS is free software. Read the license for copyright notices and details
*/

defined('_ELXIS_') or die ('Direct access to this location is not allowed.');


class usersDbTable extends elxisDbTable {


	/*************************************************/
	/* CONSTRUCT PARENT CLASS AND SET INITIAL VALUES */
	/*************************************************/
	public function __construct() {
		parent::__construct('#__users', 'uid');

		$this->columns = array(
			'uid' => array('type' => 'integer', 'value' => null),
			'firstname' => array('type' => 'string', 'value' => null),
			'lastname' => array('type' => 'string', 'value' => null),
			'uname' => array('type' => 'string', 'value' => null),
			'pword' => array('type' => 'string', 'value' => null),
			'block' => array('type' => 'bit', 'value' => 0),
			'activation' => array('type' => 'string', 'value' => null),
			'gid' => array('type' => 'integer', 'value' => 7),
			'groupname' => array('type' => 'string', 'value' => 'Guest'),
			'avatar' => array('type' => 'string', 'value' => null),
			'preflang' => array('type' => 'string', 'value' => null),
			'timezone' => array('type' => 'string', 'value' => null),
			'country' => array('type' => 'string', 'value' => null),
			'city' => array('type' => 'string', 'value' => null),
			'address' => array('type' => 'string', 'value' => null),
			'postalcode' => array('type' => 'string', 'value' => null),
			'website' => array('type' => 'string', 'value' => null),
			'email' => array('type' => 'string', 'value' => null),
			'phone' => array('type' => 'string', 'value' => null),
			'mobile' => array('type' => 'string', 'value' => null),
			'gender' => array('type' => 'string', 'value' => null),
			'birthdate' => array('type' => 'string', 'value' => null),
			'occupation' => array('type' => 'string', 'value' => null),
			'registerdate' => array('type' => 'string', 'value' => null),
			'lastvisitdate' => array('type' => 'string', 'value' => null),
			'expiredate' => array('type' => 'string', 'value' => null),
			'profile_views' => array('type' => 'integer', 'value' => 0),
			'times_online' => array('type' => 'integer', 'value' => 0),
			'params' => array('type' => 'text', 'value' => null),
			'lastclicks' => array('type' => 'text', 'value' => null),
			'otp' => array('type' => 'string', 'value' => null),
			'pwlastchange' => array('type' => 'integer', 'value' => 0),
			'failedlogin' => array('type' => 'string', 'value' => null),
			'blockuntil' => array('type' => 'integer', 'value' => 0)
		);

		$eDate = eFactory::getDate();
		$this->setValue('lastvisitdate', $eDate->getDate());
		$this->setValue('expiredate', '2060-01-01 00:00:00');
	}


	/**********************/
	/* CHECK ROW VALIDITY */
	/**********************/
	public function check() {
		/**
		Note by datahell: to reduce script execution time and db queries the full check is performed only 
		for new users or on edit user profile by calling manually the fullCheck method before saving data. 
		*/
		$this->gid = (int)$this->gid;
		if (($this->gid < 1) || ($this->gid == 7) || ($this->gid == 6)) {
			$this->errorMsg = eFactory::getLang()->get('INVUSERGROUP');
			return false;
		}

		$this->uname = trim($this->uname);
		if ($this->uname == '') {
			$eLang = eFactory::getLang();
			$this->errorMsg = sprintf($eLang->get('FIELDNOEMPTY'), $eLang->get('USERNAME'));
			return false;
		}
		$uname = preg_replace('/[^A-Z\-\_0-9]/i', '', $this->uname);
		if ($uname != $this->uname) {
			$this->errorMsg = eFactory::getLang()->get('INVALIDUNAME');
			return false;
		}

		if (strlen($this->uname) < 4) {
			$this->errorMsg = eFactory::getLang()->get('INVALIDUNAME');
			return false;
		}


		$this->email = trim($this->email);
		if ($this->email == '') {
			$eLang = eFactory::getLang();
			$this->errorMsg = sprintf($eLang->get('FIELDNOEMPTY'), $eLang->get('EMAIL'));
			return false;
		}

		if (!filter_var($this->email, FILTER_VALIDATE_EMAIL)) {
			$this->errorMsg = eFactory::getLang()->get('INVALIDEMAIL');
			return false;
		}

		$uid = (int)$this->uid;
		if ($uid < 1) {
			$this->lastvisitdate = '1970-01-01 00:00:00';
			if (in_array($this->uname, array('admin', 'administrator', 'manager', 'root', 'elxis'))) {
				$this->errorMsg = 'You cannot use a common username such as '.$this->uname;
				return false;
			}
		} else {
			$parts = preg_split("/[\s-]+/", $this->lastvisitdate);
			if (!$parts || (count($parts) != 4)) {
				$this->lastvisitdate = eFactory::getDate()->getDate();
			} else {
				if (!checkdate($parts[1], $parts[2], $parts[0])) {
					$this->lastvisitdate = eFactory::getDate()->getDate();
				}
			}
			unset($parts);
		}

		return true;
	}


	/*******************************************************************/
	/* FULL CHECK OF ROW VALIDITY (USE ON NEW ACCOUNT OR EDIT PROFILE) */
	/*******************************************************************/
	public function fullCheck() {
		$uid = (int)$this->uid;
		$eLang = eFactory::getLang();
		$this->gid = (int)$this->gid;
		if (($this->gid < 1) || ($this->gid == 7) || ($this->gid == 6)) {
			$this->errorMsg = $eLang->get('INVUSERGROUP');
			return false;
		}

		$elxis = eFactory::getElxis();
		if (($uid < 1) && (!defined('ELXIS_ADMIN'))) {
			$this->gid = 5;
			if ($elxis->getConfig('REGISTRATION') !== 1) {
				$this->errorMsg = 'New users registration is not allowed!';
				return false;
			}
		}

		$stmt = $this->db->prepare("SELECT ".$this->db->quoteId('groupname')." FROM ".$this->db->quoteId('#__groups')." WHERE ".$this->db->quoteId('gid')." = :groupid");
		$stmt->bindParam(':groupid', $this->columns['gid']['value'], PDO::PARAM_INT); //avoid PHP 5.2 overload bug with $this->gid
		$stmt->execute();
		$gname = (string)$stmt->fetchResult();
		if (!$gname || ($gname == '')) {
			$this->errorMsg = $eLang->get('INVUSERGROUP');
			return false;
		}
		$this->groupname = $gname;
		unset($gname);

		$this->uname = trim($this->uname);
		if ($this->uname == '') {
			$this->errorMsg = sprintf($eLang->get('FIELDNOEMPTY'), $eLang->get('USERNAME'));
			return false;
		}
		$uname = preg_replace('/[^A-Z\-\_0-9]/i', '', $this->uname);
		if ($uname != $this->uname) {
			$this->errorMsg = $eLang->get('INVALIDUNAME');
			return false;
		}
		if (strlen($this->uname) < 4) {
			$this->errorMsg = $eLang->get('INVALIDUNAME');
			return false;
		}

		$stmt = $this->db->prepare("SELECT COUNT(".$this->db->quoteId('uid').") FROM ".$this->db->quoteId('#__users')." WHERE ".$this->db->quoteId('uname')." = :username");
		$stmt->bindParam(':username', $this->columns['uname']['value'], PDO::PARAM_STR); //avoid PHP 5.2 overload bug with $this->uname
		$stmt->execute();
		$c = (int)$stmt->fetchResult();
		if (($uid < 1) && ($c > 0)) {
			$this->errorMsg = sprintf($eLang->get('UNAMETAKEN'), $this->uname);
			return false;
		} else if (($uid > 0) && ($c <> 1)) {
			$this->errorMsg = 'You can not change username!';
			return false;
		}

		$this->pword = trim($this->pword);
		if ($this->pword == '') {
			$this->errorMsg = sprintf($eLang->get('FIELDNOEMPTY'), $eLang->get('PASSWORD'));
			return false;
		}

		$pword = $elxis->sanitizeVar($this->pword, true, false);
		if ($pword != $this->pword) {
			$this->errorMsg = $eLang->get('INVALIDPASS');
			return false;
		}
		if (eUTF::strlen($this->pword) < 8) { //valid check for both unencrypted and encrypted password
			$this->errorMsg = $eLang->get('INVALIDPASS');
			return false;
		}

		$this->email = trim($this->email);
		if ($this->email == '') {
			$this->errorMsg = sprintf($eLang->get('FIELDNOEMPTY'), $eLang->get('EMAIL'));
			return false;
		}

		if (!filter_var($this->email, FILTER_VALIDATE_EMAIL)) {
			$this->errorMsg = $eLang->get('INVALIDEMAIL');
			return false;
		}

		if (($elxis->getConfig('REGISTRATION_EMAIL_DOMAIN') != '') || ($elxis->getConfig('REGISTRATION_EXCLUDE_EMAIL_DOMAINS') != '')) {
			$parts = explode('@', $this->email);
			if (!$parts || !is_array($parts) || (count($parts) != 2)) {
				$this->errorMsg = $eLang->get('INVALIDEMAIL');
				return false;
			}
			$emaildomain = strtolower($parts[1]);
			if ($elxis->getConfig('REGISTRATION_EMAIL_DOMAIN') != '') {
				if ($emaildomain != $elxis->getConfig('REGISTRATION_EMAIL_DOMAIN')) {
					if ($eLang->exist('ONLYMAILFROMALLOW')) {
						$this->errorMsg = sprintf($eLang->get('ONLYMAILFROMALLOW'), $elxis->getConfig('REGISTRATION_EMAIL_DOMAIN'));
					} else {
						$this->errorMsg = 'Only email addresses from '.$elxis->getConfig('REGISTRATION_EMAIL_DOMAIN').' are allowed!';
					}
					return false;
				}
			}
			if ($elxis->getConfig('REGISTRATION_EXCLUDE_EMAIL_DOMAINS') != '') {
				$exdomains = explode(',', $elxis->getConfig('REGISTRATION_EXCLUDE_EMAIL_DOMAINS'));
				if ($exdomains && is_array($exdomains) && (count($exdomains) > 0)) {
					foreach ($exdomains as $exdomain) {
						if ($emaildomain == $exdomain) {
							if ($eLang->exist('EMAILADDRNOTACC')) {
								$this->errorMsg = sprintf($eLang->get('EMAILADDRNOTACC'), $emaildomain);
							} else {
								$this->errorMsg = 'Email addresses from '.$emaildomain.' are not acceptable!';
							}
							return false;
						}
					}
				}
			}
			unset($parts, $emaildomain);
		}

		$stmt = $this->db->prepare("SELECT ".$this->db->quoteId('uid')." FROM ".$this->db->quoteId('#__users')." WHERE ".$this->db->quoteId('email')." = :mail");
		$stmt->bindParam(':mail', $this->columns['email']['value'], PDO::PARAM_STR); //avoid PHP 5.2 overload bug with $this->email
		$stmt->execute();
		$uids = $stmt->fetchCol();
		$c = count($uids);
		if ($c > 1) {
			$this->errorMsg = 'There are '.$c.' users sharing the same email address! Please change your email!';
			return false;
		} else if ($c == 1) {
			if ($uids[0] != $uid) {
				if ($eLang->exist('EMAILTAKEN')) {
					$this->errorMsg = sprintf($eLang->get('EMAILTAKEN'), $this->email);
				} else {
					$this->errorMsg = 'Email '.$this->email.' is already in use by an other user. Please select another.';
				}
				return false;
			}
		}

		$pat = "#([\!]|[\(]|[\)]|[\;]|[\"]|[\']|[\=]|[\$]|[\#]|[\<]|[\>]|[\*]|[\/]|[\%]|[\~]|[\`]|[\^]|[\|]|[\{]|[\}]|[\\\])#u";

		$this->firstname = $elxis->sanitizeVar($this->firstname, true, false);
		$this->firstname = eUTF::trim(preg_replace($pat, '', $this->firstname));
		$this->lastname = $elxis->sanitizeVar($this->lastname, true, false);
		$this->lastname = eUTF::trim(preg_replace($pat, '', $this->lastname));
		if (trim($this->firstname) == '') {
			$this->errorMsg = sprintf($eLang->get('FIELDNOEMPTY'), $eLang->get('FIRSTNAME'));
			return false;
		}
		if (trim($this->lastname) == '') {
			$this->errorMsg = sprintf($eLang->get('FIELDNOEMPTY'), $eLang->get('LASTNAME'));
			return false;
		}

		if ($this->country == '') {
			$this->country = null;
		} else {
			$this->country = $elxis->sanitizeVar($this->country, true, false);
			$this->country = eUTF::trim(preg_replace($pat, '', $this->country));
		}
		if ($this->city == '') {
			$this->city = null;
		} else {
			$this->city = $elxis->sanitizeVar($this->city, true, false);
			$this->city = eUTF::trim(preg_replace($pat, '', $this->city));
		}
		if ($this->address == '') {
			$this->address = null;
		} else {
			$this->address = $elxis->sanitizeVar($this->address, true, false);
			$this->address = eUTF::trim(preg_replace($pat, '', $this->address));
		}
		if ($this->postalcode == '') {
			$this->postalcode = null;
		} else {
			$this->postalcode = $elxis->sanitizeVar($this->postalcode, true, false);
			$this->postalcode = eUTF::trim(preg_replace($pat, '', $this->postalcode));
		}
		if (($this->gender != 'male') && ($this->gender != 'female')) { $this->gender = null; }
		if (trim($this->website) != '') {
			if (!filter_var($this->website, FILTER_VALIDATE_URL)) { $this->website = null; }
		} else {
			$this->website = null;
		}
		$this->phone = (trim($this->phone) == '') ? null : filter_var($this->phone, FILTER_SANITIZE_NUMBER_INT);
		$this->mobile = (trim($this->mobile) == '') ? null : filter_var($this->mobile, FILTER_SANITIZE_NUMBER_INT);
		if ($this->occupation == '') {
			$this->occupation = null;
		} else {
			$this->occupation = $elxis->sanitizeVar($this->occupation, true, false);
			$this->occupation = eUTF::trim(preg_replace($pat, '', $this->occupation));
		}

		if (trim($this->timezone) == '') {
			$this->timezone = null;
		} else {
			$this->timezone = $elxis->sanitizeVar($this->timezone);
		}
		if (trim($this->preflang) != '') {
			if (!file_exists(ELXIS_PATH.'/language/'.$this->preflang.'/'.$this->preflang.'.php')) { $this->preflang = null; }
		}
		$this->params = (trim($this->params) == '') ? null : $this->params;
		if (trim($this->avatar) != '') {
			$ext = strtolower(substr($this->avatar, strrpos($this->avatar, '.') + 1));
			if (!in_array($ext, array('png', 'jpg', 'jpeg', 'gif', 'webp'))) { $this->avatar = null; }
			unset($ext);
		} else {
			$this->avatar = null;
		}

		if ((trim($this->expiredate) == '') || ($this->expiredate == '2060-01-01 00:00:00')) {
			$this->expiredate == '2060-01-01 00:00:00';
		} else {
			$parts = preg_split("/[\s-]+/", $this->expiredate);
			if (!$parts || (count($parts) != 4)) {
				$this->errorMsg = $eLang->exist('INVACCEXPDATE') ? $eLang->get('INVACCEXPDATE') :' Invalid account expiration date!';
				return false;
			} else {
				if (!checkdate($parts[1], $parts[2], $parts[0])) {
					$this->errorMsg = $eLang->exist('INVACCEXPDATE') ? $eLang->get('INVACCEXPDATE') :' Invalid account expiration date!';
					return false;
				}
			}
			unset($parts);
		}

		if ($uid < 1) {
			$rulesfile = ELXIS_PATH.'/includes/libraries/elxis/defender/email.rules.php';
			if (file_exists($rulesfile)) {
				include($rulesfile);
				if (isset($rules) && is_array($rules) && (count($rules) > 0)) {
					$parts = explode('@', $this->email);
					$emaildomain = $parts[1];
					foreach ($rules as $rule) {
						if ($rule == $emaildomain) {
							$this->errorMsg = 'Use of temporary email providers like '.$rule.' is forbidden!';
							return false;
						}
					}
				}
			}
		}

		if ($uid < 1) {
			$this->registerdate = eFactory::getDate()->getDate();
		} else {
			$parts = preg_split("/[\s-]+/", $this->registerdate);
			if (!$parts || (count($parts) != 4)) {
				$this->registerdate = eFactory::getDate()->getDate();
			} else {
				if (!checkdate($parts[1], $parts[2], $parts[0])) {
					$this->registerdate = eFactory::getDate()->getDate();
				}
			}
			unset($parts);
		}

		if (trim($this->birthdate) != '') {
			$parts = preg_split("/[\s-]+/", $this->birthdate);
			if (!$parts || (count($parts) < 3) || (count($parts) > 4)) {
				$this->birthdate = null;
			} else {
				if (!checkdate($parts[1], $parts[2], $parts[0])) {
					$this->birthdate = null;
				}
			}
			unset($parts);
		} else {
			$this->birthdate = null;
		}

		$this->block = (int)$this->block;
		$this->profile_views = (int)$this->profile_views;
		$this->times_online = (int)$this->times_online;
		if ($uid < 1) {
			if ($elxis->getConfig('REGISTRATION_ACTIVATION') === 0) {
				$this->activation = null;
				$this->block = defined('ELXIS_ADMIN') ? $this->block : 0;
			} else if ($elxis->getConfig('REGISTRATION_ACTIVATION') === 2) {
				$this->activation = null;
				$this->block = defined('ELXIS_ADMIN') ? $this->block : 1;
			} else {
				if (trim($this->activation) == '') {
					$act = '';
					while (strlen($act) < 40) { $act .= mt_rand(0, mt_getrandmax()); }
					$this->activation = sha1($act);
					$this->block = defined('ELXIS_ADMIN') ? $this->block : 1;
				}
			}
		} else {
			if (trim($this->activation) == '') {
				$this->activation = null;
			} else {
				$this->activation = $elxis->sanitizeVar($this->activation);
			}
		}

		if ($uid < 1) {
			$this->lastvisitdate = '1970-01-01 00:00:00';
		} else {
			$parts = preg_split("/[\s-]+/", $this->lastvisitdate);
			if (!$parts || (count($parts) != 4)) {
				$this->lastvisitdate = eFactory::getDate()->getDate();
			} else {
				if (!checkdate($parts[1], $parts[2], $parts[0])) {
					$this->lastvisitdate = eFactory::getDate()->getDate();
				}
			}
			unset($parts);
		}

		return true;
	}


	public function addUserClick($returnclicks=false) {
		$clicks = array();
		if (trim($this->lastclicks) != '') { $clicks = json_decode($this->lastclicks, true); }
		if ($clicks) {
			if (count($clicks) > 9) {
				$olderts = 0;
				$olderidx = -1;
				foreach ($clicks as $k => $click) {
					$ts = (int)$click['ts'];
					if ($olderidx == -1) {
						$olderidx = $k;
						$olderts = $ts;
					} else {
						if ($ts < $olderts) {
							$olderidx = $k;
							$olderts = $ts;
						}
					}
				}
				unset($clicks[$olderidx]);
			}
		}

		$url = eFactory::getURI()->getRealUriString();
		$clicks[] = array('ts' => time(), 'page' => $url);

		$this->lastclicks = json_encode($clicks);
		//$this->setValue('lastclicks', json_encode($clicks));
		if ($returnclicks) { return $this->lastclicks; }
	}

}

?>