mirror of
https://github.com/haiwen/seafile-server.git
synced 2025-09-01 07:26:37 +00:00
While the code currently verifies whether an entry exists in the VirtualRepo table before creating a new one, it is possible that bugs in the code (such as race conditions) could result in extraneous entries in the table. This change enforces the entries within the table be unique, preventing the existing of a duplicate entry even if other areas of the code have bugs. This change does not implement an appropriate migration for the case where a server already has duplicate entries in the VirtualRepo table that would cause the constraint to fail to be added. This is a partial fix for haiwen/seafile#2449.
4696 lines
159 KiB
C
4696 lines
159 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
#include "common.h"
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <openssl/sha.h>
|
|
#include <openssl/rand.h>
|
|
|
|
#include <timer.h>
|
|
#include "utils.h"
|
|
#include "log.h"
|
|
|
|
#include "seafile-session.h"
|
|
#include "commit-mgr.h"
|
|
#include "branch-mgr.h"
|
|
#include "repo-mgr.h"
|
|
#include "fs-mgr.h"
|
|
#include "seafile-error.h"
|
|
#include "seafile-crypt.h"
|
|
|
|
#include "seaf-db.h"
|
|
#include "seaf-utils.h"
|
|
|
|
#define REAP_TOKEN_INTERVAL 300 /* 5 mins */
|
|
#define DECRYPTED_TOKEN_TTL 3600 /* 1 hour */
|
|
#define SCAN_TRASH_DAYS 1 /* one day */
|
|
#define TRASH_EXPIRE_DAYS 30 /* one month */
|
|
|
|
typedef struct DecryptedToken {
|
|
char *token;
|
|
gint64 reap_time;
|
|
} DecryptedToken;
|
|
|
|
struct _SeafRepoManagerPriv {
|
|
/* (encrypted_token, session_key) -> decrypted token */
|
|
GHashTable *decrypted_tokens;
|
|
pthread_rwlock_t lock;
|
|
CcnetTimer *reap_token_timer;
|
|
|
|
CcnetTimer *scan_trash_timer;
|
|
};
|
|
|
|
static void
|
|
load_repo (SeafRepoManager *manager, SeafRepo *repo);
|
|
|
|
static int create_db_tables_if_not_exist (SeafRepoManager *mgr);
|
|
|
|
static int save_branch_repo_map (SeafRepoManager *manager, SeafBranch *branch);
|
|
|
|
static int reap_token (void *data);
|
|
static void decrypted_token_free (DecryptedToken *token);
|
|
|
|
gboolean
|
|
is_repo_id_valid (const char *id)
|
|
{
|
|
if (!id)
|
|
return FALSE;
|
|
|
|
return is_uuid_valid (id);
|
|
}
|
|
|
|
SeafRepo*
|
|
seaf_repo_new (const char *id, const char *name, const char *desc)
|
|
{
|
|
SeafRepo* repo;
|
|
|
|
/* valid check */
|
|
|
|
|
|
repo = g_new0 (SeafRepo, 1);
|
|
memcpy (repo->id, id, 36);
|
|
repo->id[36] = '\0';
|
|
|
|
repo->name = g_strdup(name);
|
|
repo->desc = g_strdup(desc);
|
|
|
|
repo->ref_cnt = 1;
|
|
|
|
return repo;
|
|
}
|
|
|
|
void
|
|
seaf_repo_free (SeafRepo *repo)
|
|
{
|
|
if (repo->name) g_free (repo->name);
|
|
if (repo->desc) g_free (repo->desc);
|
|
if (repo->head) seaf_branch_unref (repo->head);
|
|
if (repo->virtual_info)
|
|
seaf_virtual_repo_info_free (repo->virtual_info);
|
|
g_free (repo->last_modifier);
|
|
g_free (repo);
|
|
}
|
|
|
|
void
|
|
seaf_repo_ref (SeafRepo *repo)
|
|
{
|
|
g_atomic_int_inc (&repo->ref_cnt);
|
|
}
|
|
|
|
void
|
|
seaf_repo_unref (SeafRepo *repo)
|
|
{
|
|
if (!repo)
|
|
return;
|
|
|
|
if (g_atomic_int_dec_and_test (&repo->ref_cnt))
|
|
seaf_repo_free (repo);
|
|
}
|
|
|
|
static void
|
|
set_head_common (SeafRepo *repo, SeafBranch *branch)
|
|
{
|
|
if (repo->head)
|
|
seaf_branch_unref (repo->head);
|
|
repo->head = branch;
|
|
seaf_branch_ref(branch);
|
|
}
|
|
|
|
int
|
|
seaf_repo_set_head (SeafRepo *repo, SeafBranch *branch)
|
|
{
|
|
if (save_branch_repo_map (repo->manager, branch) < 0)
|
|
return -1;
|
|
set_head_common (repo, branch);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
seaf_repo_from_commit (SeafRepo *repo, SeafCommit *commit)
|
|
{
|
|
repo->name = g_strdup (commit->repo_name);
|
|
repo->desc = g_strdup (commit->repo_desc);
|
|
repo->encrypted = commit->encrypted;
|
|
repo->repaired = commit->repaired;
|
|
repo->last_modify = commit->ctime;
|
|
memcpy (repo->root_id, commit->root_id, 40);
|
|
if (repo->encrypted) {
|
|
repo->enc_version = commit->enc_version;
|
|
if (repo->enc_version == 1)
|
|
memcpy (repo->magic, commit->magic, 32);
|
|
else if (repo->enc_version == 2) {
|
|
memcpy (repo->magic, commit->magic, 64);
|
|
memcpy (repo->random_key, commit->random_key, 96);
|
|
} else if (repo->enc_version == 3) {
|
|
memcpy (repo->magic, commit->magic, 64);
|
|
memcpy (repo->random_key, commit->random_key, 96);
|
|
memcpy (repo->salt, commit->salt, 64);
|
|
} else if (repo->enc_version == 4) {
|
|
memcpy (repo->magic, commit->magic, 64);
|
|
memcpy (repo->random_key, commit->random_key, 96);
|
|
memcpy (repo->salt, commit->salt, 64);
|
|
}
|
|
}
|
|
repo->no_local_history = commit->no_local_history;
|
|
repo->version = commit->version;
|
|
repo->last_modifier = g_strdup (commit->creator_name);
|
|
}
|
|
|
|
void
|
|
seaf_repo_to_commit (SeafRepo *repo, SeafCommit *commit)
|
|
{
|
|
commit->repo_name = g_strdup (repo->name);
|
|
commit->repo_desc = g_strdup (repo->desc);
|
|
commit->encrypted = repo->encrypted;
|
|
commit->repaired = repo->repaired;
|
|
if (commit->encrypted) {
|
|
commit->enc_version = repo->enc_version;
|
|
if (commit->enc_version == 1)
|
|
commit->magic = g_strdup (repo->magic);
|
|
else if (commit->enc_version == 2) {
|
|
commit->magic = g_strdup (repo->magic);
|
|
commit->random_key = g_strdup (repo->random_key);
|
|
} else if (commit->enc_version == 3) {
|
|
commit->magic = g_strdup (repo->magic);
|
|
commit->random_key = g_strdup (repo->random_key);
|
|
commit->salt = g_strdup (repo->salt);
|
|
} else if (commit->enc_version == 4) {
|
|
commit->magic = g_strdup (repo->magic);
|
|
commit->random_key = g_strdup (repo->random_key);
|
|
commit->salt = g_strdup (repo->salt);
|
|
}
|
|
}
|
|
commit->no_local_history = repo->no_local_history;
|
|
commit->version = repo->version;
|
|
}
|
|
|
|
static gboolean
|
|
collect_commit (SeafCommit *commit, void *vlist, gboolean *stop)
|
|
{
|
|
GList **commits = vlist;
|
|
|
|
/* The traverse function will unref the commit, so we need to ref it.
|
|
*/
|
|
seaf_commit_ref (commit);
|
|
*commits = g_list_prepend (*commits, commit);
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_get_commits (SeafRepo *repo)
|
|
{
|
|
GList *branches;
|
|
GList *ptr;
|
|
SeafBranch *branch;
|
|
GList *commits = NULL;
|
|
|
|
branches = seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo->id);
|
|
if (branches == NULL) {
|
|
seaf_warning ("Failed to get branch list of repo %s.\n", repo->id);
|
|
return NULL;
|
|
}
|
|
|
|
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
|
|
branch = ptr->data;
|
|
gboolean res = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
|
|
repo->id,
|
|
repo->version,
|
|
branch->commit_id,
|
|
collect_commit,
|
|
&commits,
|
|
FALSE);
|
|
if (!res) {
|
|
for (ptr = commits; ptr != NULL; ptr = ptr->next)
|
|
seaf_commit_unref ((SeafCommit *)(ptr->data));
|
|
g_list_free (commits);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
commits = g_list_reverse (commits);
|
|
|
|
out:
|
|
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
|
|
seaf_branch_unref ((SeafBranch *)ptr->data);
|
|
}
|
|
return commits;
|
|
}
|
|
|
|
gboolean
|
|
should_ignore_file(const char *filename, void *data)
|
|
{
|
|
/* GPatternSpec **spec = ignore_patterns; */
|
|
|
|
if (!g_utf8_validate (filename, -1, NULL)) {
|
|
seaf_warning ("File name %s contains non-UTF8 characters, skip.\n", filename);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Ignore file/dir if its name is too long. */
|
|
if (strlen(filename) >= SEAF_DIR_NAME_LEN)
|
|
return TRUE;
|
|
|
|
if (strchr (filename, '/'))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
collect_repo_id (SeafDBRow *row, void *data);
|
|
|
|
static int
|
|
scan_trash (void *data)
|
|
{
|
|
GList *repo_ids = NULL;
|
|
SeafRepoManager *mgr = seaf->repo_mgr;
|
|
gint64 trash_expire_interval = TRASH_EXPIRE_DAYS * 24 * 3600;
|
|
int expire_days = seaf_cfg_manager_get_config_int (seaf->cfg_mgr,
|
|
"library_trash",
|
|
"expire_days");
|
|
if (expire_days > 0) {
|
|
trash_expire_interval = expire_days * 24 * 3600;
|
|
}
|
|
|
|
gint64 expire_time = time(NULL) - trash_expire_interval;
|
|
char *sql = "SELECT repo_id FROM RepoTrash WHERE del_time <= ?";
|
|
|
|
int ret = seaf_db_statement_foreach_row (seaf->db, sql,
|
|
collect_repo_id, &repo_ids,
|
|
1, "int64", expire_time);
|
|
if (ret < 0) {
|
|
seaf_warning ("Get expired repo from trash failed.");
|
|
string_list_free (repo_ids);
|
|
return TRUE;
|
|
}
|
|
|
|
GList *iter;
|
|
char *repo_id;
|
|
for (iter=repo_ids; iter; iter=iter->next) {
|
|
repo_id = iter->data;
|
|
ret = seaf_repo_manager_del_repo_from_trash (mgr, repo_id, NULL);
|
|
if (ret < 0)
|
|
break;
|
|
}
|
|
|
|
string_list_free (repo_ids);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
init_scan_trash_timer (SeafRepoManagerPriv *priv, GKeyFile *config)
|
|
{
|
|
int scan_days;
|
|
GError *error = NULL;
|
|
|
|
scan_days = g_key_file_get_integer (config,
|
|
"library_trash", "scan_days",
|
|
&error);
|
|
if (error) {
|
|
scan_days = SCAN_TRASH_DAYS;
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
priv->scan_trash_timer = ccnet_timer_new (scan_trash, NULL,
|
|
scan_days * 24 * 3600 * 1000);
|
|
}
|
|
|
|
SeafRepoManager*
|
|
seaf_repo_manager_new (SeafileSession *seaf)
|
|
{
|
|
SeafRepoManager *mgr = g_new0 (SeafRepoManager, 1);
|
|
|
|
mgr->priv = g_new0 (SeafRepoManagerPriv, 1);
|
|
mgr->seaf = seaf;
|
|
|
|
mgr->priv->decrypted_tokens = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free,
|
|
(GDestroyNotify)decrypted_token_free);
|
|
pthread_rwlock_init (&mgr->priv->lock, NULL);
|
|
mgr->priv->reap_token_timer = ccnet_timer_new (reap_token, mgr,
|
|
REAP_TOKEN_INTERVAL * 1000);
|
|
|
|
init_scan_trash_timer (mgr->priv, seaf->config);
|
|
|
|
return mgr;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_init (SeafRepoManager *mgr)
|
|
{
|
|
/* On the server, we load repos into memory on-demand, because
|
|
* there are too many repos.
|
|
*/
|
|
if (create_db_tables_if_not_exist (mgr) < 0) {
|
|
seaf_warning ("[repo mgr] failed to create tables.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (seaf_repo_manager_init_merge_scheduler() < 0) {
|
|
seaf_warning ("Failed to init merge scheduler.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_start (SeafRepoManager *mgr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_add_repo (SeafRepoManager *manager,
|
|
SeafRepo *repo)
|
|
{
|
|
SeafDB *db = manager->seaf->db;
|
|
|
|
if (seaf_db_statement_query (db, "INSERT INTO Repo (repo_id) VALUES (?)",
|
|
1, "string", repo->id) < 0)
|
|
return -1;
|
|
|
|
repo->manager = manager;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
add_deleted_repo_record (SeafRepoManager *mgr, const char *repo_id)
|
|
{
|
|
if (seaf_db_type(seaf->db) == SEAF_DB_TYPE_PGSQL) {
|
|
gboolean exists, err;
|
|
|
|
exists = seaf_db_statement_exists (seaf->db,
|
|
"SELECT repo_id FROM GarbageRepos "
|
|
"WHERE repo_id=?",
|
|
&err, 1, "string", repo_id);
|
|
if (err)
|
|
return -1;
|
|
|
|
if (!exists) {
|
|
return seaf_db_statement_query(seaf->db,
|
|
"INSERT INTO GarbageRepos (repo_id) VALUES (?)",
|
|
1, "string", repo_id);
|
|
}
|
|
|
|
return 0;
|
|
} else {
|
|
return seaf_db_statement_query (seaf->db,
|
|
"REPLACE INTO GarbageRepos (repo_id) VALUES (?)",
|
|
1, "string", repo_id);
|
|
}
|
|
}
|
|
|
|
static int
|
|
add_deleted_repo_to_trash (SeafRepoManager *mgr, const char *repo_id,
|
|
SeafCommit *commit)
|
|
{
|
|
char *owner = NULL;
|
|
int ret = -1;
|
|
|
|
owner = seaf_repo_manager_get_repo_owner (mgr, repo_id);
|
|
if (!owner) {
|
|
seaf_warning ("Failed to get owner for repo %.8s.\n", repo_id);
|
|
goto out;
|
|
}
|
|
|
|
gint64 size = seaf_repo_manager_get_repo_size (mgr, repo_id);
|
|
if (size == -1) {
|
|
seaf_warning ("Failed to get size of repo %.8s.\n", repo_id);
|
|
goto out;
|
|
}
|
|
|
|
ret = seaf_db_statement_query (mgr->seaf->db,
|
|
"INSERT INTO RepoTrash (repo_id, repo_name, head_id, "
|
|
"owner_id, size, org_id, del_time) "
|
|
"values (?, ?, ?, ?, ?, -1, ?)", 6,
|
|
"string", repo_id,
|
|
"string", commit->repo_name,
|
|
"string", commit->commit_id,
|
|
"string", owner,
|
|
"int64", size,
|
|
"int64", (gint64)time(NULL));
|
|
out:
|
|
g_free (owner);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
remove_virtual_repo_ondisk (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
SeafDB *db = mgr->seaf->db;
|
|
|
|
/* Remove record in repo table first.
|
|
* Once this is commited, we can gc the other tables later even if
|
|
* we're interrupted.
|
|
*/
|
|
if (seaf_db_statement_query (db, "DELETE FROM Repo WHERE repo_id = ?",
|
|
1, "string", repo_id) < 0)
|
|
return -1;
|
|
|
|
/* remove branch */
|
|
GList *p;
|
|
GList *branch_list =
|
|
seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo_id);
|
|
for (p = branch_list; p; p = p->next) {
|
|
SeafBranch *b = (SeafBranch *)p->data;
|
|
seaf_repo_manager_branch_repo_unmap (mgr, b);
|
|
seaf_branch_manager_del_branch (seaf->branch_mgr, repo_id, b->name);
|
|
}
|
|
seaf_branch_list_free (branch_list);
|
|
|
|
seaf_db_statement_query (db, "DELETE FROM RepoOwner WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (db, "DELETE FROM SharedRepo WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (db, "DELETE FROM RepoGroup WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
if (!seaf->cloud_mode) {
|
|
seaf_db_statement_query (db, "DELETE FROM InnerPubRepo WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
}
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoUserToken WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoValidSince WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoSize WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
/* For GC commit objects for this virtual repo. Fs and blocks are GC
|
|
* from the parent repo.
|
|
*/
|
|
add_deleted_repo_record (mgr, repo_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
get_branch (SeafDBRow *row, void *vid)
|
|
{
|
|
char *ret = vid;
|
|
const char *commit_id;
|
|
|
|
commit_id = seaf_db_row_get_column_text (row, 0);
|
|
memcpy (ret, commit_id, 41);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static SeafCommit*
|
|
get_head_commit (SeafRepoManager *mgr, const char *repo_id, gboolean *has_err)
|
|
{
|
|
char commit_id[41];
|
|
char *sql;
|
|
|
|
commit_id[0] = 0;
|
|
sql = "SELECT commit_id FROM Branch WHERE name=? AND repo_id=?";
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
get_branch, commit_id,
|
|
2, "string", "master", "string", repo_id) < 0) {
|
|
*has_err = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
if (commit_id[0] == 0)
|
|
return NULL;
|
|
|
|
SeafCommit *head_commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo_id,
|
|
1, commit_id);
|
|
|
|
return head_commit;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_del_repo (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
GError **error)
|
|
{
|
|
gboolean has_err = FALSE;
|
|
|
|
SeafCommit *head_commit = get_head_commit (mgr, repo_id, &has_err);
|
|
if (has_err) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get head commit from db");
|
|
return -1;
|
|
}
|
|
if (!head_commit) {
|
|
// head commit is missing, del repo directly
|
|
goto del_repo;
|
|
}
|
|
|
|
if (add_deleted_repo_to_trash (mgr, repo_id, head_commit) < 0) {
|
|
// Add repo to trash failed, del repo directly
|
|
seaf_warning ("Failed to add repo %.8s to trash, delete directly.\n",
|
|
repo_id);
|
|
}
|
|
|
|
seaf_commit_unref (head_commit);
|
|
|
|
del_repo:
|
|
if (seaf_db_statement_query (mgr->seaf->db, "DELETE FROM Repo WHERE repo_id = ?",
|
|
1, "string", repo_id) < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to delete repo from db");
|
|
return -1;
|
|
}
|
|
|
|
/* remove branch */
|
|
GList *p;
|
|
GList *branch_list = seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo_id);
|
|
for (p = branch_list; p; p = p->next) {
|
|
SeafBranch *b = (SeafBranch *)p->data;
|
|
seaf_repo_manager_branch_repo_unmap (mgr, b);
|
|
seaf_branch_manager_del_branch (seaf->branch_mgr, repo_id, b->name);
|
|
}
|
|
seaf_branch_list_free (branch_list);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoOwner WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM SharedRepo WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoGroup WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
if (!seaf->cloud_mode) {
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM InnerPubRepo WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
}
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoUserToken WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoHistoryLimit WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoValidSince WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoSize WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
/* Remove virtual repos when origin repo is deleted. */
|
|
GList *vrepos, *ptr;
|
|
vrepos = seaf_repo_manager_get_virtual_repo_ids_by_origin (mgr, repo_id);
|
|
for (ptr = vrepos; ptr != NULL; ptr = ptr->next)
|
|
remove_virtual_repo_ondisk (mgr, (char *)ptr->data);
|
|
string_list_free (vrepos);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoInfo "
|
|
"WHERE repo_id IN (SELECT repo_id FROM VirtualRepo "
|
|
"WHERE origin_repo=?)",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM VirtualRepo "
|
|
"WHERE repo_id=? OR origin_repo=?",
|
|
2, "string", repo_id, "string", repo_id);
|
|
|
|
if (!head_commit)
|
|
add_deleted_repo_record(mgr, repo_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_del_virtual_repo (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
int ret = remove_virtual_repo_ondisk (mgr, repo_id);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM VirtualRepo WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
}
|
|
|
|
static gboolean
|
|
repo_exists_in_db (SeafDB *db, const char *id, gboolean *db_err)
|
|
{
|
|
return seaf_db_statement_exists (db,
|
|
"SELECT repo_id FROM Repo WHERE repo_id = ?",
|
|
db_err, 1, "string", id);
|
|
}
|
|
|
|
gboolean
|
|
create_repo_fill_size (SeafDBRow *row, void *data)
|
|
{
|
|
SeafRepo **repo = data;
|
|
SeafBranch *head;
|
|
|
|
const char *repo_id = seaf_db_row_get_column_text (row, 0);
|
|
gint64 size = seaf_db_row_get_column_int64 (row, 1);
|
|
const char *commit_id = seaf_db_row_get_column_text (row, 2);
|
|
const char *vrepo_id = seaf_db_row_get_column_text (row, 3);
|
|
gint64 file_count = seaf_db_row_get_column_int64 (row, 7);
|
|
int status = seaf_db_row_get_column_int(row, 8);
|
|
|
|
*repo = seaf_repo_new (repo_id, NULL, NULL);
|
|
if (!*repo)
|
|
return FALSE;
|
|
|
|
if (!commit_id) {
|
|
(*repo)->is_corrupted = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
(*repo)->size = size;
|
|
(*repo)->file_count = file_count;
|
|
head = seaf_branch_new ("master", repo_id, commit_id);
|
|
(*repo)->head = head;
|
|
(*repo)->status = status;
|
|
|
|
if (vrepo_id) {
|
|
const char *origin_repo_id = seaf_db_row_get_column_text (row, 4);
|
|
const char *origin_path = seaf_db_row_get_column_text (row, 5);
|
|
const char *base_commit = seaf_db_row_get_column_text (row, 6);
|
|
|
|
SeafVirtRepo *vinfo = g_new0 (SeafVirtRepo, 1);
|
|
memcpy (vinfo->repo_id, vrepo_id, 36);
|
|
memcpy (vinfo->origin_repo_id, origin_repo_id, 36);
|
|
vinfo->path = g_strdup(origin_path);
|
|
memcpy (vinfo->base_commit, base_commit, 40);
|
|
|
|
(*repo)->virtual_info = vinfo;
|
|
memcpy ((*repo)->store_id, origin_repo_id, 36);
|
|
} else {
|
|
memcpy ((*repo)->store_id, repo_id, 36);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static SeafRepo*
|
|
get_repo_from_db (SeafRepoManager *mgr, const char *id, gboolean *db_err)
|
|
{
|
|
SeafRepo *repo = NULL;
|
|
const char *sql;
|
|
|
|
if (seaf_db_type(mgr->seaf->db) != SEAF_DB_TYPE_PGSQL)
|
|
sql = "SELECT r.repo_id, s.size, b.commit_id, "
|
|
"v.repo_id, v.origin_repo, v.path, v.base_commit, fc.file_count, i.status FROM "
|
|
"Repo r LEFT JOIN Branch b ON r.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoSize s ON r.repo_id = s.repo_id "
|
|
"LEFT JOIN VirtualRepo v ON r.repo_id = v.repo_id "
|
|
"LEFT JOIN RepoFileCount fc ON r.repo_id = fc.repo_id "
|
|
"LEFT JOIN RepoInfo i on r.repo_id = i.repo_id "
|
|
"WHERE r.repo_id = ? AND b.name = 'master'";
|
|
else
|
|
sql = "SELECT r.repo_id, s.\"size\", b.commit_id, "
|
|
"v.repo_id, v.origin_repo, v.path, v.base_commit, fc.file_count, i.status FROM "
|
|
"Repo r LEFT JOIN Branch b ON r.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoSize s ON r.repo_id = s.repo_id "
|
|
"LEFT JOIN VirtualRepo v ON r.repo_id = v.repo_id "
|
|
"LEFT JOIN RepoFileCount fc ON r.repo_id = fc.repo_id "
|
|
"LEFT JOIN RepoInfo i on r.repo_id = i.repo_id "
|
|
"WHERE r.repo_id = ? AND b.name = 'master'";
|
|
|
|
int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
create_repo_fill_size, &repo,
|
|
1, "string", id);
|
|
if (ret < 0)
|
|
*db_err = TRUE;
|
|
|
|
return repo;
|
|
}
|
|
|
|
SeafRepo*
|
|
seaf_repo_manager_get_repo (SeafRepoManager *manager, const gchar *id)
|
|
{
|
|
int len = strlen(id);
|
|
SeafRepo *repo = NULL;
|
|
gboolean has_err = FALSE;
|
|
|
|
if (len >= 37)
|
|
return NULL;
|
|
|
|
repo = get_repo_from_db (manager, id, &has_err);
|
|
|
|
if (repo) {
|
|
if (repo->is_corrupted) {
|
|
seaf_repo_unref (repo);
|
|
return NULL;
|
|
}
|
|
|
|
load_repo (manager, repo);
|
|
if (repo->is_corrupted) {
|
|
seaf_repo_unref (repo);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return repo;
|
|
}
|
|
|
|
SeafRepo*
|
|
seaf_repo_manager_get_repo_ex (SeafRepoManager *manager, const gchar *id)
|
|
{
|
|
int len = strlen(id);
|
|
gboolean has_err = FALSE;
|
|
SeafRepo *ret = NULL;
|
|
|
|
if (len >= 37)
|
|
return NULL;
|
|
|
|
ret = get_repo_from_db (manager, id, &has_err);
|
|
if (has_err) {
|
|
ret = seaf_repo_new(id, NULL, NULL);
|
|
ret->is_corrupted = TRUE;
|
|
return ret;
|
|
}
|
|
|
|
if (ret) {
|
|
if (ret->is_corrupted) {
|
|
return ret;
|
|
}
|
|
|
|
load_repo (manager, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
seaf_repo_manager_repo_exists (SeafRepoManager *manager, const gchar *id)
|
|
{
|
|
gboolean db_err = FALSE;
|
|
return repo_exists_in_db (manager->seaf->db, id, &db_err);
|
|
}
|
|
|
|
static int
|
|
save_branch_repo_map (SeafRepoManager *manager, SeafBranch *branch)
|
|
{
|
|
if (seaf_db_type(seaf->db) == SEAF_DB_TYPE_PGSQL) {
|
|
gboolean exists, err;
|
|
int rc;
|
|
|
|
exists = seaf_db_statement_exists (seaf->db,
|
|
"SELECT repo_id FROM RepoHead WHERE repo_id=?",
|
|
&err, 1, "string", branch->repo_id);
|
|
if (err)
|
|
return -1;
|
|
|
|
if (exists)
|
|
rc = seaf_db_statement_query (seaf->db,
|
|
"UPDATE RepoHead SET branch_name=? "
|
|
"WHERE repo_id=?",
|
|
2, "string", branch->name,
|
|
"string", branch->repo_id);
|
|
else
|
|
rc = seaf_db_statement_query (seaf->db,
|
|
"INSERT INTO RepoHead (repo_id, branch_name) VALUES (?, ?)",
|
|
2, "string", branch->repo_id,
|
|
"string", branch->name);
|
|
return rc;
|
|
} else {
|
|
return seaf_db_statement_query (seaf->db,
|
|
"REPLACE INTO RepoHead (repo_id, branch_name) VALUES (?, ?)",
|
|
2, "string", branch->repo_id,
|
|
"string", branch->name);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_branch_repo_unmap (SeafRepoManager *manager, SeafBranch *branch)
|
|
{
|
|
return seaf_db_statement_query (seaf->db,
|
|
"DELETE FROM RepoHead WHERE branch_name = ?"
|
|
" AND repo_id = ?",
|
|
2, "string", branch->name,
|
|
"string", branch->repo_id);
|
|
}
|
|
|
|
int
|
|
set_repo_commit_to_db (const char *repo_id, const char *repo_name, gint64 update_time,
|
|
int version, gboolean is_encrypted, const char *last_modifier)
|
|
{
|
|
char *sql;
|
|
gboolean exists = FALSE, db_err = FALSE;
|
|
|
|
sql = "SELECT 1 FROM RepoInfo WHERE repo_id=?";
|
|
exists = seaf_db_statement_exists (seaf->db, sql, &db_err, 1, "string", repo_id);
|
|
if (db_err)
|
|
return -1;
|
|
|
|
if (update_time == 0)
|
|
update_time = (gint64)time(NULL);
|
|
|
|
if (exists) {
|
|
sql = "UPDATE RepoInfo SET name=?, update_time=?, version=?, is_encrypted=?, "
|
|
"last_modifier=? WHERE repo_id=?";
|
|
if (seaf_db_statement_query (seaf->db, sql, 6,
|
|
"string", repo_name,
|
|
"int64", update_time,
|
|
"int", version,
|
|
"int", (is_encrypted ? 1:0),
|
|
"string", last_modifier,
|
|
"string", repo_id) < 0) {
|
|
seaf_warning ("Failed to update repo info for repo %s.\n", repo_id);
|
|
return -1;
|
|
}
|
|
} else {
|
|
sql = "INSERT INTO RepoInfo (repo_id, name, update_time, version, is_encrypted, last_modifier) "
|
|
"VALUES (?, ?, ?, ?, ?, ?)";
|
|
if (seaf_db_statement_query (seaf->db, sql, 6,
|
|
"string", repo_id,
|
|
"string", repo_name,
|
|
"int64", update_time,
|
|
"int", version,
|
|
"int", (is_encrypted ? 1:0),
|
|
"string", last_modifier) < 0) {
|
|
seaf_warning ("Failed to add repo info for repo %s.\n", repo_id);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
load_repo_commit (SeafRepoManager *manager,
|
|
SeafRepo *repo)
|
|
{
|
|
SeafCommit *commit;
|
|
|
|
commit = seaf_commit_manager_get_commit_compatible (manager->seaf->commit_mgr,
|
|
repo->id,
|
|
repo->head->commit_id);
|
|
if (!commit) {
|
|
seaf_warning ("Commit %s:%s is missing\n", repo->id, repo->head->commit_id);
|
|
repo->is_corrupted = TRUE;
|
|
return;
|
|
}
|
|
|
|
seaf_repo_from_commit (repo, commit);
|
|
|
|
seaf_commit_unref (commit);
|
|
}
|
|
|
|
static void
|
|
load_repo (SeafRepoManager *manager, SeafRepo *repo)
|
|
{
|
|
repo->manager = manager;
|
|
|
|
load_repo_commit (manager, repo);
|
|
}
|
|
|
|
static void
|
|
load_mini_repo (SeafRepoManager *manager, SeafRepo *repo)
|
|
{
|
|
repo->manager = manager;
|
|
SeafCommit *commit;
|
|
|
|
commit = seaf_commit_manager_get_commit_compatible (manager->seaf->commit_mgr,
|
|
repo->id,
|
|
repo->head->commit_id);
|
|
if (!commit) {
|
|
seaf_warning ("Commit %s:%s is missing\n", repo->id, repo->head->commit_id);
|
|
repo->is_corrupted = TRUE;
|
|
return;
|
|
}
|
|
|
|
repo->name = g_strdup (commit->repo_name);
|
|
repo->encrypted = commit->encrypted;
|
|
repo->last_modify = commit->ctime;
|
|
repo->version = commit->version;
|
|
repo->last_modifier = g_strdup (commit->creator_name);
|
|
|
|
seaf_commit_unref (commit);
|
|
}
|
|
|
|
static int
|
|
create_tables_mysql (SeafRepoManager *mgr)
|
|
{
|
|
SeafDB *db = mgr->seaf->db;
|
|
char *sql;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS Repo (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(37), UNIQUE INDEX (repo_id))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoOwner ("
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(37), "
|
|
"owner_id VARCHAR(255),"
|
|
"UNIQUE INDEX (repo_id), INDEX (owner_id))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoGroup (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,"
|
|
"repo_id CHAR(37), "
|
|
"group_id INTEGER, user_name VARCHAR(255), permission CHAR(15), "
|
|
"UNIQUE INDEX (group_id, repo_id), "
|
|
"INDEX (repo_id), INDEX (user_name))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS InnerPubRepo ("
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(37),"
|
|
"permission CHAR(15), UNIQUE INDEX (repo_id))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoUserToken ("
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(37), "
|
|
"email VARCHAR(255), "
|
|
"token CHAR(41), "
|
|
"UNIQUE INDEX (repo_id, token), INDEX (email))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoTokenPeerInfo ("
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"token CHAR(41), "
|
|
"peer_id CHAR(41), "
|
|
"peer_ip VARCHAR(41), "
|
|
"peer_name VARCHAR(255), "
|
|
"sync_time BIGINT, "
|
|
"client_ver VARCHAR(20), UNIQUE INDEX(token))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoHead ("
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(37), branch_name VARCHAR(10), UNIQUE INDEX(repo_id))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoSize ("
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(37),"
|
|
"size BIGINT UNSIGNED,"
|
|
"head_id CHAR(41), UNIQUE INDEX (repo_id))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoHistoryLimit ("
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(37), days INTEGER, UNIQUE INDEX(repo_id))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoValidSince ("
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(37), timestamp BIGINT, UNIQUE INDEX(repo_id))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS WebAP (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(37), "
|
|
"access_property CHAR(10), UNIQUE INDEX(repo_id))"
|
|
"ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS VirtualRepo (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(36),"
|
|
"origin_repo CHAR(36), path TEXT, base_commit CHAR(40), UNIQUE INDEX(repo_id), INDEX(origin_repo)), "
|
|
"UNIQUE INDEX(origin_repo,path) ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS GarbageRepos (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(36), UNIQUE INDEX(repo_id))";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoTrash (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(36),"
|
|
"repo_name VARCHAR(255), head_id CHAR(40), owner_id VARCHAR(255),"
|
|
"size BIGINT(20), org_id INTEGER, del_time BIGINT, "
|
|
"UNIQUE INDEX(repo_id), INDEX(owner_id), INDEX(org_id))ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoFileCount ("
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(36),"
|
|
"file_count BIGINT UNSIGNED, UNIQUE INDEX(repo_id))ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoInfo (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
|
"repo_id CHAR(36), "
|
|
"name VARCHAR(255) NOT NULL, update_time BIGINT, version INTEGER, "
|
|
"is_encrypted INTEGER, last_modifier VARCHAR(255), status INTEGER DEFAULT 0, UNIQUE INDEX(repo_id)) ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS WebUploadTempFiles ( "
|
|
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, repo_id CHAR(40) NOT NULL, "
|
|
"file_path TEXT NOT NULL, tmp_file_path TEXT NOT NULL) ENGINE=INNODB";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
create_tables_sqlite (SeafRepoManager *mgr)
|
|
{
|
|
SeafDB *db = mgr->seaf->db;
|
|
char *sql;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS Repo (repo_id CHAR(37) PRIMARY KEY)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
/* Owner */
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoOwner ("
|
|
"repo_id CHAR(37) PRIMARY KEY, "
|
|
"owner_id TEXT)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
sql = "CREATE INDEX IF NOT EXISTS OwnerIndex ON RepoOwner (owner_id)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
/* Group repo */
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoGroup (repo_id CHAR(37), "
|
|
"group_id INTEGER, user_name TEXT, permission CHAR(15))";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE UNIQUE INDEX IF NOT EXISTS groupid_repoid_indx on "
|
|
"RepoGroup (group_id, repo_id)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE INDEX IF NOT EXISTS repogroup_repoid_index on "
|
|
"RepoGroup (repo_id)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE INDEX IF NOT EXISTS repogroup_username_indx on "
|
|
"RepoGroup (user_name)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
/* Public repo */
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS InnerPubRepo ("
|
|
"repo_id CHAR(37) PRIMARY KEY,"
|
|
"permission CHAR(15))";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoUserToken ("
|
|
"repo_id CHAR(37), "
|
|
"email VARCHAR(255), "
|
|
"token CHAR(41))";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE UNIQUE INDEX IF NOT EXISTS repo_token_indx on "
|
|
"RepoUserToken (repo_id, token)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE INDEX IF NOT EXISTS repo_token_email_indx on "
|
|
"RepoUserToken (email)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoTokenPeerInfo ("
|
|
"token CHAR(41) PRIMARY KEY, "
|
|
"peer_id CHAR(41), "
|
|
"peer_ip VARCHAR(41), "
|
|
"peer_name VARCHAR(255), "
|
|
"sync_time BIGINT, "
|
|
"client_ver VARCHAR(20))";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoHead ("
|
|
"repo_id CHAR(37) PRIMARY KEY, branch_name VARCHAR(10))";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoSize ("
|
|
"repo_id CHAR(37) PRIMARY KEY,"
|
|
"size BIGINT UNSIGNED,"
|
|
"head_id CHAR(41))";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoHistoryLimit ("
|
|
"repo_id CHAR(37) PRIMARY KEY, days INTEGER)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoValidSince ("
|
|
"repo_id CHAR(37) PRIMARY KEY, timestamp BIGINT)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS WebAP (repo_id CHAR(37) PRIMARY KEY, "
|
|
"access_property CHAR(10))";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS VirtualRepo (repo_id CHAR(36) PRIMARY KEY,"
|
|
"origin_repo CHAR(36), path TEXT, base_commit CHAR(40))";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE INDEX IF NOT EXISTS virtualrepo_origin_repo_idx "
|
|
"ON VirtualRepo (origin_repo)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE INDEX IF NOT EXISTS virtualrepo_unique_idx "
|
|
"ON VirtualRepo (origin_repo, path)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS GarbageRepos (repo_id CHAR(36) PRIMARY KEY)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoTrash (repo_id CHAR(36) PRIMARY KEY,"
|
|
"repo_name VARCHAR(255), head_id CHAR(40), owner_id VARCHAR(255), size BIGINT UNSIGNED,"
|
|
"org_id INTEGER, del_time BIGINT)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE INDEX IF NOT EXISTS repotrash_owner_id_idx ON RepoTrash(owner_id)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE INDEX IF NOT EXISTS repotrash_org_id_idx ON RepoTrash(org_id)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoFileCount ("
|
|
"repo_id CHAR(36) PRIMARY KEY,"
|
|
"file_count BIGINT UNSIGNED)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS RepoInfo (repo_id CHAR(36) PRIMARY KEY, "
|
|
"name VARCHAR(255) NOT NULL, update_time INTEGER, version INTEGER, "
|
|
"is_encrypted INTEGER, last_modifier VARCHAR(255), status INTEGER DEFAULT 0)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
sql = "CREATE TABLE IF NOT EXISTS WebUploadTempFiles (repo_id CHAR(40) NOT NULL, "
|
|
"file_path TEXT NOT NULL, tmp_file_path TEXT NOT NULL)";
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* static int */
|
|
/* create_tables_pgsql (SeafRepoManager *mgr) */
|
|
/* { */
|
|
/* SeafDB *db = mgr->seaf->db; */
|
|
/* char *sql; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS Repo (repo_id CHAR(36) PRIMARY KEY)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoOwner (" */
|
|
/* "repo_id CHAR(36) PRIMARY KEY, " */
|
|
/* "owner_id VARCHAR(255))"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* if (!pgsql_index_exists (db, "repoowner_owner_idx")) { */
|
|
/* sql = "CREATE INDEX repoowner_owner_idx ON RepoOwner (owner_id)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoGroup (repo_id CHAR(36), " */
|
|
/* "group_id INTEGER, user_name VARCHAR(255), permission VARCHAR(15), " */
|
|
/* "UNIQUE (group_id, repo_id))"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* if (!pgsql_index_exists (db, "repogroup_repoid_idx")) { */
|
|
/* sql = "CREATE INDEX repogroup_repoid_idx ON RepoGroup (repo_id)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* if (!pgsql_index_exists (db, "repogroup_username_idx")) { */
|
|
/* sql = "CREATE INDEX repogroup_username_idx ON RepoGroup (user_name)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS InnerPubRepo (" */
|
|
/* "repo_id CHAR(36) PRIMARY KEY," */
|
|
/* "permission VARCHAR(15))"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoUserToken (" */
|
|
/* "repo_id CHAR(36), " */
|
|
/* "email VARCHAR(255), " */
|
|
/* "token CHAR(40), " */
|
|
/* "UNIQUE (repo_id, token))"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* if (!pgsql_index_exists (db, "repousertoken_email_idx")) { */
|
|
/* sql = "CREATE INDEX repousertoken_email_idx ON RepoUserToken (email)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoTokenPeerInfo (" */
|
|
/* "token CHAR(40) PRIMARY KEY, " */
|
|
/* "peer_id CHAR(40), " */
|
|
/* "peer_ip VARCHAR(40), " */
|
|
/* "peer_name VARCHAR(255), " */
|
|
/* "sync_time BIGINT, " */
|
|
/* "client_ver VARCHAR(20))"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoHead (" */
|
|
/* "repo_id CHAR(36) PRIMARY KEY, branch_name VARCHAR(10))"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoSize (" */
|
|
/* "repo_id CHAR(36) PRIMARY KEY," */
|
|
/* "size BIGINT," */
|
|
/* "head_id CHAR(40))"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoHistoryLimit (" */
|
|
/* "repo_id CHAR(36) PRIMARY KEY, days INTEGER)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoValidSince (" */
|
|
/* "repo_id CHAR(36) PRIMARY KEY, timestamp BIGINT)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS WebAP (repo_id CHAR(36) PRIMARY KEY, " */
|
|
/* "access_property VARCHAR(10))"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS VirtualRepo (repo_id CHAR(36) PRIMARY KEY," */
|
|
/* "origin_repo CHAR(36), path TEXT, base_commit CHAR(40))"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* if (!pgsql_index_exists (db, "virtualrepo_origin_repo_idx")) { */
|
|
/* sql = "CREATE INDEX virtualrepo_origin_repo_idx ON VirtualRepo (origin_repo)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS GarbageRepos (repo_id CHAR(36) PRIMARY KEY)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoTrash (repo_id CHAR(36) PRIMARY KEY," */
|
|
/* "repo_name VARCHAR(255), head_id CHAR(40), owner_id VARCHAR(255), size bigint," */
|
|
/* "org_id INTEGER, del_time BIGINT)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* if (!pgsql_index_exists (db, "repotrash_owner_id")) { */
|
|
/* sql = "CREATE INDEX repotrash_owner_id on RepoTrash(owner_id)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
/* } */
|
|
/* if (!pgsql_index_exists (db, "repotrash_org_id")) { */
|
|
/* sql = "CREATE INDEX repotrash_org_id on RepoTrash(org_id)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoFileCount (" */
|
|
/* "repo_id CHAR(36) PRIMARY KEY," */
|
|
/* "file_count BIGINT)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS WebUploadTempFiles (repo_id CHAR(40) NOT NULL, " */
|
|
/* "file_path TEXT NOT NULL, tmp_file_path TEXT NOT NULL)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* sql = "CREATE TABLE IF NOT EXISTS RepoInfo (repo_id CHAR(36) PRIMARY KEY, " */
|
|
/* "name VARCHAR(255) NOT NULL, update_time BIGINT, version INTEGER, " */
|
|
/* "is_encrypted INTEGER, last_modifier VARCHAR(255), status INTEGER DEFAULT 0)"; */
|
|
/* if (seaf_db_query (db, sql) < 0) */
|
|
/* return -1; */
|
|
|
|
/* return 0; */
|
|
/* } */
|
|
|
|
static int
|
|
create_db_tables_if_not_exist (SeafRepoManager *mgr)
|
|
{
|
|
if (!mgr->seaf->create_tables && seaf_db_type (mgr->seaf->db) != SEAF_DB_TYPE_PGSQL)
|
|
return 0;
|
|
|
|
SeafDB *db = mgr->seaf->db;
|
|
int db_type = seaf_db_type (db);
|
|
|
|
if (db_type == SEAF_DB_TYPE_MYSQL)
|
|
return create_tables_mysql (mgr);
|
|
else if (db_type == SEAF_DB_TYPE_SQLITE)
|
|
return create_tables_sqlite (mgr);
|
|
/* else if (db_type == SEAF_DB_TYPE_PGSQL) */
|
|
/* return create_tables_pgsql (mgr); */
|
|
|
|
g_return_val_if_reached (-1);
|
|
}
|
|
|
|
/*
|
|
* Repo properties functions.
|
|
*/
|
|
|
|
static inline char *
|
|
generate_repo_token ()
|
|
{
|
|
char *uuid = gen_uuid ();
|
|
unsigned char sha1[20];
|
|
char token[41];
|
|
SHA_CTX s;
|
|
|
|
SHA1_Init (&s);
|
|
SHA1_Update (&s, uuid, strlen(uuid));
|
|
SHA1_Final (sha1, &s);
|
|
|
|
rawdata_to_hex (sha1, token, 20);
|
|
|
|
g_free (uuid);
|
|
|
|
return g_strdup (token);
|
|
}
|
|
|
|
static int
|
|
add_repo_token (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *email,
|
|
const char *token,
|
|
GError **error)
|
|
{
|
|
int rc = seaf_db_statement_query (mgr->seaf->db,
|
|
"INSERT INTO RepoUserToken (repo_id, email, token) VALUES (?, ?, ?)",
|
|
3, "string", repo_id, "string", email,
|
|
"string", token);
|
|
|
|
if (rc < 0) {
|
|
seaf_warning ("failed to add repo token. repo = %s, email = %s\n",
|
|
repo_id, email);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_generate_repo_token (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *email,
|
|
GError **error)
|
|
{
|
|
char *token = generate_repo_token ();
|
|
if (add_repo_token (mgr, repo_id, email, token, error) < 0) {
|
|
g_free (token);
|
|
return NULL;
|
|
}
|
|
|
|
return token;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_add_token_peer_info (SeafRepoManager *mgr,
|
|
const char *token,
|
|
const char *peer_id,
|
|
const char *peer_ip,
|
|
const char *peer_name,
|
|
gint64 sync_time,
|
|
const char *client_ver)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (seaf_db_statement_query (mgr->seaf->db,
|
|
"INSERT INTO RepoTokenPeerInfo (token, peer_id, peer_ip, peer_name, sync_time, client_ver)"
|
|
"VALUES (?, ?, ?, ?, ?, ?)",
|
|
6, "string", token,
|
|
"string", peer_id,
|
|
"string", peer_ip,
|
|
"string", peer_name,
|
|
"int64", sync_time,
|
|
"string", client_ver) < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_update_token_peer_info (SeafRepoManager *mgr,
|
|
const char *token,
|
|
const char *peer_ip,
|
|
gint64 sync_time,
|
|
const char *client_ver)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (seaf_db_statement_query (mgr->seaf->db,
|
|
"UPDATE RepoTokenPeerInfo SET "
|
|
"peer_ip=?, sync_time=?, client_ver=? WHERE token=?",
|
|
4, "string", peer_ip,
|
|
"int64", sync_time,
|
|
"string", client_ver,
|
|
"string", token) < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
seaf_repo_manager_token_peer_info_exists (SeafRepoManager *mgr,
|
|
const char *token)
|
|
{
|
|
gboolean db_error = FALSE;
|
|
|
|
return seaf_db_statement_exists (mgr->seaf->db,
|
|
"SELECT token FROM RepoTokenPeerInfo WHERE token=?",
|
|
&db_error, 1, "string", token);
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_delete_token (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *token,
|
|
const char *user,
|
|
GError **error)
|
|
{
|
|
char *token_owner;
|
|
|
|
token_owner = seaf_repo_manager_get_email_by_token (mgr, repo_id, token);
|
|
if (!token_owner || strcmp (user, token_owner) != 0) {
|
|
seaf_warning ("Requesting user is %s, token owner is %s, "
|
|
"refuse to delete token %.10s.\n", user, token_owner, token);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Permission denied");
|
|
return -1;
|
|
}
|
|
|
|
if (seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoUserToken "
|
|
"WHERE repo_id=? and token=?",
|
|
2, "string", repo_id, "string", token) < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
|
|
return -1;
|
|
}
|
|
|
|
if (seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoTokenPeerInfo WHERE token=?",
|
|
1, "string", token) < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
|
|
return -1;
|
|
}
|
|
|
|
GList *tokens = NULL;
|
|
tokens = g_list_append (tokens, g_strdup(token));
|
|
seaf_http_server_invalidate_tokens (seaf->http_server, tokens);
|
|
g_list_free_full (tokens, (GDestroyNotify)g_free);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
collect_repo_token (SeafDBRow *row, void *data)
|
|
{
|
|
GList **ret_list = data;
|
|
const char *repo_id, *repo_owner, *email, *token;
|
|
const char *peer_id, *peer_ip, *peer_name;
|
|
gint64 sync_time;
|
|
const char *client_ver;
|
|
|
|
repo_id = seaf_db_row_get_column_text (row, 0);
|
|
repo_owner = seaf_db_row_get_column_text (row, 1);
|
|
email = seaf_db_row_get_column_text (row, 2);
|
|
token = seaf_db_row_get_column_text (row, 3);
|
|
|
|
peer_id = seaf_db_row_get_column_text (row, 4);
|
|
peer_ip = seaf_db_row_get_column_text (row, 5);
|
|
peer_name = seaf_db_row_get_column_text (row, 6);
|
|
sync_time = seaf_db_row_get_column_int64 (row, 7);
|
|
client_ver = seaf_db_row_get_column_text (row, 8);
|
|
|
|
char *owner_l = g_ascii_strdown (repo_owner, -1);
|
|
char *email_l = g_ascii_strdown (email, -1);
|
|
|
|
SeafileRepoTokenInfo *repo_token_info;
|
|
repo_token_info = g_object_new (SEAFILE_TYPE_REPO_TOKEN_INFO,
|
|
"repo_id", repo_id,
|
|
"repo_owner", owner_l,
|
|
"email", email_l,
|
|
"token", token,
|
|
"peer_id", peer_id,
|
|
"peer_ip", peer_ip,
|
|
"peer_name", peer_name,
|
|
"sync_time", sync_time,
|
|
"client_ver", client_ver,
|
|
NULL);
|
|
|
|
*ret_list = g_list_prepend (*ret_list, repo_token_info);
|
|
|
|
g_free (owner_l);
|
|
g_free (email_l);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fill_in_token_info (GList *info_list)
|
|
{
|
|
GList *ptr;
|
|
SeafileRepoTokenInfo *info;
|
|
SeafRepo *repo;
|
|
char *repo_name;
|
|
|
|
for (ptr = info_list; ptr; ptr = ptr->next) {
|
|
info = ptr->data;
|
|
repo = seaf_repo_manager_get_repo (seaf->repo_mgr,
|
|
seafile_repo_token_info_get_repo_id(info));
|
|
if (repo)
|
|
repo_name = g_strdup(repo->name);
|
|
else
|
|
repo_name = g_strdup("Unknown");
|
|
seaf_repo_unref (repo);
|
|
|
|
g_object_set (info, "repo_name", repo_name, NULL);
|
|
g_free (repo_name);
|
|
}
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_list_repo_tokens (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
GError **error)
|
|
{
|
|
GList *ret_list = NULL;
|
|
char *sql;
|
|
gboolean db_err = FALSE;
|
|
|
|
if (!repo_exists_in_db (mgr->seaf->db, repo_id, &db_err)) {
|
|
if (db_err) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
sql = "SELECT u.repo_id, o.owner_id, u.email, u.token, "
|
|
"p.peer_id, p.peer_ip, p.peer_name, p.sync_time, p.client_ver "
|
|
"FROM RepoUserToken u LEFT JOIN RepoTokenPeerInfo p "
|
|
"ON u.token = p.token, RepoOwner o "
|
|
"WHERE u.repo_id = ? and o.repo_id = ? ";
|
|
|
|
int n_row = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_repo_token, &ret_list,
|
|
2, "string", repo_id,
|
|
"string", repo_id);
|
|
if (n_row < 0) {
|
|
seaf_warning ("DB error when get token info for repo %.10s.\n",
|
|
repo_id);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
|
|
}
|
|
|
|
fill_in_token_info (ret_list);
|
|
|
|
return g_list_reverse(ret_list);
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_list_repo_tokens_by_email (SeafRepoManager *mgr,
|
|
const char *email,
|
|
GError **error)
|
|
{
|
|
GList *ret_list = NULL;
|
|
char *sql;
|
|
|
|
sql = "SELECT u.repo_id, o.owner_id, u.email, u.token, "
|
|
"p.peer_id, p.peer_ip, p.peer_name, p.sync_time, p.client_ver "
|
|
"FROM RepoUserToken u LEFT JOIN RepoTokenPeerInfo p "
|
|
"ON u.token = p.token, RepoOwner o "
|
|
"WHERE u.email = ? and u.repo_id = o.repo_id";
|
|
|
|
int n_row = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_repo_token, &ret_list,
|
|
1, "string", email);
|
|
if (n_row < 0) {
|
|
seaf_warning ("DB error when get token info for email %s.\n",
|
|
email);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
|
|
}
|
|
|
|
fill_in_token_info (ret_list);
|
|
|
|
return g_list_reverse(ret_list);
|
|
}
|
|
|
|
static gboolean
|
|
collect_token_list (SeafDBRow *row, void *data)
|
|
{
|
|
GList **p_tokens = data;
|
|
const char *token;
|
|
|
|
token = seaf_db_row_get_column_text (row, 0);
|
|
*p_tokens = g_list_prepend (*p_tokens, g_strdup(token));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Delete all repo tokens for a given user on a given client
|
|
*/
|
|
|
|
int
|
|
seaf_repo_manager_delete_repo_tokens_by_peer_id (SeafRepoManager *mgr,
|
|
const char *email,
|
|
const char *peer_id,
|
|
GList **tokens,
|
|
GError **error)
|
|
{
|
|
int ret = 0;
|
|
const char *template;
|
|
GList *token_list = NULL;
|
|
GString *token_list_str = g_string_new ("");
|
|
GString *sql = g_string_new ("");
|
|
GList *ptr;
|
|
int rc = 0;
|
|
|
|
template = "SELECT u.token "
|
|
"FROM RepoUserToken u, RepoTokenPeerInfo p "
|
|
"WHERE u.token = p.token "
|
|
"AND u.email = ? AND p.peer_id = ?";
|
|
rc = seaf_db_statement_foreach_row (mgr->seaf->db, template,
|
|
collect_token_list, &token_list,
|
|
2, "string", email, "string", peer_id);
|
|
if (rc < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
|
|
goto out;
|
|
}
|
|
|
|
if (rc == 0)
|
|
goto out;
|
|
|
|
for (ptr = token_list; ptr; ptr = ptr->next) {
|
|
const char *token = (char *)ptr->data;
|
|
if (token_list_str->len == 0)
|
|
g_string_append_printf (token_list_str, "'%s'", token);
|
|
else
|
|
g_string_append_printf (token_list_str, ",'%s'", token);
|
|
}
|
|
|
|
/* Note that there is a size limit on sql query. In MySQL it's 1MB by default.
|
|
* Normally the token_list won't be that long.
|
|
*/
|
|
g_string_printf (sql, "DELETE FROM RepoUserToken WHERE token in (%s)",
|
|
token_list_str->str);
|
|
rc = seaf_db_statement_query (mgr->seaf->db, sql->str, 0);
|
|
if (rc < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
|
|
goto out;
|
|
}
|
|
|
|
g_string_printf (sql, "DELETE FROM RepoTokenPeerInfo WHERE token in (%s)",
|
|
token_list_str->str);
|
|
rc = seaf_db_statement_query (mgr->seaf->db, sql->str, 0);
|
|
if (rc < 0)
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
|
|
|
|
out:
|
|
g_string_free (token_list_str, TRUE);
|
|
g_string_free (sql, TRUE);
|
|
|
|
if (rc < 0) {
|
|
ret = -1;
|
|
g_list_free_full (token_list, (GDestroyNotify)g_free);
|
|
} else {
|
|
*tokens = token_list;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_delete_repo_tokens_by_email (SeafRepoManager *mgr,
|
|
const char *email,
|
|
GError **error)
|
|
{
|
|
int ret = 0;
|
|
const char *template;
|
|
GList *token_list = NULL;
|
|
GList *ptr;
|
|
GString *token_list_str = g_string_new ("");
|
|
GString *sql = g_string_new ("");
|
|
int rc;
|
|
|
|
template = "SELECT u.token "
|
|
"FROM RepoUserToken u, RepoTokenPeerInfo p "
|
|
"WHERE u.token = p.token "
|
|
"AND u.email = ?";
|
|
rc = seaf_db_statement_foreach_row (mgr->seaf->db, template,
|
|
collect_token_list, &token_list,
|
|
1, "string", email);
|
|
if (rc < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
|
|
goto out;
|
|
}
|
|
|
|
if (rc == 0)
|
|
goto out;
|
|
|
|
for (ptr = token_list; ptr; ptr = ptr->next) {
|
|
const char *token = (char *)ptr->data;
|
|
if (token_list_str->len == 0)
|
|
g_string_append_printf (token_list_str, "'%s'", token);
|
|
else
|
|
g_string_append_printf (token_list_str, ",'%s'", token);
|
|
}
|
|
|
|
/* Note that there is a size limit on sql query. In MySQL it's 1MB by default.
|
|
* Normally the token_list won't be that long.
|
|
*/
|
|
g_string_printf (sql, "DELETE FROM RepoUserToken WHERE token in (%s)",
|
|
token_list_str->str);
|
|
rc = seaf_db_statement_query (mgr->seaf->db, sql->str, 0);
|
|
if (rc < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
|
|
goto out;
|
|
}
|
|
|
|
g_string_printf (sql, "DELETE FROM RepoTokenPeerInfo WHERE token in (%s)",
|
|
token_list_str->str);
|
|
rc = seaf_db_statement_query (mgr->seaf->db, sql->str, 0);
|
|
if (rc < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
|
|
goto out;
|
|
}
|
|
|
|
seaf_http_server_invalidate_tokens (seaf->http_server, token_list);
|
|
|
|
out:
|
|
g_string_free (token_list_str, TRUE);
|
|
g_string_free (sql, TRUE);
|
|
g_list_free_full (token_list, (GDestroyNotify)g_free);
|
|
|
|
if (rc < 0) {
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
get_email_by_token_cb (SeafDBRow *row, void *data)
|
|
{
|
|
char **email_ptr = data;
|
|
|
|
const char *email = (const char *) seaf_db_row_get_column_text (row, 0);
|
|
*email_ptr = g_ascii_strdown (email, -1);
|
|
/* There should be only one result. */
|
|
return FALSE;
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_get_email_by_token (SeafRepoManager *manager,
|
|
const char *repo_id,
|
|
const char *token)
|
|
{
|
|
if (!repo_id || !token)
|
|
return NULL;
|
|
|
|
char *email = NULL;
|
|
char *sql;
|
|
|
|
sql = "SELECT email FROM RepoUserToken "
|
|
"WHERE repo_id = ? AND token = ?";
|
|
|
|
seaf_db_statement_foreach_row (seaf->db, sql,
|
|
get_email_by_token_cb, &email,
|
|
2, "string", repo_id, "string", token);
|
|
|
|
return email;
|
|
}
|
|
|
|
static gboolean
|
|
get_repo_size (SeafDBRow *row, void *vsize)
|
|
{
|
|
gint64 *psize = vsize;
|
|
|
|
*psize = seaf_db_row_get_column_int64 (row, 0);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gint64
|
|
seaf_repo_manager_get_repo_size (SeafRepoManager *mgr, const char *repo_id)
|
|
{
|
|
gint64 size = 0;
|
|
char *sql;
|
|
|
|
sql = "SELECT size FROM RepoSize WHERE repo_id=?";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
get_repo_size, &size,
|
|
1, "string", repo_id) < 0)
|
|
return -1;
|
|
|
|
return size;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_set_repo_history_limit (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
int days)
|
|
{
|
|
SeafVirtRepo *vinfo;
|
|
SeafDB *db = mgr->seaf->db;
|
|
|
|
vinfo = seaf_repo_manager_get_virtual_repo_info (mgr, repo_id);
|
|
if (vinfo) {
|
|
seaf_virtual_repo_info_free (vinfo);
|
|
return 0;
|
|
}
|
|
|
|
if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
|
|
gboolean exists, err;
|
|
int rc;
|
|
|
|
exists = seaf_db_statement_exists (db,
|
|
"SELECT repo_id FROM RepoHistoryLimit "
|
|
"WHERE repo_id=?",
|
|
&err, 1, "string", repo_id);
|
|
if (err)
|
|
return -1;
|
|
|
|
if (exists)
|
|
rc = seaf_db_statement_query (db,
|
|
"UPDATE RepoHistoryLimit SET days=? "
|
|
"WHERE repo_id=?",
|
|
2, "int", days, "string", repo_id);
|
|
else
|
|
rc = seaf_db_statement_query (db,
|
|
"INSERT INTO RepoHistoryLimit (repo_id, days) VALUES "
|
|
"(?, ?)",
|
|
2, "string", repo_id, "int", days);
|
|
return rc;
|
|
} else {
|
|
if (seaf_db_statement_query (db,
|
|
"REPLACE INTO RepoHistoryLimit (repo_id, days) VALUES (?, ?)",
|
|
2, "string", repo_id, "int", days) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
get_history_limit_cb (SeafDBRow *row, void *data)
|
|
{
|
|
int *limit = data;
|
|
|
|
*limit = seaf_db_row_get_column_int (row, 0);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_get_repo_history_limit (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
SeafVirtRepo *vinfo;
|
|
const char *r_repo_id = repo_id;
|
|
char *sql;
|
|
int per_repo_days = -1;
|
|
int ret;
|
|
|
|
vinfo = seaf_repo_manager_get_virtual_repo_info (mgr, repo_id);
|
|
if (vinfo)
|
|
r_repo_id = vinfo->origin_repo_id;
|
|
|
|
sql = "SELECT days FROM RepoHistoryLimit WHERE repo_id=?";
|
|
|
|
ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_history_limit_cb,
|
|
&per_repo_days, 1, "string", r_repo_id);
|
|
if (ret == 0) {
|
|
// limit not set, return global one
|
|
per_repo_days= seaf_cfg_manager_get_config_int (mgr->seaf->cfg_mgr,
|
|
"history", "keep_days");
|
|
}
|
|
|
|
// db error or limit set as negative, means keep full history, return -1
|
|
if (per_repo_days < 0)
|
|
per_repo_days = -1;
|
|
|
|
seaf_virtual_repo_info_free (vinfo);
|
|
|
|
return per_repo_days;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_set_repo_valid_since (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
gint64 timestamp)
|
|
{
|
|
SeafDB *db = mgr->seaf->db;
|
|
|
|
if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
|
|
gboolean exists, err;
|
|
int rc;
|
|
|
|
exists = seaf_db_statement_exists (db,
|
|
"SELECT repo_id FROM RepoValidSince WHERE "
|
|
"repo_id=?", &err, 1, "string", repo_id);
|
|
if (err)
|
|
return -1;
|
|
|
|
if (exists)
|
|
rc = seaf_db_statement_query (db,
|
|
"UPDATE RepoValidSince SET timestamp=?"
|
|
" WHERE repo_id=?",
|
|
2, "int64", timestamp, "string", repo_id);
|
|
else
|
|
rc = seaf_db_statement_query (db,
|
|
"INSERT INTO RepoValidSince (repo_id, timestamp) VALUES "
|
|
"(?, ?)", 2, "string", repo_id,
|
|
"int64", timestamp);
|
|
if (rc < 0)
|
|
return -1;
|
|
} else {
|
|
if (seaf_db_statement_query (db,
|
|
"REPLACE INTO RepoValidSince (repo_id, timestamp) VALUES (?, ?)",
|
|
2, "string", repo_id, "int64", timestamp) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
gint64
|
|
seaf_repo_manager_get_repo_valid_since (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
char *sql;
|
|
|
|
sql = "SELECT timestamp FROM RepoValidSince WHERE repo_id=?";
|
|
/* Also return -1 if doesn't exist. */
|
|
return seaf_db_statement_get_int64 (mgr->seaf->db, sql, 1, "string", repo_id);
|
|
}
|
|
|
|
gint64
|
|
seaf_repo_manager_get_repo_truncate_time (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
int days;
|
|
gint64 timestamp;
|
|
|
|
days = seaf_repo_manager_get_repo_history_limit (mgr, repo_id);
|
|
timestamp = seaf_repo_manager_get_repo_valid_since (mgr, repo_id);
|
|
|
|
gint64 now = (gint64)time(NULL);
|
|
if (days > 0)
|
|
return MAX (now - days * 24 * 3600, timestamp);
|
|
else if (days < 0)
|
|
return timestamp;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Permission related functions.
|
|
*/
|
|
|
|
/* Owner functions. */
|
|
|
|
int
|
|
seaf_repo_manager_set_repo_owner (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *email)
|
|
{
|
|
SeafDB *db = mgr->seaf->db;
|
|
char sql[256];
|
|
char *orig_owner = NULL;
|
|
int ret = 0;
|
|
|
|
orig_owner = seaf_repo_manager_get_repo_owner (mgr, repo_id);
|
|
if (g_strcmp0 (orig_owner, email) == 0)
|
|
goto out;
|
|
|
|
if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
|
|
gboolean err;
|
|
snprintf(sql, sizeof(sql),
|
|
"SELECT repo_id FROM RepoOwner WHERE repo_id=?");
|
|
if (seaf_db_statement_exists (db, sql, &err,
|
|
1, "string", repo_id))
|
|
snprintf(sql, sizeof(sql),
|
|
"UPDATE RepoOwner SET owner_id='%s' WHERE "
|
|
"repo_id='%s'", email, repo_id);
|
|
else
|
|
snprintf(sql, sizeof(sql),
|
|
"INSERT INTO RepoOwner (repo_id, owner_id) VALUES ('%s', '%s')",
|
|
repo_id, email);
|
|
if (err) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (seaf_db_query (db, sql) < 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
} else {
|
|
if (seaf_db_statement_query (db, "REPLACE INTO RepoOwner (repo_id, owner_id) VALUES (?, ?)",
|
|
2, "string", repo_id, "string", email) < 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* If the repo was newly created, no need to remove share and virtual repos. */
|
|
if (!orig_owner)
|
|
goto out;
|
|
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM SharedRepo WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoGroup WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
if (!seaf->cloud_mode) {
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM InnerPubRepo WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
}
|
|
|
|
/* Remove virtual repos when repo ownership changes. */
|
|
GList *vrepos, *ptr;
|
|
vrepos = seaf_repo_manager_get_virtual_repo_ids_by_origin (mgr, repo_id);
|
|
for (ptr = vrepos; ptr != NULL; ptr = ptr->next)
|
|
remove_virtual_repo_ondisk (mgr, (char *)ptr->data);
|
|
string_list_free (vrepos);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db, "DELETE FROM VirtualRepo "
|
|
"WHERE repo_id=? OR origin_repo=?",
|
|
2, "string", repo_id, "string", repo_id);
|
|
|
|
out:
|
|
g_free (orig_owner);
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
get_owner (SeafDBRow *row, void *data)
|
|
{
|
|
char **owner_id = data;
|
|
|
|
const char *owner = (const char *) seaf_db_row_get_column_text (row, 0);
|
|
*owner_id = g_ascii_strdown (owner, -1);
|
|
/* There should be only one result. */
|
|
return FALSE;
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_get_repo_owner (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
char *sql;
|
|
char *ret = NULL;
|
|
|
|
sql = "SELECT owner_id FROM RepoOwner WHERE repo_id=?";
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
get_owner, &ret,
|
|
1, "string", repo_id) < 0) {
|
|
seaf_warning ("Failed to get owner id for repo %s.\n", repo_id);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
collect_repo_id (SeafDBRow *row, void *data)
|
|
{
|
|
GList **p_ids = data;
|
|
const char *repo_id;
|
|
|
|
repo_id = seaf_db_row_get_column_text (row, 0);
|
|
*p_ids = g_list_prepend (*p_ids, g_strdup(repo_id));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_orphan_repo_list (SeafRepoManager *mgr)
|
|
{
|
|
GList *id_list = NULL, *ptr;
|
|
GList *ret = NULL;
|
|
char sql[256];
|
|
|
|
snprintf (sql, sizeof(sql), "SELECT Repo.repo_id FROM Repo LEFT JOIN "
|
|
"RepoOwner ON Repo.repo_id = RepoOwner.repo_id WHERE "
|
|
"RepoOwner.owner_id is NULL");
|
|
|
|
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
|
|
collect_repo_id, &id_list) < 0)
|
|
return NULL;
|
|
|
|
for (ptr = id_list; ptr; ptr = ptr->next) {
|
|
char *repo_id = ptr->data;
|
|
SeafRepo *repo = seaf_repo_manager_get_repo (mgr, repo_id);
|
|
if (repo != NULL)
|
|
ret = g_list_prepend (ret, repo);
|
|
}
|
|
|
|
string_list_free (id_list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
collect_repos_fill_size_commit (SeafDBRow *row, void *data)
|
|
{
|
|
GList **prepos = data;
|
|
SeafRepo *repo;
|
|
SeafBranch *head;
|
|
|
|
const char *repo_id = seaf_db_row_get_column_text (row, 0);
|
|
gint64 size = seaf_db_row_get_column_int64 (row, 1);
|
|
const char *commit_id = seaf_db_row_get_column_text (row, 2);
|
|
const char *repo_name = seaf_db_row_get_column_text (row, 3);
|
|
gint64 update_time = seaf_db_row_get_column_int64 (row, 4);
|
|
int version = seaf_db_row_get_column_int (row, 5);
|
|
gboolean is_encrypted = seaf_db_row_get_column_int (row, 6) ? TRUE : FALSE;
|
|
const char *last_modifier = seaf_db_row_get_column_text (row, 7);
|
|
int status = seaf_db_row_get_column_int (row, 8);
|
|
|
|
repo = seaf_repo_new (repo_id, NULL, NULL);
|
|
if (!repo)
|
|
return TRUE;
|
|
|
|
if (!commit_id) {
|
|
repo->is_corrupted = TRUE;
|
|
goto out;
|
|
}
|
|
|
|
repo->size = size;
|
|
if (seaf_db_row_get_column_count (row) == 10) {
|
|
gint64 file_count = seaf_db_row_get_column_int64 (row, 9);
|
|
repo->file_count = file_count;
|
|
}
|
|
head = seaf_branch_new ("master", repo_id, commit_id);
|
|
repo->head = head;
|
|
if (repo_name) {
|
|
repo->name = g_strdup (repo_name);
|
|
repo->last_modify = update_time;
|
|
repo->version = version;
|
|
repo->encrypted = is_encrypted;
|
|
repo->last_modifier = g_strdup (last_modifier);
|
|
repo->status = status;
|
|
}
|
|
|
|
out:
|
|
*prepos = g_list_prepend (*prepos, repo);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_repos_by_owner (SeafRepoManager *mgr,
|
|
const char *email,
|
|
int ret_corrupted,
|
|
int start,
|
|
int limit,
|
|
gboolean *db_err)
|
|
{
|
|
GList *repo_list = NULL, *ptr;
|
|
GList *ret = NULL;
|
|
char *sql;
|
|
SeafRepo *repo = NULL;
|
|
int db_type = seaf_db_type(mgr->seaf->db);
|
|
|
|
if (start == -1 && limit == -1) {
|
|
if (db_type != SEAF_DB_TYPE_PGSQL)
|
|
sql = "SELECT o.repo_id, s.size, b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status FROM "
|
|
"RepoOwner o LEFT JOIN RepoSize s ON o.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON o.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoInfo i ON o.repo_id = i.repo_id "
|
|
"WHERE owner_id=? AND "
|
|
"o.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
|
|
"ORDER BY i.update_time DESC, o.repo_id";
|
|
else
|
|
sql = "SELECT o.repo_id, s.\"size\", b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status FROM "
|
|
"RepoOwner o LEFT JOIN RepoSize s ON o.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON o.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoInfo i ON o.repo_id = i.repo_id "
|
|
"WHERE owner_id=? AND "
|
|
"o.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
|
|
"ORDER BY i.update_time DESC, o.repo_id";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_repos_fill_size_commit, &repo_list,
|
|
1, "string", email) < 0) {
|
|
if (db_err)
|
|
*db_err = TRUE;
|
|
return NULL;
|
|
}
|
|
} else {
|
|
if (db_type != SEAF_DB_TYPE_PGSQL)
|
|
sql = "SELECT o.repo_id, s.size, b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status FROM "
|
|
"RepoOwner o LEFT JOIN RepoSize s ON o.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON o.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoInfo i ON o.repo_id = i.repo_id "
|
|
"WHERE owner_id=? AND "
|
|
"o.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
|
|
"ORDER BY i.update_time DESC, o.repo_id "
|
|
"LIMIT ? OFFSET ?";
|
|
else
|
|
sql = "SELECT o.repo_id, s.\"size\", b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status FROM "
|
|
"RepoOwner o LEFT JOIN RepoSize s ON o.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON o.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoInfo i ON o.repo_id = i.repo_id "
|
|
"WHERE owner_id=? AND "
|
|
"o.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
|
|
"ORDER BY i.update_time DESC, o.repo_id "
|
|
"LIMIT ? OFFSET ?";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_repos_fill_size_commit,
|
|
&repo_list,
|
|
3, "string", email,
|
|
"int", limit,
|
|
"int", start) < 0) {
|
|
if (db_err)
|
|
*db_err = TRUE;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
for (ptr = repo_list; ptr; ptr = ptr->next) {
|
|
repo = ptr->data;
|
|
if (ret_corrupted) {
|
|
if (!repo->is_corrupted && (!repo->name || !repo->last_modifier)) {
|
|
load_mini_repo (mgr, repo);
|
|
if (!repo->is_corrupted)
|
|
set_repo_commit_to_db (repo->id, repo->name, repo->last_modify,
|
|
repo->version, (repo->encrypted ? 1 : 0),
|
|
repo->last_modifier);
|
|
}
|
|
} else {
|
|
if (repo->is_corrupted) {
|
|
seaf_repo_unref (repo);
|
|
continue;
|
|
}
|
|
if (!repo->name || !repo->last_modifier) {
|
|
load_mini_repo (mgr, repo);
|
|
if (!repo->is_corrupted)
|
|
set_repo_commit_to_db (repo->id, repo->name, repo->last_modify,
|
|
repo->version, (repo->encrypted ? 1 : 0),
|
|
repo->last_modifier);
|
|
}
|
|
if (repo->is_corrupted) {
|
|
seaf_repo_unref (repo);
|
|
continue;
|
|
}
|
|
}
|
|
if (repo != NULL)
|
|
ret = g_list_prepend (ret, repo);
|
|
}
|
|
g_list_free (repo_list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_search_repos_by_name (SeafRepoManager *mgr, const char *name)
|
|
{
|
|
GList *repo_list = NULL;
|
|
char *sql = NULL;
|
|
|
|
char *db_patt = g_strdup_printf ("%%%s%%", name);
|
|
|
|
switch (seaf_db_type(seaf->db)) {
|
|
case SEAF_DB_TYPE_MYSQL:
|
|
sql = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status FROM "
|
|
"RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON i.repo_id = b.repo_id "
|
|
"WHERE i.name COLLATE UTF8_GENERAL_CI LIKE ? AND "
|
|
"i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
|
|
"i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
|
|
"ORDER BY i.update_time DESC, i.repo_id";
|
|
break;
|
|
case SEAF_DB_TYPE_PGSQL:
|
|
sql = "SELECT i.repo_id, s.\"size\", b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status FROM "
|
|
"RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON i.repo_id = b.repo_id "
|
|
"WHERE i.name ILIKE ? AND "
|
|
"i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
|
|
"i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
|
|
"ORDER BY i.update_time DESC, i.repo_id";
|
|
break;
|
|
case SEAF_DB_TYPE_SQLITE:
|
|
sql = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status FROM "
|
|
"RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON i.repo_id = b.repo_id "
|
|
"WHERE i.name LIKE ? COLLATE NOCASE AND "
|
|
"i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
|
|
"i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
|
|
"ORDER BY i.update_time DESC, i.repo_id";
|
|
break;
|
|
default:
|
|
g_free (db_patt);
|
|
return NULL;
|
|
}
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_repos_fill_size_commit, &repo_list,
|
|
1, "string", db_patt) < 0) {
|
|
g_free (db_patt);
|
|
return NULL;
|
|
}
|
|
|
|
g_free (db_patt);
|
|
return repo_list;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_repo_id_list (SeafRepoManager *mgr)
|
|
{
|
|
GList *ret = NULL;
|
|
char sql[256];
|
|
|
|
snprintf (sql, 256, "SELECT repo_id FROM Repo");
|
|
|
|
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
|
|
collect_repo_id, &ret) < 0)
|
|
return NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_repo_list (SeafRepoManager *mgr, int start, int limit, const char *order_by)
|
|
{
|
|
GList *ret = NULL;
|
|
char *sql_base = NULL;
|
|
char sql[512];
|
|
int rc;
|
|
|
|
if (start == -1 && limit == -1) {
|
|
switch (seaf_db_type(mgr->seaf->db)) {
|
|
case SEAF_DB_TYPE_MYSQL:
|
|
sql_base = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status, f.file_count FROM "
|
|
"RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON i.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoFileCount f ON i.repo_id = f.repo_id "
|
|
"WHERE i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
|
|
"i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) ";
|
|
if (g_strcmp0 (order_by, "size") == 0)
|
|
snprintf (sql, sizeof(sql), "%sORDER BY s.size DESC, i.repo_id", sql_base);
|
|
else if (g_strcmp0 (order_by, "file_count") == 0)
|
|
snprintf (sql, sizeof(sql), "%sORDER BY f.file_count DESC, i.repo_id", sql_base);
|
|
else
|
|
snprintf (sql, sizeof(sql), "%sORDER BY i.update_time DESC, i.repo_id", sql_base);
|
|
break;
|
|
case SEAF_DB_TYPE_SQLITE:
|
|
sql_base= "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status, f.file_count FROM "
|
|
"RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON i.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoFileCount f ON i.repo_id = f.repo_id "
|
|
"WHERE i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
|
|
"i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) ";
|
|
if (g_strcmp0 (order_by, "size") == 0)
|
|
snprintf (sql, sizeof(sql), "%sORDER BY s.size DESC, i.repo_id", sql_base);
|
|
else if (g_strcmp0 (order_by, "file_count") == 0)
|
|
snprintf (sql, sizeof(sql), "%sORDER BY f.file_count DESC, i.repo_id", sql_base);
|
|
else
|
|
snprintf (sql, sizeof(sql), "%sORDER BY i.update_time DESC, i.repo_id", sql_base);
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
rc = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_repos_fill_size_commit, &ret,
|
|
0);
|
|
} else {
|
|
switch (seaf_db_type(mgr->seaf->db)) {
|
|
case SEAF_DB_TYPE_MYSQL:
|
|
sql_base = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status, f.file_count FROM "
|
|
"RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON i.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoFileCount f ON i.repo_id = f.repo_id "
|
|
"WHERE i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
|
|
"i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) ";
|
|
if (g_strcmp0 (order_by, "size") == 0)
|
|
snprintf (sql, sizeof(sql), "%sORDER BY s.size DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
|
|
else if (g_strcmp0 (order_by, "file_count") == 0)
|
|
snprintf (sql, sizeof(sql), "%sORDER BY f.file_count DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
|
|
else
|
|
snprintf (sql, sizeof(sql), "%sORDER BY i.update_time DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
|
|
break;
|
|
case SEAF_DB_TYPE_SQLITE:
|
|
sql_base = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
|
|
"i.version, i.is_encrypted, i.last_modifier, i.status, f.file_count FROM "
|
|
"RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
|
|
"LEFT JOIN Branch b ON i.repo_id = b.repo_id "
|
|
"LEFT JOIN RepoFileCount f ON i.repo_id = f.repo_id "
|
|
"WHERE i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
|
|
"i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) ";
|
|
if (g_strcmp0 (order_by, "size") == 0)
|
|
snprintf (sql, sizeof(sql), "%sORDER BY s.size DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
|
|
else if (g_strcmp0 (order_by, "file_count") == 0)
|
|
snprintf (sql, sizeof(sql), "%sORDER BY f.file_count DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
|
|
else
|
|
snprintf (sql, sizeof(sql), "%sORDER BY i.update_time DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
rc = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_repos_fill_size_commit, &ret,
|
|
2, "int", limit, "int", start);
|
|
}
|
|
|
|
if (rc < 0)
|
|
return NULL;
|
|
|
|
return g_list_reverse (ret);
|
|
}
|
|
|
|
gint64
|
|
seaf_repo_manager_count_repos (SeafRepoManager *mgr, GError **error)
|
|
{
|
|
gint64 num = seaf_db_get_int64 (mgr->seaf->db,
|
|
"SELECT COUNT(repo_id) FROM Repo");
|
|
if (num < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to count repos from db");
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_repo_ids_by_owner (SeafRepoManager *mgr,
|
|
const char *email)
|
|
{
|
|
GList *ret = NULL;
|
|
char *sql;
|
|
|
|
sql = "SELECT repo_id FROM RepoOwner WHERE owner_id=?";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_repo_id, &ret,
|
|
1, "string", email) < 0) {
|
|
string_list_free (ret);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
collect_trash_repo (SeafDBRow *row, void *data)
|
|
{
|
|
GList **trash_repos = data;
|
|
const char *repo_id;
|
|
const char *repo_name;
|
|
const char *head_id;
|
|
const char *owner_id;
|
|
gint64 size;
|
|
gint64 del_time;
|
|
|
|
repo_id = seaf_db_row_get_column_text (row, 0);
|
|
repo_name = seaf_db_row_get_column_text (row, 1);
|
|
head_id = seaf_db_row_get_column_text (row, 2);
|
|
owner_id = seaf_db_row_get_column_text (row, 3);
|
|
size = seaf_db_row_get_column_int64 (row, 4);
|
|
del_time = seaf_db_row_get_column_int64 (row, 5);
|
|
|
|
|
|
if (!repo_id || !repo_name || !head_id || !owner_id)
|
|
return TRUE;
|
|
|
|
SeafileTrashRepo *trash_repo = g_object_new (SEAFILE_TYPE_TRASH_REPO,
|
|
"repo_id", repo_id,
|
|
"repo_name", repo_name,
|
|
"head_id", head_id,
|
|
"owner_id", owner_id,
|
|
"size", size,
|
|
"del_time", del_time,
|
|
NULL);
|
|
if (!trash_repo)
|
|
return FALSE;
|
|
|
|
SeafCommit *commit = seaf_commit_manager_get_commit_compatible (seaf->commit_mgr,
|
|
repo_id, head_id);
|
|
if (!commit) {
|
|
seaf_warning ("Commit %s not found in repo %s\n", head_id, repo_id);
|
|
g_object_unref (trash_repo);
|
|
return TRUE;
|
|
}
|
|
g_object_set (trash_repo, "encrypted", commit->encrypted, NULL);
|
|
seaf_commit_unref (commit);
|
|
|
|
*trash_repos = g_list_prepend (*trash_repos, trash_repo);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_trash_repo_list (SeafRepoManager *mgr,
|
|
int start,
|
|
int limit,
|
|
GError **error)
|
|
{
|
|
GList *trash_repos = NULL;
|
|
int rc;
|
|
|
|
if (start == -1 && limit == -1)
|
|
rc = seaf_db_statement_foreach_row (mgr->seaf->db,
|
|
"SELECT repo_id, repo_name, head_id, owner_id, "
|
|
"size, del_time FROM RepoTrash ORDER BY del_time DESC",
|
|
collect_trash_repo, &trash_repos,
|
|
0);
|
|
else
|
|
rc = seaf_db_statement_foreach_row (mgr->seaf->db,
|
|
"SELECT repo_id, repo_name, head_id, owner_id, "
|
|
"size, del_time FROM RepoTrash "
|
|
"ORDER BY del_time DESC LIMIT ? OFFSET ?",
|
|
collect_trash_repo, &trash_repos,
|
|
2, "int", limit, "int", start);
|
|
|
|
if (rc < 0) {
|
|
while (trash_repos) {
|
|
g_object_unref (trash_repos->data);
|
|
trash_repos = g_list_delete_link (trash_repos, trash_repos);
|
|
}
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get trashed repo from db.");
|
|
return NULL;
|
|
}
|
|
|
|
return g_list_reverse (trash_repos);
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_trash_repos_by_owner (SeafRepoManager *mgr,
|
|
const char *owner,
|
|
GError **error)
|
|
{
|
|
GList *trash_repos = NULL;
|
|
int rc;
|
|
|
|
rc = seaf_db_statement_foreach_row (mgr->seaf->db,
|
|
"SELECT repo_id, repo_name, head_id, owner_id, "
|
|
"size, del_time FROM RepoTrash WHERE owner_id = ?",
|
|
collect_trash_repo, &trash_repos,
|
|
1, "string", owner);
|
|
|
|
if (rc < 0) {
|
|
while (trash_repos) {
|
|
g_object_unref (trash_repos->data);
|
|
trash_repos = g_list_delete_link (trash_repos, trash_repos);
|
|
}
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get trashed repo from db.");
|
|
return NULL;
|
|
}
|
|
|
|
return trash_repos;
|
|
}
|
|
|
|
SeafileTrashRepo *
|
|
seaf_repo_manager_get_repo_from_trash (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
SeafileTrashRepo *ret = NULL;
|
|
GList *trash_repos = NULL;
|
|
char *sql;
|
|
int rc;
|
|
|
|
sql = "SELECT repo_id, repo_name, head_id, owner_id, size, del_time FROM RepoTrash "
|
|
"WHERE repo_id = ?";
|
|
rc = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_trash_repo, &trash_repos,
|
|
1, "string", repo_id);
|
|
if (rc < 0)
|
|
return NULL;
|
|
|
|
/* There should be only one results, since repo_id is a PK. */
|
|
if (trash_repos)
|
|
ret = trash_repos->data;
|
|
|
|
g_list_free (trash_repos);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_del_repo_from_trash (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
GError **error)
|
|
{
|
|
/* As long as the repo is successfully moved into GarbageRepo table,
|
|
* we consider this operation successful.
|
|
*/
|
|
if (add_deleted_repo_record (mgr, repo_id) < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"DB error: Add deleted record");
|
|
return -1;
|
|
}
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoFileCount WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoTrash WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoInfo WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_empty_repo_trash (SeafRepoManager *mgr, GError **error)
|
|
{
|
|
GList *trash_repos = NULL, *ptr;
|
|
SeafileTrashRepo *repo;
|
|
|
|
trash_repos = seaf_repo_manager_get_trash_repo_list (mgr, -1, -1, error);
|
|
if (*error)
|
|
return -1;
|
|
|
|
for (ptr = trash_repos; ptr; ptr = ptr->next) {
|
|
repo = ptr->data;
|
|
seaf_repo_manager_del_repo_from_trash (mgr,
|
|
seafile_trash_repo_get_repo_id(repo),
|
|
NULL);
|
|
g_object_unref (repo);
|
|
}
|
|
|
|
g_list_free (trash_repos);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_empty_repo_trash_by_owner (SeafRepoManager *mgr,
|
|
const char *owner,
|
|
GError **error)
|
|
{
|
|
GList *trash_repos = NULL, *ptr;
|
|
SeafileTrashRepo *repo;
|
|
|
|
trash_repos = seaf_repo_manager_get_trash_repos_by_owner (mgr, owner, error);
|
|
if (*error)
|
|
return -1;
|
|
|
|
for (ptr = trash_repos; ptr; ptr = ptr->next) {
|
|
repo = ptr->data;
|
|
seaf_repo_manager_del_repo_from_trash (mgr,
|
|
seafile_trash_repo_get_repo_id(repo),
|
|
NULL);
|
|
g_object_unref (repo);
|
|
}
|
|
|
|
g_list_free (trash_repos);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_restore_repo_from_trash (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
GError **error)
|
|
{
|
|
SeafileTrashRepo *repo = NULL;
|
|
int ret = 0;
|
|
gboolean exists = FALSE;
|
|
gboolean db_err;
|
|
|
|
repo = seaf_repo_manager_get_repo_from_trash (mgr, repo_id);
|
|
if (!repo) {
|
|
seaf_warning ("Repo %.8s not found in trash.\n", repo_id);
|
|
return -1;
|
|
}
|
|
|
|
SeafDBTrans *trans = seaf_db_begin_transaction (mgr->seaf->db);
|
|
|
|
exists = seaf_db_trans_check_for_existence (trans,
|
|
"SELECT 1 FROM Repo WHERE repo_id=?",
|
|
&db_err, 1, "string", repo_id);
|
|
|
|
if (!exists) {
|
|
ret = seaf_db_trans_query (trans,
|
|
"INSERT INTO Repo(repo_id) VALUES (?)",
|
|
1, "string", repo_id) < 0;
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"DB error: Insert Repo.");
|
|
seaf_db_rollback (trans);
|
|
seaf_db_trans_close (trans);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
exists = seaf_db_trans_check_for_existence (trans,
|
|
"SELECT 1 FROM RepoOwner WHERE repo_id=?",
|
|
&db_err, 1, "string", repo_id);
|
|
|
|
if (!exists) {
|
|
ret = seaf_db_trans_query (trans,
|
|
"INSERT INTO RepoOwner (repo_id, owner_id) VALUES (?, ?)",
|
|
2, "string", repo_id,
|
|
"string", seafile_trash_repo_get_owner_id(repo));
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"DB error: Insert Repo Owner.");
|
|
seaf_db_rollback (trans);
|
|
seaf_db_trans_close (trans);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
exists = seaf_db_trans_check_for_existence (trans,
|
|
"SELECT 1 FROM Branch WHERE repo_id=?",
|
|
&db_err, 1, "string", repo_id);
|
|
if (!exists) {
|
|
ret = seaf_db_trans_query (trans,
|
|
"INSERT INTO Branch (name, repo_id, commit_id) VALUES ('master', ?, ?)",
|
|
2, "string", repo_id,
|
|
"string", seafile_trash_repo_get_head_id(repo));
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"DB error: Insert Branch.");
|
|
seaf_db_rollback (trans);
|
|
seaf_db_trans_close (trans);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
exists = seaf_db_trans_check_for_existence (trans,
|
|
"SELECT 1 FROM RepoHead WHERE repo_id=?",
|
|
&db_err, 1, "string", repo_id);
|
|
if (!exists) {
|
|
ret = seaf_db_trans_query (trans,
|
|
"INSERT INTO RepoHead (repo_id, branch_name) VALUES (?, 'master')",
|
|
1, "string", repo_id);
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"DB error: Set RepoHead.");
|
|
seaf_db_rollback (trans);
|
|
seaf_db_trans_close (trans);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
// Restore repo size
|
|
exists = seaf_db_trans_check_for_existence (trans,
|
|
"SELECT 1 FROM RepoSize WHERE repo_id=?",
|
|
&db_err, 1, "string", repo_id);
|
|
|
|
if (!exists) {
|
|
ret = seaf_db_trans_query (trans,
|
|
"INSERT INTO RepoSize (repo_id, size, head_id) VALUES (?, ?, ?)",
|
|
3, "string", repo_id,
|
|
"int64", seafile_trash_repo_get_size (repo),
|
|
"string", seafile_trash_repo_get_head_id (repo));
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"DB error: Insert Repo Size.");
|
|
seaf_db_rollback (trans);
|
|
seaf_db_trans_close (trans);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ret = seaf_db_trans_query (trans,
|
|
"DELETE FROM RepoTrash WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"DB error: delete from RepoTrash.");
|
|
seaf_db_rollback (trans);
|
|
seaf_db_trans_close (trans);
|
|
goto out;
|
|
}
|
|
|
|
if (seaf_db_commit (trans) < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"DB error: Failed to commit.");
|
|
seaf_db_rollback (trans);
|
|
ret = -1;
|
|
}
|
|
|
|
seaf_db_trans_close (trans);
|
|
|
|
out:
|
|
g_object_unref (repo);
|
|
return ret;
|
|
}
|
|
|
|
/* Web access permission. */
|
|
|
|
int
|
|
seaf_repo_manager_set_access_property (SeafRepoManager *mgr, const char *repo_id,
|
|
const char *ap)
|
|
{
|
|
int rc;
|
|
|
|
if (seaf_repo_manager_query_access_property (mgr, repo_id) == NULL) {
|
|
rc = seaf_db_statement_query (mgr->seaf->db,
|
|
"INSERT INTO WebAP (repo_id, access_property) VALUES (?, ?)",
|
|
2, "string", repo_id, "string", ap);
|
|
} else {
|
|
rc = seaf_db_statement_query (mgr->seaf->db,
|
|
"UPDATE WebAP SET access_property=? "
|
|
"WHERE repo_id=?",
|
|
2, "string", ap, "string", repo_id);
|
|
}
|
|
|
|
if (rc < 0) {
|
|
seaf_warning ("DB error when set access property for repo %s, %s.\n", repo_id, ap);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
get_ap (SeafDBRow *row, void *data)
|
|
{
|
|
char **ap = data;
|
|
|
|
*ap = g_strdup (seaf_db_row_get_column_text (row, 0));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_query_access_property (SeafRepoManager *mgr, const char *repo_id)
|
|
{
|
|
char *sql;
|
|
char *ret = NULL;
|
|
|
|
sql = "SELECT access_property FROM WebAP WHERE repo_id=?";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_ap, &ret,
|
|
1, "string", repo_id) < 0) {
|
|
seaf_warning ("DB error when get access property for repo %s.\n", repo_id);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Group repos. */
|
|
|
|
int
|
|
seaf_repo_manager_add_group_repo (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
int group_id,
|
|
const char *owner,
|
|
const char *permission,
|
|
GError **error)
|
|
{
|
|
if (seaf_db_statement_query (mgr->seaf->db,
|
|
"INSERT INTO RepoGroup (repo_id, group_id, user_name, permission) VALUES (?, ?, ?, ?)",
|
|
4, "string", repo_id, "int", group_id,
|
|
"string", owner, "string", permission) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_del_group_repo (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
int group_id,
|
|
GError **error)
|
|
{
|
|
return seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM RepoGroup WHERE group_id=? "
|
|
"AND repo_id=?",
|
|
2, "int", group_id, "string", repo_id);
|
|
}
|
|
|
|
static gboolean
|
|
get_group_ids_cb (SeafDBRow *row, void *data)
|
|
{
|
|
GList **plist = data;
|
|
|
|
int group_id = seaf_db_row_get_column_int (row, 0);
|
|
|
|
*plist = g_list_prepend (*plist, (gpointer)(long)group_id);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_groups_by_repo (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
GError **error)
|
|
{
|
|
char *sql;
|
|
GList *group_ids = NULL;
|
|
|
|
sql = "SELECT group_id FROM RepoGroup WHERE repo_id = ?";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_ids_cb,
|
|
&group_ids, 1, "string", repo_id) < 0) {
|
|
g_list_free (group_ids);
|
|
return NULL;
|
|
}
|
|
|
|
return g_list_reverse (group_ids);
|
|
}
|
|
|
|
static gboolean
|
|
get_group_perms_cb (SeafDBRow *row, void *data)
|
|
{
|
|
GList **plist = data;
|
|
GroupPerm *perm = g_new0 (GroupPerm, 1);
|
|
|
|
perm->group_id = seaf_db_row_get_column_int (row, 0);
|
|
const char *permission = seaf_db_row_get_column_text(row, 1);
|
|
g_strlcpy (perm->permission, permission, sizeof(perm->permission));
|
|
|
|
*plist = g_list_prepend (*plist, perm);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_group_perm_by_repo (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
GError **error)
|
|
{
|
|
char *sql;
|
|
GList *group_perms = NULL, *p;
|
|
|
|
sql = "SELECT group_id, permission FROM RepoGroup WHERE repo_id = ?";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_perms_cb,
|
|
&group_perms, 1, "string", repo_id) < 0) {
|
|
for (p = group_perms; p != NULL; p = p->next)
|
|
g_free (p->data);
|
|
g_list_free (group_perms);
|
|
return NULL;
|
|
}
|
|
|
|
return g_list_reverse (group_perms);
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_set_group_repo_perm (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
int group_id,
|
|
const char *permission,
|
|
GError **error)
|
|
{
|
|
return seaf_db_statement_query (mgr->seaf->db,
|
|
"UPDATE RepoGroup SET permission=? WHERE "
|
|
"repo_id=? AND group_id=?",
|
|
3, "string", permission, "string", repo_id,
|
|
"int", group_id);
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_set_subdir_group_perm_by_path (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *username,
|
|
int group_id,
|
|
const char *permission,
|
|
const char *path)
|
|
{
|
|
return seaf_db_statement_query (mgr->seaf->db,
|
|
"UPDATE RepoGroup SET permission=? WHERE repo_id IN "
|
|
"(SELECT repo_id FROM VirtualRepo WHERE origin_repo=? AND path=?) "
|
|
"AND group_id=? AND user_name=?",
|
|
5, "string", permission,
|
|
"string", repo_id,
|
|
"string", path,
|
|
"int", group_id,
|
|
"string", username);
|
|
}
|
|
static gboolean
|
|
get_group_repoids_cb (SeafDBRow *row, void *data)
|
|
{
|
|
GList **p_list = data;
|
|
|
|
char *repo_id = g_strdup ((const char *)seaf_db_row_get_column_text (row, 0));
|
|
|
|
*p_list = g_list_prepend (*p_list, repo_id);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_group_repoids (SeafRepoManager *mgr,
|
|
int group_id,
|
|
GError **error)
|
|
{
|
|
char *sql;
|
|
GList *repo_ids = NULL;
|
|
|
|
sql = "SELECT repo_id FROM RepoGroup WHERE group_id = ?";
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_repoids_cb,
|
|
&repo_ids, 1, "int", group_id) < 0)
|
|
return NULL;
|
|
|
|
return g_list_reverse (repo_ids);
|
|
}
|
|
|
|
static gboolean
|
|
get_group_repos_cb (SeafDBRow *row, void *data)
|
|
{
|
|
GList **p_list = data;
|
|
SeafileRepo *srepo = NULL;
|
|
|
|
const char *repo_id = seaf_db_row_get_column_text (row, 0);
|
|
const char *vrepo_id = seaf_db_row_get_column_text (row, 1);
|
|
int group_id = seaf_db_row_get_column_int (row, 2);
|
|
const char *user_name = seaf_db_row_get_column_text (row, 3);
|
|
const char *permission = seaf_db_row_get_column_text (row, 4);
|
|
const char *commit_id = seaf_db_row_get_column_text (row, 5);
|
|
gint64 size = seaf_db_row_get_column_int64 (row, 6);
|
|
const char *repo_name = seaf_db_row_get_column_text (row, 9);
|
|
gint64 update_time = seaf_db_row_get_column_int64 (row, 10);
|
|
int version = seaf_db_row_get_column_int (row, 11);
|
|
gboolean is_encrypted = seaf_db_row_get_column_int (row, 12) ? TRUE : FALSE;
|
|
const char *last_modifier = seaf_db_row_get_column_text (row, 13);
|
|
int status = seaf_db_row_get_column_int (row, 14);
|
|
|
|
char *user_name_l = g_ascii_strdown (user_name, -1);
|
|
|
|
srepo = g_object_new (SEAFILE_TYPE_REPO,
|
|
"share_type", "group",
|
|
"repo_id", repo_id,
|
|
"id", repo_id,
|
|
"head_cmmt_id", commit_id,
|
|
"group_id", group_id,
|
|
"user", user_name_l,
|
|
"permission", permission,
|
|
"is_virtual", (vrepo_id != NULL),
|
|
"size", size,
|
|
"status", status,
|
|
NULL);
|
|
g_free (user_name_l);
|
|
|
|
if (srepo != NULL) {
|
|
if (vrepo_id) {
|
|
const char *origin_repo_id = seaf_db_row_get_column_text (row, 7);
|
|
const char *origin_path = seaf_db_row_get_column_text (row, 8);
|
|
const char *origin_repo_name = seaf_db_row_get_column_text (row, 15);
|
|
g_object_set (srepo, "store_id", origin_repo_id,
|
|
"origin_repo_id", origin_repo_id,
|
|
"origin_repo_name", origin_repo_name,
|
|
"origin_path", origin_path, NULL);
|
|
} else {
|
|
g_object_set (srepo, "store_id", repo_id, NULL);
|
|
}
|
|
if (repo_name) {
|
|
g_object_set (srepo, "name", repo_name,
|
|
"repo_name", repo_name,
|
|
"last_modify", update_time,
|
|
"last_modified", update_time,
|
|
"version", version,
|
|
"encrypted", is_encrypted,
|
|
"last_modifier", last_modifier, NULL);
|
|
}
|
|
*p_list = g_list_prepend (*p_list, srepo);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
seaf_fill_repo_obj_from_commit (GList **repos)
|
|
{
|
|
SeafileRepo *repo;
|
|
SeafCommit *commit;
|
|
char *repo_id;
|
|
char *commit_id;
|
|
char *repo_name = NULL;
|
|
char *last_modifier = NULL;
|
|
GList *p = *repos;
|
|
GList *next;
|
|
|
|
while (p) {
|
|
repo = p->data;
|
|
g_object_get (repo, "name", &repo_name, NULL);
|
|
g_object_get (repo, "last_modifier", &last_modifier, NULL);
|
|
if (!repo_name || !last_modifier) {
|
|
g_object_get (repo, "repo_id", &repo_id, "head_cmmt_id", &commit_id, NULL);
|
|
commit = seaf_commit_manager_get_commit_compatible (seaf->commit_mgr,
|
|
repo_id, commit_id);
|
|
if (!commit) {
|
|
seaf_warning ("Commit %s not found in repo %s\n", commit_id, repo_id);
|
|
g_object_unref (repo);
|
|
next = p->next;
|
|
*repos = g_list_delete_link (*repos, p);
|
|
p = next;
|
|
if (repo_name)
|
|
g_free (repo_name);
|
|
if (last_modifier)
|
|
g_free (last_modifier);
|
|
} else {
|
|
g_object_set (repo, "name", commit->repo_name,
|
|
"repo_name", commit->repo_name,
|
|
"last_modify", commit->ctime,
|
|
"last_modified", commit->ctime,
|
|
"version", commit->version,
|
|
"encrypted", commit->encrypted,
|
|
"last_modifier", commit->creator_name,
|
|
NULL);
|
|
|
|
/* Set to database */
|
|
set_repo_commit_to_db (repo_id, commit->repo_name, commit->ctime, commit->version,
|
|
commit->encrypted, commit->creator_name);
|
|
seaf_commit_unref (commit);
|
|
}
|
|
g_free (repo_id);
|
|
g_free (commit_id);
|
|
}
|
|
if (repo_name)
|
|
g_free (repo_name);
|
|
if (last_modifier)
|
|
g_free (last_modifier);
|
|
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_repos_by_group (SeafRepoManager *mgr,
|
|
int group_id,
|
|
GError **error)
|
|
{
|
|
char *sql;
|
|
GList *repos = NULL;
|
|
GList *p;
|
|
|
|
sql = "SELECT RepoGroup.repo_id, v.repo_id, "
|
|
"group_id, user_name, permission, commit_id, s.size, "
|
|
"v.origin_repo, v.path, i.name, "
|
|
"i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
|
|
"(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo) "
|
|
"FROM RepoGroup LEFT JOIN VirtualRepo v ON "
|
|
"RepoGroup.repo_id = v.repo_id "
|
|
"LEFT JOIN RepoInfo i ON RepoGroup.repo_id = i.repo_id "
|
|
"LEFT JOIN RepoSize s ON RepoGroup.repo_id = s.repo_id, "
|
|
"Branch WHERE group_id = ? AND "
|
|
"RepoGroup.repo_id = Branch.repo_id AND "
|
|
"Branch.name = 'master'";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_repos_cb,
|
|
&repos, 1, "int", group_id) < 0) {
|
|
for (p = repos; p; p = p->next) {
|
|
g_object_unref (p->data);
|
|
}
|
|
g_list_free (repos);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get repos by group from db.");
|
|
return NULL;
|
|
}
|
|
|
|
seaf_fill_repo_obj_from_commit (&repos);
|
|
|
|
return g_list_reverse (repos);
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_group_repos_by_owner (SeafRepoManager *mgr,
|
|
const char *owner,
|
|
GError **error)
|
|
{
|
|
char *sql;
|
|
GList *repos = NULL;
|
|
GList *p;
|
|
|
|
sql = "SELECT RepoGroup.repo_id, v.repo_id, "
|
|
"group_id, user_name, permission, commit_id, s.size, "
|
|
"v.origin_repo, v.path, i.name, "
|
|
"i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
|
|
"(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo) "
|
|
"FROM RepoGroup LEFT JOIN VirtualRepo v ON "
|
|
"RepoGroup.repo_id = v.repo_id "
|
|
"LEFT JOIN RepoInfo i ON RepoGroup.repo_id = i.repo_id "
|
|
"LEFT JOIN RepoSize s ON RepoGroup.repo_id = s.repo_id, "
|
|
"Branch WHERE user_name = ? AND "
|
|
"RepoGroup.repo_id = Branch.repo_id AND "
|
|
"Branch.name = 'master'";
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_repos_cb,
|
|
&repos, 1, "string", owner) < 0) {
|
|
for (p = repos; p; p = p->next) {
|
|
g_object_unref (p->data);
|
|
}
|
|
g_list_free (repos);
|
|
return NULL;
|
|
}
|
|
|
|
seaf_fill_repo_obj_from_commit (&repos);
|
|
|
|
return g_list_reverse (repos);
|
|
}
|
|
|
|
static gboolean
|
|
get_group_repo_owner (SeafDBRow *row, void *data)
|
|
{
|
|
char **share_from = data;
|
|
|
|
const char *owner = (const char *) seaf_db_row_get_column_text (row, 0);
|
|
*share_from = g_ascii_strdown (owner, -1);
|
|
/* There should be only one result. */
|
|
return FALSE;
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_get_group_repo_owner (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
GError **error)
|
|
{
|
|
char *sql;
|
|
char *ret = NULL;
|
|
|
|
sql = "SELECT user_name FROM RepoGroup WHERE repo_id = ?";
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
get_group_repo_owner, &ret,
|
|
1, "string", repo_id) < 0) {
|
|
seaf_warning ("DB error when get repo share from for repo %s.\n",
|
|
repo_id);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_remove_group_repos (SeafRepoManager *mgr,
|
|
int group_id,
|
|
const char *owner,
|
|
GError **error)
|
|
{
|
|
SeafDB *db = mgr->seaf->db;
|
|
int rc;
|
|
|
|
if (!owner) {
|
|
rc = seaf_db_statement_query (db, "DELETE FROM RepoGroup WHERE group_id=?",
|
|
1, "int", group_id);
|
|
} else {
|
|
rc = seaf_db_statement_query (db,
|
|
"DELETE FROM RepoGroup WHERE group_id=? AND "
|
|
"user_name = ?",
|
|
2, "int", group_id, "string", owner);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* Inner public repos */
|
|
|
|
int
|
|
seaf_repo_manager_set_inner_pub_repo (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *permission)
|
|
{
|
|
SeafDB *db = mgr->seaf->db;
|
|
char sql[256];
|
|
|
|
if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
|
|
gboolean err;
|
|
snprintf(sql, sizeof(sql),
|
|
"SELECT repo_id FROM InnerPubRepo WHERE repo_id=?");
|
|
if (seaf_db_statement_exists (db, sql, &err,
|
|
1, "string", repo_id))
|
|
snprintf(sql, sizeof(sql),
|
|
"UPDATE InnerPubRepo SET permission='%s' "
|
|
"WHERE repo_id='%s'", permission, repo_id);
|
|
else
|
|
snprintf(sql, sizeof(sql),
|
|
"INSERT INTO InnerPubRepo (repo_id, permission) VALUES "
|
|
"('%s', '%s')", repo_id, permission);
|
|
if (err)
|
|
return -1;
|
|
return seaf_db_query (db, sql);
|
|
} else {
|
|
return seaf_db_statement_query (db,
|
|
"REPLACE INTO InnerPubRepo (repo_id, permission) VALUES (?, ?)",
|
|
2, "string", repo_id, "string", permission);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_unset_inner_pub_repo (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
return seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM InnerPubRepo WHERE repo_id = ?",
|
|
1, "string", repo_id);
|
|
}
|
|
|
|
gboolean
|
|
seaf_repo_manager_is_inner_pub_repo (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
gboolean db_err = FALSE;
|
|
|
|
return seaf_db_statement_exists (mgr->seaf->db,
|
|
"SELECT repo_id FROM InnerPubRepo WHERE repo_id=?",
|
|
&db_err, 1, "string", repo_id);
|
|
}
|
|
|
|
static gboolean
|
|
collect_public_repos (SeafDBRow *row, void *data)
|
|
{
|
|
GList **ret = (GList **)data;
|
|
SeafileRepo *srepo;
|
|
const char *repo_id, *vrepo_id, *owner, *permission, *commit_id;
|
|
gint64 size;
|
|
|
|
repo_id = seaf_db_row_get_column_text (row, 0);
|
|
vrepo_id = seaf_db_row_get_column_text (row, 1);
|
|
owner = seaf_db_row_get_column_text (row, 2);
|
|
permission = seaf_db_row_get_column_text (row, 3);
|
|
commit_id = seaf_db_row_get_column_text (row, 4);
|
|
size = seaf_db_row_get_column_int64 (row, 5);
|
|
const char *repo_name = seaf_db_row_get_column_text (row, 8);
|
|
gint64 update_time = seaf_db_row_get_column_int64 (row, 9);
|
|
int version = seaf_db_row_get_column_int (row, 10);
|
|
gboolean is_encrypted = seaf_db_row_get_column_int (row, 11) ? TRUE : FALSE;
|
|
const char *last_modifier = seaf_db_row_get_column_text (row, 12);
|
|
int status = seaf_db_row_get_column_int (row, 13);
|
|
|
|
char *owner_l = g_ascii_strdown (owner, -1);
|
|
|
|
srepo = g_object_new (SEAFILE_TYPE_REPO,
|
|
"share_type", "public",
|
|
"repo_id", repo_id,
|
|
"id", repo_id,
|
|
"head_cmmt_id", commit_id,
|
|
"permission", permission,
|
|
"user", owner_l,
|
|
"is_virtual", (vrepo_id != NULL),
|
|
"size", size,
|
|
"status", status,
|
|
NULL);
|
|
g_free (owner_l);
|
|
|
|
if (srepo) {
|
|
if (vrepo_id) {
|
|
const char *origin_repo_id = seaf_db_row_get_column_text (row, 6);
|
|
const char *origin_path = seaf_db_row_get_column_text (row, 7);
|
|
g_object_set (srepo, "store_id", origin_repo_id,
|
|
"origin_repo_id", origin_repo_id,
|
|
"origin_path", origin_path, NULL);
|
|
} else {
|
|
g_object_set (srepo, "store_id", repo_id, NULL);
|
|
}
|
|
|
|
if (repo_name) {
|
|
g_object_set (srepo, "name", repo_name,
|
|
"repo_name", repo_name,
|
|
"last_modify", update_time,
|
|
"last_modified", update_time,
|
|
"version", version,
|
|
"encrypted", is_encrypted,
|
|
"last_modifier", last_modifier, NULL);
|
|
}
|
|
|
|
*ret = g_list_prepend (*ret, srepo);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_list_inner_pub_repos (SeafRepoManager *mgr, gboolean *db_err)
|
|
{
|
|
GList *ret = NULL, *p;
|
|
char *sql;
|
|
|
|
sql = "SELECT InnerPubRepo.repo_id, VirtualRepo.repo_id, "
|
|
"owner_id, permission, commit_id, s.size, "
|
|
"VirtualRepo.origin_repo, VirtualRepo.path, i.name, "
|
|
"i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status "
|
|
"FROM InnerPubRepo LEFT JOIN VirtualRepo ON "
|
|
"InnerPubRepo.repo_id=VirtualRepo.repo_id "
|
|
"LEFT JOIN RepoInfo i ON InnerPubRepo.repo_id = i.repo_id "
|
|
"LEFT JOIN RepoSize s ON InnerPubRepo.repo_id = s.repo_id, RepoOwner, Branch "
|
|
"WHERE InnerPubRepo.repo_id=RepoOwner.repo_id AND "
|
|
"InnerPubRepo.repo_id = Branch.repo_id AND Branch.name = 'master'";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_public_repos, &ret,
|
|
0) < 0) {
|
|
for (p = ret; p != NULL; p = p->next)
|
|
g_object_unref (p->data);
|
|
g_list_free (ret);
|
|
if (db_err)
|
|
*db_err = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
seaf_fill_repo_obj_from_commit (&ret);
|
|
|
|
return g_list_reverse (ret);
|
|
}
|
|
|
|
gint64
|
|
seaf_repo_manager_count_inner_pub_repos (SeafRepoManager *mgr)
|
|
{
|
|
char sql[256];
|
|
|
|
snprintf (sql, 256, "SELECT COUNT(*) FROM InnerPubRepo");
|
|
|
|
return seaf_db_get_int64(mgr->seaf->db, sql);
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_list_inner_pub_repos_by_owner (SeafRepoManager *mgr,
|
|
const char *user)
|
|
{
|
|
GList *ret = NULL, *p;
|
|
char *sql;
|
|
|
|
sql = "SELECT InnerPubRepo.repo_id, VirtualRepo.repo_id, "
|
|
"owner_id, permission, commit_id, s.size, "
|
|
"VirtualRepo.origin_repo, VirtualRepo.path, i.name, "
|
|
"i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status "
|
|
"FROM InnerPubRepo LEFT JOIN VirtualRepo ON "
|
|
"InnerPubRepo.repo_id=VirtualRepo.repo_id "
|
|
"LEFT JOIN RepoInfo i ON InnerPubRepo.repo_id = i.repo_id "
|
|
"LEFT JOIN RepoSize s ON InnerPubRepo.repo_id = s.repo_id, RepoOwner, Branch "
|
|
"WHERE InnerPubRepo.repo_id=RepoOwner.repo_id AND owner_id=? "
|
|
"AND InnerPubRepo.repo_id = Branch.repo_id AND Branch.name = 'master'";
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
|
|
collect_public_repos, &ret,
|
|
1, "string", user) < 0) {
|
|
for (p = ret; p != NULL; p = p->next)
|
|
g_object_unref (p->data);
|
|
g_list_free (ret);
|
|
return NULL;
|
|
}
|
|
|
|
seaf_fill_repo_obj_from_commit (&ret);
|
|
|
|
return g_list_reverse (ret);
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_get_inner_pub_repo_perm (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
char *sql;
|
|
|
|
sql = "SELECT permission FROM InnerPubRepo WHERE repo_id=?";
|
|
return seaf_db_statement_get_string(mgr->seaf->db, sql, 1, "string", repo_id);
|
|
}
|
|
|
|
|
|
int
|
|
seaf_repo_manager_is_valid_filename (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *filename,
|
|
GError **error)
|
|
{
|
|
if (should_ignore_file(filename, NULL))
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
create_repo_common (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *repo_name,
|
|
const char *repo_desc,
|
|
const char *user,
|
|
const char *magic,
|
|
const char *random_key,
|
|
const char *salt,
|
|
int enc_version,
|
|
GError **error)
|
|
{
|
|
SeafRepo *repo = NULL;
|
|
SeafCommit *commit = NULL;
|
|
SeafBranch *master = NULL;
|
|
int ret = -1;
|
|
|
|
if (enc_version != 4 && enc_version != 3 && enc_version != 2 && enc_version != -1) {
|
|
seaf_warning ("Unsupported enc version %d.\n", enc_version);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
|
|
"Unsupported encryption version");
|
|
return -1;
|
|
}
|
|
|
|
if (enc_version >= 2) {
|
|
if (!magic || strlen(magic) != 64) {
|
|
seaf_warning ("Bad magic.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
|
|
"Bad magic");
|
|
return -1;
|
|
}
|
|
if (!random_key || strlen(random_key) != 96) {
|
|
seaf_warning ("Bad random key.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
|
|
"Bad random key");
|
|
return -1;
|
|
}
|
|
}
|
|
if (enc_version >= 3) {
|
|
if (!salt || strlen(salt) != 64) {
|
|
seaf_warning ("Bad salt.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
|
|
"Bad salt");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
repo = seaf_repo_new (repo_id, repo_name, repo_desc);
|
|
|
|
repo->no_local_history = TRUE;
|
|
if (enc_version >= 2) {
|
|
repo->encrypted = TRUE;
|
|
repo->enc_version = enc_version;
|
|
memcpy (repo->magic, magic, 64);
|
|
memcpy (repo->random_key, random_key, 96);
|
|
}
|
|
if (enc_version >= 3)
|
|
memcpy (repo->salt, salt, 64);
|
|
|
|
repo->version = CURRENT_REPO_VERSION;
|
|
memcpy (repo->store_id, repo_id, 36);
|
|
|
|
commit = seaf_commit_new (NULL, repo->id,
|
|
EMPTY_SHA1, /* root id */
|
|
user, /* creator */
|
|
EMPTY_SHA1, /* creator id */
|
|
"Created library", /* description */
|
|
0); /* ctime */
|
|
|
|
seaf_repo_to_commit (repo, commit);
|
|
if (seaf_commit_manager_add_commit (seaf->commit_mgr, commit) < 0) {
|
|
seaf_warning ("Failed to add commit.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to add commit");
|
|
goto out;
|
|
}
|
|
|
|
master = seaf_branch_new ("master", repo->id, commit->commit_id);
|
|
if (seaf_branch_manager_add_branch (seaf->branch_mgr, master) < 0) {
|
|
seaf_warning ("Failed to add branch.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to add branch");
|
|
goto out;
|
|
}
|
|
|
|
if (seaf_repo_set_head (repo, master) < 0) {
|
|
seaf_warning ("Failed to set repo head.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to set repo head.");
|
|
goto out;
|
|
}
|
|
|
|
if (seaf_repo_manager_add_repo (mgr, repo) < 0) {
|
|
seaf_warning ("Failed to add repo.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to add repo.");
|
|
goto out;
|
|
}
|
|
|
|
seaf_repo_manager_update_repo_info (mgr, repo->id, repo->head->commit_id);
|
|
|
|
ret = 0;
|
|
out:
|
|
if (repo)
|
|
seaf_repo_unref (repo);
|
|
if (commit)
|
|
seaf_commit_unref (commit);
|
|
if (master)
|
|
seaf_branch_unref (master);
|
|
|
|
return ret;
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_create_new_repo (SeafRepoManager *mgr,
|
|
const char *repo_name,
|
|
const char *repo_desc,
|
|
const char *owner_email,
|
|
const char *passwd,
|
|
int enc_version,
|
|
GError **error)
|
|
{
|
|
char *repo_id = NULL;
|
|
char salt[65], magic[65], random_key[97];
|
|
|
|
repo_id = gen_uuid ();
|
|
|
|
if (passwd && passwd[0] != 0) {
|
|
if (seafile_generate_repo_salt (salt) < 0) {
|
|
goto bad;
|
|
}
|
|
seafile_generate_magic (enc_version, repo_id, passwd, salt, magic);
|
|
if (seafile_generate_random_key (passwd, enc_version, salt, random_key) < 0) {
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
int rc;
|
|
if (passwd)
|
|
rc = create_repo_common (mgr, repo_id, repo_name, repo_desc, owner_email,
|
|
magic, random_key, salt, enc_version, error);
|
|
else
|
|
rc = create_repo_common (mgr, repo_id, repo_name, repo_desc, owner_email,
|
|
NULL, NULL, NULL, -1, error);
|
|
if (rc < 0)
|
|
goto bad;
|
|
|
|
if (seaf_repo_manager_set_repo_owner (mgr, repo_id, owner_email) < 0) {
|
|
seaf_warning ("Failed to set repo owner.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to set repo owner.");
|
|
goto bad;
|
|
}
|
|
|
|
return repo_id;
|
|
|
|
bad:
|
|
if (repo_id)
|
|
g_free (repo_id);
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_create_enc_repo (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *repo_name,
|
|
const char *repo_desc,
|
|
const char *owner_email,
|
|
const char *magic,
|
|
const char *random_key,
|
|
const char *salt,
|
|
int enc_version,
|
|
GError **error)
|
|
{
|
|
if (!repo_id || !is_uuid_valid (repo_id)) {
|
|
seaf_warning ("Invalid repo_id.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
|
|
"Invalid repo id");
|
|
return NULL;
|
|
}
|
|
|
|
if (seaf_repo_manager_repo_exists (mgr, repo_id)) {
|
|
seaf_warning ("Repo %s exists, refuse to create.\n", repo_id);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
|
|
"Repo already exists");
|
|
return NULL;
|
|
}
|
|
|
|
if (create_repo_common (mgr, repo_id, repo_name, repo_desc, owner_email,
|
|
magic, random_key, salt, enc_version, error) < 0)
|
|
return NULL;
|
|
|
|
if (seaf_repo_manager_set_repo_owner (mgr, repo_id, owner_email) < 0) {
|
|
seaf_warning ("Failed to set repo owner.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to set repo owner.");
|
|
return NULL;
|
|
}
|
|
|
|
return g_strdup (repo_id);
|
|
}
|
|
|
|
static int reap_token (void *data)
|
|
{
|
|
SeafRepoManager *mgr = data;
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
DecryptedToken *t;
|
|
|
|
pthread_rwlock_wrlock (&mgr->priv->lock);
|
|
|
|
gint64 now = (gint64)time(NULL);
|
|
|
|
g_hash_table_iter_init (&iter, mgr->priv->decrypted_tokens);
|
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
t = value;
|
|
if (now >= t->reap_time)
|
|
g_hash_table_iter_remove (&iter);
|
|
}
|
|
|
|
pthread_rwlock_unlock (&mgr->priv->lock);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void decrypted_token_free (DecryptedToken *token)
|
|
{
|
|
if (!token)
|
|
return;
|
|
g_free (token->token);
|
|
g_free (token);
|
|
}
|
|
|
|
void
|
|
seaf_repo_manager_add_decrypted_token (SeafRepoManager *mgr,
|
|
const char *encrypted_token,
|
|
const char *session_key,
|
|
const char *decrypted_token)
|
|
{
|
|
char key[256];
|
|
DecryptedToken *token;
|
|
|
|
snprintf (key, sizeof(key), "%s%s", encrypted_token, session_key);
|
|
key[255] = 0;
|
|
|
|
pthread_rwlock_wrlock (&mgr->priv->lock);
|
|
|
|
token = g_new0 (DecryptedToken, 1);
|
|
token->token = g_strdup(decrypted_token);
|
|
token->reap_time = (gint64)time(NULL) + DECRYPTED_TOKEN_TTL;
|
|
|
|
g_hash_table_insert (mgr->priv->decrypted_tokens,
|
|
g_strdup(key),
|
|
token);
|
|
|
|
pthread_rwlock_unlock (&mgr->priv->lock);
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_get_decrypted_token (SeafRepoManager *mgr,
|
|
const char *encrypted_token,
|
|
const char *session_key)
|
|
{
|
|
char key[256];
|
|
DecryptedToken *token;
|
|
|
|
snprintf (key, sizeof(key), "%s%s", encrypted_token, session_key);
|
|
key[255] = 0;
|
|
|
|
pthread_rwlock_rdlock (&mgr->priv->lock);
|
|
token = g_hash_table_lookup (mgr->priv->decrypted_tokens, key);
|
|
pthread_rwlock_unlock (&mgr->priv->lock);
|
|
|
|
if (token)
|
|
return g_strdup(token->token);
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
get_shared_users (SeafDBRow *row, void *data)
|
|
{
|
|
GList **shared_users = data;
|
|
const char *user = seaf_db_row_get_column_text (row, 0);
|
|
const char *perm = seaf_db_row_get_column_text (row, 1);
|
|
const char *repo_id = seaf_db_row_get_column_text (row, 2);
|
|
|
|
SeafileSharedUser *uobj = g_object_new (SEAFILE_TYPE_SHARED_USER,
|
|
"repo_id", repo_id,
|
|
"user", user,
|
|
"perm", perm,
|
|
NULL);
|
|
*shared_users = g_list_prepend (*shared_users, uobj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_shared_users_for_subdir (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *path,
|
|
const char *from_user,
|
|
GError **error)
|
|
{
|
|
GList *shared_users = NULL;
|
|
int ret = seaf_db_statement_foreach_row (mgr->seaf->db,
|
|
"SELECT to_email, permission, v.repo_id "
|
|
"FROM SharedRepo s, VirtualRepo v "
|
|
"WHERE s.repo_id = v.repo_id AND v.origin_repo = ? "
|
|
"AND v.path = ? AND s.from_email = ?",
|
|
get_shared_users, &shared_users, 3, "string", repo_id,
|
|
"string", path, "string", from_user);
|
|
if (ret < 0) {
|
|
seaf_warning ("Failed to get shared users for %.8s(%s).\n", repo_id, path);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get shared users for subdir from db");
|
|
while (shared_users) {
|
|
g_object_unref (shared_users->data);
|
|
shared_users = g_list_delete_link (shared_users, shared_users);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
return shared_users;
|
|
}
|
|
|
|
static gboolean
|
|
get_shared_groups (SeafDBRow *row, void *data)
|
|
{
|
|
GList **shared_groups = data;
|
|
int group = seaf_db_row_get_column_int (row, 0);
|
|
const char *perm = seaf_db_row_get_column_text (row, 1);
|
|
const char *repo_id = seaf_db_row_get_column_text (row, 2);
|
|
|
|
SeafileSharedGroup *gobj = g_object_new (SEAFILE_TYPE_SHARED_GROUP,
|
|
"repo_id", repo_id,
|
|
"group_id", group,
|
|
"perm", perm,
|
|
NULL);
|
|
|
|
*shared_groups = g_list_prepend (*shared_groups, gobj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_shared_groups_for_subdir (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *path,
|
|
const char *from_user,
|
|
GError **error)
|
|
{
|
|
GList *shared_groups = NULL;
|
|
int ret = seaf_db_statement_foreach_row (mgr->seaf->db,
|
|
"SELECT group_id, permission, v.repo_id "
|
|
"FROM RepoGroup r, VirtualRepo v "
|
|
"WHERE r.repo_id = v.repo_id AND v.origin_repo = ? "
|
|
"AND v.path = ? AND r.user_name = ?",
|
|
get_shared_groups, &shared_groups, 3, "string", repo_id,
|
|
"string", path, "string", from_user);
|
|
if (ret < 0) {
|
|
seaf_warning ("Failed to get shared groups for %.8s(%s).\n", repo_id, path);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get shared groups fro subdir from db");
|
|
while (shared_groups) {
|
|
g_object_unref (shared_groups->data);
|
|
shared_groups = g_list_delete_link (shared_groups, shared_groups);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
return shared_groups;
|
|
}
|
|
int
|
|
seaf_repo_manager_edit_repo (const char *repo_id,
|
|
const char *name,
|
|
const char *description,
|
|
const char *user,
|
|
GError **error)
|
|
{
|
|
SeafRepo *repo = NULL;
|
|
SeafCommit *commit = NULL, *parent = NULL;
|
|
int ret = 0;
|
|
|
|
if (!name && !description) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
|
|
"At least one argument should be non-null");
|
|
return -1;
|
|
}
|
|
|
|
if (!is_uuid_valid (repo_id)) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid repo id");
|
|
return -1;
|
|
}
|
|
|
|
retry:
|
|
repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
|
|
if (!repo) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "No such library");
|
|
return -1;
|
|
}
|
|
if (!name)
|
|
name = repo->name;
|
|
if (!description)
|
|
description = repo->desc;
|
|
|
|
/*
|
|
* We only change repo_name or repo_desc, so just copy the head commit
|
|
* and change these two fields.
|
|
*/
|
|
parent = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
|
repo->id, repo->version,
|
|
repo->head->commit_id);
|
|
if (!parent) {
|
|
seaf_warning ("Failed to get commit %s:%s.\n",
|
|
repo->id, repo->head->commit_id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (!user) {
|
|
user = parent->creator_name;
|
|
}
|
|
|
|
commit = seaf_commit_new (NULL,
|
|
repo->id,
|
|
parent->root_id,
|
|
user,
|
|
EMPTY_SHA1,
|
|
"Changed library name or description",
|
|
0);
|
|
commit->parent_id = g_strdup(parent->commit_id);
|
|
seaf_repo_to_commit (repo, commit);
|
|
|
|
g_free (commit->repo_name);
|
|
commit->repo_name = g_strdup(name);
|
|
g_free (commit->repo_desc);
|
|
commit->repo_desc = g_strdup(description);
|
|
|
|
if (seaf_commit_manager_add_commit (seaf->commit_mgr, commit) < 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
seaf_branch_set_commit (repo->head, commit->commit_id);
|
|
if (seaf_branch_manager_test_and_update_branch (seaf->branch_mgr,
|
|
repo->head,
|
|
parent->commit_id) < 0) {
|
|
seaf_repo_unref (repo);
|
|
seaf_commit_unref (commit);
|
|
seaf_commit_unref (parent);
|
|
repo = NULL;
|
|
commit = NULL;
|
|
parent = NULL;
|
|
goto retry;
|
|
}
|
|
|
|
seaf_repo_manager_update_repo_info (seaf->repo_mgr, repo_id, repo->head->commit_id);
|
|
|
|
out:
|
|
seaf_commit_unref (commit);
|
|
seaf_commit_unref (parent);
|
|
seaf_repo_unref (repo);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
get_total_file_number_cb (SeafDBRow *row, void *vdata)
|
|
{
|
|
gint64 *data = (gint64 *)vdata;
|
|
gint64 count = seaf_db_row_get_column_int64 (row, 0);
|
|
*data = count;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gint64
|
|
seaf_get_total_file_number (GError **error)
|
|
{
|
|
gint64 count = 0;
|
|
int ret = seaf_db_statement_foreach_row (seaf->db,
|
|
"SELECT SUM(file_count) FROM RepoFileCount f "
|
|
"LEFT JOIN VirtualRepo v "
|
|
"ON f.repo_id=v.repo_id,"
|
|
"Repo r "
|
|
"WHERE v.repo_id IS NULL AND "
|
|
"f.repo_id=r.repo_id",
|
|
get_total_file_number_cb,
|
|
&count, 0);
|
|
if (ret < 0) {
|
|
seaf_warning ("Failed to get total file number.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get total file number from db.");
|
|
return -1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
gboolean
|
|
get_total_storage_cb(SeafDBRow *row, void *vdata)
|
|
{
|
|
gint64 *data = (gint64 *)vdata;
|
|
gint64 size = seaf_db_row_get_column_int64 (row, 0);
|
|
*data = size;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gint64
|
|
seaf_get_total_storage (GError **error)
|
|
{
|
|
gint64 size = 0;
|
|
int ret;
|
|
if (seaf_db_type(seaf->db) == SEAF_DB_TYPE_PGSQL) {
|
|
ret = seaf_db_statement_foreach_row (seaf->db,
|
|
"SELECT SUM(\"size\") FROM RepoSize s "
|
|
"LEFT JOIN VirtualRepo v "
|
|
"ON s.repo_id=v.repo_id "
|
|
"WHERE v.repo_id IS NULL",
|
|
get_total_storage_cb,
|
|
&size, 0);
|
|
} else {
|
|
ret = seaf_db_statement_foreach_row (seaf->db,
|
|
"SELECT SUM(size) FROM RepoSize s "
|
|
"LEFT JOIN VirtualRepo v "
|
|
"ON s.repo_id=v.repo_id "
|
|
"WHERE v.repo_id IS NULL",
|
|
get_total_storage_cb,
|
|
&size, 0);
|
|
}
|
|
if (ret < 0) {
|
|
seaf_warning ("Failed to get total storage occupation.\n");
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get total storage occupation from db.");
|
|
return -1;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_add_upload_tmp_file (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *file_path,
|
|
const char *tmp_file,
|
|
GError **error)
|
|
{
|
|
char *file_path_with_slash = NULL;
|
|
|
|
if (file_path[0] == '/') {
|
|
file_path_with_slash = g_strdup(file_path);
|
|
} else {
|
|
file_path_with_slash = g_strconcat("/", file_path, NULL);
|
|
}
|
|
|
|
int ret = seaf_db_statement_query (mgr->seaf->db,
|
|
"INSERT INTO WebUploadTempFiles "
|
|
"(repo_id, file_path, tmp_file_path) "
|
|
"VALUES (?, ?, ?)", 3, "string", repo_id,
|
|
"string", file_path_with_slash, "string", tmp_file);
|
|
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to add upload tmp file record to db.");
|
|
}
|
|
|
|
g_free (file_path_with_slash);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_del_upload_tmp_file (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *file_path,
|
|
GError **error)
|
|
{
|
|
char *file_path_with_slash = NULL, *file_path_no_slash = NULL;
|
|
|
|
/* Due to a bug in early versions of 7.0, some file_path may be stored in the db without
|
|
* a leading slash. To be compatible with those records, we need to check the path
|
|
* with and without leading slash.
|
|
*/
|
|
if (file_path[0] == '/') {
|
|
file_path_with_slash = g_strdup(file_path);
|
|
file_path_no_slash = g_strdup(file_path+1);
|
|
} else {
|
|
file_path_with_slash = g_strconcat("/", file_path, NULL);
|
|
file_path_no_slash = g_strdup(file_path);
|
|
}
|
|
|
|
int ret = seaf_db_statement_query (mgr->seaf->db,
|
|
"DELETE FROM WebUploadTempFiles WHERE "
|
|
"repo_id = ? AND file_path IN (?, ?)",
|
|
3, "string", repo_id,
|
|
"string", file_path_with_slash,
|
|
"string", file_path_no_slash);
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to delete upload tmp file record from db.");
|
|
}
|
|
|
|
g_free (file_path_with_slash);
|
|
g_free (file_path_no_slash);
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
get_tmp_file_path (SeafDBRow *row, void *data)
|
|
{
|
|
char **path = data;
|
|
|
|
*path = g_strdup (seaf_db_row_get_column_text (row, 0));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
char *
|
|
seaf_repo_manager_get_upload_tmp_file (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *file_path,
|
|
GError **error)
|
|
{
|
|
char *tmp_file_path = NULL;
|
|
char *file_path_with_slash = NULL, *file_path_no_slash = NULL;
|
|
|
|
/* Due to a bug in early versions of 7.0, some file_path may be stored in the db without
|
|
* a leading slash. To be compatible with those records, we need to check the path
|
|
* with and without leading slash.
|
|
* The correct file_path in db should be with a leading slash.
|
|
*/
|
|
if (file_path[0] == '/') {
|
|
file_path_with_slash = g_strdup(file_path);
|
|
file_path_no_slash = g_strdup(file_path+1);
|
|
} else {
|
|
file_path_with_slash = g_strconcat("/", file_path, NULL);
|
|
file_path_no_slash = g_strdup(file_path);
|
|
}
|
|
|
|
int ret = seaf_db_statement_foreach_row (mgr->seaf->db,
|
|
"SELECT tmp_file_path FROM WebUploadTempFiles "
|
|
"WHERE repo_id = ? AND file_path = ?",
|
|
get_tmp_file_path, &tmp_file_path,
|
|
2, "string", repo_id,
|
|
"string", file_path_with_slash);
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get upload temp file path from db.");
|
|
goto out;
|
|
}
|
|
|
|
if (!tmp_file_path) {
|
|
/* Try file_path without slash. */
|
|
int ret = seaf_db_statement_foreach_row (mgr->seaf->db,
|
|
"SELECT tmp_file_path FROM WebUploadTempFiles "
|
|
"WHERE repo_id = ? AND file_path = ?",
|
|
get_tmp_file_path, &tmp_file_path,
|
|
2, "string", repo_id,
|
|
"string", file_path_no_slash);
|
|
if (ret < 0) {
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get upload temp file path from db.");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
g_free (file_path_with_slash);
|
|
g_free (file_path_no_slash);
|
|
return tmp_file_path;
|
|
}
|
|
|
|
gint64
|
|
seaf_repo_manager_get_upload_tmp_file_offset (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *file_path,
|
|
GError **error)
|
|
{
|
|
char *tmp_file_path = NULL;
|
|
SeafStat file_stat;
|
|
|
|
tmp_file_path = seaf_repo_manager_get_upload_tmp_file (mgr, repo_id,
|
|
file_path, error);
|
|
if (*error) {
|
|
return -1;
|
|
}
|
|
|
|
if (!tmp_file_path)
|
|
return 0;
|
|
|
|
if (seaf_stat (tmp_file_path, &file_stat) < 0) {
|
|
if (errno == ENOENT) {
|
|
seaf_message ("Temp file %s doesn't exist, remove reocrd from db.\n",
|
|
tmp_file_path);
|
|
if (seaf_repo_manager_del_upload_tmp_file (mgr, repo_id,
|
|
file_path, error) < 0) {
|
|
g_free (tmp_file_path);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
seaf_warning ("Failed to stat temp file %s: %s.\n",
|
|
tmp_file_path, strerror(errno));
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to stat temp file.");
|
|
g_free (tmp_file_path);
|
|
return -1;
|
|
}
|
|
|
|
g_free (tmp_file_path);
|
|
|
|
return file_stat.st_size;
|
|
}
|
|
|
|
void
|
|
seaf_repo_manager_update_repo_info (SeafRepoManager *mgr,
|
|
const char *repo_id, const char *head_commit_id)
|
|
{
|
|
SeafCommit *head;
|
|
|
|
head = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
|
repo_id, 1, head_commit_id);
|
|
if (!head) {
|
|
seaf_warning ("Failed to get commit %s:%s.\n", repo_id, head_commit_id);
|
|
return;
|
|
}
|
|
|
|
set_repo_commit_to_db (repo_id, head->repo_name, head->ctime, head->version,
|
|
(head->encrypted ? 1 : 0), head->creator_name);
|
|
|
|
seaf_commit_unref (head);
|
|
}
|
|
|
|
char *
|
|
seaf_get_trash_repo_owner (const char *repo_id)
|
|
{
|
|
char *sql = "SELECT owner_id from RepoTrash WHERE repo_id = ?";
|
|
return seaf_db_statement_get_string(seaf->db, sql, 1, "string", repo_id);
|
|
}
|
|
|
|
GObject *
|
|
seaf_get_group_shared_repo_by_path (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *path,
|
|
int group_id,
|
|
gboolean is_org,
|
|
GError **error)
|
|
{
|
|
char *sql;
|
|
char *real_repo_id = NULL;
|
|
GList *repo = NULL;
|
|
GObject *ret = NULL;
|
|
|
|
/* If path is NULL, 'repo_id' represents for the repo we want,
|
|
* otherwise, 'repo_id' represents for the origin repo,
|
|
* find virtual repo by path first.
|
|
*/
|
|
if (path != NULL) {
|
|
real_repo_id = seaf_repo_manager_get_virtual_repo_id (mgr, repo_id, path, NULL);
|
|
if (!real_repo_id) {
|
|
seaf_warning ("Failed to get virtual repo_id by path %s, origin_repo: %s\n", path, repo_id);
|
|
return NULL;
|
|
}
|
|
}
|
|
if (!real_repo_id)
|
|
real_repo_id = g_strdup (repo_id);
|
|
|
|
if (!is_org)
|
|
sql = "SELECT RepoGroup.repo_id, v.repo_id, "
|
|
"group_id, user_name, permission, commit_id, s.size, "
|
|
"v.origin_repo, v.path, i.name, "
|
|
"i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
|
|
"(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo) "
|
|
"FROM RepoGroup LEFT JOIN VirtualRepo v ON "
|
|
"RepoGroup.repo_id = v.repo_id "
|
|
"LEFT JOIN RepoInfo i ON RepoGroup.repo_id = i.repo_id "
|
|
"LEFT JOIN RepoSize s ON RepoGroup.repo_id = s.repo_id, "
|
|
"Branch WHERE group_id = ? AND "
|
|
"RepoGroup.repo_id = Branch.repo_id AND "
|
|
"RepoGroup.repo_id = ? AND "
|
|
"Branch.name = 'master'";
|
|
else
|
|
sql = "SELECT OrgGroupRepo.repo_id, v.repo_id, "
|
|
"group_id, owner, permission, commit_id, s.size, "
|
|
"v.origin_repo, v.path, i.name, "
|
|
"i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
|
|
"(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo) "
|
|
"FROM OrgGroupRepo LEFT JOIN VirtualRepo v ON "
|
|
"OrgGroupRepo.repo_id = v.repo_id "
|
|
"LEFT JOIN RepoInfo i ON OrgRepoGroup.repo_id = i.repo_id "
|
|
"LEFT JOIN RepoSize s ON OrgGroupRepo.repo_id = s.repo_id, "
|
|
"Branch WHERE group_id = ? AND "
|
|
"OrgGroupRepo.repo_id = Branch.repo_id AND "
|
|
"OrgGroupRepo.repo_id = ? AND "
|
|
"Branch.name = 'master'";
|
|
|
|
/* The list 'repo' should have only one repo,
|
|
* use existing api get_group_repos_cb() to get it.
|
|
*/
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_repos_cb,
|
|
&repo, 2, "int", group_id,
|
|
"string", real_repo_id) < 0) {
|
|
g_free (real_repo_id);
|
|
g_list_free (repo);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get repo by group_id from db.");
|
|
return NULL;
|
|
}
|
|
g_free (real_repo_id);
|
|
|
|
if (repo) {
|
|
seaf_fill_repo_obj_from_commit (&repo);
|
|
if (repo)
|
|
ret = (GObject *)(repo->data);
|
|
g_list_free (repo);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
GList *
|
|
seaf_get_group_repos_by_user (SeafRepoManager *mgr,
|
|
const char *user,
|
|
int org_id,
|
|
GError **error)
|
|
{
|
|
CcnetGroup *group;
|
|
GList *groups = NULL, *p, *q;
|
|
GList *repos = NULL;
|
|
SeafileRepo *repo = NULL;
|
|
GString *sql = NULL;
|
|
int group_id = 0;
|
|
|
|
/* Get the groups this user belongs to. */
|
|
groups = ccnet_group_manager_get_groups_by_user (seaf->group_mgr, user,
|
|
1, NULL);
|
|
if (!groups) {
|
|
goto out;
|
|
}
|
|
|
|
sql = g_string_new ("");
|
|
g_string_printf (sql, "SELECT g.repo_id, v.repo_id, "
|
|
"group_id, %s, permission, commit_id, s.size, "
|
|
"v.origin_repo, v.path, i.name, "
|
|
"i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
|
|
"(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo)"
|
|
"FROM %s g LEFT JOIN VirtualRepo v ON "
|
|
"g.repo_id = v.repo_id "
|
|
"LEFT JOIN RepoInfo i ON g.repo_id = i.repo_id "
|
|
"LEFT JOIN RepoSize s ON g.repo_id = s.repo_id, "
|
|
"Branch b WHERE g.repo_id = b.repo_id AND "
|
|
"b.name = 'master' AND group_id IN (",
|
|
org_id < 0 ? "user_name" : "owner",
|
|
org_id < 0 ? "RepoGroup" : "OrgGroupRepo");
|
|
for (p = groups; p != NULL; p = p->next) {
|
|
group = p->data;
|
|
g_object_get (group, "id", &group_id, NULL);
|
|
|
|
g_string_append_printf (sql, "%d", group_id);
|
|
if (p->next)
|
|
g_string_append_printf (sql, ",");
|
|
}
|
|
g_string_append_printf (sql, " ) ORDER BY group_id");
|
|
|
|
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql->str, get_group_repos_cb,
|
|
&repos, 0) < 0) {
|
|
for (p = repos; p; p = p->next) {
|
|
g_object_unref (p->data);
|
|
}
|
|
g_list_free (repos);
|
|
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
|
"Failed to get user group repos from db.");
|
|
seaf_warning ("Failed to get user[%s] group repos from db.\n", user);
|
|
goto out;
|
|
}
|
|
|
|
int repo_group_id = 0;
|
|
char *group_name = NULL;
|
|
q = repos;
|
|
|
|
/* Add group_name to repo. Both groups and repos are listed by group_id in descending order */
|
|
for (p = groups; p; p = p->next) {
|
|
group = p->data;
|
|
g_object_get (group, "id", &group_id, NULL);
|
|
g_object_get (group, "group_name", &group_name, NULL);
|
|
|
|
for (; q; q = q->next) {
|
|
repo = q->data;
|
|
g_object_get (repo, "group_id", &repo_group_id, NULL);
|
|
if (repo_group_id == group_id)
|
|
g_object_set (repo, "group_name", group_name, NULL);
|
|
else
|
|
break;
|
|
}
|
|
g_free (group_name);
|
|
if (q == NULL)
|
|
break;
|
|
}
|
|
|
|
seaf_fill_repo_obj_from_commit (&repos);
|
|
|
|
out:
|
|
if (sql)
|
|
g_string_free (sql, TRUE);
|
|
|
|
for (p = groups; p != NULL; p = p->next)
|
|
g_object_unref ((GObject *)p->data);
|
|
g_list_free (groups);
|
|
|
|
return g_list_reverse (repos);
|
|
}
|
|
|
|
typedef struct RepoPath {
|
|
char *repo_id;
|
|
char *path;
|
|
int group_id;
|
|
} RepoPath;
|
|
|
|
|
|
gboolean
|
|
convert_repo_path_cb (SeafDBRow *row, void *data)
|
|
{
|
|
GList **repo_paths = data;
|
|
|
|
const char *repo_id = seaf_db_row_get_column_text (row, 0);
|
|
const char *path = seaf_db_row_get_column_text (row, 1);
|
|
int group_id = seaf_db_row_get_column_int (row, 2);
|
|
|
|
RepoPath *rp = g_new0(RepoPath, 1);
|
|
rp->repo_id = g_strdup(repo_id);
|
|
rp->path = g_strdup(path);
|
|
rp->group_id = group_id;
|
|
*repo_paths = g_list_append (*repo_paths, rp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
free_repo_path (gpointer data)
|
|
{
|
|
if (!data)
|
|
return;
|
|
|
|
RepoPath *rp = data;
|
|
g_free (rp->repo_id);
|
|
g_free (rp->path);
|
|
g_free (rp);
|
|
}
|
|
|
|
static char *
|
|
filter_path (GList *repo_paths, const char *path)
|
|
{
|
|
GList *ptr = NULL;
|
|
int len;
|
|
const char *relative_path;
|
|
char *ret = NULL;
|
|
RepoPath *rp = NULL, res;
|
|
res.repo_id = NULL;
|
|
res.path = NULL;
|
|
res.group_id = 0;
|
|
|
|
/* Find nearest item which contains @path, */
|
|
for (ptr = repo_paths; ptr; ptr = ptr->next) {
|
|
rp = ptr->data;
|
|
len = strlen(rp->path);
|
|
if (strncmp(rp->path, path, len) == 0 && (path[len] == '/' || path[len] == '\0')) {
|
|
|
|
if (g_strcmp0(rp->path, res.path) > 0) {
|
|
res.path = rp->path;
|
|
res.repo_id = rp->repo_id;
|
|
res.group_id = rp->group_id;
|
|
}
|
|
}
|
|
}
|
|
if (res.repo_id && res.path) {
|
|
relative_path = path + strlen(res.path);
|
|
if (relative_path[0] == '\0')
|
|
relative_path = "/";
|
|
|
|
json_t *json = json_object ();
|
|
json_object_set_string_member(json, "repo_id", res.repo_id);
|
|
json_object_set_string_member(json, "path", relative_path);
|
|
if (res.group_id > 0)
|
|
json_object_set_int_member(json, "group_id", res.group_id);
|
|
ret = json_dumps (json, 0);
|
|
json_decref (json);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Convert origin repo and path to virtual repo and relative path */
|
|
char *
|
|
seaf_repo_manager_convert_repo_path (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
const char *path,
|
|
const char *user,
|
|
gboolean is_org,
|
|
GError **error)
|
|
{
|
|
char *ret = NULL;
|
|
int rc;
|
|
int group_id;
|
|
GString *sql;
|
|
CcnetGroup *group;
|
|
GList *groups = NULL, *p1;
|
|
GList *repo_paths = NULL;
|
|
SeafVirtRepo *vinfo = NULL;
|
|
const char *r_repo_id = repo_id;
|
|
char *r_path = NULL;
|
|
|
|
vinfo = seaf_repo_manager_get_virtual_repo_info (mgr, repo_id);
|
|
if (vinfo) {
|
|
r_repo_id = vinfo->origin_repo_id;
|
|
r_path = g_strconcat (vinfo->path, path, NULL);
|
|
} else {
|
|
r_path = g_strdup(path);
|
|
}
|
|
|
|
sql = g_string_new ("");
|
|
g_string_printf (sql, "SELECT v.repo_id, path, 0 FROM VirtualRepo v, %s s WHERE "
|
|
"v.origin_repo=? AND v.repo_id=s.repo_id AND s.to_email=?",
|
|
is_org ? "OrgSharedRepo" : "SharedRepo");
|
|
rc = seaf_db_statement_foreach_row (seaf->db,
|
|
sql->str, convert_repo_path_cb,
|
|
&repo_paths, 2,
|
|
"string", r_repo_id, "string", user);
|
|
if (rc < 0) {
|
|
seaf_warning("Failed to convert repo path [%s:%s] to virtual repo path, db_error.\n",
|
|
repo_id, path);
|
|
goto out;
|
|
}
|
|
ret = filter_path(repo_paths, r_path);
|
|
g_list_free_full(repo_paths, free_repo_path);
|
|
repo_paths = NULL;
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Get the groups this user belongs to. */
|
|
|
|
groups = ccnet_group_manager_get_groups_by_user (seaf->group_mgr, user,
|
|
1, NULL);
|
|
if (!groups) {
|
|
goto out;
|
|
}
|
|
|
|
g_string_printf (sql, "SELECT v.repo_id, path, r.group_id FROM VirtualRepo v, %s r WHERE "
|
|
"v.origin_repo=? AND v.repo_id=r.repo_id AND r.group_id IN(",
|
|
is_org ? "OrgGroupRepo" : "RepoGroup");
|
|
for (p1 = groups; p1 != NULL; p1 = p1->next) {
|
|
group = p1->data;
|
|
g_object_get (group, "id", &group_id, NULL);
|
|
|
|
g_string_append_printf (sql, "%d", group_id);
|
|
if (p1->next)
|
|
g_string_append_printf (sql, ",");
|
|
}
|
|
g_string_append_printf (sql, ")");
|
|
|
|
rc = seaf_db_statement_foreach_row (seaf->db,
|
|
sql->str, convert_repo_path_cb,
|
|
&repo_paths, 1,
|
|
"string", r_repo_id);
|
|
if (rc < 0) {
|
|
seaf_warning("Failed to convert repo path [%s:%s] to virtual repo path, db error.\n",
|
|
repo_id, path);
|
|
g_string_free (sql, TRUE);
|
|
goto out;
|
|
}
|
|
ret = filter_path(repo_paths, r_path);
|
|
g_list_free_full(repo_paths, free_repo_path);
|
|
|
|
out:
|
|
g_free (r_path);
|
|
if (vinfo)
|
|
seaf_virtual_repo_info_free (vinfo);
|
|
g_string_free (sql, TRUE);
|
|
for (p1 = groups; p1 != NULL; p1 = p1->next)
|
|
g_object_unref ((GObject *)p1->data);
|
|
g_list_free (groups);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_set_repo_status(SeafRepoManager *mgr,
|
|
const char *repo_id, RepoStatus status)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (seaf_db_statement_query (mgr->seaf->db,
|
|
"UPDATE RepoInfo SET status=? "
|
|
"WHERE repo_id=? OR repo_id IN "
|
|
"(SELECT repo_id FROM VirtualRepo WHERE origin_repo=?)",
|
|
3, "int", status,
|
|
"string", repo_id, "string", repo_id) < 0)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_get_repo_status(SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
char *sql = "SELECT status FROM RepoInfo WHERE repo_id=?";
|
|
|
|
return seaf_db_statement_get_int (mgr->seaf->db, sql,
|
|
1, "string", repo_id);
|
|
}
|