<?php
/**
 * Coppermine Photo Gallery
 *
 * v1.0 originally written by Gregory Demar
 *
 * @copyright  Copyright (c) 2003-2023 Coppermine Dev Team
 * @license    GNU General Public License version 3 or later; see LICENSE
 *
 * bridge/coppermine.inc.php
 * @since  1.6.26
 */

defined('IN_COPPERMINE') or die('Not in Coppermine...');

if (isset($bridge_lookup)) {
	// Do nothing - the default bridge file "coppermine.inc.php" is not an option in the bridge manager.
	// In other bridge files, we populate an array that specifies what bridging options are available for that particular file
} else {

	// Switch that allows overriding the bridge manager with hard-coded values
	define('USE_BRIDGEMGR', 1);

	require_once 'bridge/udb_base.inc.php';

	class coppermine_udb extends core_udb
	{
		var $boardurl,
			$use_post_based_groups,
			$client_id,
			$multigroups,
			$group_override,
			$db,
			$table,
			$usertable,
			$groupstable,
			$sessionstable,
			$field,
			$page,
			$admingroups,
			$guestgroup,
			$sessiontime,
			$session_id;

		public function __construct ()
		{
			global $BRIDGE, $CONFIG, $CPGDB;

			$superCage = Inspekt::makeSuperCage();

			if (!USE_BRIDGEMGR) {
				$this->boardurl = 'http://localhost/coppermine';
				include_once '../include/config.inc.php';
			} else {
				$this->boardurl = $CONFIG['site_url'];
				$this->use_post_based_groups = @$BRIDGE['use_post_based_groups'];
			}

			// A hash that's a little specific to the client's configuration
			$this->client_id = md5($superCage->server->getRaw('HTTP_USER_AGENT').$CONFIG['site_url']);

			$this->multigroups = 1;

			$this->group_override = !$this->use_post_based_groups;

			// Database connection settings
			$this->db = array(
				'name' => $CONFIG['dbname'],
				'host' => $CONFIG['dbserver'] ? $CONFIG['dbserver'] : 'localhost',
				'user' => $CONFIG['dbuser'],
				'password' => $CONFIG['dbpass'],
				'prefix' =>$CONFIG['TABLE_PREFIX']
			);

			// Board table names
			$this->table = array(
				'users' => 'users',
				'groups' => 'usergroups',
				'sessions' => 'sessions'
			);

			// Derived full table names
			$this->usertable = '`' . $this->db['name'] . '`.' . $this->db['prefix'] . $this->table['users'];
			$this->groupstable =  '`' . $this->db['name'] . '`.' . $this->db['prefix'] . $this->table['groups'];
			$this->sessionstable =  '`' . $this->db['name'] . '`.' . $this->db['prefix'] . $this->table['sessions'];

			// Table field names
			$this->field = array(
				'username' => 'user_name', // name of 'username' field in users table
				'user_id' => 'user_id', // name of 'id' field in users table
				'password' => 'user_password', // name of 'password' field in users table
				'email' => 'user_email', // name of 'email' field in users table
				'regdate' => 'UNIX_TIMESTAMP(user_regdate)', // name of 'registered' field in users table
				'lastvisit' => 'UNIX_TIMESTAMP(user_lastvisit)', // last time user logged in
				'active' => 'user_active', // is user account active?
				'location' => "''", // name of 'location' field in users table
				'website' => "''", // name of 'website' field in users table
				'usertbl_group_id' => 'user_group', // name of 'group id' field in users table
				'grouptbl_group_id' => 'group_id', // name of 'group id' field in groups table
				'grouptbl_group_name' => 'group_name' // name of 'group name' field in groups table
			);

			// Pages to redirect to
			$this->page = array(
				'register' => 'register.php',
				'editusers' => 'usermgr.php',
				'edituserprofile' => 'profile.php'
			);

			// Group ids - admin and guest only.
			$this->admingroups = array(1);
			$this->guestgroup = 3;

			// Connect to db
			$this->connect($CPGDB);
		}


		// Login function
		public function login ($username = null, $password = null, $remember = false)
		{
			global $CONFIG;

			// Create the session_id from concat(cookievalue,client_id)
			$session_id = $this->session_id.$this->client_id;

			// Check the login method (username, email address or both)
			switch($CONFIG['login_method']) {
				case 'both':
					$sql_user_email = "(user_name = '$username' OR user_email = '$username')";
					break;
				case 'email':
					$sql_user_email = "user_email = '$username'";
					break;
				case 'username':
				default:
					$sql_user_email = "user_name = '$username'";
					break;
			}

			$USER_DATA = cpg_get_user_data($sql_user_email, $password);
			if ($USER_DATA === false) {
				return false;
			}
			if (CPGPluginAPI::action('authorize_user', $USER_DATA) === false) {
				return false;
			}

			// Update lastvisit value and salt password if needed
			$salt_password = !$USER_DATA['user_password_salt'] ? ', '.cpg_password_create_update_string($password) : '';
			$sql = "UPDATE {$this->usertable} SET user_lastvisit = NOW() $salt_password WHERE user_id = {$USER_DATA['user_id']}";

			$this->query($sql);

			// If this is a 'remember me' login set the remember field to true
			if ($remember) {
				$remember_sql = ",remember = '1' ";
				// Change cookie life time to 2 weeks
				if (CPG_COOKIES_ALLOWED) {
					setcookie( $this->client_id, $this->session_id, time() + (CPG_WEEK*2), $CONFIG['cookie_path'] );
				}
			} else {
				$remember_sql = '';
				// Kill the cookie when closing the browser
				if (CPG_COOKIES_ALLOWED) {
					setcookie( $this->client_id, $this->session_id, 0, $CONFIG['cookie_path'] );
				}
			}

			// Update guest session with user's information
			$sql  = "UPDATE {$this->sessionstable} SET ";
			$sql .= "user_id = {$USER_DATA['user_id']} ";
			$sql .= $remember_sql;
			$sql .= "WHERE session_id = '" . md5($session_id) . "'";
			$this->query($sql);

			return $USER_DATA;
		}


		// Logout function
		public function logout ()
		{
			// Revert authenticated session to a guest session
			$session_id = $this->session_id.$this->client_id;
			$sql  = "UPDATE {$this->sessionstable} SET user_id = 0, remember = 0 WHERE session_id = '" . md5($session_id) . "'";
			$this->query($sql);
		}

		// Get groups of which user is member
		public function get_groups ($user)
		{
			$groups = array($user['group_id']);

			$sql = "SELECT user_group_list FROM {$this->usertable} AS u WHERE {$this->field['user_id']}='{$user['id']}' AND user_group_list <> ''";

			$result = $this->query($sql);

			if ($row = $result->fetchArray(true)) {
				$groups = array_merge($groups, explode(',', $row['user_group_list']));
			}

			return $groups;
		}

		// definition of actions required to convert a password from user database form to cookie form
		public function udb_hash_db ($password)
		{
			return $password;
		}


		// definition of how to extract id, name, group from a session cookie
		public function session_extraction ()
		{
			global $CONFIG;

			$superCage = Inspekt::makeSuperCage();

			// Default anonymous values
			$id = 0;
			$pass = '';

			// Get the session cookie value
			$sessioncookie = $superCage->cookie->getEscaped($this->client_id);

			// Create the session id by concat(session_cookie_value, client_id)
			$session_id = $sessioncookie.$this->client_id;

			// Lifetime of 'remember me' session is 2 weeks
			$rememberme_life_time = time()-(CPG_WEEK*2);

			// Lifetime of normal session is 1 hour
			$session_life_time = time()-CPG_HOUR;

			// Clean up old sessions every 5 minutes at maximum
			if ($CONFIG['session_cleanup'] < time() - 300) {

				// Delete old sessions
				$sql = "DELETE FROM {$this->sessionstable} WHERE time < $session_life_time AND remember = 0";
				$this->query($sql);

				// Delete stale 'remember me' sessions
				$sql = "DELETE FROM {$this->sessionstable} WHERE time < $rememberme_life_time";
				$this->query($sql);

				// Update database entry
				$sql = "UPDATE {$CONFIG['TABLE_CONFIG']} SET value = ".time()." WHERE name = 'session_cleanup'";
				$this->query($sql);
			}

			// Check for valid session if session_cookie_value exists
			if ($sessioncookie) {

				// Check for valid session
				$sql =  "SELECT user_id, time FROM {$this->sessionstable} WHERE session_id = '" . md5($session_id) . "'";
				$result = cpg_db_query($sql);

				// If session exists...
				if ($result->numRows()) {
					$row = $result->fetchAssoc(true);
					$row['user_id'] = (int) $row['user_id'];
					$this->sessiontime = $row['time'];
					$this->session_id = $sessioncookie;

					// If there is user id, check validity
					if ($row['user_id']) {
						$sql = "SELECT user_id, user_password FROM {$this->usertable} WHERE user_id={$row['user_id']}";
						$result = $this->query($sql);
	
						// If user exists, return id and pass
						if ($result->numRows()) {
							$row = $result->fetchAssoc(true);
							$pass = $row['user_password'];
							$id = (int) $row['user_id'];
						}
					}

				// If not a valid session exists, create a new session
				} else {

					$this->create_session();
				}

			// No session exists; create one
			} else {

				$this->create_session();
			}

			return ($id) ? array($id, $pass) : false;
		}


		// Function used to keep the session alive
		public function session_update ()
		{
			// don't update null sessions
			if (!isset($this->session_id) || !$this->session_id) {
				return false;
			}

			// only update session time once per minute at maximum
			if (!isset($this->sessiontime) || (time() - $this->sessiontime < 60)) {
				return false;
			}

			$session_id = $this->session_id.$this->client_id;
			$sql = "UPDATE {$this->sessionstable} SET time = ".time()." WHERE session_id = '" . md5($session_id) . "'";
			cpg_db_query($sql);

			if (USER_ID > 0) {
				$sql = "UPDATE {$this->usertable} SET user_lastvisit = NOW() WHERE user_id = '".USER_ID."'";
				cpg_db_query($sql);
			}
		}


		// Create a new session with the cookie lifetime set to 2 weeks
		public function create_session ()
		{
			global $CONFIG;

			$superCage = Inspekt::makeSuperCage();

			// don't create sessions for people that don't accept cookies anyway
			if (!$superCage->cookie->keyExists($CONFIG['cookie_name'].'_data')) {
				return false;
			}

			// start session
			$this->session_id = $this->generateId();
			$session_id = $this->session_id.$this->client_id;

			$sql = "INSERT INTO {$this->sessionstable} (session_id, time) VALUES ('" . md5($session_id) . "', ".time().")";

			// insert the guest session
			$this->query($sql);

			// set the session cookie
			if (CPG_COOKIES_ALLOWED) {
				setcookie( $this->client_id, $this->session_id, time() + (CPG_WEEK*2), $CONFIG['cookie_path'] );
			}
		}


		// Modified function taken from Mambo session class
		public function generateId ()
		{
			$failsafe = 20;
			$randnum = 0;
			while ($failsafe--) {
				$randnum = md5( uniqid( microtime(), 1 ));
				$session_id = $randnum.$this->client_id;
				if ($randnum != "") {
					$sql = "SELECT session_id FROM {$this->sessionstable} WHERE session_id = '" . md5($session_id) . "'";
					$result = $this->query($sql);
					if (!$result->numRows()) {
							break;
					}
					$result->free();
				}
			}
			return $randnum;
		}

		/*
		 * Overidden functions !!DO NOT REMOVE OR CPG WILL NOT WORK CORRECTLY!!
		 */
		// definition of how to extract an id and password hash from a cookie
		function cookie_extraction ()
		{
			return false;
		}

		// Register
		function register_page ()
		{	}

		// Edit users
		function edit_users ()
		{	}

		// View users
		function view_users ()
		{	}

		// View user profile
		function view_profile ($uid)
		{	}

		// Edit user profile
		function edit_profile ($uid)
		{	}

		function login_page ()
		{	}

		function logout_page ()
		{
			$this->logout();
		}

		/* Note : we don't want to overide this - the groups need to be resynced to coppermine default after un-integration
		 * Rebuttal: without overriding and removing Coppermine group deletion (see below) its impossible to add new groups to a non-bridged install.
		 */
		function synchronize_groups ()
		{
			global $CONFIG ;

			if ($this->use_post_based_groups){
				if ($this->group_overrride){
					$udb_groups = $this->collect_groups();
				} else {
					$sql = "SELECT * FROM {$this->groupstable} WHERE {$this->field['grouptbl_group_name']} <> ''";

					$result = $this->query($sql);

					$udb_groups = array();

					while ($row = $result->fetchAssoc()) {
						$udb_groups[$row[$this->field['grouptbl_group_id']]+100] = utf_ucfirst(utf_strtolower($row[$this->field['grouptbl_group_name']]));
					}
					$result->free();
				}
			} else {
				$udb_groups = array(1 =>'Administrators', 2=> 'Registered', 3=>'Guests');
			}

			$result = cpg_db_query("SELECT group_id, group_name FROM {$CONFIG['TABLE_USERGROUPS']} WHERE 1");

			while ($row = $result->fetchArray()) {
				$cpg_groups[$row['group_id']] = $row['group_name'];
			}
			$result->free();

			/* Must be removed to allow new groups to be created in an unbridged install.
			// Scan Coppermine groups that need to be deleted
			foreach($cpg_groups as $c_group_id => $c_group_name) {
				if ((!isset($udb_groups[$c_group_id]))) {
					cpg_db_query("DELETE FROM {$CONFIG['TABLE_USERGROUPS']} WHERE group_id = '" . $c_group_id . "' LIMIT 1");
					unset($cpg_groups[$c_group_id]);
				}
			}
			*/

			// Scan udb groups that need to be created inside Coppermine table
			foreach($udb_groups as $i_group_id => $i_group_name) {
				if ((!isset($cpg_groups[$i_group_id]))) {
					// add admin info
					$admin_access = in_array($i_group_id-100, $this->admingroups) ? '1' : '0';
					cpg_db_query("INSERT INTO {$CONFIG['TABLE_USERGROUPS']} (group_id, group_name, has_admin_access) VALUES ('$i_group_id', '" . addslashes($i_group_name) . "', '$admin_access')");
					$cpg_groups[$i_group_id] = $i_group_name;
				}
			}

			// Update Group names
			foreach($udb_groups as $i_group_id => $i_group_name) {
				if ($cpg_groups[$i_group_id] != $i_group_name) {
					cpg_db_query("UPDATE {$CONFIG['TABLE_USERGROUPS']} SET group_name = '" . addslashes($i_group_name) . "' WHERE group_id = '$i_group_id' LIMIT 1");
				}
			}
			// fix admin grp
			if (!$this->use_post_based_groups) cpg_db_query("UPDATE {$CONFIG['TABLE_USERGROUPS']} SET has_admin_access = '1' WHERE group_id = '1' LIMIT 1");

		}

	}	// end class

	// and go !
	$cpg_udb = new coppermine_udb;
}
//EOF