<?php

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

class pb_Repository {
    protected $id;
    protected $title = NULL;
    protected $userId = 0;
    protected $description = NULL;
    protected $requiresUserAccess = NULL; // Tells if the access is restricted to valid users
    protected $isPublic = NULL; // Tells if the subscription requires an authorization

    public function __construct($userId, $title, $description = NULL, $requiresUserAccess = TRUE, $isPublic = FALSE) {
        $this->id = -1;
        $this->userId = $userId;
        $this->title = $title;
        $this->description = ($description == NULL) ? '' : $description;
        $this->requiresUserAccess = (bool) $requiresUserAccess;
        $this->isPublic = (bool) $isPublic;
    }
	public function getId() {
        return $this->id;
    }
	public function setId($id) {
        $this->id = $id;
    }
    public function getUserId() {
        return $this->userId;
    }
    public function setUserId($userId) {
        $this->userId = $userId;
    }
	public function getTitle() {
        return $this->title;
    }
	public function setTitle($title) {
        $this->title = $title;
    }
	public function getDescription() {
        return $this->description;
    }
	public function setDescription($description) {
        $this->description = $description;
    }
    public function requiresUserAccess() {
        return $this->requiresUserAccess;
    }
    public function setRequiresUserAccess($requiresUserAccess) {
        $this->requiresUserAccess = (bool) $requiresUserAccess;
    }
    public function isPublic() {
        return $this->isPublic;
    }
    public function setIsPublic($isPublic) {
        $this->isPublic = (bool) $isPublic;
    }
    public function getFolder() {
        return pb_Repository::getFolderById($this->id);
        //return PB_PATH . REPOSITORY_PATH . '/' . $this->id;
    }
	public function insert() {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('INSERT INTO ' . DB_PREFIX . 'REPOSITORY (USER_ID, TITLE, DESCRIPTION, REQUIRES_USER_ACCESS, IS_PUBLIC)'
            . ' VALUES(%d, \'%s\', \'%s\', %d, %d)',
            $this->getUserId(), $this->getTitle(), $this->getDescription(), $this->requiresUserAccess(), $this->isPublic());
        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 . 'REPOSITORY SET USER_ID=%d, TITLE=\'%s\', DESCRIPTION=\'%s\', REQUIRES_USER_ACCESS=%d, IS_PUBLIC=%d WHERE ID=%d',
            $this->getId(), $this->getTitle(), $this->getDescription(), $this->requiresUserAccess(), $this->isPublic(), $this->getUserId());
        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 . 'REPOSITORY WHERE ID=%d', $this->id);
        if ($connection->getUpdateCount() != 1) {
            throw new Exception('Database failure');
        }
        $connection->executef('DELETE FROM ' . DB_PREFIX . 'REPOSITORY_USER WHERE REPO_ID=%d', $this->id);
        if ($connection->getUpdateCount() != 1) {
            throw new Exception("Database failure");
        }
    }
    public function insertUser($userId, $rights) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('INSERT INTO ' . DB_PREFIX . 'REPOSITORY_USER (REPO_ID, USER_ID, RIGHTS) VALUES(%d, %d, \'%s\')',
            $this->getId(), $userId, $rights);
        if ($connection->getUpdateCount() != 1) {
            throw new Exception("Database failure");
        }
    }
    public function updateUser($userId, $rights) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('UPDATE ' . DB_PREFIX . 'REPOSITORY_USER SET RIGHTS=\'%s\' WHERE REPO_ID=%d AND USER_ID=%d',
            $rights, $this->getId(), $userId);
        if ($connection->getUpdateCount() != 1) {
            throw new Exception('Database failure');
        }
    }
    public function deleteUser($userId) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('DELETE FROM ' . DB_PREFIX . 'REPOSITORY_USER WHERE REPO_ID=%d AND USER_ID=%d',
            $this->getId(), $userId);
        if ($connection->getUpdateCount() != 1) {
            throw new Exception("Database failure");
        }
    }
    public function create() {
        $this->insert();
        Helper::mkdir($this->getFolder());
        $this->addUser($this->getUserId(), 'read,comment,admin,write');
        if ($this->requiresUserAccess()) {
            // generate the .htaccess
            $file = fopen($this->getFolder() . '/.htaccess', 'w');
            fwrite($file, sprintf(HTACCESS_FORMAT, SITE_PATH . pb_Repository::getRelFolderById($this->getId()) . '/' . USER_HTPASSWD_FILE));
            fclose($file);
            // generate the access dummy file
            $file = fopen($this->getFolder() . '/' . PRIVATE_ACCESS_FILE, 'w');
            fwrite($file, "<access><ok /></access>\n");
            fclose($file);
        }
    }
    public function hasUserId($userId) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT COUNT(*) NB FROM ' . DB_PREFIX . 'REPOSITORY_USER WHERE REPO_ID=%d AND USER_ID=%d', $this->getId(), $userId);
        $rs = $connection->getResultSet();
        if (! $rs->next()) {
            throw new Exception('Database failure');
        }
        return $rs->getRowObject()->NB > 0;
    }
    public function getUserIds() {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT DISTINCT USER_ID FROM ' . DB_PREFIX . 'REPOSITORY_USER WHERE REPO_ID=%d', $this->getId());
        $rs = $connection->getResultSet();
        $userIds = array();
        while ($rs->next()) {
            $userIds[] = $rs->getRowObject()->USER_ID;
        }
        return $userIds;
    }
    public function generateHtpasswd() {
        if (! $this->requiresUserAccess()) {
            return;
        }
        $userIds = $this->getUserIds();
        // generate the users passwords file
        $file = fopen($this->getFolder() . '/' . USER_HTPASSWD_FILE, "w");
        foreach ($userIds as $userId) {
            $user = pb_User::fetchFromId($userId);
            if (($user != NULL) && ($user->hasRight('read'))) {
                fwrite($file, $user->getLogin() . ':' . $user->getPassword() . "\n");
            }
        }
        fclose($file);
    }
    public function addUser($userId, $rights = '') {
        $this->insertUser($userId, $rights);
        $this->generateHtpasswd();
    }
    public function modifyUser($userId, $rights = '') {
        $this->updateUser($userId, $rights);
        $this->generateHtpasswd();
    }
    public function removeUser($userId) {
        $this->deleteUser($userId);
        $this->generateHtpasswd();
    }
    public static function createFromResultSet($rs) {
        $object = $rs->getRowObject();
        $repository = new pb_Repository($object->USER_ID, $object->TITLE, $object->DESCRIPTION, $object->REQUIRES_USER_ACCESS, $object->IS_PUBLIC);
        $repository->setId($object->ID);
        return $repository;
    }
    public static function getSelectionFields() {
        return 'ID, USER_ID, TITLE, DESCRIPTION, REQUIRES_USER_ACCESS, IS_PUBLIC';
    }
    public static function fetchFromId($id) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT ' . pb_Repository::getSelectionFields() . ' FROM ' . DB_PREFIX . 'REPOSITORY WHERE ID=%d', $id);
        $rs = $connection->getResultSet();
        if (! $rs->next()) {
            return NULL;
        }
        return pb_Repository::createFromResultSet($rs);
    }
    // TODO Move to sql or util
    public static function getIdsFromResultSet($rs) {
        $repositoryIds = array();
        while ($rs->next()) {
            $repositoryIds[] = $rs->getRowObject()->ID;
        }
        return $repositoryIds;
    }
    public static function getRepositoryIds() {
        $connection = Helper::getInstance()->getSQL();
        $connection->execute('SELECT ID FROM ' . DB_PREFIX . 'REPOSITORY');
        return pb_Repository::getIdsFromResultSet($connection->getResultSet());
    }
    public static function getPublicRepositoryIds() {
        $connection = Helper::getInstance()->getSQL();
        $connection->execute('SELECT ID FROM ' . DB_PREFIX . 'REPOSITORY WHERE REQUIRES_USER_ACCESS=0 AND IS_PUBLIC<>0');
        return pb_Repository::getIdsFromResultSet($connection->getResultSet());
    }
    public static function getRepositoryIdsByOwnerId($ownerId) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT ID FROM ' . DB_PREFIX . 'REPOSITORY WHERE USER_ID=%d', $ownerId);
        return pb_Repository::getIdsFromResultSet($connection->getResultSet());
    }
    public static function getRepositoryIdsByUserId($userId) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT REPO_ID AS ID FROM ' . DB_PREFIX . 'REPOSITORY_USER WHERE USER_ID=%d', $userId);
        return pb_Repository::getIdsFromResultSet($connection->getResultSet());
    }
    public static function getUserRights($repositoryId, $userId) {
        $connection = Helper::getInstance()->getSQL();
        $connection->executef('SELECT RIGHTS FROM ' . DB_PREFIX . 'REPOSITORY_USER WHERE REPO_ID=%d AND USER_ID=%d', $repositoryId, $userId);
        $rs = $connection->getResultSet();
        if (! $rs->next()) {
            return NULL;
        }
        return $rs->getRowObject()->RIGHTS;
    }
    public static function generateHtpasswdByUserId($userId) {
        $repositoryIds = pb_Repository::getRepositoryIdsByUserId($authUser->getId());
        foreach ($repositoryIds as $repositoryId) {
            $repository = pb_Repository::fetchFromId($repositoryId);
            $repository->generateHtpasswd();
        }
    }
    public static function getRelFolderById($repositoryId) {
        return REPOSITORY_PATH . sprintf('/%02d', $repositoryId);
    }
    public static function getFolderById($repositoryId) {
        return PB_PATH . pb_Repository::getRelFolderById($repositoryId);
    }
    public static function initializeTable() {
        $connection = Helper::getInstance()->getSQL();
        $connection->execute('DROP TABLE IF EXISTS ' . DB_PREFIX . 'REPOSITORY');
        $connection->execute('CREATE TABLE ' . DB_PREFIX . 'REPOSITORY (' .
            'ID bigint(20) unsigned NOT NULL auto_increment,' .
            'USER_ID bigint(20) unsigned NOT NULL, ' .
            'TITLE varchar(256) NOT NULL default \'\', ' .
            'DESCRIPTION varchar(1024) NOT NULL default \'\', ' .
            'REQUIRES_USER_ACCESS tinyint(1) NOT NULL default \'1\', ' .
            'IS_PUBLIC tinyint(1) NOT NULL default \'0\', ' .
            'PRIMARY KEY (ID)' .
            ') CHARACTER SET utf8 COLLATE utf8_bin');
        $connection->execute('DROP TABLE IF EXISTS ' . DB_PREFIX . 'REPOSITORY_USER');
        // read,comment,admin,write
        $connection->execute('CREATE TABLE ' . DB_PREFIX . 'REPOSITORY_USER (' .
            'REPO_ID bigint(20) unsigned NOT NULL, ' .
            'USER_ID bigint(20) unsigned NOT NULL, ' .
            'RIGHTS varchar(64) NOT NULL default \'\', ' .
            'CONSTRAINT UC_REPO_USER UNIQUE (REPO_ID, USER_ID)' .
            ') CHARACTER SET utf8 COLLATE utf8_bin');
    }
}

function repository_appendRepository($exchange, $response, $repository) {
    if ($repository == NULL) {
        return;
    }
    $response->appendChild($exchange->createResponseElement('repository', array(
        'id' => $repository->getId(),
        'title' => $repository->getTitle(),
        'userId' => $repository->getUserId(),
        'description' => $repository->getDescription(),
        'requiresUserAccess' => $repository->requiresUserAccess() ? 'true' : 'false',
        'isPublic' => $repository->isPublic() ? 'true' : 'false'
    )));
}

function repository_listReply($exchange, $repositoryIds) {
    $response = $exchange->createResponseElement('repositoryListResult');
    foreach ($repositoryIds as $repositoryId) {
        $repository = pb_Repository::fetchFromId($repositoryId);
        repository_appendRepository($exchange, $response, $repository);
    }
    $exchange->replyXML($response);
}

function repository_create($exchange) {
    $request = $exchange->getRequest();
    $title = $request->getAttribute('title');
    $description = $request->getAttribute('description');
    $requiresUserAccess = sxe_Server::getDOMElementAttribute($request, 'requiresUserAccess', 'true') == 'true';
    $isPublic = sxe_Server::getDOMElementAttribute($request, 'isPublic', 'false') == 'true';
    $authUser = $exchange->getServer()->getContextKey('user');
    $userId = $authUser->getId();
    $repository = new pb_Repository($authUser->getId(), $title, $description, $requiresUserAccess, $isPublic);
    $repository->create();
    $response = $exchange->createResponseElement('createRepositoryResult');
    repository_appendRepository($exchange, $response, $repository);
    $exchange->replyXML($response);
}

function repository_delete($exchange) {
    $request = $exchange->getRequest();
    $id = $request->getAttribute('id');
    $repository = pb_Repository::fetchFromId($id);
    $authUser = $exchange->getServer()->getContextKey('user');
    if ($repository == NULL) {
        throw new Exception("Repository id " . $id . " not found");
    }
    if (($repository->getUserId() != $authUser->getId()) && (! $authUser->hasRight('admin'))) {
        throw new Exception('Not owner');
    }
    $repository->delete();
    // TODO remove dir
    $exchange->done("Repository deleted");
}

function repository_modify($exchange) {
    $request = $exchange->getRequest();
    $id = $request->getAttribute('id');
    $title = $request->getAttribute('title');
    $description = $request->getAttribute('description');
    $requiresUserAccess = sxe_Server::getDOMElementAttribute($request, 'requiresUserAccess', 'true') == 'true';
    $isPublic = sxe_Server::getDOMElementAttribute($request, 'isPublic', 'false') == 'true';
    $authUser = $exchange->getServer()->getContextKey('user');
    $repository = pb_Repository::fetchFromId($id);
    if ($repository == NULL) {
        throw new Exception("Repository id " . $id . " not found");
    }
    if (($repository->getUserId() != $authUser->getId()) && (! $authUser->hasRight('admin'))) {
        throw new Exception('Not owner');
    }
    if ($title == NULL) {
        $repository.setTitle($title);
    }
    if ($description == NULL) {
        $repository.setDescription($description);
    }
    if ($requiresUserAccess == NULL) {
        $repository.setRequiresUserAccess($requiresUserAccess);
    }
    if ($isPublic == NULL) {
        $repository.setIsPublic($isPublic);
    }
    $repository->update();
    $response = $exchange->createResponseElement('modifyRepositoryResult');
    repository_appendRepository($exchange, $response, $repository);
    $exchange->replyXML($response);
}

function repository_listPublic($exchange) {
    repository_listReply($exchange, pb_Repository::getPublicRepositoryIds());
}

function repository_list($exchange) {
    $request = $exchange->getRequest();
    $select = sxe_Server::getDOMElementAttribute($request, 'select', 'all');
    $authUser = $exchange->getServer()->getContextKey('user');
    $repositoryIds = NULL;
    switch ($select) {
    case 'all':
        $repositoryIds = pb_Repository::getRepositoryIds();
        break;
    case 'owned':
        $repositoryIds = pb_Repository::getRepositoryIdsByOwnerId($authUser->getId());
        break;
    case 'subscribed':
        $repositoryIds = pb_Repository::getRepositoryIdsByUserId($authUser->getId());
        break;
    default:
        throw new Exception('Invalid selection criteria ' . $select);
        break;
    }
    repository_listReply($exchange, $repositoryIds);
}

function repository_addUser($exchange) {
    $request = $exchange->getRequest();
    $repositoryId = $request->getAttribute('repositoryId');
    $userId = $request->getAttribute('userId');
    $rights = sxe_Server::getDOMElementAttribute($request, 'rights', '');
    $authUser = $exchange->getServer()->getContextKey('user');
    $repository = pb_Repository::fetchFromId($repositoryId);
    if ($repository == NULL) {
        throw new Exception("Repository id " . $repositoryId . " not found");
    }
    if ($repository->isPublic()) {
        $rights = '';
    } else if (($repository->getUserId() != $authUser->getId()) && (! $authUser->hasRight('admin'))) {
        throw new Exception('Not owner');
    }
    $repository->addUser($userId, $rights);
    $exchange->done('User added to the repository');
}

function repository_removeUser($exchange) {
    $request = $exchange->getRequest();
    $repositoryId = $request->getAttribute('repositoryId');
    $userId = $request->getAttribute('userId');
    $authUser = $exchange->getServer()->getContextKey('user');
    $repository = pb_Repository::fetchFromId($repositoryId);
    if ($repository == NULL) {
        throw new Exception("Repository id " . $repositoryId . " not found");
    }
    if (($userId != $authUser->getId()) && ($repository->getUserId() != $authUser->getId()) && (! $authUser->hasRight('admin'))) {
        throw new Exception('Not owner');
    }
    $repository->removeUser($userId);
    $exchange->done('User removed from the repository');
}

function repository_modifyUser($exchange) {
    $request = $exchange->getRequest();
    $repositoryId = $request->getAttribute('repositoryId');
    $userId = $request->getAttribute('userId');
    $rights = $request->getAttribute('rights');
    $authUser = $exchange->getServer()->getContextKey('user');
    $repository = pb_Repository::fetchFromId($repositoryId);
    if ($repository == NULL) {
        throw new Exception("Repository id " . $repositoryId . " not found");
    }
    if (($userId != $authUser->getId()) && ($repository->getUserId() != $authUser->getId()) && (! $authUser->hasRight('admin'))) {
        throw new Exception('Not owner');
    }
    $repository->modifyUser($userId, $rights);
    $exchange->done('User rights modified for the repository');
}

?>