<?php

/**
 * Project:
 * Contenido Content Management System
 *
 * Description:
 * Contenido User classes
 *
 * Requirements:
 * @con_php_req 5.0
 *
 *
 * @package    Contenido Backend classes
 * @version    1.0.3
 * @author     Timo A. Hummel
 * @copyright  four for business AG <www.4fb.de>
 * @license    http://www.contenido.org/license/LIZENZ.txt
 * @link       http://www.4fb.de
 * @link       http://www.contenido.org
 * @since      file available since contenido release <= 4.6
 *
 * {@internal
 *   created unknown
 *   modified 2008-06-30, Dominik Ziegler, add security fix
 *   modified 2009-05-18, Andreas Lindner, add method getGroupIDsByUserID to class User
 *   modified 2009-12-17, Dominik Ziegler, added support for username fallback
 *   modified 2010-05-20, Oliver Lohkemper, add param forceActive in User::getSystemAdmins()
 *   modified 2011-02-05, Murat Purc, Cleanup/formatting, documentation, standardize
 *                                    getUserProperties()
 *
 *   $Id$:
 * }}
 *
 */
if (!defined('CON_FRAMEWORK')) {
    die('Illegal call');
}

class Users {

    /**
     * Storage of the source table to use for the user informations
     * @var string Contains the source table
     * @access private
     */
    var $table;

    /**
     * DB_ConLite instance
     * @var object Contains the database object
     * @access private
     */
    var $db;

    /**
     * Constructor Function
     * @param string $table The table to use as information source
     */
    function __construct($table = '') {
        if ($table == '') {
            global $cfg;
            $this->table = $cfg['tab']['phplib_auth_user_md5'];
        } else {
            $this->table = $table;
        }

        $this->db = new DB_ConLite();
    }

    /**
     * Creates a new user by specifying its username.
     *
     * @param   string  $username  Specifies the username
     * @return  int     Userid of created user
     */
    function create($username) {
        $newuserid = md5($username);
        $sql = "SELECT user_id FROM " . $this->table
                . " WHERE user_id = '" . Contenido_Security::escapeDB($newuserid, $this->db) . "'";
        $this->db->query($sql);
        if ($this->db->next_record()) {
            return false;
        }

        $sql = "INSERT INTO " . $this->table
                . " SET user_id = '" . Contenido_Security::escapeDB($newuserid, $this->db) . "',"
                . " username = '" . Contenido_Security::escapeDB($username, $this->db) . "'";
        $this->db->query($sql);

        return $newuserid;
    }

    /**
     * Removes the specified user from the database.
     *
     * @param   string  $userid  Specifies the user ID
     * @return  bool    True if the delete was successful
     */
    function deleteUserByID($userid) {
        $sql = "DELETE FROM " . $this->table
                . " WHERE user_id = '" . Contenido_Security::escapeDB($userid, $this->db) . "'";
        $this->db->query($sql);
        return ($this->db->affected_rows() == 0) ? false : true;
    }

    /**
     * Removes the specified user from the database.
     *
     * @param   string  $userid  Specifies the username
     * @return  bool    True if the delete was successful
     */
    function deleteUserByUsername($username) {
        $sql = "DELETE FROM " . $this->table
                . " WHERE username = '" . Contenido_Security::escapeDB($username, $this->db) . "'";
        $this->db->query($sql);
        return ($this->db->affected_rows() == 0) ? false : true;
    }

    /**
     * Returns all users which are accessible by the current user.
     *
     * @param   array  $perms          Permissions array
     * @param   bool   $includeAdmins  Flag to get admins (admin and sysadmin) too
     * @return  array  Array of user objects
     */
    function getAccessibleUsers($perms, $includeAdmins = false) {
        global $cfg;
        $limit = array();

        $clientclass = new Client();

        $allClients = $clientclass->getAvailableClients();

        foreach ($allClients as $key => $value) {
            if (in_array("client[" . $key . "]", $perms) || in_array("admin[" . $key . "]", $perms)) {
                $limit[] = 'perms LIKE "%client[' . $key . ']%"';
                if ($includeAdmins) {
                    $limit[] = 'perms LIKE "%admin[' . $key . ']%"';
                }
            }

            if (in_array("admin[" . $key . "]", $perms)) {
                $limit[] = 'perms LIKE "%admin[' . $key . ']%"';
            }
        }

        if ($includeAdmins) {
            $limit[] = 'perms LIKE "%sysadmin%"';
        }

        $db = new DB_ConLite();

        if (count($limit) > 0) {
            $limitSQL = implode(' OR ', $limit);
        }

        if (in_array('sysadmin', $perms)) {
            $limitSQL = '1';
        }

        $sql = "SELECT
                    user_id, username, realname
                FROM
                " . $cfg['tab']['phplib_auth_user_md5'] . " WHERE 1 AND " . $limitSQL . " ORDER BY realname, username";

        $db->query($sql);

        $users = array();

        while ($db->next_record()) {
            $users[$db->f('user_id')] = array(
                'username' => $db->f('username'),
                'realname' => $db->f('realname'),
            );
        }

        return ($users);
    }

}

/**
 * Class User
 * Class for user information and management
 * @author Timo A. Hummel <Timo.Hummel@4fb.de>
 * @version 1.0
 * @copyright four for business 2003
 */
class User {

    /**
     * Storage of the source table to use for the user informations
     * @var string Contains the source table
     * @access private
     */
    var $table;

    /**
     * DB_ConLite instance
     * @var object Contains the database object
     * @access private
     */
    var $db;

    /**
     * Storage of the source table to use for the user informations
     * @var array Contains the source table
     * @access private
     */
    var $values;

    /**
     * Storage of the fields which were modified
     * @var array Contains the field names which where modified
     * @access private
     */
    var $modifiedValues;

    /**
     * Constructor Function.
     *
     * @param  string  $table  The table to use as information source
     */
    function __construct($table = '') {
        if ($table == '') {
            global $cfg;
            $this->table = $cfg['tab']['phplib_auth_user_md5'];
        } else {
            $this->table = $table;
        }

        $this->db = new DB_ConLite();
    }

    /**
     * Loads a user from the database by its username.
     *
     * @param   string  $username Specifies the username
     * @return  bool    True if the load was successful
     */
    function loadUserByUsername($username) {
        // SQL-Statement to select by username
        $sql = "SELECT * FROM " . $this->table
                . " WHERE username = '" . Contenido_Security::escapeDB($username, $this->db) . "'";

        // Query the database
        $this->db->query($sql);

        // Advance to the next record, return false if nothing found
        if (!$this->db->next_record()) {
            return false;
        }

        $this->values = $this->db->toArray();

        return true;
    }

    /**
     * Loads a user from the database by its userID.
     *
     * @param   string  $userid  Specifies the userID
     * @return  bool    True if the load was successful
     */
    function loadUserByUserID($userID) {
        // SQL-Statement to select by userID
        $sql = "SELECT * FROM " . $this->table
                . " WHERE user_id = '" . Contenido_Security::escapeDB($userID, $this->db) . "'";

        // Query the database
        $this->db->query($sql);

        // Advance to the next record, return false if nothing found
        if (!$this->db->next_record()) {
            return false;
        }

        $this->values = $this->db->toArray();

        return true;
    }

    /**
     * Function returns effective perms for user including group rights as perm string.
     *
     * @author Timo Trautmann
     * @return  string  Current users permissions
     */
    function getEffectiveUserPerms() {
        global $cfg, $perm;

        //first get users own permissions and filter them into result array $aUserPerms
        $aUserPerms = array();
        $aUserPermsSelf = explode(',', $this->values['perms']);
        foreach ($aUserPermsSelf as $sPerm) {
            if (trim($sPerm) != '') {
                array_push($aUserPerms, $sPerm);
            }
        }

        //get all corresponding groups for this user
        $groups = $perm->getGroupsForUser($this->values['user_id']);

        foreach ($groups as $value) {
            //get global group permissions
            $oGroup = new Group();
            $oGroup->loadGroupByGroupID($value);
            $sGroupPerm = $oGroup->getField('perms');

            //add group permissions to $aUserPerms if they were not alredy defined before
            $aGroupPerms = explode(',', $sGroupPerm);
            foreach ($aGroupPerms as $sPerm) {
                if (!in_array($sPerm, $aUserPerms) && trim($sPerm) != '') {
                    array_push($aUserPerms, $sPerm);
                }
            }
        }
        return implode(',', $aUserPerms);
    }

    /**
     * Gets the value of a specific field.
     *
     * @param   string  $field  Specifies the field to retrieve
     * @return  mixed   Value of the field
     */
    function getField($field) {
        return ($this->values[$field]);
    }

    /**
     * Sets the value of a specific field.
     *
     * @param  string  $field  Specifies the field to set
     * @param  string  $value  Specifies the value to set
     */
    function setField($field, $value) {
        $this->modifiedValues[$field] = true;
        $this->values[$field] = $value;
    }

    /**
     * Stores the modified user object to the database
     * @return  bool
     */
    function store() {
        $sql = "UPDATE " . $this->table . " SET ";
        $first = true;

        foreach ($this->modifiedValues as $key => $value) {
            if ($first == true) {
                $sql .= "$key = '" . $this->values[$key] . "'";
                $first = false;
            } else {
                $sql .= ", $key = '" . $this->values[$key] . "'";
            }
        }

        $sql .= " WHERE user_id = '" . Contenido_Security::escapeDB($this->values['user_id'], $this->db) . "'";

        $this->db->query($sql);

        if ($this->db->affected_rows() < 1) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Stores the modified user object to the database
     * @param string type Specifies the type (class, category etc) for the property to retrieve
     * @param string name Specifies the name of the property to retrieve
     * @param boolean group Specifies if this function should recursively search in groups
     * @return string The value of the retrieved property
     */
    function getUserProperty($type, $name, $group = false) {
        global $cfg, $perm;
        
        if(empty($this->values)) {
            return false;
        }

        if (!is_object($perm)) {
            $perm = new Contenido_Perm();
        }

        $result = false;

        if ($group == true) {
            $groups = $perm->getGroupsForUser($this->values['user_id']);

            if (is_array($groups)) {
                foreach ($groups as $value) {
                    $sql = "SELECT value FROM " . $cfg['tab']['group_prop'] . "
                    WHERE group_id = '" . Contenido_Security::escapeDB($value, $this->db) . "'
                      AND type = '" . Contenido_Security::escapeDB($type, $this->db) . "'
                      AND name = '" . Contenido_Security::escapeDB($name, $this->db) . "'";
                    $this->db->query($sql);

                    if ($this->db->next_record()) {
                        $result = $this->db->f('value');
                    }
                }
            }
        }

        $sql = "SELECT value FROM " . $cfg['tab']['user_prop'] . "
                WHERE user_id = '" . Contenido_Security::escapeDB($this->values['user_id'], $this->db) . "'
                  AND type = '" . Contenido_Security::escapeDB($type, $this->db) . "'
                  AND name = '" . Contenido_Security::escapeDB($name, $this->db) . "'";
        $this->db->query($sql);

        if ($this->db->next_record()) {
            $result = $this->db->f('value');
        }

        if ($result !== false) {
            return urldecode($result);
        } else {
            return false;
        }
    }

    /**
     * Stores the modified user object to the database
     *
     * @param   string    sType   Specifies the type (class, category etc) for the property to retrieve
     * @param   boolean   bGroup  Specifies if this function should recursively search in groups
     * @return  array             The value of the retrieved property
     * */
    function getUserPropertiesByType($sType, $bGroup = false) {
        global $cfg, $perm;

        if (!is_object($perm)) {
            $perm = new Contenido_Perm();
        }

        $aResult = array();

        if ($bGroup == true) {
            $aGroups = $perm->getGroupsForUser($this->values['user_id']);

            if (is_array($aGroups)) {
                foreach ($aGroups as $iID) {
                    $sSQL = "SELECT name, value FROM " . $cfg['tab']['group_prop'] . "
                             WHERE group_id = '" . Contenido_Security::escapeDB($iID, $this->db) . "'
                                AND type = '" . Contenido_Security::escapeDB($sType, $this->db) . "'";
                    $this->db->query($sSQL);

                    while ($this->db->next_record()) {
                        $aResult[$this->db->f('name')] = urldecode($this->db->f('value'));
                    }
                }
            }
        }

        $sSQL = "SELECT name, value FROM " . $cfg['tab']['user_prop'] . "
                 WHERE user_id = '" . Contenido_Security::escapeDB($this->values['user_id'], $this->db) . "'
                 AND type = '" . Contenido_Security::escapeDB($sType, $this->db) . "'";
        $this->db->query($sSQL);

        while ($this->db->next_record()) {
            $aResult[$this->db->f('name')] = urldecode($this->db->f('value'));
        }

        return $aResult;
    }

    /**
     * Retrieves all available properties of the user
     * @return  array  Assoziative properties list as follows:
     *                 - $arr[iduserprop][name]
     *                 - $arr[iduserprop][type]
     *                 - $arr[iduserprop][value]
     */
    function getUserProperties() {
        global $cfg;

        $aProps = array();

        $sql = "SELECT iduserprop, type, name, value FROM " . $cfg['tab']['user_prop'] . "
                WHERE user_id = '" . Contenido_Security::escapeDB($this->values['user_id'], $this->db) . "'";
        $this->db->query($sql);

        if ($this->db->num_rows() == 0) {
            return $aProps;
        }

        while ($this->db->next_record()) {
            $aProps[$this->db->f('iduserprop')] = array(
                'name' => $this->db->f('name'),
                'type' => $this->db->f('type'),
                'value' => $this->db->f('value'),
            );
        }

        return $aProps;
    }

    /**
     * Stores a property to the database
     * @param string type Specifies the type (class, category etc) for the property to retrieve
     * @param string name Specifies the name of the property to retrieve
     * @param string value Specifies the value to insert
     */
    function setUserProperty($type, $name, $value) {
        global $cfg;

        $value = urlencode($value);

        // Check if such an entry already exists
        if ($this->getUserProperty($type, $name) !== false) {
            $sql = "UPDATE " . $cfg['tab']['user_prop'] . "
                    SET value = '" . Contenido_Security::escapeDB($value, $this->db) . "'
                    WHERE user_id = '" . Contenido_Security::escapeDB($this->values['user_id'], $this->db) . "'
                      AND type = '" . Contenido_Security::escapeDB($type, $this->db) . "'
                      AND name = '" . Contenido_Security::escapeDB($name, $this->db) . "'";
            $this->db->query($sql);
        } else {
            $sql = "INSERT INTO  " . $cfg['tab']['user_prop'] . "
                    SET value = '" . Contenido_Security::escapeDB($value, $this->db) . "',
                        user_id = '" . Contenido_Security::escapeDB($this->values['user_id'], $this->db) . "',
                          type = '" . Contenido_Security::escapeDB($type, $this->db) . "',
                          name = '" . Contenido_Security::escapeDB($name, $this->db) . "',
                        iduserprop = '" . Contenido_Security::toInteger($this->db->nextid($cfg['tab']['user_prop'])) . "'";
            $this->db->query($sql);
        }
    }

    /**
     * Deletes a user property from the table
     * @param string type Specifies the type (class, category etc) for the property to retrieve
     * @param string name Specifies the name of the property to retrieve
     */
    function deleteUserProperty($type, $name) {
        global $cfg;

        // Delete record from table
        $sql = "DELETE FROM  " . $cfg['tab']['user_prop'] . "
                    WHERE user_id = '" . Contenido_Security::escapeDB($this->values['user_id'], $this->db) . "' AND
                          type = '" . Contenido_Security::escapeDB($type, $this->db) . "' AND
                          name = '" . Contenido_Security::escapeDB($name, $this->db) . "'";
        $this->db->query($sql);
    }

    /**
     * Returns all users available in the system
     * @param   string  $sort  SQL sort part
     * @return  array          Array with id and name entries
     */
    function getAvailableUsers($sort = 'ORDER BY realname ASC') {
        global $cfg;

        $db = new DB_ConLite();

        $sql = "SELECT
                    user_id, username, realname
                FROM
                " . $cfg['tab']['phplib_auth_user_md5'] . " " . $sort;

        $db->query($sql);

        $users = array();

        while ($db->next_record()) {
            $users[$db->f('user_id')] = array(
                'username' => $db->f('username'),
                'realname' => $db->f('realname'),
            );
        }

        return ($users);
    }

    /**
     * Returns all system admins available in the system
     * @param   boolean  $forceActive  Is forceActive true return only activ Sysadmins
     * @return  array                  Array with id and name entries
     */
    function getSystemAdmins($forceActive = false) {
        global $cfg;

        $db = new DB_ConLite();

        $sql = "SELECT
                    user_id, username, realname, email
                FROM
                " . $cfg['tab']['phplib_auth_user_md5'] . "
                WHERE
                    perms LIKE \"%sysadmin%\"";

        if ($forceActive === true) {
            $sql .= " AND (valid_from <= NOW() OR valid_from = '0000-00-00' OR valid_from = '1000-01-01')
                AND (valid_to >= NOW() OR valid_to = '0000-00-00' OR valid_to = '1000-01-01') ";
        }

        $db->query($sql);

        $users = array();

        while ($db->next_record()) {
            $users[$db->f('user_id')] = array(
                'username' => $db->f('username'),
                'realname' => $db->f('realname'),
                'email' => $db->f('email'),
            );
        }

        return ($users);
    }

    /**
     * Returns all system admins available in the system
     * @param   int    $client
     * @return  array   Array with id and name entries
     */
    function getClientAdmins($client) {
        global $cfg;

        $db = new DB_ConLite();

        $client = Contenido_Security::escapeDB($client, $db);

        $sql = "SELECT
                    user_id, username, realname, email
                FROM
                " . $cfg['tab']['phplib_auth_user_md5'] . "
                WHERE
                    perms LIKE \"%admin[" . $client . "]%\"";

        $db->query($sql);

        $users = array();

        while ($db->next_record()) {
            $users[$db->f('user_id')] = array(
                'username' => $db->f('username'),
                'realname' => $db->f('realname'),
                'email' => $db->f('email'),
            );
        }

        return ($users);
    }

    /**
     * Returns the username of the given userid
     * @param  int     $userid
     * @return string  Username if found, or emptry string if not.
     */
    function getUsername($userid) {
        global $cfg;

        $db = new DB_ConLite();

        $sql = "SELECT
                    username
                FROM
                " . $cfg['tab']['phplib_auth_user_md5'] . "
                WHERE
                    user_id = '" . Contenido_Security::escapeDB($userid, $db) . "'";

        $db->query($sql);
        $db->next_record();
        return ($db->f('username'));
    }

    /**
     * Returns the realname of the given userid
     * @param   int     $userid
     * @param   bool    $bAllowFallbackOnUsername
     * @return  string  Realname if found, or emptry string if not.
     */
    function getRealname($userid, $bAllowFallbackOnUsername = false) {
        global $cfg;

        $db = new DB_ConLite();

        $sql = "SELECT
                    realname
                FROM
                " . $cfg['tab']['phplib_auth_user_md5'] . "
                WHERE
                    user_id = '" . Contenido_Security::escapeDB($userid, $db) . "'";

        $db->query($sql);
        $db->next_record();

        if ($db->f('realname') == '' && $bAllowFallbackOnUsername == true) {
            return ($this->getUsername($userid));
        } else {
            return ($db->f('realname'));
        }
    }

    /**
     * Returns the realname of the given username
     * @param  string  $username
     * @return string  Realname if found, or emptry string if not.
     */
    function getRealnameByUserName($username) {
        global $cfg;

        $db = new DB_ConLite();

        $sql = "SELECT
                    realname
                FROM
                " . $cfg['tab']['phplib_auth_user_md5'] . "
                WHERE
                    username = '" . Contenido_Security::escapeDB($username, $db) . "'";

        $db->query($sql);
        $db->next_record();
        return ($db->f('realname'));
    }

    /**
     * Returns the groups a user is in
     * @param   int    $userid
     * @return  array  Real names of groups
     */
    function getGroupsByUserID($userid) {
        global $cfg;

        $db = new DB_ConLite();

        $sql = "SELECT
                    a.group_id
                FROM
                    " . $cfg['tab']['groups'] . " AS a,
                    " . $cfg['tab']['groupmembers'] . " AS b
                WHERE
                    (a.group_id  = b.group_id)
                    AND
                    (b.user_id = '" . Contenido_Security::escapeDB($userid, $db) . "')
                ";

        $db->query($sql);

        $arrGroups = array();

        $oGroup = new Group();

        while ($db->next_record()) {
            $oGroup->loadGroupByGroupID($db->f('group_id'));
            $sTemp = $oGroup->getField('groupname');
            $sTemp = substr($sTemp, 4, strlen($sTemp) - 4);

            $sDescription = trim($oGroup->getField('description'));

            if ($sDescription != '') {
                $sTemp .= ' (' . $sDescription . ')';
            }

            $arrGroups[] = $sTemp;
        }
        return $arrGroups;
    }

    /**
     * Returns the groups a user is in
     * @param   int    $userid
     * @return  array  Ids of groups
     */
    function getGroupIDsByUserID($userid) {
        global $cfg;

        $db = new DB_ConLite();

        $sql = "SELECT
                    a.group_id
                FROM
                    " . $cfg['tab']['groups'] . " AS a,
                    " . $cfg['tab']['groupmembers'] . " AS b
                WHERE
                    (a.group_id  = b.group_id)
                    AND
                    (b.user_id = '" . Contenido_Security::escapeDB($userid, $db) . "')
                ";

        $db->query($sql);

        $arrGroups = array();

        while ($db->next_record()) {
            $arrGroups[] = $db->f('group_id');
        }
        return $arrGroups;
    }

}
