<?php

require_once('util.php');
require_once('net.php');

class pb_User {
    protected $id;
    protected $login = NULL;
    protected $password = NULL;
    protected $email = NULL;
    protected $rights = NULL; // read,repository,admin
    protected $activationKey = NULL;
    protected $creationDate = NULL;
    protected $lastAuthenticationDate = NULL;

    public function __construct($login, $password, $email = '', $rights = '',
            $creationDate = NULL, $activationKey = '', $lastAuthenticationDate = NULL, $alertThreshold = 16) {
        $this->login = $login;
        $this->password = $password;
        $this->email = $email;
        $this->rights = $rights;
        $this->activationKey = $activationKey;
        $this->creationDate = $creationDate;
        $this->lastAuthenticationDate = $lastAuthenticationDate;
        $this->alertThreshold = $alertThreshold;
    }
    public function getId() {
        return $this->id;
    }
    public function setId($id) {
        $this->id = $id;
    }
	public function getLogin() {
        return $this->login;
    }
	public function setLogin($login) {
        $this->login = $login;
    }
	public function getPassword() {
        return $this->password;
    }
	public function setPassword($password) {
        $this->password = $password;
    }
	public function getEMail() {
        return $this->email;
    }
	public function setEMail($email) {
        $this->email = $email;
    }
	public function getRights() {
        return $this->rights;
    }
	public function setRights($rights) {
        $this->rights = $rights;
    }
	public function getActivationKey() {
        return $this->activationKey;
    }
	public function setActivationKey($activationKey) {
        $this->activationKey = $activationKey;
    }
	public function getCreationDate() {
        return $this->creationDate;
    }
	public function setCreationDate($creationDate) {
        $this->creationDate = $creationDate;
    }
	public function getLastAuthenticationDate() {
        return $this->lastAuthenticationDate;
    }
	public function setLastAuthenticationDate($lastAuthenticationDate) {
        $this->lastAuthenticationDate = $lastAuthenticationDate;
    }
    public function getAlertThreshold() {
        return $this->alertThreshold;
    }
    public function setAlertThreshold($alertThreshold) {
        $this->alertThreshold = $alertThreshold;
    }
	public function hasRight($requiredRight) {
        return util_RightHandler::hasRight($requiredRight, $this->rights);
    }
	public function insert() {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('INSERT INTO ' . DB_PREFIX . 'USER (LOGIN, PASSWORD, EMAIL, RIGHTS, CREATION_DATE, ACTIVATION_KEY, LAST_AUTHENTICATION_DATE, ALERT_THRESHOLD)' .
            ' VALUES(\'%s\', \'%s\', \'%s\', \'%s\', ' . sql_Connection::fromTimestamp('%d') . ', \'%s\', ' . sql_Connection::fromTimestamp('%d') . ', %d)',
            $this->login, $this->password, $this->email, $this->rights, $this->creationDate, $this->activationKey, $this->lastAuthenticationDate, $this->alertThreshold);
        if ($connection->getUpdateCount() != 1) {
            throw new Exception('Database failure');
        }
        $this->setId($connection->lastInsertId());
    }
	public function update() {
        if ($this->id == NULL) {
            throw new Exception('Invalid id');
        }
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('UPDATE ' . DB_PREFIX . 'USER SET LOGIN=\'%s\', PASSWORD=\'%s\', EMAIL=\'%s\', RIGHTS=\'%s\', CREATION_DATE=' .
            sql_Connection::fromTimestamp('%d') . ', ACTIVATION_KEY=\'%s\', LAST_AUTHENTICATION_DATE=' .
            sql_Connection::fromTimestamp('%d') . ', ALERT_THRESHOLD=%d WHERE ID=%d',
            $this->login, $this->password, $this->email, $this->rights, $this->creationDate,
            $this->activationKey, $this->lastAuthenticationDate, $this->alertThreshold, $this->id);
        if ($connection->getUpdateCount() != 1) {
            throw new Exception('Database failure');
        }
    }
	public function delete() {
        if ($this->id == NULL) {
            throw new Exception('Invalid id');
        }
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('DELETE FROM ' . DB_PREFIX . 'USER WHERE ID=%d', $this->id);
        if ($connection->getUpdateCount() != 1) {
            throw new Exception('Database failure');
        }
    }
    public function generateHtpasswd() {
        pb_Repository::generateHtpasswdByUserId($this->getId());
        pb_User::generateGlobalHtpasswd();
    }
    public static function generateGlobalHtpasswd() {
        $connection = Helper::getInstance()->getSQL();
        // fetch all users
        $connection->executef("SELECT LOGIN, PASSWORD FROM " . DB_PREFIX . "USER WHERE ACTIVATION_KEY='' AND RIGHTS<>''");
        $rs = $connection->getResultSet();
        // generate the global users passwords file
        $file = fopen(PB_REPOSITORY_PATH . '/' . USER_HTPASSWD_FILE, "w");
        fwrite($file, 'anonymous:' . sha1('anonymous') . "\n");
        while ($rs->next()) {
            $user = $rs->getRowObject();
            fwrite($file, $user->LOGIN . ':' . $user->PASSWORD . "\n");
        }
        fclose($file);
    }
    public static function createFromResultSet($rs) {
        $object = $rs->getRowObject();
        $user = new pb_User($object->LOGIN, $object->PASSWORD, $object->EMAIL, $object->RIGHTS,
        	$object->CREATION_DATE, $object->ACTIVATION_KEY, $object->LAST_AUTHENTICATION_DATE, $object->ALERT_THRESHOLD);
        $user->setId($object->ID);
        return $user;
    }
    public static function getSelectionFields() {
        return 'ID, PASSWORD, LOGIN, EMAIL, RIGHTS, ' . sql_Connection::toTimestamp('CREATION_DATE') .
        	', ACTIVATION_KEY, ' . sql_Connection::toTimestamp('LAST_AUTHENTICATION_DATE') . ', ALERT_THRESHOLD';
    }
    public static function fetchFromId($id) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT ' . pb_User::getSelectionFields() . ' FROM ' . DB_PREFIX . 'USER WHERE ID=%d', $id);
        $rs = $connection->getResultSet();
        if (! $rs->next()) {
            return NULL;
        }
        return pb_User::createFromResultSet($rs);
    }
    public static function fetchFromLogin($login) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT ' . pb_User::getSelectionFields() . ' FROM ' . DB_PREFIX . 'USER WHERE LOGIN=\'%s\'', $login);
        $rs = $connection->getResultSet();
        if (! $rs->next()) {
            return NULL;
        }
        return pb_User::createFromResultSet($rs);
    }
    public static function fetchFromEMail($email) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT ' . pb_User::getSelectionFields() . ' FROM ' . DB_PREFIX . 'USER WHERE EMAIL=\'%s\'', $email);
        $rs = $connection->getResultSet();
        if (! $rs->next()) {
            return NULL;
        }
        return pb_User::createFromResultSet($rs);
    }
    public static function loginOrEMailExists($login, $email) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT COUNT(*) AS NB FROM ' . DB_PREFIX . 'USER WHERE LOGIN=\'%s\' OR EMAIL=\'%s\'',
            $login, $email);
        $rs = $connection->getResultSet();
        if (! $rs->next()) {
            throw new Exception('Database failure');
        }
        return $rs->getRowObject()->NB > 0;
    }
    public static function initializeTable() {
        $connection = Helper::getInstance()->getSQL();
        $connection->execute('DROP TABLE IF EXISTS ' . DB_PREFIX . 'USER');
        $connection->execute('CREATE TABLE ' . DB_PREFIX . 'USER (' .
            'ID bigint(20) unsigned NOT NULL auto_increment,' .
            'LOGIN varchar(60) NOT NULL default \'\', ' .
            'PASSWORD varchar(64) NOT NULL default \'\', ' .
            'EMAIL varchar(100) NOT NULL default \'\',' .
            'RIGHTS varchar(64) NOT NULL default \'\', ' .
            'CREATION_DATE datetime NOT NULL default \'0000-00-00 00:00:00\', ' .
            'LAST_AUTHENTICATION_DATE datetime NULL default \'0000-00-00 00:00:00\', ' .
            'ACTIVATION_KEY varchar(60) NOT NULL default \'\', ' .
            'ALERT_THRESHOLD int(5) NOT NULL default \'16\', ' .
            'PRIMARY KEY (ID), ' .
            //'KEY LOGIN_KEY (LOGIN)' .
            'CONSTRAINT UC_LOGIN UNIQUE (LOGIN)' .
            ') CHARACTER SET utf8 COLLATE utf8_bin');
        // TODO Check
    }
    public static function generateActivationKey($text) {
        return sha1(mt_rand(10000,99999) . microtime() . $text);
    }
}

function user_create($exchange) {
    $request = $exchange->getRequest();
    $login = $request->getAttribute('login');
    $password = $request->getAttribute('password');
    $email = $request->getAttribute('email');
    if (! Helper::loginIsValid($login)) {
        throw new Exception("Invalid login");
    }
    if (! net_Mail::emailIsValid($email)) {
        throw new Exception("Invalid e-mail address");
    }
    // check that the login is free
    if (pb_User::loginOrEMailExists($login, $email)) {
        throw new Exception('The login or the email already exists');
    }
    // generate a random key
    $activationKey = pb_User::generateActivationKey($email);
    // create the user
    $user = new pb_User($login, $password, $email, '', time(), $activationKey);
    // generate and send the activation mail
    $query = http_build_query(array(
        'operation' => 'activateUser',
        'login' => $login,
        'activationKey' => $activationKey,
        'mode' => 'html'
    ));
    //$query = Helper::getInstance()->getSiteAddress() . '#' . $query;
    $query = Helper::getInstance()->getSiteAddress() . SCRIPTS_PATH . '/default.php?' . $query;
    $res = Helper::getInstance()->getRes();
    $mail = Helper::getInstance()->createMail($email,
        $res->getf('user.activation.mail.title', Helper::getInstance()->getSiteName()),
        $res->getf('user.activation.mail.message', $query));
    if (! $mail->send()) {
        throw new Exception("Mail delivery failure, please contact admin");
    }
    // add the user
    $user->insert();
    $exchange->done("User created");
}

function user_activate($exchange) {
    $request = $exchange->getRequest();
    $login = $request->getAttribute('login');
    $activationKey = $request->getAttribute('activationKey');
    $replyMode = $request->getAttribute('mode');
    $user = pb_User::fetchFromLogin($login);
    if ($user == NULL) {
        throw new Exception("User " . $login . " not found");
    }
    if ($user->getActivationKey() != $activationKey) {
        throw new Exception("Invalid activation key");
    }
    $user->setActivationKey('');
    $user->update();
    pb_User::generateGlobalHtpasswd();
    // TODO Change this?
    $userAdmin = pb_User::fetchFromLogin(ADMIN_LOGIN);
    // send authorization mail
    $query = http_build_query(array(
        'operation' => 'authorizeUser',
        'login' => $login,
        'authLogin' => $userAdmin->getLogin(),
        'authPassword' => $userAdmin->getPassword()
    ));
    $res = Helper::getInstance()->getRes();
    $mail = Helper::getInstance()->createMail(Helper::getInstance()->getAdminEMail(),
        $res->getf('user.authorisation.mail.title', Helper::getInstance()->getSiteName()),
        $res->getf('user.authorisation.mail.message', $user->getEMail(), Helper::getInstance()->getSiteAddress() . SCRIPTS_PATH . "/default.php?" . $query));
    if (! $mail->send()) {
        throw new Exception("Mail delivery failure, please contact admin");
    }
    if (($replyMode != NULL) && ($replyMode == 'html')) {
        $exchange->getResponse()->cancel();
        // TODO Move to util.php
        echo "<html><head><title>" . Helper::getInstance()->getSiteName() . "</title>\n";
        echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n";
        echo "</head><body>\n";
        echo $res->getf('user.activation.reply');
        echo "\n</body></html>\n";
    } else {
        $exchange->done("User activated");
    }
}

// TODO Mark the user as deleted but keep it for id/link...
function user_delete($exchange) {
    $request = $exchange->getRequest();
    $userId = $request->getAttribute('userId');
    $user = pb_User::fetchFromId($userId);
    if ($user == NULL) {
        throw new Exception('User id ' . $userId . ' not found');
    }
    /*
    $login = $request->getAttribute('login');
    $user = pb_User::fetchFromLogin($login);
    if ($user != NULL) {
        $user->delete();
    }
    */
    $exchange->done('User deleted');
}

/*function user_delete($exchange) {
    $authUser = $exchange->getServer()->getContextKey('user');
    $authUser->delete();
    $exchange->done('User deleted');
}*/

function user_authorize($exchange) {
    $request = $exchange->getRequest();
    $login = $request->getAttribute('login');
    $rights = sxe_Server::getDOMElementAttribute($request, 'rights', 'read');
    $user = pb_User::fetchFromLogin($login);
    if ($user == NULL) {
        throw new Exception("User " . $login . " not found");
    }
    $user->setRights($rights);
    $user->update();
    // send activation mail
    $res = Helper::getInstance()->getRes();
    $mail = Helper::getInstance()->createMail($user->getEMail(),
        $res->getf('user.welcome.mail.title', Helper::getInstance()->getSiteName()),
        $res->getf('user.welcome.mail.message', $login, Helper::getInstance()->getSiteAddress()));
    if (! $mail->send()) {
        throw new Exception("Mail delivery failure, please contact admin");
    }
    $exchange->done("User authorized");
}

function user_modify($exchange) {
    $request = $exchange->getRequest();
    $email = $request->getAttribute('email');
    $alertThreshold = $request->getAttribute('alertThreshold');
    $login = $request->getAttribute('login');
    $password = $request->getAttribute('password');
    $user = $exchange->getServer()->getContextKey('user');
    if ($email != NULL) {
        // Need confirmation email
        $user->setEMail($email);
    }
    if ($alertThreshold != NULL) {
        $user->setAlertThreshold($alertThreshold);
    }
    if ($login != NULL) {
        // Check that the login is free
        if (pb_User::loginOrEMailExists($login, $email)) {
            throw new Exception('The login or the email already exists');
        }
        $user->setLogin($login);
    }
    if ($password != NULL) {
        // TODO Need confirmation email
        $user->setPassword($password);
    }
    if (($email != NULL) || ($alertThreshold != NULL) || ($login != NULL) || ($password != NULL)) {
        $user->update();
        if (($login != NULL) || ($password != NULL)) {
            $user->generateHtpasswd();
        }
    }
    user_reply($exchange, 'ModifyUserResult', $user);
}

function user_modifyRights($exchange) {
    $request = $exchange->getRequest();
    $userId = $request->getAttribute('userId');
    $rights = $request->getAttribute('rights');
    $user = pb_User::fetchFromId($userId);
    if ($user == NULL) {
        throw new Exception('User id ' . $userId . ' not found');
    }
    $user->setRights($rights);
    $user->update();
    $user->generateHtpasswd();
    $exchange->done("User rights modified");
}

function user_lostPassword($exchange) {
    $request = $exchange->getRequest();
    $email = $request->getAttribute('email');
    $password = $request->getAttribute('password');
    $user = pb_User::fetchFromEMail($email);
    if ($user == NULL) {
        throw new Exception("Unknown e-mail address");
    }
    // TODO Check that the user has been activated...
    // generate a random key
    $activationKey = pb_User::generateActivationKey($email);
    $user->setActivationKey($activationKey);
    $user->update();
    // generate and send the activation mail
    $query = http_build_query(array(
        'operation' => 'changeUserPassword',
        'login' => $user->getLogin(),
        'password' => $password,
        'activationKey' => $activationKey,
        'mode' => 'html'
    ));
    $query = Helper::getInstance()->getSiteAddress() . SCRIPTS_PATH . '/default.php?' . $query;
    $res = Helper::getInstance()->getRes();
    $mail = Helper::getInstance()->createMail($email,
        $res->getf('user.lostPassword.mail.title', Helper::getInstance()->getSiteName()),
        $res->getf('user.lostPassword.mail.message', $query));
    if (! $mail->send()) {
        throw new Exception("Mail delivery failure, please contact admin");
    }
    $exchange->done("Confirmation EMail sent");
}

function user_changePassword($exchange) {
    $request = $exchange->getRequest();
    $login = $request->getAttribute('login');
    $password = $request->getAttribute('password');
    $activationKey = $request->getAttribute('activationKey');
    $replyMode = $request->getAttribute('mode');
    $user = pb_User::fetchFromLogin($login);
    if ($user == NULL) {
        throw new Exception("User " . $login . " not found");
    }
    if ($user->getActivationKey() != $activationKey) {
        throw new Exception("Invalid activation key");
    }
    $user->setActivationKey('');
    $user->setPassword($password);
    $user->update();
    $user->generateHtpasswd();
    $res = Helper::getInstance()->getRes();
    if (($replyMode != NULL) && ($replyMode == 'html')) {
        $exchange->getResponse()->cancel();
        // TODO Move to util.php
        echo "<html><head><title>" . Helper::getInstance()->getSiteName() . "</title>\n";
        echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n";
        echo "</head><body>\n";
        echo $res->getf('user.changePassword.reply');
        echo "\n</body></html>\n";
    } else {
        $exchange->done("User password changed");
    }
}

function user_list($exchange) {
    $response = $exchange->createResponseElement("userListResult");
    $connection = Helper::getInstance()->getSQL();
    $connection->executef('SELECT ' . pb_User::getSelectionFields() . ' FROM ' . DB_PREFIX . 'USER');
    $rs = $connection->getResultSet();
    while ($rs->next()) {
        $user = pb_User::createFromResultSet($rs);
        $response->appendChild($exchange->createResponseElement('user', array(
            'id' => $user->getId(),
            'login' => $user->getLogin(),
            'email' => $user->getEMail(),
            'rights' => $user->getRights(),
            'creationDate' => $user->getCreationDate(),
            'lastAuthenticationDate' => $user->getLastAuthenticationDate(),
            'alertThreshold' => $user->getAlertThreshold()
        )));
    }
    $exchange->replyXML($response);
}

function user_reply($exchange, $tagName, $user = NULL) {
    if ($user == NULL) {
        $user = $exchange->getServer()->getContextKey('user');
    }
    $response = $exchange->createResponseElement($tagName);
    $response->appendChild($exchange->createResponseElement('user', array(
        'id' => $user->getId(),
        'login' => $user->getLogin(),
        'email' => $user->getEMail(),
        'rights' => $user->getRights(),
        'creationDate' => $user->getCreationDate(),
        'lastAuthenticationDate' => $user->getLastAuthenticationDate(),
        'alertThreshold' => $user->getAlertThreshold()
    )));
    $exchange->replyXML($response);
}

function user_get($exchange) {
    user_reply($exchange, 'GetUserResult');
}

function user_authenticate($exchange) {
    $user = $exchange->getServer()->getContextKey('user');
    $lastAuthenticationDate = $user->getLastAuthenticationDate();
    $user->setLastAuthenticationDate(time());
    $user->update();
    $user->setLastAuthenticationDate($lastAuthenticationDate);
    user_reply($exchange, 'AuthenticateUserResult', $user);
}

function user_sendMail($exchange) {
    $login = $request->getAttribute('login');
    $subject = $request->getAttribute('subject');
    $message = $request->getAttribute('message');
    $user = pb_User::fetchFromLogin($login);
    if ($user == NULL) {
        throw new Exception('User ' . $login . ' not found');
    }
    $mail = Helper::getInstance()->createMail($user->getEMail(), $subject, $message);
    if (! $mail->send()) {
        throw new Exception('Mail delivery failure');
    }
    $exchange->done('E-Mail sent');
}

function user_feed($exchange) {
    $lastPublicationDate = pb_Media::getLastPublicationDate();
    $emailList = array();
    $res = Helper::getInstance()->getRes();
    $connection = Helper::getInstance()->getSQL();
    $connection->executef('SELECT ' . pb_User::getSelectionFields() . ' FROM ' . DB_PREFIX . 'USER WHERE '
        . 'LAST_AUTHENTICATION_DATE > CREATION_DATE AND ALERT_THRESHOLD > 0 AND '
        . 'LAST_AUTHENTICATION_DATE < ' . sql_Connection::fromTimestamp('%d'), $lastPublicationDate);
    $rs = $connection->getResultSet();
    $emailCount = 0;
    $failureCount = 0;
    //echo '<!-- lastPublicationDate: ' . date('c', $lastPublicationDate) . ' -->';
    while ($rs->next()) {
        $user = pb_User::createFromResultSet($rs);
        $connection->executef('SELECT COUNT(*) AS COUNT FROM ' . DB_PREFIX . 'MEDIA WHERE PUBLICATION_DATE > '
            . sql_Connection::fromTimestamp('%d'), $user->getLastAuthenticationDate());
        $crs = $connection->getResultSet();
        if (! $crs->next()) {
            throw new Exception('Database failure');
        }
        $count = $crs->getRowObject()->COUNT;
        //echo '<!-- ' . $user->getLogin() . ' count: ' . $count . ', alertThreshold: ' . $user->getAlertThreshold() . ' -->';
        if ($count < $user->getAlertThreshold()) {
            continue;
        }
        $mail = Helper::getInstance()->createMail($user->getEMail(),
            $res->getf($count == 1 ? 'user.feed.single.mail.title' : 'user.feed.mail.title',
                Helper::getInstance()->getSiteName(), $count),
            $res->getf($count == 1 ? 'user.feed.single.mail.message' : 'user.feed.mail.message',
                $user->getLogin(), $count, Helper::getInstance()->getSiteAddress()));
        if ($mail->send()) {
            $emailCount++;
        } else {
            $failureCount++;
        }
    }
    $response = $exchange->createResponseElement('userFeedResult', array(
        'emailCount' => $emailCount,
        'failureCount' => $failureCount
    ));
    $exchange->replyXML($response);
}

?>