1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 #include "common.h"
4 
5 #include <glib/gstdio.h>
6 
7 #include <openssl/sha.h>
8 #include <openssl/rand.h>
9 
10 #include <timer.h>
11 #include "utils.h"
12 #include "log.h"
13 
14 #include "seafile-session.h"
15 #include "commit-mgr.h"
16 #include "branch-mgr.h"
17 #include "repo-mgr.h"
18 #include "fs-mgr.h"
19 #include "seafile-error.h"
20 #include "seafile-crypt.h"
21 
22 #include "seaf-db.h"
23 #include "seaf-utils.h"
24 
25 #define REAP_TOKEN_INTERVAL 300 /* 5 mins */
26 #define DECRYPTED_TOKEN_TTL 3600 /* 1 hour */
27 #define SCAN_TRASH_DAYS 1 /* one day */
28 #define TRASH_EXPIRE_DAYS 30 /* one month */
29 
30 typedef struct DecryptedToken {
31     char *token;
32     gint64 reap_time;
33 } DecryptedToken;
34 
35 struct _SeafRepoManagerPriv {
36     /* (encrypted_token, session_key) -> decrypted token */
37     GHashTable *decrypted_tokens;
38     pthread_rwlock_t lock;
39     CcnetTimer *reap_token_timer;
40 
41     CcnetTimer *scan_trash_timer;
42 };
43 
44 static void
45 load_repo (SeafRepoManager *manager, SeafRepo *repo);
46 
47 static int create_db_tables_if_not_exist (SeafRepoManager *mgr);
48 
49 static int save_branch_repo_map (SeafRepoManager *manager, SeafBranch *branch);
50 
51 static int reap_token (void *data);
52 static void decrypted_token_free (DecryptedToken *token);
53 
54 gboolean
is_repo_id_valid(const char * id)55 is_repo_id_valid (const char *id)
56 {
57     if (!id)
58         return FALSE;
59 
60     return is_uuid_valid (id);
61 }
62 
63 SeafRepo*
seaf_repo_new(const char * id,const char * name,const char * desc)64 seaf_repo_new (const char *id, const char *name, const char *desc)
65 {
66     SeafRepo* repo;
67 
68     /* valid check */
69 
70 
71     repo = g_new0 (SeafRepo, 1);
72     memcpy (repo->id, id, 36);
73     repo->id[36] = '\0';
74 
75     repo->name = g_strdup(name);
76     repo->desc = g_strdup(desc);
77 
78     repo->ref_cnt = 1;
79 
80     return repo;
81 }
82 
83 void
seaf_repo_free(SeafRepo * repo)84 seaf_repo_free (SeafRepo *repo)
85 {
86     if (repo->name) g_free (repo->name);
87     if (repo->desc) g_free (repo->desc);
88     if (repo->head) seaf_branch_unref (repo->head);
89     if (repo->virtual_info)
90         seaf_virtual_repo_info_free (repo->virtual_info);
91     g_free (repo->last_modifier);
92     g_free (repo);
93 }
94 
95 void
seaf_repo_ref(SeafRepo * repo)96 seaf_repo_ref (SeafRepo *repo)
97 {
98     g_atomic_int_inc (&repo->ref_cnt);
99 }
100 
101 void
seaf_repo_unref(SeafRepo * repo)102 seaf_repo_unref (SeafRepo *repo)
103 {
104     if (!repo)
105         return;
106 
107     if (g_atomic_int_dec_and_test (&repo->ref_cnt))
108         seaf_repo_free (repo);
109 }
110 
111 static void
set_head_common(SeafRepo * repo,SeafBranch * branch)112 set_head_common (SeafRepo *repo, SeafBranch *branch)
113 {
114     if (repo->head)
115         seaf_branch_unref (repo->head);
116     repo->head = branch;
117     seaf_branch_ref(branch);
118 }
119 
120 int
seaf_repo_set_head(SeafRepo * repo,SeafBranch * branch)121 seaf_repo_set_head (SeafRepo *repo, SeafBranch *branch)
122 {
123     if (save_branch_repo_map (repo->manager, branch) < 0)
124         return -1;
125     set_head_common (repo, branch);
126     return 0;
127 }
128 
129 void
seaf_repo_from_commit(SeafRepo * repo,SeafCommit * commit)130 seaf_repo_from_commit (SeafRepo *repo, SeafCommit *commit)
131 {
132     repo->name = g_strdup (commit->repo_name);
133     repo->desc = g_strdup (commit->repo_desc);
134     repo->encrypted = commit->encrypted;
135     repo->repaired = commit->repaired;
136     repo->last_modify = commit->ctime;
137     memcpy (repo->root_id, commit->root_id, 40);
138     if (repo->encrypted) {
139         repo->enc_version = commit->enc_version;
140         if (repo->enc_version == 1)
141             memcpy (repo->magic, commit->magic, 32);
142         else if (repo->enc_version == 2) {
143             memcpy (repo->magic, commit->magic, 64);
144             memcpy (repo->random_key, commit->random_key, 96);
145         } else if (repo->enc_version == 3) {
146             memcpy (repo->magic, commit->magic, 64);
147             memcpy (repo->random_key, commit->random_key, 96);
148             memcpy (repo->salt, commit->salt, 64);
149         } else if (repo->enc_version == 4) {
150             memcpy (repo->magic, commit->magic, 64);
151             memcpy (repo->random_key, commit->random_key, 96);
152             memcpy (repo->salt, commit->salt, 64);
153         }
154     }
155     repo->no_local_history = commit->no_local_history;
156     repo->version = commit->version;
157     repo->last_modifier = g_strdup (commit->creator_name);
158 }
159 
160 void
seaf_repo_to_commit(SeafRepo * repo,SeafCommit * commit)161 seaf_repo_to_commit (SeafRepo *repo, SeafCommit *commit)
162 {
163     commit->repo_name = g_strdup (repo->name);
164     commit->repo_desc = g_strdup (repo->desc);
165     commit->encrypted = repo->encrypted;
166     commit->repaired = repo->repaired;
167     if (commit->encrypted) {
168         commit->enc_version = repo->enc_version;
169         if (commit->enc_version == 1)
170             commit->magic = g_strdup (repo->magic);
171         else if (commit->enc_version == 2) {
172             commit->magic = g_strdup (repo->magic);
173             commit->random_key = g_strdup (repo->random_key);
174         } else if (commit->enc_version == 3) {
175             commit->magic = g_strdup (repo->magic);
176             commit->random_key = g_strdup (repo->random_key);
177             commit->salt = g_strdup (repo->salt);
178         } else if (commit->enc_version == 4) {
179             commit->magic = g_strdup (repo->magic);
180             commit->random_key = g_strdup (repo->random_key);
181             commit->salt = g_strdup (repo->salt);
182         }
183     }
184     commit->no_local_history = repo->no_local_history;
185     commit->version = repo->version;
186 }
187 
188 static gboolean
collect_commit(SeafCommit * commit,void * vlist,gboolean * stop)189 collect_commit (SeafCommit *commit, void *vlist, gboolean *stop)
190 {
191     GList **commits = vlist;
192 
193     /* The traverse function will unref the commit, so we need to ref it.
194      */
195     seaf_commit_ref (commit);
196     *commits = g_list_prepend (*commits, commit);
197     return TRUE;
198 }
199 
200 GList *
seaf_repo_get_commits(SeafRepo * repo)201 seaf_repo_get_commits (SeafRepo *repo)
202 {
203     GList *branches;
204     GList *ptr;
205     SeafBranch *branch;
206     GList *commits = NULL;
207 
208     branches = seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo->id);
209     if (branches == NULL) {
210         seaf_warning ("Failed to get branch list of repo %s.\n", repo->id);
211         return NULL;
212     }
213 
214     for (ptr = branches; ptr != NULL; ptr = ptr->next) {
215         branch = ptr->data;
216         gboolean res = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
217                                                                  repo->id,
218                                                                  repo->version,
219                                                                  branch->commit_id,
220                                                                  collect_commit,
221                                                                  &commits,
222                                                                  FALSE);
223         if (!res) {
224             for (ptr = commits; ptr != NULL; ptr = ptr->next)
225                 seaf_commit_unref ((SeafCommit *)(ptr->data));
226             g_list_free (commits);
227             goto out;
228         }
229     }
230 
231     commits = g_list_reverse (commits);
232 
233 out:
234     for (ptr = branches; ptr != NULL; ptr = ptr->next) {
235         seaf_branch_unref ((SeafBranch *)ptr->data);
236     }
237     return commits;
238 }
239 
240 gboolean
should_ignore_file(const char * filename,void * data)241 should_ignore_file(const char *filename, void *data)
242 {
243     /* GPatternSpec **spec = ignore_patterns; */
244 
245     if (!g_utf8_validate (filename, -1, NULL)) {
246         seaf_warning ("File name %s contains non-UTF8 characters, skip.\n", filename);
247         return TRUE;
248     }
249 
250     /* Ignore file/dir if its name is too long. */
251     if (strlen(filename) >= SEAF_DIR_NAME_LEN)
252         return TRUE;
253 
254     if (strchr (filename, '/'))
255         return TRUE;
256 
257     return FALSE;
258 }
259 
260 static gboolean
261 collect_repo_id (SeafDBRow *row, void *data);
262 
263 static int
scan_trash(void * data)264 scan_trash (void *data)
265 {
266     GList *repo_ids = NULL;
267     SeafRepoManager *mgr = seaf->repo_mgr;
268     gint64 trash_expire_interval = TRASH_EXPIRE_DAYS * 24 * 3600;
269     int expire_days = seaf_cfg_manager_get_config_int (seaf->cfg_mgr,
270                                                        "library_trash",
271                                                        "expire_days");
272     if (expire_days > 0) {
273         trash_expire_interval = expire_days * 24 * 3600;
274     }
275 
276     gint64 expire_time = time(NULL) - trash_expire_interval;
277     char *sql = "SELECT repo_id FROM RepoTrash WHERE del_time <= ?";
278 
279     int ret = seaf_db_statement_foreach_row (seaf->db, sql,
280                                              collect_repo_id, &repo_ids,
281                                              1, "int64", expire_time);
282     if (ret < 0) {
283         seaf_warning ("Get expired repo from trash failed.");
284         string_list_free (repo_ids);
285         return TRUE;
286     }
287 
288     GList *iter;
289     char *repo_id;
290     for (iter=repo_ids; iter; iter=iter->next) {
291         repo_id = iter->data;
292         ret = seaf_repo_manager_del_repo_from_trash (mgr, repo_id, NULL);
293         if (ret < 0)
294             break;
295     }
296 
297     string_list_free (repo_ids);
298 
299     return TRUE;
300 }
301 
302 static void
init_scan_trash_timer(SeafRepoManagerPriv * priv,GKeyFile * config)303 init_scan_trash_timer (SeafRepoManagerPriv *priv, GKeyFile *config)
304 {
305     int scan_days;
306     GError *error = NULL;
307 
308     scan_days = g_key_file_get_integer (config,
309                                         "library_trash", "scan_days",
310                                         &error);
311     if (error) {
312        scan_days = SCAN_TRASH_DAYS;
313        g_clear_error (&error);
314     }
315 
316     priv->scan_trash_timer = ccnet_timer_new (scan_trash, NULL,
317                                               scan_days * 24 * 3600 * 1000);
318 }
319 
320 SeafRepoManager*
seaf_repo_manager_new(SeafileSession * seaf)321 seaf_repo_manager_new (SeafileSession *seaf)
322 {
323     SeafRepoManager *mgr = g_new0 (SeafRepoManager, 1);
324 
325     mgr->priv = g_new0 (SeafRepoManagerPriv, 1);
326     mgr->seaf = seaf;
327 
328     mgr->priv->decrypted_tokens = g_hash_table_new_full (g_str_hash, g_str_equal,
329                                                          g_free,
330                                                          (GDestroyNotify)decrypted_token_free);
331     pthread_rwlock_init (&mgr->priv->lock, NULL);
332     mgr->priv->reap_token_timer = ccnet_timer_new (reap_token, mgr,
333                                                    REAP_TOKEN_INTERVAL * 1000);
334 
335     init_scan_trash_timer (mgr->priv, seaf->config);
336 
337     return mgr;
338 }
339 
340 int
seaf_repo_manager_init(SeafRepoManager * mgr)341 seaf_repo_manager_init (SeafRepoManager *mgr)
342 {
343     /* On the server, we load repos into memory on-demand, because
344      * there are too many repos.
345      */
346     if (create_db_tables_if_not_exist (mgr) < 0) {
347         seaf_warning ("[repo mgr] failed to create tables.\n");
348         return -1;
349     }
350 
351     if (seaf_repo_manager_init_merge_scheduler() < 0) {
352         seaf_warning ("Failed to init merge scheduler.\n");
353         return -1;
354     }
355 
356     return 0;
357 }
358 
359 int
seaf_repo_manager_start(SeafRepoManager * mgr)360 seaf_repo_manager_start (SeafRepoManager *mgr)
361 {
362     return 0;
363 }
364 
365 int
seaf_repo_manager_add_repo(SeafRepoManager * manager,SeafRepo * repo)366 seaf_repo_manager_add_repo (SeafRepoManager *manager,
367                             SeafRepo *repo)
368 {
369     SeafDB *db = manager->seaf->db;
370 
371     if (seaf_db_statement_query (db, "INSERT INTO Repo (repo_id) VALUES (?)",
372                                  1, "string", repo->id) < 0)
373         return -1;
374 
375     repo->manager = manager;
376 
377     return 0;
378 }
379 
380 static int
add_deleted_repo_record(SeafRepoManager * mgr,const char * repo_id)381 add_deleted_repo_record (SeafRepoManager *mgr, const char *repo_id)
382 {
383     if (seaf_db_type(seaf->db) == SEAF_DB_TYPE_PGSQL) {
384         gboolean exists, err;
385 
386         exists = seaf_db_statement_exists (seaf->db,
387                                            "SELECT repo_id FROM GarbageRepos "
388                                            "WHERE repo_id=?",
389                                            &err, 1, "string", repo_id);
390         if (err)
391             return -1;
392 
393         if (!exists) {
394             return seaf_db_statement_query(seaf->db,
395                                            "INSERT INTO GarbageRepos (repo_id) VALUES (?)",
396                                            1, "string", repo_id);
397         }
398 
399         return 0;
400     } else {
401         return seaf_db_statement_query (seaf->db,
402                                         "REPLACE INTO GarbageRepos (repo_id) VALUES (?)",
403                                         1, "string", repo_id);
404     }
405 }
406 
407 static int
add_deleted_repo_to_trash(SeafRepoManager * mgr,const char * repo_id,SeafCommit * commit)408 add_deleted_repo_to_trash (SeafRepoManager *mgr, const char *repo_id,
409                            SeafCommit *commit)
410 {
411     char *owner = NULL;
412     int ret = -1;
413 
414     owner = seaf_repo_manager_get_repo_owner (mgr, repo_id);
415     if (!owner) {
416         seaf_warning ("Failed to get owner for repo %.8s.\n", repo_id);
417         goto out;
418     }
419 
420     gint64 size = seaf_repo_manager_get_repo_size (mgr, repo_id);
421     if (size == -1) {
422         seaf_warning ("Failed to get size of repo %.8s.\n", repo_id);
423         goto out;
424     }
425 
426     ret =  seaf_db_statement_query (mgr->seaf->db,
427                                     "INSERT INTO RepoTrash (repo_id, repo_name, head_id, "
428                                     "owner_id, size, org_id, del_time) "
429                                     "values (?, ?, ?, ?, ?, -1, ?)", 6,
430                                     "string", repo_id,
431                                     "string", commit->repo_name,
432                                     "string", commit->commit_id,
433                                     "string", owner,
434                                     "int64", size,
435                                     "int64", (gint64)time(NULL));
436 out:
437     g_free (owner);
438 
439     return ret;
440 }
441 
442 static int
remove_virtual_repo_ondisk(SeafRepoManager * mgr,const char * repo_id)443 remove_virtual_repo_ondisk (SeafRepoManager *mgr,
444                             const char *repo_id)
445 {
446     SeafDB *db = mgr->seaf->db;
447 
448     /* Remove record in repo table first.
449      * Once this is commited, we can gc the other tables later even if
450      * we're interrupted.
451      */
452     if (seaf_db_statement_query (db, "DELETE FROM Repo WHERE repo_id = ?",
453                                  1, "string", repo_id) < 0)
454         return -1;
455 
456     /* remove branch */
457     GList *p;
458     GList *branch_list =
459         seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo_id);
460     for (p = branch_list; p; p = p->next) {
461         SeafBranch *b = (SeafBranch *)p->data;
462         seaf_repo_manager_branch_repo_unmap (mgr, b);
463         seaf_branch_manager_del_branch (seaf->branch_mgr, repo_id, b->name);
464     }
465     seaf_branch_list_free (branch_list);
466 
467     seaf_db_statement_query (db, "DELETE FROM RepoOwner WHERE repo_id = ?",
468                    1, "string", repo_id);
469 
470     seaf_db_statement_query (db, "DELETE FROM SharedRepo WHERE repo_id = ?",
471                    1, "string", repo_id);
472 
473     seaf_db_statement_query (db, "DELETE FROM RepoGroup WHERE repo_id = ?",
474                    1, "string", repo_id);
475 
476     if (!seaf->cloud_mode) {
477         seaf_db_statement_query (db, "DELETE FROM InnerPubRepo WHERE repo_id = ?",
478                                  1, "string", repo_id);
479     }
480 
481     seaf_db_statement_query (mgr->seaf->db,
482                              "DELETE FROM RepoUserToken WHERE repo_id = ?",
483                              1, "string", repo_id);
484 
485     seaf_db_statement_query (mgr->seaf->db,
486                              "DELETE FROM RepoValidSince WHERE repo_id = ?",
487                              1, "string", repo_id);
488 
489     seaf_db_statement_query (mgr->seaf->db,
490                              "DELETE FROM RepoSize WHERE repo_id = ?",
491                              1, "string", repo_id);
492 
493     /* For GC commit objects for this virtual repo. Fs and blocks are GC
494      * from the parent repo.
495      */
496     add_deleted_repo_record (mgr, repo_id);
497 
498     return 0;
499 }
500 
501 static gboolean
get_branch(SeafDBRow * row,void * vid)502 get_branch (SeafDBRow *row, void *vid)
503 {
504     char *ret = vid;
505     const char *commit_id;
506 
507     commit_id = seaf_db_row_get_column_text (row, 0);
508     memcpy (ret, commit_id, 41);
509 
510     return FALSE;
511 }
512 
513 static SeafCommit*
get_head_commit(SeafRepoManager * mgr,const char * repo_id,gboolean * has_err)514 get_head_commit (SeafRepoManager *mgr, const char *repo_id, gboolean *has_err)
515 {
516     char commit_id[41];
517     char *sql;
518 
519     commit_id[0] = 0;
520     sql = "SELECT commit_id FROM Branch WHERE name=? AND repo_id=?";
521     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
522                                        get_branch, commit_id,
523                                        2, "string", "master", "string", repo_id) < 0) {
524         *has_err = TRUE;
525         return NULL;
526     }
527 
528     if (commit_id[0] == 0)
529         return NULL;
530 
531     SeafCommit *head_commit = seaf_commit_manager_get_commit (seaf->commit_mgr, repo_id,
532                                                               1, commit_id);
533 
534     return head_commit;
535 }
536 
537 int
seaf_repo_manager_del_repo(SeafRepoManager * mgr,const char * repo_id,GError ** error)538 seaf_repo_manager_del_repo (SeafRepoManager *mgr,
539                             const char *repo_id,
540                             GError **error)
541 {
542     gboolean has_err = FALSE;
543 
544     SeafCommit *head_commit = get_head_commit (mgr, repo_id, &has_err);
545     if (has_err) {
546         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
547                      "Failed to get head commit from db");
548         return -1;
549     }
550     if (!head_commit) {
551         // head commit is missing, del repo directly
552         goto del_repo;
553     }
554 
555     if (add_deleted_repo_to_trash (mgr, repo_id, head_commit) < 0) {
556         // Add repo to trash failed, del repo directly
557         seaf_warning ("Failed to add repo %.8s to trash, delete directly.\n",
558                       repo_id);
559     }
560 
561     seaf_commit_unref (head_commit);
562 
563 del_repo:
564     if (seaf_db_statement_query (mgr->seaf->db, "DELETE FROM Repo WHERE repo_id = ?",
565                                  1, "string", repo_id) < 0) {
566         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
567                      "Failed to delete repo from db");
568         return -1;
569     }
570 
571     /* remove branch */
572     GList *p;
573     GList *branch_list = seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo_id);
574     for (p = branch_list; p; p = p->next) {
575         SeafBranch *b = (SeafBranch *)p->data;
576         seaf_repo_manager_branch_repo_unmap (mgr, b);
577         seaf_branch_manager_del_branch (seaf->branch_mgr, repo_id, b->name);
578     }
579     seaf_branch_list_free (branch_list);
580 
581     seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoOwner WHERE repo_id = ?",
582                              1, "string", repo_id);
583 
584     seaf_db_statement_query (mgr->seaf->db, "DELETE FROM SharedRepo WHERE repo_id = ?",
585                              1, "string", repo_id);
586 
587     seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoGroup WHERE repo_id = ?",
588                              1, "string", repo_id);
589 
590     if (!seaf->cloud_mode) {
591         seaf_db_statement_query (mgr->seaf->db, "DELETE FROM InnerPubRepo WHERE repo_id = ?",
592                                  1, "string", repo_id);
593     }
594 
595     seaf_db_statement_query (mgr->seaf->db,
596                              "DELETE FROM RepoUserToken WHERE repo_id = ?",
597                              1, "string", repo_id);
598 
599     seaf_db_statement_query (mgr->seaf->db,
600                              "DELETE FROM RepoHistoryLimit WHERE repo_id = ?",
601                              1, "string", repo_id);
602 
603     seaf_db_statement_query (mgr->seaf->db,
604                              "DELETE FROM RepoValidSince WHERE repo_id = ?",
605                              1, "string", repo_id);
606 
607     seaf_db_statement_query (mgr->seaf->db,
608                              "DELETE FROM RepoSize WHERE repo_id = ?",
609                              1, "string", repo_id);
610 
611     /* Remove virtual repos when origin repo is deleted. */
612     GList *vrepos, *ptr;
613     vrepos = seaf_repo_manager_get_virtual_repo_ids_by_origin (mgr, repo_id);
614     for (ptr = vrepos; ptr != NULL; ptr = ptr->next)
615         remove_virtual_repo_ondisk (mgr, (char *)ptr->data);
616     string_list_free (vrepos);
617 
618     seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoInfo "
619                              "WHERE repo_id IN (SELECT repo_id FROM VirtualRepo "
620                              "WHERE origin_repo=?)",
621                              1, "string", repo_id);
622 
623     seaf_db_statement_query (mgr->seaf->db, "DELETE FROM VirtualRepo "
624                              "WHERE repo_id=? OR origin_repo=?",
625                              2, "string", repo_id, "string", repo_id);
626 
627     if (!head_commit)
628         add_deleted_repo_record(mgr, repo_id);
629 
630     return 0;
631 }
632 
633 int
seaf_repo_manager_del_virtual_repo(SeafRepoManager * mgr,const char * repo_id)634 seaf_repo_manager_del_virtual_repo (SeafRepoManager *mgr,
635                                     const char *repo_id)
636 {
637     int ret = remove_virtual_repo_ondisk (mgr, repo_id);
638 
639     if (ret < 0)
640         return ret;
641 
642     return seaf_db_statement_query (mgr->seaf->db,
643                                     "DELETE FROM VirtualRepo WHERE repo_id = ?",
644                                     1, "string", repo_id);
645 }
646 
647 static gboolean
repo_exists_in_db(SeafDB * db,const char * id,gboolean * db_err)648 repo_exists_in_db (SeafDB *db, const char *id, gboolean *db_err)
649 {
650     return seaf_db_statement_exists (db,
651                                      "SELECT repo_id FROM Repo WHERE repo_id = ?",
652                                      db_err, 1, "string", id);
653 }
654 
655 gboolean
create_repo_fill_size(SeafDBRow * row,void * data)656 create_repo_fill_size (SeafDBRow *row, void *data)
657 {
658     SeafRepo **repo = data;
659     SeafBranch *head;
660 
661     const char *repo_id = seaf_db_row_get_column_text (row, 0);
662     gint64 size = seaf_db_row_get_column_int64 (row, 1);
663     const char *commit_id = seaf_db_row_get_column_text (row, 2);
664     const char *vrepo_id = seaf_db_row_get_column_text (row, 3);
665     gint64 file_count = seaf_db_row_get_column_int64 (row, 7);
666     int status = seaf_db_row_get_column_int(row, 8);
667 
668     *repo = seaf_repo_new (repo_id, NULL, NULL);
669     if (!*repo)
670         return FALSE;
671 
672     if (!commit_id) {
673         (*repo)->is_corrupted = TRUE;
674         return FALSE;
675     }
676 
677     (*repo)->size = size;
678     (*repo)->file_count = file_count;
679     head = seaf_branch_new ("master", repo_id, commit_id);
680     (*repo)->head = head;
681     (*repo)->status = status;
682 
683     if (vrepo_id) {
684         const char *origin_repo_id = seaf_db_row_get_column_text (row, 4);
685         const char *origin_path = seaf_db_row_get_column_text (row, 5);
686         const char *base_commit = seaf_db_row_get_column_text (row, 6);
687 
688         SeafVirtRepo *vinfo = g_new0 (SeafVirtRepo, 1);
689         memcpy (vinfo->repo_id, vrepo_id, 36);
690         memcpy (vinfo->origin_repo_id, origin_repo_id, 36);
691         vinfo->path = g_strdup(origin_path);
692         memcpy (vinfo->base_commit, base_commit, 40);
693 
694         (*repo)->virtual_info = vinfo;
695         memcpy ((*repo)->store_id, origin_repo_id, 36);
696     } else {
697         memcpy ((*repo)->store_id, repo_id, 36);
698     }
699 
700     return TRUE;
701 }
702 
703 static SeafRepo*
get_repo_from_db(SeafRepoManager * mgr,const char * id,gboolean * db_err)704 get_repo_from_db (SeafRepoManager *mgr, const char *id, gboolean *db_err)
705 {
706     SeafRepo *repo = NULL;
707     const char *sql;
708 
709     if (seaf_db_type(mgr->seaf->db) != SEAF_DB_TYPE_PGSQL)
710         sql = "SELECT r.repo_id, s.size, b.commit_id, "
711             "v.repo_id, v.origin_repo, v.path, v.base_commit, fc.file_count, i.status FROM "
712             "Repo r LEFT JOIN Branch b ON r.repo_id = b.repo_id "
713             "LEFT JOIN RepoSize s ON r.repo_id = s.repo_id "
714             "LEFT JOIN VirtualRepo v ON r.repo_id = v.repo_id "
715             "LEFT JOIN RepoFileCount fc ON r.repo_id = fc.repo_id "
716             "LEFT JOIN RepoInfo i on r.repo_id = i.repo_id "
717             "WHERE r.repo_id = ? AND b.name = 'master'";
718     else
719         sql = "SELECT r.repo_id, s.\"size\", b.commit_id, "
720             "v.repo_id, v.origin_repo, v.path, v.base_commit, fc.file_count, i.status FROM "
721             "Repo r LEFT JOIN Branch b ON r.repo_id = b.repo_id "
722             "LEFT JOIN RepoSize s ON r.repo_id = s.repo_id "
723             "LEFT JOIN VirtualRepo v ON r.repo_id = v.repo_id "
724             "LEFT JOIN RepoFileCount fc ON r.repo_id = fc.repo_id "
725             "LEFT JOIN RepoInfo i on r.repo_id = i.repo_id "
726             "WHERE r.repo_id = ? AND b.name = 'master'";
727 
728     int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
729                                              create_repo_fill_size, &repo,
730                                              1, "string", id);
731     if (ret < 0)
732         *db_err = TRUE;
733 
734     return repo;
735 }
736 
737 SeafRepo*
seaf_repo_manager_get_repo(SeafRepoManager * manager,const gchar * id)738 seaf_repo_manager_get_repo (SeafRepoManager *manager, const gchar *id)
739 {
740     int len = strlen(id);
741     SeafRepo *repo = NULL;
742     gboolean has_err = FALSE;
743 
744     if (len >= 37)
745         return NULL;
746 
747     repo = get_repo_from_db (manager, id, &has_err);
748 
749     if (repo) {
750         if (repo->is_corrupted) {
751             seaf_repo_unref (repo);
752             return NULL;
753         }
754 
755         load_repo (manager, repo);
756         if (repo->is_corrupted) {
757             seaf_repo_unref (repo);
758             return NULL;
759         }
760     }
761 
762     return repo;
763 }
764 
765 SeafRepo*
seaf_repo_manager_get_repo_ex(SeafRepoManager * manager,const gchar * id)766 seaf_repo_manager_get_repo_ex (SeafRepoManager *manager, const gchar *id)
767 {
768     int len = strlen(id);
769     gboolean has_err = FALSE;
770     SeafRepo *ret = NULL;
771 
772     if (len >= 37)
773         return NULL;
774 
775     ret = get_repo_from_db (manager, id, &has_err);
776     if (has_err) {
777         ret = seaf_repo_new(id, NULL, NULL);
778         ret->is_corrupted = TRUE;
779         return ret;
780     }
781 
782     if (ret) {
783         if (ret->is_corrupted) {
784             return ret;
785         }
786 
787         load_repo (manager, ret);
788     }
789 
790     return ret;
791 }
792 
793 gboolean
seaf_repo_manager_repo_exists(SeafRepoManager * manager,const gchar * id)794 seaf_repo_manager_repo_exists (SeafRepoManager *manager, const gchar *id)
795 {
796     gboolean db_err = FALSE;
797     return repo_exists_in_db (manager->seaf->db, id, &db_err);
798 }
799 
800 static int
save_branch_repo_map(SeafRepoManager * manager,SeafBranch * branch)801 save_branch_repo_map (SeafRepoManager *manager, SeafBranch *branch)
802 {
803     if (seaf_db_type(seaf->db) == SEAF_DB_TYPE_PGSQL) {
804         gboolean exists, err;
805         int rc;
806 
807         exists = seaf_db_statement_exists (seaf->db,
808                                            "SELECT repo_id FROM RepoHead WHERE repo_id=?",
809                                            &err, 1, "string", branch->repo_id);
810         if (err)
811             return -1;
812 
813         if (exists)
814             rc = seaf_db_statement_query (seaf->db,
815                                           "UPDATE RepoHead SET branch_name=? "
816                                           "WHERE repo_id=?",
817                                           2, "string", branch->name,
818                                           "string", branch->repo_id);
819         else
820             rc = seaf_db_statement_query (seaf->db,
821                                           "INSERT INTO RepoHead (repo_id, branch_name) VALUES (?, ?)",
822                                           2, "string", branch->repo_id,
823                                           "string", branch->name);
824         return rc;
825     } else {
826         return seaf_db_statement_query (seaf->db,
827                                         "REPLACE INTO RepoHead (repo_id, branch_name) VALUES (?, ?)",
828                                         2, "string", branch->repo_id,
829                                         "string", branch->name);
830     }
831 
832     return -1;
833 }
834 
835 int
seaf_repo_manager_branch_repo_unmap(SeafRepoManager * manager,SeafBranch * branch)836 seaf_repo_manager_branch_repo_unmap (SeafRepoManager *manager, SeafBranch *branch)
837 {
838     return seaf_db_statement_query (seaf->db,
839                                     "DELETE FROM RepoHead WHERE branch_name = ?"
840                                     " AND repo_id = ?",
841                                     2, "string", branch->name,
842                                     "string", branch->repo_id);
843 }
844 
845 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)846 set_repo_commit_to_db (const char *repo_id, const char *repo_name, gint64 update_time,
847                        int version, gboolean is_encrypted, const char *last_modifier)
848 {
849     char *sql;
850     gboolean exists = FALSE, db_err = FALSE;
851 
852     sql = "SELECT 1 FROM RepoInfo WHERE repo_id=?";
853     exists = seaf_db_statement_exists (seaf->db, sql, &db_err, 1, "string", repo_id);
854     if (db_err)
855         return -1;
856 
857     if (update_time == 0)
858         update_time = (gint64)time(NULL);
859 
860     if (exists) {
861         sql = "UPDATE RepoInfo SET name=?, update_time=?, version=?, is_encrypted=?, "
862             "last_modifier=? WHERE repo_id=?";
863         if (seaf_db_statement_query (seaf->db, sql, 6,
864                                      "string", repo_name,
865                                      "int64", update_time,
866                                      "int", version,
867                                      "int", (is_encrypted ? 1:0),
868                                      "string", last_modifier,
869                                      "string", repo_id) < 0) {
870             seaf_warning ("Failed to update repo info for repo %s.\n", repo_id);
871             return -1;
872         }
873     } else {
874         sql = "INSERT INTO RepoInfo (repo_id, name, update_time, version, is_encrypted, last_modifier) "
875             "VALUES (?, ?, ?, ?, ?, ?)";
876         if (seaf_db_statement_query (seaf->db, sql, 6,
877                                      "string", repo_id,
878                                      "string", repo_name,
879                                      "int64", update_time,
880                                      "int", version,
881                                      "int", (is_encrypted ? 1:0),
882                                      "string", last_modifier) < 0) {
883             seaf_warning ("Failed to add repo info for repo %s.\n", repo_id);
884             return -1;
885         }
886     }
887 
888     return 0;
889 }
890 
891 static void
load_repo_commit(SeafRepoManager * manager,SeafRepo * repo)892 load_repo_commit (SeafRepoManager *manager,
893                   SeafRepo *repo)
894 {
895     SeafCommit *commit;
896 
897     commit = seaf_commit_manager_get_commit_compatible (manager->seaf->commit_mgr,
898                                                         repo->id,
899                                                         repo->head->commit_id);
900     if (!commit) {
901         seaf_warning ("Commit %s:%s is missing\n", repo->id, repo->head->commit_id);
902         repo->is_corrupted = TRUE;
903         return;
904     }
905 
906     seaf_repo_from_commit (repo, commit);
907 
908     seaf_commit_unref (commit);
909 }
910 
911 static void
load_repo(SeafRepoManager * manager,SeafRepo * repo)912 load_repo (SeafRepoManager *manager, SeafRepo *repo)
913 {
914     repo->manager = manager;
915 
916     load_repo_commit (manager, repo);
917 }
918 
919 static void
load_mini_repo(SeafRepoManager * manager,SeafRepo * repo)920 load_mini_repo (SeafRepoManager *manager, SeafRepo *repo)
921 {
922     repo->manager = manager;
923     SeafCommit *commit;
924 
925     commit = seaf_commit_manager_get_commit_compatible (manager->seaf->commit_mgr,
926                                                         repo->id,
927                                                         repo->head->commit_id);
928     if (!commit) {
929         seaf_warning ("Commit %s:%s is missing\n", repo->id, repo->head->commit_id);
930         repo->is_corrupted = TRUE;
931         return;
932     }
933 
934     repo->name = g_strdup (commit->repo_name);
935     repo->encrypted = commit->encrypted;
936     repo->last_modify = commit->ctime;
937     repo->version = commit->version;
938     repo->last_modifier = g_strdup (commit->creator_name);
939 
940     seaf_commit_unref (commit);
941 }
942 
943 static int
create_tables_mysql(SeafRepoManager * mgr)944 create_tables_mysql (SeafRepoManager *mgr)
945 {
946     SeafDB *db = mgr->seaf->db;
947     char *sql;
948 
949     sql = "CREATE TABLE IF NOT EXISTS Repo (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
950           "repo_id CHAR(37), UNIQUE INDEX (repo_id))"
951         "ENGINE=INNODB";
952     if (seaf_db_query (db, sql) < 0)
953         return -1;
954 
955     sql = "CREATE TABLE IF NOT EXISTS RepoOwner ("
956         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
957         "repo_id CHAR(37), "
958         "owner_id VARCHAR(255),"
959         "UNIQUE INDEX (repo_id), INDEX (owner_id))"
960         "ENGINE=INNODB";
961     if (seaf_db_query (db, sql) < 0)
962         return -1;
963 
964     sql = "CREATE TABLE IF NOT EXISTS RepoGroup (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,"
965         "repo_id CHAR(37), "
966         "group_id INTEGER, user_name VARCHAR(255), permission CHAR(15), "
967         "UNIQUE INDEX (group_id, repo_id), "
968         "INDEX (repo_id), INDEX (user_name))"
969         "ENGINE=INNODB";
970     if (seaf_db_query (db, sql) < 0)
971         return -1;
972 
973     sql = "CREATE TABLE IF NOT EXISTS InnerPubRepo ("
974         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
975         "repo_id CHAR(37),"
976         "permission CHAR(15), UNIQUE INDEX (repo_id))"
977         "ENGINE=INNODB";
978     if (seaf_db_query (db, sql) < 0)
979         return -1;
980 
981     sql = "CREATE TABLE IF NOT EXISTS RepoUserToken ("
982         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
983         "repo_id CHAR(37), "
984         "email VARCHAR(255), "
985         "token CHAR(41), "
986         "UNIQUE INDEX (repo_id, token), INDEX (email))"
987         "ENGINE=INNODB";
988     if (seaf_db_query (db, sql) < 0)
989         return -1;
990 
991     sql = "CREATE TABLE IF NOT EXISTS RepoTokenPeerInfo ("
992         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
993         "token CHAR(41), "
994         "peer_id CHAR(41), "
995         "peer_ip VARCHAR(41), "
996         "peer_name VARCHAR(255), "
997         "sync_time BIGINT, "
998         "client_ver VARCHAR(20), UNIQUE INDEX(token))"
999         "ENGINE=INNODB";
1000     if (seaf_db_query (db, sql) < 0)
1001         return -1;
1002 
1003     sql = "CREATE TABLE IF NOT EXISTS RepoHead ("
1004         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1005         "repo_id CHAR(37), branch_name VARCHAR(10), UNIQUE INDEX(repo_id))"
1006         "ENGINE=INNODB";
1007     if (seaf_db_query (db, sql) < 0)
1008         return -1;
1009 
1010     sql = "CREATE TABLE IF NOT EXISTS RepoSize ("
1011         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1012         "repo_id CHAR(37),"
1013         "size BIGINT UNSIGNED,"
1014         "head_id CHAR(41), UNIQUE INDEX (repo_id))"
1015         "ENGINE=INNODB";
1016     if (seaf_db_query (db, sql) < 0)
1017         return -1;
1018 
1019     sql = "CREATE TABLE IF NOT EXISTS RepoHistoryLimit ("
1020         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1021         "repo_id CHAR(37), days INTEGER, UNIQUE INDEX(repo_id))"
1022         "ENGINE=INNODB";
1023     if (seaf_db_query (db, sql) < 0)
1024         return -1;
1025 
1026     sql = "CREATE TABLE IF NOT EXISTS RepoValidSince ("
1027         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1028         "repo_id CHAR(37), timestamp BIGINT, UNIQUE INDEX(repo_id))"
1029         "ENGINE=INNODB";
1030     if (seaf_db_query (db, sql) < 0)
1031         return -1;
1032 
1033     sql = "CREATE TABLE IF NOT EXISTS WebAP (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1034         "repo_id CHAR(37), "
1035         "access_property CHAR(10), UNIQUE INDEX(repo_id))"
1036         "ENGINE=INNODB";
1037     if (seaf_db_query (db, sql) < 0)
1038         return -1;
1039 
1040     sql = "CREATE TABLE IF NOT EXISTS VirtualRepo (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1041         "repo_id CHAR(36),"
1042         "origin_repo CHAR(36), path TEXT, base_commit CHAR(40), UNIQUE INDEX(repo_id), INDEX(origin_repo))"
1043         "ENGINE=INNODB";
1044     if (seaf_db_query (db, sql) < 0)
1045         return -1;
1046 
1047     sql = "CREATE TABLE IF NOT EXISTS GarbageRepos (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1048           "repo_id CHAR(36), UNIQUE INDEX(repo_id))";
1049     if (seaf_db_query (db, sql) < 0)
1050         return -1;
1051 
1052     sql = "CREATE TABLE IF NOT EXISTS RepoTrash (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1053         "repo_id CHAR(36),"
1054         "repo_name VARCHAR(255), head_id CHAR(40), owner_id VARCHAR(255),"
1055         "size BIGINT(20), org_id INTEGER, del_time BIGINT, "
1056         "UNIQUE INDEX(repo_id), INDEX(owner_id), INDEX(org_id))ENGINE=INNODB";
1057     if (seaf_db_query (db, sql) < 0)
1058         return -1;
1059 
1060     sql = "CREATE TABLE IF NOT EXISTS RepoFileCount ("
1061         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1062         "repo_id CHAR(36),"
1063         "file_count BIGINT UNSIGNED, UNIQUE INDEX(repo_id))ENGINE=INNODB";
1064     if (seaf_db_query (db, sql) < 0)
1065         return -1;
1066 
1067     sql = "CREATE TABLE IF NOT EXISTS RepoInfo (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
1068         "repo_id CHAR(36), "
1069         "name VARCHAR(255) NOT NULL, update_time BIGINT, version INTEGER, "
1070         "is_encrypted INTEGER, last_modifier VARCHAR(255), status INTEGER DEFAULT 0, UNIQUE INDEX(repo_id)) ENGINE=INNODB";
1071     if (seaf_db_query (db, sql) < 0)
1072         return -1;
1073 
1074     sql = "CREATE TABLE IF NOT EXISTS WebUploadTempFiles ( "
1075         "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, repo_id CHAR(40) NOT NULL, "
1076         "file_path TEXT NOT NULL, tmp_file_path TEXT NOT NULL) ENGINE=INNODB";
1077     if (seaf_db_query (db, sql) < 0)
1078         return -1;
1079 
1080     return 0;
1081 }
1082 
1083 static int
create_tables_sqlite(SeafRepoManager * mgr)1084 create_tables_sqlite (SeafRepoManager *mgr)
1085 {
1086     SeafDB *db = mgr->seaf->db;
1087     char *sql;
1088 
1089     sql = "CREATE TABLE IF NOT EXISTS Repo (repo_id CHAR(37) PRIMARY KEY)";
1090     if (seaf_db_query (db, sql) < 0)
1091         return -1;
1092 
1093     /* Owner */
1094 
1095     sql = "CREATE TABLE IF NOT EXISTS RepoOwner ("
1096         "repo_id CHAR(37) PRIMARY KEY, "
1097         "owner_id TEXT)";
1098     if (seaf_db_query (db, sql) < 0)
1099         return -1;
1100     sql = "CREATE INDEX IF NOT EXISTS OwnerIndex ON RepoOwner (owner_id)";
1101     if (seaf_db_query (db, sql) < 0)
1102         return -1;
1103 
1104     /* Group repo */
1105 
1106     sql = "CREATE TABLE IF NOT EXISTS RepoGroup (repo_id CHAR(37), "
1107         "group_id INTEGER, user_name TEXT, permission CHAR(15))";
1108     if (seaf_db_query (db, sql) < 0)
1109         return -1;
1110 
1111     sql = "CREATE UNIQUE INDEX IF NOT EXISTS groupid_repoid_indx on "
1112         "RepoGroup (group_id, repo_id)";
1113     if (seaf_db_query (db, sql) < 0)
1114         return -1;
1115 
1116     sql = "CREATE INDEX IF NOT EXISTS repogroup_repoid_index on "
1117         "RepoGroup (repo_id)";
1118     if (seaf_db_query (db, sql) < 0)
1119         return -1;
1120 
1121     sql = "CREATE INDEX IF NOT EXISTS repogroup_username_indx on "
1122         "RepoGroup (user_name)";
1123     if (seaf_db_query (db, sql) < 0)
1124         return -1;
1125 
1126     /* Public repo */
1127 
1128     sql = "CREATE TABLE IF NOT EXISTS InnerPubRepo ("
1129         "repo_id CHAR(37) PRIMARY KEY,"
1130         "permission CHAR(15))";
1131     if (seaf_db_query (db, sql) < 0)
1132         return -1;
1133 
1134     sql = "CREATE TABLE IF NOT EXISTS RepoUserToken ("
1135         "repo_id CHAR(37), "
1136         "email VARCHAR(255), "
1137         "token CHAR(41))";
1138     if (seaf_db_query (db, sql) < 0)
1139         return -1;
1140 
1141     sql = "CREATE UNIQUE INDEX IF NOT EXISTS repo_token_indx on "
1142         "RepoUserToken (repo_id, token)";
1143     if (seaf_db_query (db, sql) < 0)
1144         return -1;
1145 
1146     sql = "CREATE INDEX IF NOT EXISTS repo_token_email_indx on "
1147         "RepoUserToken (email)";
1148     if (seaf_db_query (db, sql) < 0)
1149         return -1;
1150 
1151     sql = "CREATE TABLE IF NOT EXISTS RepoTokenPeerInfo ("
1152         "token CHAR(41) PRIMARY KEY, "
1153         "peer_id CHAR(41), "
1154         "peer_ip VARCHAR(41), "
1155         "peer_name VARCHAR(255), "
1156         "sync_time BIGINT, "
1157         "client_ver VARCHAR(20))";
1158     if (seaf_db_query (db, sql) < 0)
1159         return -1;
1160 
1161     sql = "CREATE TABLE IF NOT EXISTS RepoHead ("
1162         "repo_id CHAR(37) PRIMARY KEY, branch_name VARCHAR(10))";
1163     if (seaf_db_query (db, sql) < 0)
1164         return -1;
1165 
1166     sql = "CREATE TABLE IF NOT EXISTS RepoSize ("
1167         "repo_id CHAR(37) PRIMARY KEY,"
1168         "size BIGINT UNSIGNED,"
1169         "head_id CHAR(41))";
1170     if (seaf_db_query (db, sql) < 0)
1171         return -1;
1172 
1173     sql = "CREATE TABLE IF NOT EXISTS RepoHistoryLimit ("
1174         "repo_id CHAR(37) PRIMARY KEY, days INTEGER)";
1175     if (seaf_db_query (db, sql) < 0)
1176         return -1;
1177 
1178     sql = "CREATE TABLE IF NOT EXISTS RepoValidSince ("
1179         "repo_id CHAR(37) PRIMARY KEY, timestamp BIGINT)";
1180     if (seaf_db_query (db, sql) < 0)
1181         return -1;
1182 
1183     sql = "CREATE TABLE IF NOT EXISTS WebAP (repo_id CHAR(37) PRIMARY KEY, "
1184         "access_property CHAR(10))";
1185     if (seaf_db_query (db, sql) < 0)
1186         return -1;
1187 
1188     sql = "CREATE TABLE IF NOT EXISTS VirtualRepo (repo_id CHAR(36) PRIMARY KEY,"
1189         "origin_repo CHAR(36), path TEXT, base_commit CHAR(40))";
1190     if (seaf_db_query (db, sql) < 0)
1191         return -1;
1192 
1193     sql = "CREATE INDEX IF NOT EXISTS virtualrepo_origin_repo_idx "
1194         "ON VirtualRepo (origin_repo)";
1195     if (seaf_db_query (db, sql) < 0)
1196         return -1;
1197 
1198     sql = "CREATE TABLE IF NOT EXISTS GarbageRepos (repo_id CHAR(36) PRIMARY KEY)";
1199     if (seaf_db_query (db, sql) < 0)
1200         return -1;
1201 
1202     sql = "CREATE TABLE IF NOT EXISTS RepoTrash (repo_id CHAR(36) PRIMARY KEY,"
1203         "repo_name VARCHAR(255), head_id CHAR(40), owner_id VARCHAR(255), size BIGINT UNSIGNED,"
1204         "org_id INTEGER, del_time BIGINT)";
1205     if (seaf_db_query (db, sql) < 0)
1206         return -1;
1207 
1208     sql = "CREATE INDEX IF NOT EXISTS repotrash_owner_id_idx ON RepoTrash(owner_id)";
1209     if (seaf_db_query (db, sql) < 0)
1210         return -1;
1211 
1212     sql = "CREATE INDEX IF NOT EXISTS repotrash_org_id_idx ON RepoTrash(org_id)";
1213     if (seaf_db_query (db, sql) < 0)
1214         return -1;
1215 
1216     sql = "CREATE TABLE IF NOT EXISTS RepoFileCount ("
1217         "repo_id CHAR(36) PRIMARY KEY,"
1218         "file_count BIGINT UNSIGNED)";
1219     if (seaf_db_query (db, sql) < 0)
1220         return -1;
1221 
1222     sql = "CREATE TABLE IF NOT EXISTS RepoInfo (repo_id CHAR(36) PRIMARY KEY, "
1223         "name VARCHAR(255) NOT NULL, update_time INTEGER, version INTEGER, "
1224         "is_encrypted INTEGER, last_modifier VARCHAR(255), status INTEGER DEFAULT 0)";
1225     if (seaf_db_query (db, sql) < 0)
1226         return -1;
1227 
1228     sql = "CREATE TABLE IF NOT EXISTS WebUploadTempFiles (repo_id CHAR(40) NOT NULL, "
1229         "file_path TEXT NOT NULL, tmp_file_path TEXT NOT NULL)";
1230     if (seaf_db_query (db, sql) < 0)
1231         return -1;
1232 
1233     return 0;
1234 }
1235 
1236 /* static int */
1237 /* create_tables_pgsql (SeafRepoManager *mgr) */
1238 /* { */
1239 /*     SeafDB *db = mgr->seaf->db; */
1240 /*     char *sql; */
1241 
1242 /*     sql = "CREATE TABLE IF NOT EXISTS Repo (repo_id CHAR(36) PRIMARY KEY)"; */
1243 /*     if (seaf_db_query (db, sql) < 0) */
1244 /*         return -1; */
1245 
1246 /*     sql = "CREATE TABLE IF NOT EXISTS RepoOwner (" */
1247 /*         "repo_id CHAR(36) PRIMARY KEY, " */
1248 /*         "owner_id VARCHAR(255))"; */
1249 /*     if (seaf_db_query (db, sql) < 0) */
1250 /*         return -1; */
1251 
1252 /*     if (!pgsql_index_exists (db, "repoowner_owner_idx")) { */
1253 /*         sql = "CREATE INDEX repoowner_owner_idx ON RepoOwner (owner_id)"; */
1254 /*         if (seaf_db_query (db, sql) < 0) */
1255 /*             return -1; */
1256 /*     } */
1257 
1258 /*     sql = "CREATE TABLE IF NOT EXISTS RepoGroup (repo_id CHAR(36), " */
1259 /*         "group_id INTEGER, user_name VARCHAR(255), permission VARCHAR(15), " */
1260 /*         "UNIQUE (group_id, repo_id))"; */
1261 /*     if (seaf_db_query (db, sql) < 0) */
1262 /*         return -1; */
1263 
1264 /*     if (!pgsql_index_exists (db, "repogroup_repoid_idx")) { */
1265 /*         sql = "CREATE INDEX repogroup_repoid_idx ON RepoGroup (repo_id)"; */
1266 /*         if (seaf_db_query (db, sql) < 0) */
1267 /*             return -1; */
1268 /*     } */
1269 
1270 /*     if (!pgsql_index_exists (db, "repogroup_username_idx")) { */
1271 /*         sql = "CREATE INDEX repogroup_username_idx ON RepoGroup (user_name)"; */
1272 /*         if (seaf_db_query (db, sql) < 0) */
1273 /*             return -1; */
1274 /*     } */
1275 
1276 /*     sql = "CREATE TABLE IF NOT EXISTS InnerPubRepo (" */
1277 /*         "repo_id CHAR(36) PRIMARY KEY," */
1278 /*         "permission VARCHAR(15))"; */
1279 /*     if (seaf_db_query (db, sql) < 0) */
1280 /*         return -1; */
1281 
1282 /*     sql = "CREATE TABLE IF NOT EXISTS RepoUserToken (" */
1283 /*         "repo_id CHAR(36), " */
1284 /*         "email VARCHAR(255), " */
1285 /*         "token CHAR(40), " */
1286 /*         "UNIQUE (repo_id, token))"; */
1287 /*     if (seaf_db_query (db, sql) < 0) */
1288 /*         return -1; */
1289 
1290 /*     if (!pgsql_index_exists (db, "repousertoken_email_idx")) { */
1291 /*         sql = "CREATE INDEX repousertoken_email_idx ON RepoUserToken (email)"; */
1292 /*         if (seaf_db_query (db, sql) < 0) */
1293 /*             return -1; */
1294 /*     } */
1295 
1296 /*     sql = "CREATE TABLE IF NOT EXISTS RepoTokenPeerInfo (" */
1297 /*         "token CHAR(40) PRIMARY KEY, " */
1298 /*         "peer_id CHAR(40), " */
1299 /*         "peer_ip VARCHAR(40), " */
1300 /*         "peer_name VARCHAR(255), " */
1301 /*         "sync_time BIGINT, " */
1302 /*         "client_ver VARCHAR(20))"; */
1303 /*     if (seaf_db_query (db, sql) < 0) */
1304 /*         return -1; */
1305 
1306 /*     sql = "CREATE TABLE IF NOT EXISTS RepoHead (" */
1307 /*         "repo_id CHAR(36) PRIMARY KEY, branch_name VARCHAR(10))"; */
1308 /*     if (seaf_db_query (db, sql) < 0) */
1309 /*         return -1; */
1310 
1311 /*     sql = "CREATE TABLE IF NOT EXISTS RepoSize (" */
1312 /*         "repo_id CHAR(36) PRIMARY KEY," */
1313 /*         "size BIGINT," */
1314 /*         "head_id CHAR(40))"; */
1315 /*     if (seaf_db_query (db, sql) < 0) */
1316 /*         return -1; */
1317 
1318 /*     sql = "CREATE TABLE IF NOT EXISTS RepoHistoryLimit (" */
1319 /*         "repo_id CHAR(36) PRIMARY KEY, days INTEGER)"; */
1320 /*     if (seaf_db_query (db, sql) < 0) */
1321 /*         return -1; */
1322 
1323 /*     sql = "CREATE TABLE IF NOT EXISTS RepoValidSince (" */
1324 /*         "repo_id CHAR(36) PRIMARY KEY, timestamp BIGINT)"; */
1325 /*     if (seaf_db_query (db, sql) < 0) */
1326 /*         return -1; */
1327 
1328 /*     sql = "CREATE TABLE IF NOT EXISTS WebAP (repo_id CHAR(36) PRIMARY KEY, " */
1329 /*         "access_property VARCHAR(10))"; */
1330 /*     if (seaf_db_query (db, sql) < 0) */
1331 /*         return -1; */
1332 
1333 /*     sql = "CREATE TABLE IF NOT EXISTS VirtualRepo (repo_id CHAR(36) PRIMARY KEY," */
1334 /*         "origin_repo CHAR(36), path TEXT, base_commit CHAR(40))"; */
1335 /*     if (seaf_db_query (db, sql) < 0) */
1336 /*         return -1; */
1337 
1338 /*     if (!pgsql_index_exists (db, "virtualrepo_origin_repo_idx")) { */
1339 /*         sql = "CREATE INDEX virtualrepo_origin_repo_idx ON VirtualRepo (origin_repo)"; */
1340 /*         if (seaf_db_query (db, sql) < 0) */
1341 /*             return -1; */
1342 /*     } */
1343 
1344 /*     sql = "CREATE TABLE IF NOT EXISTS GarbageRepos (repo_id CHAR(36) PRIMARY KEY)"; */
1345 /*     if (seaf_db_query (db, sql) < 0) */
1346 /*         return -1; */
1347 
1348 /*     sql = "CREATE TABLE IF NOT EXISTS RepoTrash (repo_id CHAR(36) PRIMARY KEY," */
1349 /*         "repo_name VARCHAR(255), head_id CHAR(40), owner_id VARCHAR(255), size bigint," */
1350 /*         "org_id INTEGER, del_time BIGINT)"; */
1351 /*     if (seaf_db_query (db, sql) < 0) */
1352 /*         return -1; */
1353 
1354 /*     if (!pgsql_index_exists (db, "repotrash_owner_id")) { */
1355 /*         sql = "CREATE INDEX repotrash_owner_id on RepoTrash(owner_id)"; */
1356 /*         if (seaf_db_query (db, sql) < 0) */
1357 /*             return -1; */
1358 /*     } */
1359 /*     if (!pgsql_index_exists (db, "repotrash_org_id")) { */
1360 /*         sql = "CREATE INDEX repotrash_org_id on RepoTrash(org_id)"; */
1361 /*         if (seaf_db_query (db, sql) < 0) */
1362 /*             return -1; */
1363 /*     } */
1364 
1365 /*     sql = "CREATE TABLE IF NOT EXISTS RepoFileCount (" */
1366 /*         "repo_id CHAR(36) PRIMARY KEY," */
1367 /*         "file_count BIGINT)"; */
1368 /*     if (seaf_db_query (db, sql) < 0) */
1369 /*         return -1; */
1370 
1371 /*     sql = "CREATE TABLE IF NOT EXISTS WebUploadTempFiles (repo_id CHAR(40) NOT NULL, " */
1372 /*         "file_path TEXT NOT NULL, tmp_file_path TEXT NOT NULL)"; */
1373 /*     if (seaf_db_query (db, sql) < 0) */
1374 /*         return -1; */
1375 
1376 /*     sql = "CREATE TABLE IF NOT EXISTS RepoInfo (repo_id CHAR(36) PRIMARY KEY, " */
1377 /*         "name VARCHAR(255) NOT NULL, update_time BIGINT, version INTEGER, " */
1378 /*         "is_encrypted INTEGER, last_modifier VARCHAR(255), status INTEGER DEFAULT 0)"; */
1379 /*     if (seaf_db_query (db, sql) < 0) */
1380 /*         return -1; */
1381 
1382 /*     return 0; */
1383 /* } */
1384 
1385 static int
create_db_tables_if_not_exist(SeafRepoManager * mgr)1386 create_db_tables_if_not_exist (SeafRepoManager *mgr)
1387 {
1388     if (!mgr->seaf->create_tables && seaf_db_type (mgr->seaf->db) != SEAF_DB_TYPE_PGSQL)
1389         return 0;
1390 
1391     SeafDB *db = mgr->seaf->db;
1392     int db_type = seaf_db_type (db);
1393 
1394     if (db_type == SEAF_DB_TYPE_MYSQL)
1395         return create_tables_mysql (mgr);
1396     else if (db_type == SEAF_DB_TYPE_SQLITE)
1397         return create_tables_sqlite (mgr);
1398     /* else if (db_type == SEAF_DB_TYPE_PGSQL) */
1399     /*     return create_tables_pgsql (mgr); */
1400 
1401     g_return_val_if_reached (-1);
1402 }
1403 
1404 /*
1405  * Repo properties functions.
1406  */
1407 
1408 static inline char *
generate_repo_token()1409 generate_repo_token ()
1410 {
1411     char *uuid = gen_uuid ();
1412     unsigned char sha1[20];
1413     char token[41];
1414     SHA_CTX s;
1415 
1416     SHA1_Init (&s);
1417     SHA1_Update (&s, uuid, strlen(uuid));
1418     SHA1_Final (sha1, &s);
1419 
1420     rawdata_to_hex (sha1, token, 20);
1421 
1422     g_free (uuid);
1423 
1424     return g_strdup (token);
1425 }
1426 
1427 static int
add_repo_token(SeafRepoManager * mgr,const char * repo_id,const char * email,const char * token,GError ** error)1428 add_repo_token (SeafRepoManager *mgr,
1429                 const char *repo_id,
1430                 const char *email,
1431                 const char *token,
1432                 GError **error)
1433 {
1434     int rc = seaf_db_statement_query (mgr->seaf->db,
1435                                       "INSERT INTO RepoUserToken (repo_id, email, token) VALUES (?, ?, ?)",
1436                                       3, "string", repo_id, "string", email,
1437                                       "string", token);
1438 
1439     if (rc < 0) {
1440         seaf_warning ("failed to add repo token. repo = %s, email = %s\n",
1441                       repo_id, email);
1442         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
1443         return -1;
1444     }
1445 
1446     return 0;
1447 }
1448 
1449 char *
seaf_repo_manager_generate_repo_token(SeafRepoManager * mgr,const char * repo_id,const char * email,GError ** error)1450 seaf_repo_manager_generate_repo_token (SeafRepoManager *mgr,
1451                                        const char *repo_id,
1452                                        const char *email,
1453                                        GError **error)
1454 {
1455     char *token = generate_repo_token ();
1456     if (add_repo_token (mgr, repo_id, email, token, error) < 0) {
1457         g_free (token);
1458         return NULL;
1459     }
1460 
1461     return token;
1462 }
1463 
1464 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)1465 seaf_repo_manager_add_token_peer_info (SeafRepoManager *mgr,
1466                                        const char *token,
1467                                        const char *peer_id,
1468                                        const char *peer_ip,
1469                                        const char *peer_name,
1470                                        gint64 sync_time,
1471                                        const char *client_ver)
1472 {
1473     int ret = 0;
1474 
1475     if (seaf_db_statement_query (mgr->seaf->db,
1476                                  "INSERT INTO RepoTokenPeerInfo (token, peer_id, peer_ip, peer_name, sync_time, client_ver)"
1477                                  "VALUES (?, ?, ?, ?, ?, ?)",
1478                                  6, "string", token,
1479                                  "string", peer_id,
1480                                  "string", peer_ip,
1481                                  "string", peer_name,
1482                                  "int64", sync_time,
1483                                  "string", client_ver) < 0)
1484         ret = -1;
1485 
1486     return ret;
1487 }
1488 
1489 int
seaf_repo_manager_update_token_peer_info(SeafRepoManager * mgr,const char * token,const char * peer_ip,gint64 sync_time,const char * client_ver)1490 seaf_repo_manager_update_token_peer_info (SeafRepoManager *mgr,
1491                                           const char *token,
1492                                           const char *peer_ip,
1493                                           gint64 sync_time,
1494                                           const char *client_ver)
1495 {
1496     int ret = 0;
1497 
1498     if (seaf_db_statement_query (mgr->seaf->db,
1499                                  "UPDATE RepoTokenPeerInfo SET "
1500                                  "peer_ip=?, sync_time=?, client_ver=? WHERE token=?",
1501                                  4, "string", peer_ip,
1502                                  "int64", sync_time,
1503                                  "string", client_ver,
1504                                  "string", token) < 0)
1505         ret = -1;
1506 
1507     return ret;
1508 }
1509 
1510 gboolean
seaf_repo_manager_token_peer_info_exists(SeafRepoManager * mgr,const char * token)1511 seaf_repo_manager_token_peer_info_exists (SeafRepoManager *mgr,
1512                                           const char *token)
1513 {
1514     gboolean db_error = FALSE;
1515 
1516     return seaf_db_statement_exists (mgr->seaf->db,
1517                                      "SELECT token FROM RepoTokenPeerInfo WHERE token=?",
1518                                      &db_error, 1, "string", token);
1519 }
1520 
1521 int
seaf_repo_manager_delete_token(SeafRepoManager * mgr,const char * repo_id,const char * token,const char * user,GError ** error)1522 seaf_repo_manager_delete_token (SeafRepoManager *mgr,
1523                                 const char *repo_id,
1524                                 const char *token,
1525                                 const char *user,
1526                                 GError **error)
1527 {
1528     char *token_owner;
1529 
1530     token_owner = seaf_repo_manager_get_email_by_token (mgr, repo_id, token);
1531     if (!token_owner || strcmp (user, token_owner) != 0) {
1532         seaf_warning ("Requesting user is %s, token owner is %s, "
1533                       "refuse to delete token %.10s.\n", user, token_owner, token);
1534         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Permission denied");
1535         return -1;
1536     }
1537 
1538     if (seaf_db_statement_query (mgr->seaf->db,
1539                                  "DELETE FROM RepoUserToken "
1540                                  "WHERE repo_id=? and token=?",
1541                                  2, "string", repo_id, "string", token) < 0) {
1542         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
1543         return -1;
1544     }
1545 
1546     if (seaf_db_statement_query (mgr->seaf->db,
1547                                  "DELETE FROM RepoTokenPeerInfo WHERE token=?",
1548                                  1, "string", token) < 0) {
1549         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
1550         return -1;
1551     }
1552 
1553     GList *tokens = NULL;
1554     tokens = g_list_append (tokens, g_strdup(token));
1555     seaf_http_server_invalidate_tokens (seaf->http_server, tokens);
1556     g_list_free_full (tokens, (GDestroyNotify)g_free);
1557 
1558     return 0;
1559 }
1560 
1561 static gboolean
collect_repo_token(SeafDBRow * row,void * data)1562 collect_repo_token (SeafDBRow *row, void *data)
1563 {
1564     GList **ret_list = data;
1565     const char *repo_id, *repo_owner, *email, *token;
1566     const char *peer_id, *peer_ip, *peer_name;
1567     gint64 sync_time;
1568     const char *client_ver;
1569 
1570     repo_id = seaf_db_row_get_column_text (row, 0);
1571     repo_owner = seaf_db_row_get_column_text (row, 1);
1572     email = seaf_db_row_get_column_text (row, 2);
1573     token = seaf_db_row_get_column_text (row, 3);
1574 
1575     peer_id = seaf_db_row_get_column_text (row, 4);
1576     peer_ip = seaf_db_row_get_column_text (row, 5);
1577     peer_name = seaf_db_row_get_column_text (row, 6);
1578     sync_time = seaf_db_row_get_column_int64 (row, 7);
1579     client_ver = seaf_db_row_get_column_text (row, 8);
1580 
1581     char *owner_l = g_ascii_strdown (repo_owner, -1);
1582     char *email_l = g_ascii_strdown (email, -1);
1583 
1584     SeafileRepoTokenInfo *repo_token_info;
1585     repo_token_info = g_object_new (SEAFILE_TYPE_REPO_TOKEN_INFO,
1586                                     "repo_id", repo_id,
1587                                     "repo_owner", owner_l,
1588                                     "email", email_l,
1589                                     "token", token,
1590                                     "peer_id", peer_id,
1591                                     "peer_ip", peer_ip,
1592                                     "peer_name", peer_name,
1593                                     "sync_time", sync_time,
1594                                     "client_ver", client_ver,
1595                                     NULL);
1596 
1597     *ret_list = g_list_prepend (*ret_list, repo_token_info);
1598 
1599     g_free (owner_l);
1600     g_free (email_l);
1601 
1602     return TRUE;
1603 }
1604 
1605 static void
fill_in_token_info(GList * info_list)1606 fill_in_token_info (GList *info_list)
1607 {
1608     GList *ptr;
1609     SeafileRepoTokenInfo *info;
1610     SeafRepo *repo;
1611     char *repo_name;
1612 
1613     for (ptr = info_list; ptr; ptr = ptr->next) {
1614         info = ptr->data;
1615         repo = seaf_repo_manager_get_repo (seaf->repo_mgr,
1616                                            seafile_repo_token_info_get_repo_id(info));
1617         if (repo)
1618             repo_name = g_strdup(repo->name);
1619         else
1620             repo_name = g_strdup("Unknown");
1621         seaf_repo_unref (repo);
1622 
1623         g_object_set (info, "repo_name", repo_name, NULL);
1624         g_free (repo_name);
1625     }
1626 }
1627 
1628 GList *
seaf_repo_manager_list_repo_tokens(SeafRepoManager * mgr,const char * repo_id,GError ** error)1629 seaf_repo_manager_list_repo_tokens (SeafRepoManager *mgr,
1630                                     const char *repo_id,
1631                                     GError **error)
1632 {
1633     GList *ret_list = NULL;
1634     char *sql;
1635     gboolean db_err = FALSE;
1636 
1637     if (!repo_exists_in_db (mgr->seaf->db, repo_id, &db_err)) {
1638         if (db_err) {
1639             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
1640         }
1641         return NULL;
1642     }
1643 
1644     sql = "SELECT u.repo_id, o.owner_id, u.email, u.token, "
1645         "p.peer_id, p.peer_ip, p.peer_name, p.sync_time, p.client_ver "
1646         "FROM RepoUserToken u LEFT JOIN RepoTokenPeerInfo p "
1647         "ON u.token = p.token, RepoOwner o "
1648         "WHERE u.repo_id = ? and o.repo_id = ? ";
1649 
1650     int n_row = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
1651                                               collect_repo_token, &ret_list,
1652                                               2, "string", repo_id,
1653                                               "string", repo_id);
1654     if (n_row < 0) {
1655         seaf_warning ("DB error when get token info for repo %.10s.\n",
1656                       repo_id);
1657         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
1658     }
1659 
1660     fill_in_token_info (ret_list);
1661 
1662     return g_list_reverse(ret_list);
1663 }
1664 
1665 GList *
seaf_repo_manager_list_repo_tokens_by_email(SeafRepoManager * mgr,const char * email,GError ** error)1666 seaf_repo_manager_list_repo_tokens_by_email (SeafRepoManager *mgr,
1667                                              const char *email,
1668                                              GError **error)
1669 {
1670     GList *ret_list = NULL;
1671     char *sql;
1672 
1673     sql = "SELECT u.repo_id, o.owner_id, u.email, u.token, "
1674         "p.peer_id, p.peer_ip, p.peer_name, p.sync_time, p.client_ver "
1675         "FROM RepoUserToken u LEFT JOIN RepoTokenPeerInfo p "
1676         "ON u.token = p.token, RepoOwner o "
1677         "WHERE u.email = ? and u.repo_id = o.repo_id";
1678 
1679     int n_row = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
1680                                               collect_repo_token, &ret_list,
1681                                               1, "string", email);
1682     if (n_row < 0) {
1683         seaf_warning ("DB error when get token info for email %s.\n",
1684                       email);
1685         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "DB error");
1686     }
1687 
1688     fill_in_token_info (ret_list);
1689 
1690     return g_list_reverse(ret_list);
1691 }
1692 
1693 static gboolean
collect_token_list(SeafDBRow * row,void * data)1694 collect_token_list (SeafDBRow *row, void *data)
1695 {
1696     GList **p_tokens = data;
1697     const char *token;
1698 
1699     token = seaf_db_row_get_column_text (row, 0);
1700     *p_tokens = g_list_prepend (*p_tokens, g_strdup(token));
1701 
1702     return TRUE;
1703 }
1704 
1705 /**
1706  * Delete all repo tokens for a given user on a given client
1707  */
1708 
1709 int
seaf_repo_manager_delete_repo_tokens_by_peer_id(SeafRepoManager * mgr,const char * email,const char * peer_id,GList ** tokens,GError ** error)1710 seaf_repo_manager_delete_repo_tokens_by_peer_id (SeafRepoManager *mgr,
1711                                                  const char *email,
1712                                                  const char *peer_id,
1713                                                  GList **tokens,
1714                                                  GError **error)
1715 {
1716     int ret = 0;
1717     const char *template;
1718     GList *token_list = NULL;
1719     GString *token_list_str = g_string_new ("");
1720     GString *sql = g_string_new ("");
1721     GList *ptr;
1722     int rc = 0;
1723 
1724     template = "SELECT u.token "
1725         "FROM RepoUserToken u, RepoTokenPeerInfo p "
1726         "WHERE u.token = p.token "
1727         "AND u.email = ? AND p.peer_id = ?";
1728     rc = seaf_db_statement_foreach_row (mgr->seaf->db, template,
1729                                         collect_token_list, &token_list,
1730                                         2, "string", email, "string", peer_id);
1731     if (rc < 0) {
1732         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
1733         goto out;
1734     }
1735 
1736     if (rc == 0)
1737         goto out;
1738 
1739     for (ptr = token_list; ptr; ptr = ptr->next) {
1740         const char *token = (char *)ptr->data;
1741         if (token_list_str->len == 0)
1742             g_string_append_printf (token_list_str, "'%s'", token);
1743         else
1744             g_string_append_printf (token_list_str, ",'%s'", token);
1745     }
1746 
1747     /* Note that there is a size limit on sql query. In MySQL it's 1MB by default.
1748      * Normally the token_list won't be that long.
1749      */
1750     g_string_printf (sql, "DELETE FROM RepoUserToken WHERE token in (%s)",
1751                      token_list_str->str);
1752     rc = seaf_db_statement_query (mgr->seaf->db, sql->str, 0);
1753     if (rc < 0) {
1754         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
1755         goto out;
1756     }
1757 
1758     g_string_printf (sql, "DELETE FROM RepoTokenPeerInfo WHERE token in (%s)",
1759                      token_list_str->str);
1760     rc = seaf_db_statement_query (mgr->seaf->db, sql->str, 0);
1761     if (rc < 0)
1762         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
1763 
1764 out:
1765     g_string_free (token_list_str, TRUE);
1766     g_string_free (sql, TRUE);
1767 
1768     if (rc < 0) {
1769         ret = -1;
1770         g_list_free_full (token_list, (GDestroyNotify)g_free);
1771     } else {
1772         *tokens = token_list;
1773     }
1774 
1775     return ret;
1776 }
1777 
1778 int
seaf_repo_manager_delete_repo_tokens_by_email(SeafRepoManager * mgr,const char * email,GError ** error)1779 seaf_repo_manager_delete_repo_tokens_by_email (SeafRepoManager *mgr,
1780                                                const char *email,
1781                                                GError **error)
1782 {
1783     int ret = 0;
1784     const char *template;
1785     GList *token_list = NULL;
1786     GList *ptr;
1787     GString *token_list_str = g_string_new ("");
1788     GString *sql = g_string_new ("");
1789     int rc;
1790 
1791     template = "SELECT u.token "
1792         "FROM RepoUserToken u, RepoTokenPeerInfo p "
1793         "WHERE u.token = p.token "
1794         "AND u.email = ?";
1795     rc = seaf_db_statement_foreach_row (mgr->seaf->db, template,
1796                                         collect_token_list, &token_list,
1797                                         1, "string", email);
1798     if (rc < 0) {
1799         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
1800         goto out;
1801     }
1802 
1803     if (rc == 0)
1804         goto out;
1805 
1806     for (ptr = token_list; ptr; ptr = ptr->next) {
1807         const char *token = (char *)ptr->data;
1808         if (token_list_str->len == 0)
1809             g_string_append_printf (token_list_str, "'%s'", token);
1810         else
1811             g_string_append_printf (token_list_str, ",'%s'", token);
1812     }
1813 
1814     /* Note that there is a size limit on sql query. In MySQL it's 1MB by default.
1815      * Normally the token_list won't be that long.
1816      */
1817     g_string_printf (sql, "DELETE FROM RepoUserToken WHERE token in (%s)",
1818                      token_list_str->str);
1819     rc = seaf_db_statement_query (mgr->seaf->db, sql->str, 0);
1820     if (rc < 0) {
1821         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
1822         goto out;
1823     }
1824 
1825     g_string_printf (sql, "DELETE FROM RepoTokenPeerInfo WHERE token in (%s)",
1826                      token_list_str->str);
1827     rc = seaf_db_statement_query (mgr->seaf->db, sql->str, 0);
1828     if (rc < 0) {
1829         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, "DB error");
1830         goto out;
1831     }
1832 
1833     seaf_http_server_invalidate_tokens (seaf->http_server, token_list);
1834 
1835 out:
1836     g_string_free (token_list_str, TRUE);
1837     g_string_free (sql, TRUE);
1838     g_list_free_full (token_list, (GDestroyNotify)g_free);
1839 
1840     if (rc < 0) {
1841         ret = -1;
1842     }
1843 
1844     return ret;
1845 }
1846 
1847 static gboolean
get_email_by_token_cb(SeafDBRow * row,void * data)1848 get_email_by_token_cb (SeafDBRow *row, void *data)
1849 {
1850     char **email_ptr = data;
1851 
1852     const char *email = (const char *) seaf_db_row_get_column_text (row, 0);
1853     *email_ptr = g_ascii_strdown (email, -1);
1854     /* There should be only one result. */
1855     return FALSE;
1856 }
1857 
1858 char *
seaf_repo_manager_get_email_by_token(SeafRepoManager * manager,const char * repo_id,const char * token)1859 seaf_repo_manager_get_email_by_token (SeafRepoManager *manager,
1860                                       const char *repo_id,
1861                                       const char *token)
1862 {
1863     if (!repo_id || !token)
1864         return NULL;
1865 
1866     char *email = NULL;
1867     char *sql;
1868 
1869     sql = "SELECT email FROM RepoUserToken "
1870         "WHERE repo_id = ? AND token = ?";
1871 
1872     seaf_db_statement_foreach_row (seaf->db, sql,
1873                                    get_email_by_token_cb, &email,
1874                                    2, "string", repo_id, "string", token);
1875 
1876     return email;
1877 }
1878 
1879 static gboolean
get_repo_size(SeafDBRow * row,void * vsize)1880 get_repo_size (SeafDBRow *row, void *vsize)
1881 {
1882     gint64 *psize = vsize;
1883 
1884     *psize = seaf_db_row_get_column_int64 (row, 0);
1885 
1886     return FALSE;
1887 }
1888 
1889 gint64
seaf_repo_manager_get_repo_size(SeafRepoManager * mgr,const char * repo_id)1890 seaf_repo_manager_get_repo_size (SeafRepoManager *mgr, const char *repo_id)
1891 {
1892     gint64 size = 0;
1893     char *sql;
1894 
1895     sql = "SELECT size FROM RepoSize WHERE repo_id=?";
1896 
1897     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
1898                                        get_repo_size, &size,
1899                                        1, "string", repo_id) < 0)
1900         return -1;
1901 
1902     return size;
1903 }
1904 
1905 int
seaf_repo_manager_set_repo_history_limit(SeafRepoManager * mgr,const char * repo_id,int days)1906 seaf_repo_manager_set_repo_history_limit (SeafRepoManager *mgr,
1907                                           const char *repo_id,
1908                                           int days)
1909 {
1910     SeafVirtRepo *vinfo;
1911     SeafDB *db = mgr->seaf->db;
1912 
1913     vinfo = seaf_repo_manager_get_virtual_repo_info (mgr, repo_id);
1914     if (vinfo) {
1915         seaf_virtual_repo_info_free (vinfo);
1916         return 0;
1917     }
1918 
1919     if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
1920         gboolean exists, err;
1921         int rc;
1922 
1923         exists = seaf_db_statement_exists (db,
1924                                            "SELECT repo_id FROM RepoHistoryLimit "
1925                                            "WHERE repo_id=?",
1926                                            &err, 1, "string", repo_id);
1927         if (err)
1928             return -1;
1929 
1930         if (exists)
1931             rc = seaf_db_statement_query (db,
1932                                           "UPDATE RepoHistoryLimit SET days=? "
1933                                           "WHERE repo_id=?",
1934                                           2, "int", days, "string", repo_id);
1935         else
1936             rc = seaf_db_statement_query (db,
1937                                           "INSERT INTO RepoHistoryLimit (repo_id, days) VALUES "
1938                                           "(?, ?)",
1939                                           2, "string", repo_id, "int", days);
1940         return rc;
1941     } else {
1942         if (seaf_db_statement_query (db,
1943                                      "REPLACE INTO RepoHistoryLimit (repo_id, days) VALUES (?, ?)",
1944                                      2, "string", repo_id, "int", days) < 0)
1945             return -1;
1946     }
1947 
1948     return 0;
1949 }
1950 
1951 static gboolean
get_history_limit_cb(SeafDBRow * row,void * data)1952 get_history_limit_cb (SeafDBRow *row, void *data)
1953 {
1954     int *limit = data;
1955 
1956     *limit = seaf_db_row_get_column_int (row, 0);
1957 
1958     return FALSE;
1959 }
1960 
1961 int
seaf_repo_manager_get_repo_history_limit(SeafRepoManager * mgr,const char * repo_id)1962 seaf_repo_manager_get_repo_history_limit (SeafRepoManager *mgr,
1963                                           const char *repo_id)
1964 {
1965     SeafVirtRepo *vinfo;
1966     const char *r_repo_id = repo_id;
1967     char *sql;
1968     int per_repo_days = -1;
1969     int ret;
1970 
1971     vinfo = seaf_repo_manager_get_virtual_repo_info (mgr, repo_id);
1972     if (vinfo)
1973         r_repo_id = vinfo->origin_repo_id;
1974 
1975     sql = "SELECT days FROM RepoHistoryLimit WHERE repo_id=?";
1976 
1977     ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_history_limit_cb,
1978                                          &per_repo_days, 1, "string", r_repo_id);
1979     if (ret == 0) {
1980         // limit not set, return global one
1981         per_repo_days= seaf_cfg_manager_get_config_int (mgr->seaf->cfg_mgr,
1982                                                         "history", "keep_days");
1983     }
1984 
1985     // db error or limit set as negative, means keep full history, return -1
1986     if (per_repo_days < 0)
1987         per_repo_days = -1;
1988 
1989     seaf_virtual_repo_info_free (vinfo);
1990 
1991     return per_repo_days;
1992 }
1993 
1994 int
seaf_repo_manager_set_repo_valid_since(SeafRepoManager * mgr,const char * repo_id,gint64 timestamp)1995 seaf_repo_manager_set_repo_valid_since (SeafRepoManager *mgr,
1996                                         const char *repo_id,
1997                                         gint64 timestamp)
1998 {
1999     SeafDB *db = mgr->seaf->db;
2000 
2001     if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
2002         gboolean exists, err;
2003         int rc;
2004 
2005         exists = seaf_db_statement_exists (db,
2006                                            "SELECT repo_id FROM RepoValidSince WHERE "
2007                                            "repo_id=?", &err, 1, "string", repo_id);
2008         if (err)
2009             return -1;
2010 
2011         if (exists)
2012             rc = seaf_db_statement_query (db,
2013                                           "UPDATE RepoValidSince SET timestamp=?"
2014                                           " WHERE repo_id=?",
2015                                           2, "int64", timestamp, "string", repo_id);
2016         else
2017             rc = seaf_db_statement_query (db,
2018                                           "INSERT INTO RepoValidSince (repo_id, timestamp) VALUES "
2019                                           "(?, ?)", 2, "string", repo_id,
2020                                           "int64", timestamp);
2021         if (rc < 0)
2022             return -1;
2023     } else {
2024         if (seaf_db_statement_query (db,
2025                            "REPLACE INTO RepoValidSince (repo_id, timestamp) VALUES (?, ?)",
2026                            2, "string", repo_id, "int64", timestamp) < 0)
2027             return -1;
2028     }
2029 
2030     return 0;
2031 }
2032 
2033 gint64
seaf_repo_manager_get_repo_valid_since(SeafRepoManager * mgr,const char * repo_id)2034 seaf_repo_manager_get_repo_valid_since (SeafRepoManager *mgr,
2035                                         const char *repo_id)
2036 {
2037     char *sql;
2038 
2039     sql = "SELECT timestamp FROM RepoValidSince WHERE repo_id=?";
2040     /* Also return -1 if doesn't exist. */
2041     return seaf_db_statement_get_int64 (mgr->seaf->db, sql, 1, "string", repo_id);
2042 }
2043 
2044 gint64
seaf_repo_manager_get_repo_truncate_time(SeafRepoManager * mgr,const char * repo_id)2045 seaf_repo_manager_get_repo_truncate_time (SeafRepoManager *mgr,
2046                                           const char *repo_id)
2047 {
2048     int days;
2049     gint64 timestamp;
2050 
2051     days = seaf_repo_manager_get_repo_history_limit (mgr, repo_id);
2052     timestamp = seaf_repo_manager_get_repo_valid_since (mgr, repo_id);
2053 
2054     gint64 now = (gint64)time(NULL);
2055     if (days > 0)
2056         return MAX (now - days * 24 * 3600, timestamp);
2057     else if (days < 0)
2058         return timestamp;
2059     else
2060         return 0;
2061 }
2062 
2063 /*
2064  * Permission related functions.
2065  */
2066 
2067 /* Owner functions. */
2068 
2069 int
seaf_repo_manager_set_repo_owner(SeafRepoManager * mgr,const char * repo_id,const char * email)2070 seaf_repo_manager_set_repo_owner (SeafRepoManager *mgr,
2071                                   const char *repo_id,
2072                                   const char *email)
2073 {
2074     SeafDB *db = mgr->seaf->db;
2075     char sql[256];
2076     char *orig_owner = NULL;
2077     int ret = 0;
2078 
2079     orig_owner = seaf_repo_manager_get_repo_owner (mgr, repo_id);
2080     if (g_strcmp0 (orig_owner, email) == 0)
2081         goto out;
2082 
2083     if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
2084         gboolean err;
2085         snprintf(sql, sizeof(sql),
2086                  "SELECT repo_id FROM RepoOwner WHERE repo_id=?");
2087         if (seaf_db_statement_exists (db, sql, &err,
2088                                       1, "string", repo_id))
2089             snprintf(sql, sizeof(sql),
2090                      "UPDATE RepoOwner SET owner_id='%s' WHERE "
2091                      "repo_id='%s'", email, repo_id);
2092         else
2093             snprintf(sql, sizeof(sql),
2094                      "INSERT INTO RepoOwner (repo_id, owner_id) VALUES ('%s', '%s')",
2095                      repo_id, email);
2096         if (err) {
2097             ret = -1;
2098             goto out;
2099         }
2100 
2101         if (seaf_db_query (db, sql) < 0) {
2102             ret = -1;
2103             goto out;
2104         }
2105     } else {
2106         if (seaf_db_statement_query (db, "REPLACE INTO RepoOwner (repo_id, owner_id) VALUES (?, ?)",
2107                                      2, "string", repo_id, "string", email) < 0) {
2108             ret = -1;
2109             goto out;
2110         }
2111     }
2112 
2113     /* If the repo was newly created, no need to remove share and virtual repos. */
2114     if (!orig_owner)
2115         goto out;
2116 
2117     seaf_db_statement_query (mgr->seaf->db, "DELETE FROM SharedRepo WHERE repo_id = ?",
2118                              1, "string", repo_id);
2119 
2120     seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoGroup WHERE repo_id = ?",
2121                              1, "string", repo_id);
2122 
2123     if (!seaf->cloud_mode) {
2124         seaf_db_statement_query (mgr->seaf->db, "DELETE FROM InnerPubRepo WHERE repo_id = ?",
2125                                  1, "string", repo_id);
2126     }
2127 
2128     /* Remove virtual repos when repo ownership changes. */
2129     GList *vrepos, *ptr;
2130     vrepos = seaf_repo_manager_get_virtual_repo_ids_by_origin (mgr, repo_id);
2131     for (ptr = vrepos; ptr != NULL; ptr = ptr->next)
2132         remove_virtual_repo_ondisk (mgr, (char *)ptr->data);
2133     string_list_free (vrepos);
2134 
2135     seaf_db_statement_query (mgr->seaf->db, "DELETE FROM VirtualRepo "
2136                              "WHERE repo_id=? OR origin_repo=?",
2137                              2, "string", repo_id, "string", repo_id);
2138 
2139 out:
2140     g_free (orig_owner);
2141     return ret;
2142 }
2143 
2144 static gboolean
get_owner(SeafDBRow * row,void * data)2145 get_owner (SeafDBRow *row, void *data)
2146 {
2147     char **owner_id = data;
2148 
2149     const char *owner = (const char *) seaf_db_row_get_column_text (row, 0);
2150     *owner_id = g_ascii_strdown (owner, -1);
2151     /* There should be only one result. */
2152     return FALSE;
2153 }
2154 
2155 char *
seaf_repo_manager_get_repo_owner(SeafRepoManager * mgr,const char * repo_id)2156 seaf_repo_manager_get_repo_owner (SeafRepoManager *mgr,
2157                                   const char *repo_id)
2158 {
2159     char *sql;
2160     char *ret = NULL;
2161 
2162     sql = "SELECT owner_id FROM RepoOwner WHERE repo_id=?";
2163     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
2164                                        get_owner, &ret,
2165                                        1, "string", repo_id) < 0) {
2166         seaf_warning ("Failed to get owner id for repo %s.\n", repo_id);
2167         return NULL;
2168     }
2169 
2170     return ret;
2171 }
2172 
2173 static gboolean
collect_repo_id(SeafDBRow * row,void * data)2174 collect_repo_id (SeafDBRow *row, void *data)
2175 {
2176     GList **p_ids = data;
2177     const char *repo_id;
2178 
2179     repo_id = seaf_db_row_get_column_text (row, 0);
2180     *p_ids = g_list_prepend (*p_ids, g_strdup(repo_id));
2181 
2182     return TRUE;
2183 }
2184 
2185 GList *
seaf_repo_manager_get_orphan_repo_list(SeafRepoManager * mgr)2186 seaf_repo_manager_get_orphan_repo_list (SeafRepoManager *mgr)
2187 {
2188     GList *id_list = NULL, *ptr;
2189     GList *ret = NULL;
2190     char sql[256];
2191 
2192     snprintf (sql, sizeof(sql), "SELECT Repo.repo_id FROM Repo LEFT JOIN "
2193               "RepoOwner ON Repo.repo_id = RepoOwner.repo_id WHERE "
2194               "RepoOwner.owner_id is NULL");
2195 
2196     if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
2197                                       collect_repo_id, &id_list) < 0)
2198         return NULL;
2199 
2200     for (ptr = id_list; ptr; ptr = ptr->next) {
2201         char *repo_id = ptr->data;
2202         SeafRepo *repo = seaf_repo_manager_get_repo (mgr, repo_id);
2203         if (repo != NULL)
2204             ret = g_list_prepend (ret, repo);
2205     }
2206 
2207     string_list_free (id_list);
2208 
2209     return ret;
2210 }
2211 
2212 gboolean
collect_repos_fill_size_commit(SeafDBRow * row,void * data)2213 collect_repos_fill_size_commit (SeafDBRow *row, void *data)
2214 {
2215     GList **prepos = data;
2216     SeafRepo *repo;
2217     SeafBranch *head;
2218 
2219     const char *repo_id = seaf_db_row_get_column_text (row, 0);
2220     gint64 size = seaf_db_row_get_column_int64 (row, 1);
2221     const char *commit_id = seaf_db_row_get_column_text (row, 2);
2222     const char *repo_name = seaf_db_row_get_column_text (row, 3);
2223     gint64 update_time = seaf_db_row_get_column_int64 (row, 4);
2224     int version = seaf_db_row_get_column_int (row, 5);
2225     gboolean is_encrypted = seaf_db_row_get_column_int (row, 6) ? TRUE : FALSE;
2226     const char *last_modifier = seaf_db_row_get_column_text (row, 7);
2227     int status = seaf_db_row_get_column_int (row, 8);
2228 
2229     repo = seaf_repo_new (repo_id, NULL, NULL);
2230     if (!repo)
2231         return TRUE;
2232 
2233     if (!commit_id) {
2234         repo->is_corrupted = TRUE;
2235         goto out;
2236     }
2237 
2238     repo->size = size;
2239     if (seaf_db_row_get_column_count (row) == 10) {
2240         gint64 file_count = seaf_db_row_get_column_int64 (row, 9);
2241         repo->file_count = file_count;
2242     }
2243     head = seaf_branch_new ("master", repo_id, commit_id);
2244     repo->head = head;
2245     if (repo_name) {
2246         repo->name = g_strdup (repo_name);
2247         repo->last_modify = update_time;
2248         repo->version = version;
2249         repo->encrypted = is_encrypted;
2250         repo->last_modifier = g_strdup (last_modifier);
2251         repo->status = status;
2252     }
2253 
2254 out:
2255     *prepos = g_list_prepend (*prepos, repo);
2256 
2257     return TRUE;
2258 }
2259 
2260 GList *
seaf_repo_manager_get_repos_by_owner(SeafRepoManager * mgr,const char * email,int ret_corrupted,int start,int limit,gboolean * db_err)2261 seaf_repo_manager_get_repos_by_owner (SeafRepoManager *mgr,
2262                                       const char *email,
2263                                       int ret_corrupted,
2264                                       int start,
2265                                       int limit,
2266                                       gboolean *db_err)
2267 {
2268     GList *repo_list = NULL, *ptr;
2269     GList *ret = NULL;
2270     char *sql;
2271     SeafRepo *repo = NULL;
2272     int db_type = seaf_db_type(mgr->seaf->db);
2273 
2274     if (start == -1 && limit == -1) {
2275         if (db_type != SEAF_DB_TYPE_PGSQL)
2276             sql = "SELECT o.repo_id, s.size, b.commit_id, i.name, i.update_time, "
2277                 "i.version, i.is_encrypted, i.last_modifier, i.status FROM "
2278                 "RepoOwner o LEFT JOIN RepoSize s ON o.repo_id = s.repo_id "
2279                 "LEFT JOIN Branch b ON o.repo_id = b.repo_id "
2280                 "LEFT JOIN RepoInfo i ON o.repo_id = i.repo_id "
2281                 "WHERE owner_id=? AND "
2282                 "o.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
2283                 "ORDER BY i.update_time DESC, o.repo_id";
2284         else
2285             sql = "SELECT o.repo_id, s.\"size\", b.commit_id, i.name, i.update_time, "
2286                 "i.version, i.is_encrypted, i.last_modifier, i.status FROM "
2287                 "RepoOwner o LEFT JOIN RepoSize s ON o.repo_id = s.repo_id "
2288                 "LEFT JOIN Branch b ON o.repo_id = b.repo_id "
2289                 "LEFT JOIN RepoInfo i ON o.repo_id = i.repo_id "
2290                 "WHERE owner_id=? AND "
2291                 "o.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
2292                 "ORDER BY i.update_time DESC, o.repo_id";
2293 
2294         if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
2295                                            collect_repos_fill_size_commit, &repo_list,
2296                                            1, "string", email) < 0) {
2297             if (db_err)
2298                 *db_err = TRUE;
2299             return NULL;
2300         }
2301     } else {
2302         if (db_type != SEAF_DB_TYPE_PGSQL)
2303             sql = "SELECT o.repo_id, s.size, b.commit_id, i.name, i.update_time, "
2304                 "i.version, i.is_encrypted, i.last_modifier, i.status FROM "
2305                 "RepoOwner o LEFT JOIN RepoSize s ON o.repo_id = s.repo_id "
2306                 "LEFT JOIN Branch b ON o.repo_id = b.repo_id "
2307                 "LEFT JOIN RepoInfo i ON o.repo_id = i.repo_id "
2308                 "WHERE owner_id=? AND "
2309                 "o.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
2310                 "ORDER BY i.update_time DESC, o.repo_id "
2311                 "LIMIT ? OFFSET ?";
2312         else
2313             sql = "SELECT o.repo_id, s.\"size\", b.commit_id, i.name, i.update_time, "
2314                 "i.version, i.is_encrypted, i.last_modifier, i.status FROM "
2315                 "RepoOwner o LEFT JOIN RepoSize s ON o.repo_id = s.repo_id "
2316                 "LEFT JOIN Branch b ON o.repo_id = b.repo_id "
2317                 "LEFT JOIN RepoInfo i ON o.repo_id = i.repo_id "
2318                 "WHERE owner_id=? AND "
2319                 "o.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
2320                 "ORDER BY i.update_time DESC, o.repo_id "
2321                 "LIMIT ? OFFSET ?";
2322 
2323         if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
2324                                            collect_repos_fill_size_commit,
2325                                            &repo_list,
2326                                            3, "string", email,
2327                                            "int", limit,
2328                                            "int", start) < 0) {
2329             if (db_err)
2330                 *db_err = TRUE;
2331             return NULL;
2332         }
2333     }
2334 
2335     for (ptr = repo_list; ptr; ptr = ptr->next) {
2336         repo = ptr->data;
2337         if (ret_corrupted) {
2338             if (!repo->is_corrupted && (!repo->name || !repo->last_modifier)) {
2339                 load_mini_repo (mgr, repo);
2340                 if (!repo->is_corrupted)
2341                     set_repo_commit_to_db (repo->id, repo->name, repo->last_modify,
2342                                            repo->version, (repo->encrypted ? 1 : 0),
2343                                            repo->last_modifier);
2344             }
2345         } else {
2346             if (repo->is_corrupted) {
2347                 seaf_repo_unref (repo);
2348                 continue;
2349             }
2350             if (!repo->name || !repo->last_modifier) {
2351                 load_mini_repo (mgr, repo);
2352                 if (!repo->is_corrupted)
2353                     set_repo_commit_to_db (repo->id, repo->name, repo->last_modify,
2354                                            repo->version, (repo->encrypted ? 1 : 0),
2355                                            repo->last_modifier);
2356             }
2357             if (repo->is_corrupted) {
2358                 seaf_repo_unref (repo);
2359                 continue;
2360             }
2361         }
2362         if (repo != NULL)
2363             ret = g_list_prepend (ret, repo);
2364     }
2365     g_list_free (repo_list);
2366 
2367     return ret;
2368 }
2369 
2370 GList *
seaf_repo_manager_search_repos_by_name(SeafRepoManager * mgr,const char * name)2371 seaf_repo_manager_search_repos_by_name (SeafRepoManager *mgr, const char *name)
2372 {
2373     GList *repo_list = NULL;
2374     char *sql = NULL;
2375 
2376     char *db_patt = g_strdup_printf ("%%%s%%", name);
2377 
2378     switch (seaf_db_type(seaf->db)) {
2379     case SEAF_DB_TYPE_MYSQL:
2380         sql = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
2381             "i.version, i.is_encrypted, i.last_modifier, i.status, fc.file_count FROM "
2382             "RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
2383             "LEFT JOIN Branch b ON i.repo_id = b.repo_id "
2384             "LEFT JOIN RepoFileCount fc ON i.repo_id = fc.repo_id "
2385             "WHERE i.name COLLATE UTF8_GENERAL_CI LIKE ? AND "
2386             "i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
2387             "i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
2388             "ORDER BY i.update_time DESC, i.repo_id";
2389         break;
2390     case SEAF_DB_TYPE_PGSQL:
2391         sql = "SELECT i.repo_id, s.\"size\", b.commit_id, i.name, i.update_time, "
2392             "i.version, i.is_encrypted, i.last_modifier, i.status, fc.file_count FROM "
2393             "RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
2394             "LEFT JOIN Branch b ON i.repo_id = b.repo_id "
2395             "LEFT JOIN RepoFileCount fc ON i.repo_id = fc.repo_id "
2396             "WHERE i.name ILIKE ? AND "
2397             "i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
2398             "i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
2399             "ORDER BY i.update_time DESC, i.repo_id";
2400         break;
2401     case SEAF_DB_TYPE_SQLITE:
2402         sql = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
2403             "i.version, i.is_encrypted, i.last_modifier, i.status, fc.file_count FROM "
2404             "RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
2405             "LEFT JOIN Branch b ON i.repo_id = b.repo_id "
2406             "LEFT JOIN RepoFileCount fc ON i.repo_id = fc.repo_id "
2407             "WHERE i.name LIKE ? COLLATE NOCASE AND "
2408             "i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
2409             "i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) "
2410             "ORDER BY i.update_time DESC, i.repo_id";
2411         break;
2412     default:
2413         g_free (db_patt);
2414         return NULL;
2415     }
2416 
2417     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
2418                                        collect_repos_fill_size_commit, &repo_list,
2419                                        1, "string", db_patt) < 0) {
2420         g_free (db_patt);
2421         return NULL;
2422     }
2423 
2424     g_free (db_patt);
2425     return repo_list;
2426 }
2427 
2428 GList *
seaf_repo_manager_get_repo_id_list(SeafRepoManager * mgr)2429 seaf_repo_manager_get_repo_id_list (SeafRepoManager *mgr)
2430 {
2431     GList *ret = NULL;
2432     char sql[256];
2433 
2434     snprintf (sql, 256, "SELECT repo_id FROM Repo");
2435 
2436     if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
2437                                       collect_repo_id, &ret) < 0)
2438         return NULL;
2439 
2440     return ret;
2441 }
2442 
2443 GList *
seaf_repo_manager_get_repo_list(SeafRepoManager * mgr,int start,int limit,const char * order_by)2444 seaf_repo_manager_get_repo_list (SeafRepoManager *mgr, int start, int limit, const char *order_by)
2445 {
2446     GList *ret = NULL;
2447     char *sql_base = NULL;
2448     char sql[512];
2449     int rc;
2450 
2451     if (start == -1 && limit == -1) {
2452         switch (seaf_db_type(mgr->seaf->db)) {
2453         case SEAF_DB_TYPE_MYSQL:
2454             sql_base = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
2455                 "i.version, i.is_encrypted, i.last_modifier, i.status, f.file_count FROM "
2456                 "RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
2457                 "LEFT JOIN Branch b ON i.repo_id = b.repo_id "
2458                 "LEFT JOIN RepoFileCount f ON i.repo_id = f.repo_id "
2459                 "WHERE i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
2460                 "i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) ";
2461             if (g_strcmp0 (order_by, "size") == 0)
2462                 snprintf (sql, sizeof(sql), "%sORDER BY s.size DESC, i.repo_id", sql_base);
2463             else if (g_strcmp0 (order_by, "file_count") == 0)
2464                 snprintf (sql, sizeof(sql), "%sORDER BY f.file_count DESC, i.repo_id", sql_base);
2465             else
2466                 snprintf (sql, sizeof(sql), "%sORDER BY i.update_time DESC, i.repo_id", sql_base);
2467             break;
2468         case SEAF_DB_TYPE_SQLITE:
2469             sql_base= "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
2470                 "i.version, i.is_encrypted, i.last_modifier, i.status, f.file_count FROM "
2471                 "RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
2472                 "LEFT JOIN Branch b ON i.repo_id = b.repo_id "
2473                 "LEFT JOIN RepoFileCount f ON i.repo_id = f.repo_id "
2474                 "WHERE i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
2475                 "i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) ";
2476             if (g_strcmp0 (order_by, "size") == 0)
2477                 snprintf (sql, sizeof(sql), "%sORDER BY s.size DESC, i.repo_id", sql_base);
2478             else if (g_strcmp0 (order_by, "file_count") == 0)
2479                 snprintf (sql, sizeof(sql), "%sORDER BY f.file_count DESC, i.repo_id", sql_base);
2480             else
2481                 snprintf (sql, sizeof(sql), "%sORDER BY i.update_time DESC, i.repo_id", sql_base);
2482             break;
2483         default:
2484             return NULL;
2485         }
2486 
2487         rc = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
2488                                             collect_repos_fill_size_commit, &ret,
2489                                             0);
2490     } else {
2491         switch (seaf_db_type(mgr->seaf->db)) {
2492         case SEAF_DB_TYPE_MYSQL:
2493             sql_base = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
2494                 "i.version, i.is_encrypted, i.last_modifier, i.status, f.file_count FROM "
2495                 "RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
2496                 "LEFT JOIN Branch b ON i.repo_id = b.repo_id "
2497                 "LEFT JOIN RepoFileCount f ON i.repo_id = f.repo_id "
2498                 "WHERE i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
2499                 "i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) ";
2500             if (g_strcmp0 (order_by, "size") == 0)
2501                 snprintf (sql, sizeof(sql), "%sORDER BY s.size DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
2502             else if (g_strcmp0 (order_by, "file_count") == 0)
2503                 snprintf (sql, sizeof(sql), "%sORDER BY f.file_count DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
2504             else
2505                 snprintf (sql, sizeof(sql), "%sORDER BY i.update_time DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
2506             break;
2507         case SEAF_DB_TYPE_SQLITE:
2508             sql_base = "SELECT i.repo_id, s.size, b.commit_id, i.name, i.update_time, "
2509                 "i.version, i.is_encrypted, i.last_modifier, i.status, f.file_count FROM "
2510                 "RepoInfo i LEFT JOIN RepoSize s ON i.repo_id = s.repo_id "
2511                 "LEFT JOIN Branch b ON i.repo_id = b.repo_id "
2512                 "LEFT JOIN RepoFileCount f ON i.repo_id = f.repo_id "
2513                 "WHERE i.repo_id IN (SELECT r.repo_id FROM Repo r) AND "
2514                 "i.repo_id NOT IN (SELECT v.repo_id FROM VirtualRepo v) ";
2515             if (g_strcmp0 (order_by, "size") == 0)
2516                 snprintf (sql, sizeof(sql), "%sORDER BY s.size DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
2517             else if (g_strcmp0 (order_by, "file_count") == 0)
2518                 snprintf (sql, sizeof(sql), "%sORDER BY f.file_count DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
2519             else
2520                 snprintf (sql, sizeof(sql), "%sORDER BY i.update_time DESC, i.repo_id LIMIT ? OFFSET ?", sql_base);
2521             break;
2522         default:
2523             return NULL;
2524         }
2525 
2526         rc = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
2527                                             collect_repos_fill_size_commit, &ret,
2528                                             2, "int", limit, "int", start);
2529     }
2530 
2531     if (rc < 0)
2532         return NULL;
2533 
2534     return g_list_reverse (ret);
2535 }
2536 
2537 gint64
seaf_repo_manager_count_repos(SeafRepoManager * mgr,GError ** error)2538 seaf_repo_manager_count_repos (SeafRepoManager *mgr, GError **error)
2539 {
2540     gint64 num = seaf_db_get_int64 (mgr->seaf->db,
2541                                     "SELECT COUNT(repo_id) FROM Repo");
2542     if (num < 0) {
2543         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2544                      "Failed to count repos from db");
2545     }
2546 
2547     return num;
2548 }
2549 
2550 GList *
seaf_repo_manager_get_repo_ids_by_owner(SeafRepoManager * mgr,const char * email)2551 seaf_repo_manager_get_repo_ids_by_owner (SeafRepoManager *mgr,
2552                                          const char *email)
2553 {
2554     GList *ret = NULL;
2555     char *sql;
2556 
2557     sql = "SELECT repo_id FROM RepoOwner WHERE owner_id=?";
2558 
2559     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
2560                                        collect_repo_id, &ret,
2561                                        1, "string", email) < 0) {
2562         string_list_free (ret);
2563         return NULL;
2564     }
2565 
2566     return ret;
2567 }
2568 
2569 static gboolean
collect_trash_repo(SeafDBRow * row,void * data)2570 collect_trash_repo (SeafDBRow *row, void *data)
2571 {
2572     GList **trash_repos = data;
2573     const char *repo_id;
2574     const char *repo_name;
2575     const char *head_id;
2576     const char *owner_id;
2577     gint64 size;
2578     gint64 del_time;
2579 
2580     repo_id = seaf_db_row_get_column_text (row, 0);
2581     repo_name = seaf_db_row_get_column_text (row, 1);
2582     head_id = seaf_db_row_get_column_text (row, 2);
2583     owner_id = seaf_db_row_get_column_text (row, 3);
2584     size = seaf_db_row_get_column_int64 (row, 4);
2585     del_time = seaf_db_row_get_column_int64 (row, 5);
2586 
2587 
2588     if (!repo_id || !repo_name || !head_id || !owner_id)
2589         return TRUE;
2590 
2591     SeafileTrashRepo *trash_repo = g_object_new (SEAFILE_TYPE_TRASH_REPO,
2592                                                  "repo_id", repo_id,
2593                                                  "repo_name", repo_name,
2594                                                  "head_id", head_id,
2595                                                  "owner_id", owner_id,
2596                                                  "size", size,
2597                                                  "del_time", del_time,
2598                                                  NULL);
2599     if (!trash_repo)
2600         return FALSE;
2601 
2602     SeafCommit *commit = seaf_commit_manager_get_commit_compatible (seaf->commit_mgr,
2603                                                                     repo_id, head_id);
2604     if (!commit) {
2605         seaf_warning ("Commit %s not found in repo %s\n", head_id, repo_id);
2606         g_object_unref (trash_repo);
2607         return TRUE;
2608     }
2609     g_object_set (trash_repo, "encrypted", commit->encrypted, NULL);
2610     seaf_commit_unref (commit);
2611 
2612     *trash_repos = g_list_prepend (*trash_repos, trash_repo);
2613 
2614     return TRUE;
2615 }
2616 
2617 GList *
seaf_repo_manager_get_trash_repo_list(SeafRepoManager * mgr,int start,int limit,GError ** error)2618 seaf_repo_manager_get_trash_repo_list (SeafRepoManager *mgr,
2619                                        int start,
2620                                        int limit,
2621                                        GError **error)
2622 {
2623     GList *trash_repos = NULL;
2624     int rc;
2625 
2626     if (start == -1 && limit == -1)
2627         rc = seaf_db_statement_foreach_row (mgr->seaf->db,
2628                                             "SELECT repo_id, repo_name, head_id, owner_id, "
2629                                             "size, del_time FROM RepoTrash ORDER BY del_time DESC",
2630                                             collect_trash_repo, &trash_repos,
2631                                             0);
2632     else
2633         rc = seaf_db_statement_foreach_row (mgr->seaf->db,
2634                                             "SELECT repo_id, repo_name, head_id, owner_id, "
2635                                             "size, del_time FROM RepoTrash "
2636                                             "ORDER BY del_time DESC LIMIT ? OFFSET ?",
2637                                             collect_trash_repo, &trash_repos,
2638                                             2, "int", limit, "int", start);
2639 
2640     if (rc < 0) {
2641         while (trash_repos) {
2642             g_object_unref (trash_repos->data);
2643             trash_repos = g_list_delete_link (trash_repos, trash_repos);
2644         }
2645         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2646                      "Failed to get trashed repo from db.");
2647         return NULL;
2648     }
2649 
2650     return g_list_reverse (trash_repos);
2651 }
2652 
2653 GList *
seaf_repo_manager_get_trash_repos_by_owner(SeafRepoManager * mgr,const char * owner,GError ** error)2654 seaf_repo_manager_get_trash_repos_by_owner (SeafRepoManager *mgr,
2655                                             const char *owner,
2656                                             GError **error)
2657 {
2658     GList *trash_repos = NULL;
2659     int rc;
2660 
2661     rc = seaf_db_statement_foreach_row (mgr->seaf->db,
2662                                         "SELECT repo_id, repo_name, head_id, owner_id, "
2663                                         "size, del_time FROM RepoTrash WHERE owner_id = ?",
2664                                         collect_trash_repo, &trash_repos,
2665                                         1, "string", owner);
2666 
2667     if (rc < 0) {
2668         while (trash_repos) {
2669             g_object_unref (trash_repos->data);
2670             trash_repos = g_list_delete_link (trash_repos, trash_repos);
2671         }
2672         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2673                      "Failed to get trashed repo from db.");
2674         return NULL;
2675     }
2676 
2677     return trash_repos;
2678 }
2679 
2680 SeafileTrashRepo *
seaf_repo_manager_get_repo_from_trash(SeafRepoManager * mgr,const char * repo_id)2681 seaf_repo_manager_get_repo_from_trash (SeafRepoManager *mgr,
2682                                        const char *repo_id)
2683 {
2684     SeafileTrashRepo *ret = NULL;
2685     GList *trash_repos = NULL;
2686     char *sql;
2687     int rc;
2688 
2689     sql = "SELECT repo_id, repo_name, head_id, owner_id, size, del_time FROM RepoTrash "
2690         "WHERE repo_id = ?";
2691     rc = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
2692                                         collect_trash_repo, &trash_repos,
2693                                         1, "string", repo_id);
2694     if (rc < 0)
2695         return NULL;
2696 
2697     /* There should be only one results, since repo_id is a PK. */
2698     if (trash_repos)
2699         ret = trash_repos->data;
2700 
2701     g_list_free (trash_repos);
2702     return ret;
2703 }
2704 
2705 int
seaf_repo_manager_del_repo_from_trash(SeafRepoManager * mgr,const char * repo_id,GError ** error)2706 seaf_repo_manager_del_repo_from_trash (SeafRepoManager *mgr,
2707                                        const char *repo_id,
2708                                        GError **error)
2709 {
2710     /* As long as the repo is successfully moved into GarbageRepo table,
2711      * we consider this operation successful.
2712      */
2713     if (add_deleted_repo_record (mgr, repo_id) < 0) {
2714         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2715                      "DB error: Add deleted record");
2716         return -1;
2717     }
2718 
2719     seaf_db_statement_query (mgr->seaf->db,
2720                              "DELETE FROM RepoFileCount WHERE repo_id = ?",
2721                              1, "string", repo_id);
2722 
2723     seaf_db_statement_query (mgr->seaf->db,
2724                              "DELETE FROM RepoTrash WHERE repo_id = ?",
2725                              1, "string", repo_id);
2726 
2727     seaf_db_statement_query (mgr->seaf->db,
2728                              "DELETE FROM RepoInfo WHERE repo_id = ?",
2729                              1, "string", repo_id);
2730 
2731     return 0;
2732 }
2733 
2734 int
seaf_repo_manager_empty_repo_trash(SeafRepoManager * mgr,GError ** error)2735 seaf_repo_manager_empty_repo_trash (SeafRepoManager *mgr, GError **error)
2736 {
2737     GList *trash_repos = NULL, *ptr;
2738     SeafileTrashRepo *repo;
2739 
2740     trash_repos = seaf_repo_manager_get_trash_repo_list (mgr, -1, -1, error);
2741     if (*error)
2742         return -1;
2743 
2744     for (ptr = trash_repos; ptr; ptr = ptr->next) {
2745         repo = ptr->data;
2746         seaf_repo_manager_del_repo_from_trash (mgr,
2747                                                seafile_trash_repo_get_repo_id(repo),
2748                                                NULL);
2749         g_object_unref (repo);
2750     }
2751 
2752     g_list_free (trash_repos);
2753     return 0;
2754 }
2755 
2756 int
seaf_repo_manager_empty_repo_trash_by_owner(SeafRepoManager * mgr,const char * owner,GError ** error)2757 seaf_repo_manager_empty_repo_trash_by_owner (SeafRepoManager *mgr,
2758                                              const char *owner,
2759                                              GError **error)
2760 {
2761     GList *trash_repos = NULL, *ptr;
2762     SeafileTrashRepo *repo;
2763 
2764     trash_repos = seaf_repo_manager_get_trash_repos_by_owner (mgr, owner, error);
2765     if (*error)
2766         return -1;
2767 
2768     for (ptr = trash_repos; ptr; ptr = ptr->next) {
2769         repo = ptr->data;
2770         seaf_repo_manager_del_repo_from_trash (mgr,
2771                                                seafile_trash_repo_get_repo_id(repo),
2772                                                NULL);
2773         g_object_unref (repo);
2774     }
2775 
2776     g_list_free (trash_repos);
2777     return 0;
2778 }
2779 
2780 int
seaf_repo_manager_restore_repo_from_trash(SeafRepoManager * mgr,const char * repo_id,GError ** error)2781 seaf_repo_manager_restore_repo_from_trash (SeafRepoManager *mgr,
2782                                            const char *repo_id,
2783                                            GError **error)
2784 {
2785     SeafileTrashRepo *repo = NULL;
2786     int ret = 0;
2787     gboolean exists = FALSE;
2788     gboolean db_err;
2789 
2790     repo = seaf_repo_manager_get_repo_from_trash (mgr, repo_id);
2791     if (!repo) {
2792         seaf_warning ("Repo %.8s not found in trash.\n", repo_id);
2793         return -1;
2794     }
2795 
2796     SeafDBTrans *trans = seaf_db_begin_transaction (mgr->seaf->db);
2797 
2798     exists = seaf_db_trans_check_for_existence (trans,
2799                                                 "SELECT 1 FROM Repo WHERE repo_id=?",
2800                                                 &db_err, 1, "string", repo_id);
2801 
2802     if (!exists) {
2803         ret = seaf_db_trans_query (trans,
2804                                    "INSERT INTO Repo(repo_id) VALUES (?)",
2805                                    1, "string", repo_id) < 0;
2806         if (ret < 0) {
2807             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2808                          "DB error: Insert Repo.");
2809             seaf_db_rollback (trans);
2810             seaf_db_trans_close (trans);
2811             goto out;
2812         }
2813     }
2814 
2815     exists = seaf_db_trans_check_for_existence (trans,
2816                                                 "SELECT 1 FROM RepoOwner WHERE repo_id=?",
2817                                                 &db_err, 1, "string", repo_id);
2818 
2819     if (!exists) {
2820         ret = seaf_db_trans_query (trans,
2821                                    "INSERT INTO RepoOwner (repo_id, owner_id) VALUES (?, ?)",
2822                                    2, "string", repo_id,
2823                                    "string", seafile_trash_repo_get_owner_id(repo));
2824         if (ret < 0) {
2825             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2826                          "DB error: Insert Repo Owner.");
2827             seaf_db_rollback (trans);
2828             seaf_db_trans_close (trans);
2829             goto out;
2830         }
2831     }
2832 
2833     exists = seaf_db_trans_check_for_existence (trans,
2834                                                 "SELECT 1 FROM Branch WHERE repo_id=?",
2835                                                 &db_err, 1, "string", repo_id);
2836     if (!exists) {
2837         ret = seaf_db_trans_query (trans,
2838                                    "INSERT INTO Branch (name, repo_id, commit_id) VALUES ('master', ?, ?)",
2839                                    2, "string", repo_id,
2840                                    "string", seafile_trash_repo_get_head_id(repo));
2841         if (ret < 0) {
2842             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2843                          "DB error: Insert Branch.");
2844             seaf_db_rollback (trans);
2845             seaf_db_trans_close (trans);
2846             goto out;
2847         }
2848     }
2849 
2850     exists = seaf_db_trans_check_for_existence (trans,
2851                                                 "SELECT 1 FROM RepoHead WHERE repo_id=?",
2852                                                 &db_err, 1, "string", repo_id);
2853     if (!exists) {
2854         ret = seaf_db_trans_query (trans,
2855                                    "INSERT INTO RepoHead (repo_id, branch_name) VALUES (?, 'master')",
2856                                    1, "string", repo_id);
2857         if (ret < 0) {
2858             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2859                          "DB error: Set RepoHead.");
2860             seaf_db_rollback (trans);
2861             seaf_db_trans_close (trans);
2862             goto out;
2863         }
2864     }
2865 
2866     // Restore repo size
2867     exists = seaf_db_trans_check_for_existence (trans,
2868                                                 "SELECT 1 FROM RepoSize WHERE repo_id=?",
2869                                                 &db_err, 1, "string", repo_id);
2870 
2871     if (!exists) {
2872         ret = seaf_db_trans_query (trans,
2873                                    "INSERT INTO RepoSize (repo_id, size, head_id) VALUES (?, ?, ?)",
2874                                    3, "string", repo_id,
2875                                    "int64", seafile_trash_repo_get_size (repo),
2876                                    "string", seafile_trash_repo_get_head_id (repo));
2877         if (ret < 0) {
2878             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2879                          "DB error: Insert Repo Size.");
2880             seaf_db_rollback (trans);
2881             seaf_db_trans_close (trans);
2882             goto out;
2883         }
2884     }
2885 
2886     ret = seaf_db_trans_query (trans,
2887                                "DELETE FROM RepoTrash WHERE repo_id = ?",
2888                                1, "string", repo_id);
2889     if (ret < 0) {
2890         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2891                      "DB error: delete from RepoTrash.");
2892         seaf_db_rollback (trans);
2893         seaf_db_trans_close (trans);
2894         goto out;
2895     }
2896 
2897     if (seaf_db_commit (trans) < 0) {
2898         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
2899                      "DB error: Failed to commit.");
2900         seaf_db_rollback (trans);
2901         ret = -1;
2902     }
2903 
2904     seaf_db_trans_close (trans);
2905 
2906 out:
2907     g_object_unref (repo);
2908     return ret;
2909 }
2910 
2911 /* Web access permission. */
2912 
2913 int
seaf_repo_manager_set_access_property(SeafRepoManager * mgr,const char * repo_id,const char * ap)2914 seaf_repo_manager_set_access_property (SeafRepoManager *mgr, const char *repo_id,
2915                                        const char *ap)
2916 {
2917     int rc;
2918 
2919     if (seaf_repo_manager_query_access_property (mgr, repo_id) == NULL) {
2920         rc = seaf_db_statement_query (mgr->seaf->db,
2921                                       "INSERT INTO WebAP (repo_id, access_property) VALUES (?, ?)",
2922                                       2, "string", repo_id, "string", ap);
2923     } else {
2924         rc = seaf_db_statement_query (mgr->seaf->db,
2925                                       "UPDATE WebAP SET access_property=? "
2926                                       "WHERE repo_id=?",
2927                                       2, "string", ap, "string", repo_id);
2928     }
2929 
2930     if (rc < 0) {
2931         seaf_warning ("DB error when set access property for repo %s, %s.\n", repo_id, ap);
2932         return -1;
2933     }
2934 
2935     return 0;
2936 }
2937 
2938 static gboolean
get_ap(SeafDBRow * row,void * data)2939 get_ap (SeafDBRow *row, void *data)
2940 {
2941     char **ap = data;
2942 
2943     *ap = g_strdup (seaf_db_row_get_column_text (row, 0));
2944 
2945     return FALSE;
2946 }
2947 
2948 char *
seaf_repo_manager_query_access_property(SeafRepoManager * mgr,const char * repo_id)2949 seaf_repo_manager_query_access_property (SeafRepoManager *mgr, const char *repo_id)
2950 {
2951     char *sql;
2952     char *ret = NULL;
2953 
2954     sql =  "SELECT access_property FROM WebAP WHERE repo_id=?";
2955 
2956     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_ap, &ret,
2957                                        1, "string", repo_id) < 0) {
2958         seaf_warning ("DB error when get access property for repo %s.\n", repo_id);
2959         return NULL;
2960     }
2961 
2962     return ret;
2963 }
2964 
2965 /* Group repos. */
2966 
2967 int
seaf_repo_manager_add_group_repo(SeafRepoManager * mgr,const char * repo_id,int group_id,const char * owner,const char * permission,GError ** error)2968 seaf_repo_manager_add_group_repo (SeafRepoManager *mgr,
2969                                   const char *repo_id,
2970                                   int group_id,
2971                                   const char *owner,
2972                                   const char *permission,
2973                                   GError **error)
2974 {
2975     if (seaf_db_statement_query (mgr->seaf->db,
2976                                  "INSERT INTO RepoGroup (repo_id, group_id, user_name, permission) VALUES (?, ?, ?, ?)",
2977                                  4, "string", repo_id, "int", group_id,
2978                                  "string", owner, "string", permission) < 0)
2979         return -1;
2980 
2981     return 0;
2982 }
2983 
2984 int
seaf_repo_manager_del_group_repo(SeafRepoManager * mgr,const char * repo_id,int group_id,GError ** error)2985 seaf_repo_manager_del_group_repo (SeafRepoManager *mgr,
2986                                   const char *repo_id,
2987                                   int group_id,
2988                                   GError **error)
2989 {
2990     return seaf_db_statement_query (mgr->seaf->db,
2991                                     "DELETE FROM RepoGroup WHERE group_id=? "
2992                                     "AND repo_id=?",
2993                                     2, "int", group_id, "string", repo_id);
2994 }
2995 
2996 static gboolean
get_group_ids_cb(SeafDBRow * row,void * data)2997 get_group_ids_cb (SeafDBRow *row, void *data)
2998 {
2999     GList **plist = data;
3000 
3001     int group_id = seaf_db_row_get_column_int (row, 0);
3002 
3003     *plist = g_list_prepend (*plist, (gpointer)(long)group_id);
3004 
3005     return TRUE;
3006 }
3007 
3008 GList *
seaf_repo_manager_get_groups_by_repo(SeafRepoManager * mgr,const char * repo_id,GError ** error)3009 seaf_repo_manager_get_groups_by_repo (SeafRepoManager *mgr,
3010                                       const char *repo_id,
3011                                       GError **error)
3012 {
3013     char *sql;
3014     GList *group_ids = NULL;
3015 
3016     sql =  "SELECT group_id FROM RepoGroup WHERE repo_id = ?";
3017 
3018     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_ids_cb,
3019                                        &group_ids, 1, "string", repo_id) < 0) {
3020         g_list_free (group_ids);
3021         return NULL;
3022     }
3023 
3024     return g_list_reverse (group_ids);
3025 }
3026 
3027 static gboolean
get_group_perms_cb(SeafDBRow * row,void * data)3028 get_group_perms_cb (SeafDBRow *row, void *data)
3029 {
3030     GList **plist = data;
3031     GroupPerm *perm = g_new0 (GroupPerm, 1);
3032 
3033     perm->group_id = seaf_db_row_get_column_int (row, 0);
3034     const char *permission = seaf_db_row_get_column_text(row, 1);
3035     g_strlcpy (perm->permission, permission, sizeof(perm->permission));
3036 
3037     *plist = g_list_prepend (*plist, perm);
3038 
3039     return TRUE;
3040 }
3041 
3042 GList *
seaf_repo_manager_get_group_perm_by_repo(SeafRepoManager * mgr,const char * repo_id,GError ** error)3043 seaf_repo_manager_get_group_perm_by_repo (SeafRepoManager *mgr,
3044                                           const char *repo_id,
3045                                           GError **error)
3046 {
3047     char *sql;
3048     GList *group_perms = NULL, *p;
3049 
3050     sql = "SELECT group_id, permission FROM RepoGroup WHERE repo_id = ?";
3051 
3052     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_perms_cb,
3053                                        &group_perms, 1, "string", repo_id) < 0) {
3054         for (p = group_perms; p != NULL; p = p->next)
3055             g_free (p->data);
3056         g_list_free (group_perms);
3057         return NULL;
3058     }
3059 
3060     return g_list_reverse (group_perms);
3061 }
3062 
3063 int
seaf_repo_manager_set_group_repo_perm(SeafRepoManager * mgr,const char * repo_id,int group_id,const char * permission,GError ** error)3064 seaf_repo_manager_set_group_repo_perm (SeafRepoManager *mgr,
3065                                        const char *repo_id,
3066                                        int group_id,
3067                                        const char *permission,
3068                                        GError **error)
3069 {
3070     return seaf_db_statement_query (mgr->seaf->db,
3071                                     "UPDATE RepoGroup SET permission=? WHERE "
3072                                     "repo_id=? AND group_id=?",
3073                                     3, "string", permission, "string", repo_id,
3074                                     "int", group_id);
3075 }
3076 
3077 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)3078 seaf_repo_manager_set_subdir_group_perm_by_path (SeafRepoManager *mgr,
3079                                                  const char *repo_id,
3080                                                  const char *username,
3081                                                  int group_id,
3082                                                  const char *permission,
3083                                                  const char *path)
3084 {
3085     return seaf_db_statement_query (mgr->seaf->db,
3086                                     "UPDATE RepoGroup SET permission=? WHERE repo_id IN "
3087                                     "(SELECT repo_id FROM VirtualRepo WHERE origin_repo=? AND path=?) "
3088                                     "AND group_id=? AND user_name=?",
3089                                     5, "string", permission,
3090                                     "string", repo_id,
3091                                     "string", path,
3092                                     "int", group_id,
3093                                     "string", username);
3094 }
3095 static gboolean
get_group_repoids_cb(SeafDBRow * row,void * data)3096 get_group_repoids_cb (SeafDBRow *row, void *data)
3097 {
3098     GList **p_list = data;
3099 
3100     char *repo_id = g_strdup ((const char *)seaf_db_row_get_column_text (row, 0));
3101 
3102     *p_list = g_list_prepend (*p_list, repo_id);
3103 
3104     return TRUE;
3105 }
3106 
3107 GList *
seaf_repo_manager_get_group_repoids(SeafRepoManager * mgr,int group_id,GError ** error)3108 seaf_repo_manager_get_group_repoids (SeafRepoManager *mgr,
3109                                      int group_id,
3110                                      GError **error)
3111 {
3112     char *sql;
3113     GList *repo_ids = NULL;
3114 
3115     sql =  "SELECT repo_id FROM RepoGroup WHERE group_id = ?";
3116     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_repoids_cb,
3117                                        &repo_ids, 1, "int", group_id) < 0)
3118         return NULL;
3119 
3120     return g_list_reverse (repo_ids);
3121 }
3122 
3123 static gboolean
get_group_repos_cb(SeafDBRow * row,void * data)3124 get_group_repos_cb (SeafDBRow *row, void *data)
3125 {
3126     GList **p_list = data;
3127     SeafileRepo *srepo = NULL;
3128 
3129     const char *repo_id = seaf_db_row_get_column_text (row, 0);
3130     const char *vrepo_id = seaf_db_row_get_column_text (row, 1);
3131     int group_id = seaf_db_row_get_column_int (row, 2);
3132     const char *user_name = seaf_db_row_get_column_text (row, 3);
3133     const char *permission = seaf_db_row_get_column_text (row, 4);
3134     const char *commit_id = seaf_db_row_get_column_text (row, 5);
3135     gint64 size = seaf_db_row_get_column_int64 (row, 6);
3136     const char *repo_name = seaf_db_row_get_column_text (row, 9);
3137     gint64 update_time = seaf_db_row_get_column_int64 (row, 10);
3138     int version = seaf_db_row_get_column_int (row, 11);
3139     gboolean is_encrypted = seaf_db_row_get_column_int (row, 12) ? TRUE : FALSE;
3140     const char *last_modifier = seaf_db_row_get_column_text (row, 13);
3141     int status = seaf_db_row_get_column_int (row, 14);
3142 
3143     char *user_name_l = g_ascii_strdown (user_name, -1);
3144 
3145     srepo = g_object_new (SEAFILE_TYPE_REPO,
3146                           "share_type", "group",
3147                           "repo_id", repo_id,
3148                           "id", repo_id,
3149                           "head_cmmt_id", commit_id,
3150                           "group_id", group_id,
3151                           "user", user_name_l,
3152                           "permission", permission,
3153                           "is_virtual", (vrepo_id != NULL),
3154                           "size", size,
3155                           "status", status,
3156                           NULL);
3157     g_free (user_name_l);
3158 
3159     if (srepo != NULL) {
3160         if (vrepo_id) {
3161             const char *origin_repo_id = seaf_db_row_get_column_text (row, 7);
3162             const char *origin_path = seaf_db_row_get_column_text (row, 8);
3163             const char *origin_repo_name = seaf_db_row_get_column_text (row, 15);
3164             g_object_set (srepo, "store_id", origin_repo_id,
3165                           "origin_repo_id", origin_repo_id,
3166                           "origin_repo_name", origin_repo_name,
3167                           "origin_path", origin_path, NULL);
3168         } else {
3169             g_object_set (srepo, "store_id", repo_id, NULL);
3170         }
3171         if (repo_name) {
3172             g_object_set (srepo, "name", repo_name,
3173                           "repo_name", repo_name,
3174                           "last_modify", update_time,
3175                           "last_modified", update_time,
3176                           "version", version,
3177                           "encrypted", is_encrypted,
3178                           "last_modifier", last_modifier, NULL);
3179         }
3180         *p_list = g_list_prepend (*p_list, srepo);
3181     }
3182 
3183     return TRUE;
3184 }
3185 
3186 void
seaf_fill_repo_obj_from_commit(GList ** repos)3187 seaf_fill_repo_obj_from_commit (GList **repos)
3188 {
3189     SeafileRepo *repo;
3190     SeafCommit *commit;
3191     char *repo_id;
3192     char *commit_id;
3193     char *repo_name = NULL;
3194     char *last_modifier = NULL;
3195     GList *p = *repos;
3196     GList *next;
3197 
3198     while (p) {
3199         repo = p->data;
3200         g_object_get (repo, "name", &repo_name, NULL);
3201         g_object_get (repo, "last_modifier", &last_modifier, NULL);
3202         if (!repo_name || !last_modifier) {
3203             g_object_get (repo, "repo_id", &repo_id, "head_cmmt_id", &commit_id, NULL);
3204             commit = seaf_commit_manager_get_commit_compatible (seaf->commit_mgr,
3205                                                                 repo_id, commit_id);
3206             if (!commit) {
3207                 seaf_warning ("Commit %s not found in repo %s\n", commit_id, repo_id);
3208                 g_object_unref (repo);
3209                 next = p->next;
3210                 *repos = g_list_delete_link (*repos, p);
3211                 p = next;
3212                 if (repo_name)
3213                     g_free (repo_name);
3214                 if (last_modifier)
3215                     g_free (last_modifier);
3216             } else {
3217                 g_object_set (repo, "name", commit->repo_name,
3218                               "repo_name", commit->repo_name,
3219                               "last_modify", commit->ctime,
3220                               "last_modified", commit->ctime,
3221                               "version", commit->version,
3222                               "encrypted", commit->encrypted,
3223                               "last_modifier", commit->creator_name,
3224                               NULL);
3225 
3226                 /* Set to database */
3227                 set_repo_commit_to_db (repo_id, commit->repo_name, commit->ctime, commit->version,
3228                                        commit->encrypted, commit->creator_name);
3229                 seaf_commit_unref (commit);
3230             }
3231             g_free (repo_id);
3232             g_free (commit_id);
3233         }
3234         if (repo_name)
3235             g_free (repo_name);
3236         if (last_modifier)
3237             g_free (last_modifier);
3238 
3239         p = p->next;
3240     }
3241 }
3242 
3243 GList *
seaf_repo_manager_get_repos_by_group(SeafRepoManager * mgr,int group_id,GError ** error)3244 seaf_repo_manager_get_repos_by_group (SeafRepoManager *mgr,
3245                                       int group_id,
3246                                       GError **error)
3247 {
3248     char *sql;
3249     GList *repos = NULL;
3250     GList *p;
3251 
3252     sql = "SELECT RepoGroup.repo_id, v.repo_id, "
3253         "group_id, user_name, permission, commit_id, s.size, "
3254         "v.origin_repo, v.path, i.name, "
3255         "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
3256         "(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo) "
3257         "FROM RepoGroup LEFT JOIN VirtualRepo v ON "
3258         "RepoGroup.repo_id = v.repo_id "
3259         "LEFT JOIN RepoInfo i ON RepoGroup.repo_id = i.repo_id "
3260         "LEFT JOIN RepoSize s ON RepoGroup.repo_id = s.repo_id, "
3261         "Branch WHERE group_id = ? AND "
3262         "RepoGroup.repo_id = Branch.repo_id AND "
3263         "Branch.name = 'master'";
3264 
3265     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_repos_cb,
3266                                        &repos, 1, "int", group_id) < 0) {
3267         for (p = repos; p; p = p->next) {
3268             g_object_unref (p->data);
3269         }
3270         g_list_free (repos);
3271         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
3272                      "Failed to get repos by group from db.");
3273         return NULL;
3274     }
3275 
3276     seaf_fill_repo_obj_from_commit (&repos);
3277 
3278     return g_list_reverse (repos);
3279 }
3280 
3281 GList *
seaf_repo_manager_get_group_repos_by_owner(SeafRepoManager * mgr,const char * owner,GError ** error)3282 seaf_repo_manager_get_group_repos_by_owner (SeafRepoManager *mgr,
3283                                             const char *owner,
3284                                             GError **error)
3285 {
3286     char *sql;
3287     GList *repos = NULL;
3288     GList *p;
3289 
3290     sql = "SELECT RepoGroup.repo_id, v.repo_id, "
3291         "group_id, user_name, permission, commit_id, s.size, "
3292         "v.origin_repo, v.path, i.name, "
3293         "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
3294         "(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo) "
3295         "FROM RepoGroup LEFT JOIN VirtualRepo v ON "
3296         "RepoGroup.repo_id = v.repo_id "
3297         "LEFT JOIN RepoInfo i ON RepoGroup.repo_id = i.repo_id "
3298         "LEFT JOIN RepoSize s ON RepoGroup.repo_id = s.repo_id, "
3299         "Branch WHERE user_name = ? AND "
3300         "RepoGroup.repo_id = Branch.repo_id AND "
3301         "Branch.name = 'master'";
3302     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_repos_cb,
3303                                        &repos, 1, "string", owner) < 0) {
3304         for (p = repos; p; p = p->next) {
3305             g_object_unref (p->data);
3306         }
3307         g_list_free (repos);
3308         return NULL;
3309     }
3310 
3311     seaf_fill_repo_obj_from_commit (&repos);
3312 
3313     return g_list_reverse (repos);
3314 }
3315 
3316 static gboolean
get_group_repo_owner(SeafDBRow * row,void * data)3317 get_group_repo_owner (SeafDBRow *row, void *data)
3318 {
3319     char **share_from = data;
3320 
3321     const char *owner = (const char *) seaf_db_row_get_column_text (row, 0);
3322     *share_from = g_ascii_strdown (owner, -1);
3323     /* There should be only one result. */
3324     return FALSE;
3325 }
3326 
3327 char *
seaf_repo_manager_get_group_repo_owner(SeafRepoManager * mgr,const char * repo_id,GError ** error)3328 seaf_repo_manager_get_group_repo_owner (SeafRepoManager *mgr,
3329                                         const char *repo_id,
3330                                         GError **error)
3331 {
3332     char *sql;
3333     char *ret = NULL;
3334 
3335     sql = "SELECT user_name FROM RepoGroup WHERE repo_id = ?";
3336     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
3337                                        get_group_repo_owner, &ret,
3338                                        1, "string", repo_id) < 0) {
3339         seaf_warning ("DB error when get repo share from for repo %s.\n",
3340                    repo_id);
3341         return NULL;
3342     }
3343 
3344     return ret;
3345 }
3346 
3347 int
seaf_repo_manager_remove_group_repos(SeafRepoManager * mgr,int group_id,const char * owner,GError ** error)3348 seaf_repo_manager_remove_group_repos (SeafRepoManager *mgr,
3349                                       int group_id,
3350                                       const char *owner,
3351                                       GError **error)
3352 {
3353     SeafDB *db = mgr->seaf->db;
3354     int rc;
3355 
3356     if (!owner) {
3357         rc = seaf_db_statement_query (db, "DELETE FROM RepoGroup WHERE group_id=?",
3358                                       1, "int", group_id);
3359     } else {
3360         rc = seaf_db_statement_query (db,
3361                                       "DELETE FROM RepoGroup WHERE group_id=? AND "
3362                                       "user_name = ?",
3363                                       2, "int", group_id, "string", owner);
3364     }
3365 
3366     return rc;
3367 }
3368 
3369 /* Inner public repos */
3370 
3371 int
seaf_repo_manager_set_inner_pub_repo(SeafRepoManager * mgr,const char * repo_id,const char * permission)3372 seaf_repo_manager_set_inner_pub_repo (SeafRepoManager *mgr,
3373                                       const char *repo_id,
3374                                       const char *permission)
3375 {
3376     SeafDB *db = mgr->seaf->db;
3377     char sql[256];
3378 
3379     if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
3380         gboolean err;
3381         snprintf(sql, sizeof(sql),
3382                  "SELECT repo_id FROM InnerPubRepo WHERE repo_id=?");
3383         if (seaf_db_statement_exists (db, sql, &err,
3384                                       1, "string", repo_id))
3385             snprintf(sql, sizeof(sql),
3386                      "UPDATE InnerPubRepo SET permission='%s' "
3387                      "WHERE repo_id='%s'", permission, repo_id);
3388         else
3389             snprintf(sql, sizeof(sql),
3390                      "INSERT INTO InnerPubRepo (repo_id, permission) VALUES "
3391                      "('%s', '%s')", repo_id, permission);
3392         if (err)
3393             return -1;
3394         return seaf_db_query (db, sql);
3395     } else {
3396         return seaf_db_statement_query (db,
3397                                         "REPLACE INTO InnerPubRepo (repo_id, permission) VALUES (?, ?)",
3398                                         2, "string", repo_id, "string", permission);
3399     }
3400 
3401     return -1;
3402 }
3403 
3404 int
seaf_repo_manager_unset_inner_pub_repo(SeafRepoManager * mgr,const char * repo_id)3405 seaf_repo_manager_unset_inner_pub_repo (SeafRepoManager *mgr,
3406                                         const char *repo_id)
3407 {
3408     return seaf_db_statement_query (mgr->seaf->db,
3409                                     "DELETE FROM InnerPubRepo WHERE repo_id = ?",
3410                                     1, "string", repo_id);
3411 }
3412 
3413 gboolean
seaf_repo_manager_is_inner_pub_repo(SeafRepoManager * mgr,const char * repo_id)3414 seaf_repo_manager_is_inner_pub_repo (SeafRepoManager *mgr,
3415                                      const char *repo_id)
3416 {
3417     gboolean db_err = FALSE;
3418 
3419     return seaf_db_statement_exists (mgr->seaf->db,
3420                                      "SELECT repo_id FROM InnerPubRepo WHERE repo_id=?",
3421                                      &db_err, 1, "string", repo_id);
3422 }
3423 
3424 static gboolean
collect_public_repos(SeafDBRow * row,void * data)3425 collect_public_repos (SeafDBRow *row, void *data)
3426 {
3427     GList **ret = (GList **)data;
3428     SeafileRepo *srepo;
3429     const char *repo_id, *vrepo_id, *owner, *permission, *commit_id;
3430     gint64 size;
3431 
3432     repo_id = seaf_db_row_get_column_text (row, 0);
3433     vrepo_id = seaf_db_row_get_column_text (row, 1);
3434     owner = seaf_db_row_get_column_text (row, 2);
3435     permission = seaf_db_row_get_column_text (row, 3);
3436     commit_id = seaf_db_row_get_column_text (row, 4);
3437     size = seaf_db_row_get_column_int64 (row, 5);
3438     const char *repo_name = seaf_db_row_get_column_text (row, 8);
3439     gint64 update_time = seaf_db_row_get_column_int64 (row, 9);
3440     int version = seaf_db_row_get_column_int (row, 10);
3441     gboolean is_encrypted = seaf_db_row_get_column_int (row, 11) ? TRUE : FALSE;
3442     const char *last_modifier = seaf_db_row_get_column_text (row, 12);
3443     int status = seaf_db_row_get_column_int (row, 13);
3444 
3445     char *owner_l = g_ascii_strdown (owner, -1);
3446 
3447     srepo = g_object_new (SEAFILE_TYPE_REPO,
3448                           "share_type", "public",
3449                           "repo_id", repo_id,
3450                           "id", repo_id,
3451                           "head_cmmt_id", commit_id,
3452                           "permission", permission,
3453                           "user", owner_l,
3454                           "is_virtual", (vrepo_id != NULL),
3455                           "size", size,
3456                           "status", status,
3457                           NULL);
3458     g_free (owner_l);
3459 
3460     if (srepo) {
3461         if (vrepo_id) {
3462             const char *origin_repo_id = seaf_db_row_get_column_text (row, 6);
3463             const char *origin_path = seaf_db_row_get_column_text (row, 7);
3464             g_object_set (srepo, "store_id", origin_repo_id,
3465                           "origin_repo_id", origin_repo_id,
3466                           "origin_path", origin_path, NULL);
3467         } else {
3468             g_object_set (srepo, "store_id", repo_id, NULL);
3469         }
3470 
3471         if (repo_name) {
3472             g_object_set (srepo, "name", repo_name,
3473                           "repo_name", repo_name,
3474                           "last_modify", update_time,
3475                           "last_modified", update_time,
3476                           "version", version,
3477                           "encrypted", is_encrypted,
3478                           "last_modifier", last_modifier, NULL);
3479         }
3480 
3481         *ret = g_list_prepend (*ret, srepo);
3482     }
3483 
3484     return TRUE;
3485 }
3486 
3487 GList *
seaf_repo_manager_list_inner_pub_repos(SeafRepoManager * mgr,gboolean * db_err)3488 seaf_repo_manager_list_inner_pub_repos (SeafRepoManager *mgr, gboolean *db_err)
3489 {
3490     GList *ret = NULL, *p;
3491     char *sql;
3492 
3493     sql = "SELECT InnerPubRepo.repo_id, VirtualRepo.repo_id, "
3494         "owner_id, permission, commit_id, s.size, "
3495         "VirtualRepo.origin_repo, VirtualRepo.path, i.name, "
3496         "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status "
3497         "FROM InnerPubRepo LEFT JOIN VirtualRepo ON "
3498         "InnerPubRepo.repo_id=VirtualRepo.repo_id "
3499         "LEFT JOIN RepoInfo i ON InnerPubRepo.repo_id = i.repo_id "
3500         "LEFT JOIN RepoSize s ON InnerPubRepo.repo_id = s.repo_id, RepoOwner, Branch "
3501         "WHERE InnerPubRepo.repo_id=RepoOwner.repo_id AND "
3502         "InnerPubRepo.repo_id = Branch.repo_id AND Branch.name = 'master'";
3503 
3504     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
3505                                        collect_public_repos, &ret,
3506                                        0) < 0) {
3507         for (p = ret; p != NULL; p = p->next)
3508             g_object_unref (p->data);
3509         g_list_free (ret);
3510         if (db_err)
3511             *db_err = TRUE;
3512         return NULL;
3513     }
3514 
3515     seaf_fill_repo_obj_from_commit (&ret);
3516 
3517     return g_list_reverse (ret);
3518 }
3519 
3520 gint64
seaf_repo_manager_count_inner_pub_repos(SeafRepoManager * mgr)3521 seaf_repo_manager_count_inner_pub_repos (SeafRepoManager *mgr)
3522 {
3523     char sql[256];
3524 
3525     snprintf (sql, 256, "SELECT COUNT(*) FROM InnerPubRepo");
3526 
3527     return seaf_db_get_int64(mgr->seaf->db, sql);
3528 }
3529 
3530 GList *
seaf_repo_manager_list_inner_pub_repos_by_owner(SeafRepoManager * mgr,const char * user)3531 seaf_repo_manager_list_inner_pub_repos_by_owner (SeafRepoManager *mgr,
3532                                                  const char *user)
3533 {
3534     GList *ret = NULL, *p;
3535     char *sql;
3536 
3537     sql = "SELECT InnerPubRepo.repo_id, VirtualRepo.repo_id, "
3538         "owner_id, permission, commit_id, s.size, "
3539         "VirtualRepo.origin_repo, VirtualRepo.path, i.name, "
3540         "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status "
3541         "FROM InnerPubRepo LEFT JOIN VirtualRepo ON "
3542         "InnerPubRepo.repo_id=VirtualRepo.repo_id "
3543         "LEFT JOIN RepoInfo i ON InnerPubRepo.repo_id = i.repo_id "
3544         "LEFT JOIN RepoSize s ON InnerPubRepo.repo_id = s.repo_id, RepoOwner, Branch "
3545         "WHERE InnerPubRepo.repo_id=RepoOwner.repo_id AND owner_id=? "
3546         "AND InnerPubRepo.repo_id = Branch.repo_id AND Branch.name = 'master'";
3547 
3548     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
3549                                        collect_public_repos, &ret,
3550                                        1, "string", user) < 0) {
3551         for (p = ret; p != NULL; p = p->next)
3552             g_object_unref (p->data);
3553         g_list_free (ret);
3554         return NULL;
3555     }
3556 
3557     seaf_fill_repo_obj_from_commit (&ret);
3558 
3559     return g_list_reverse (ret);
3560 }
3561 
3562 char *
seaf_repo_manager_get_inner_pub_repo_perm(SeafRepoManager * mgr,const char * repo_id)3563 seaf_repo_manager_get_inner_pub_repo_perm (SeafRepoManager *mgr,
3564                                            const char *repo_id)
3565 {
3566     char *sql;
3567 
3568     sql = "SELECT permission FROM InnerPubRepo WHERE repo_id=?";
3569     return seaf_db_statement_get_string(mgr->seaf->db, sql, 1, "string", repo_id);
3570 }
3571 
3572 
3573 int
seaf_repo_manager_is_valid_filename(SeafRepoManager * mgr,const char * repo_id,const char * filename,GError ** error)3574 seaf_repo_manager_is_valid_filename (SeafRepoManager *mgr,
3575                                      const char *repo_id,
3576                                      const char *filename,
3577                                      GError **error)
3578 {
3579     if (should_ignore_file(filename, NULL))
3580         return 0;
3581     else
3582         return 1;
3583 }
3584 
3585 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)3586 create_repo_common (SeafRepoManager *mgr,
3587                     const char *repo_id,
3588                     const char *repo_name,
3589                     const char *repo_desc,
3590                     const char *user,
3591                     const char *magic,
3592                     const char *random_key,
3593                     const char *salt,
3594                     int enc_version,
3595                     GError **error)
3596 {
3597     SeafRepo *repo = NULL;
3598     SeafCommit *commit = NULL;
3599     SeafBranch *master = NULL;
3600     int ret = -1;
3601 
3602     if (enc_version != 4 && enc_version != 3 && enc_version != 2 && enc_version != -1) {
3603         seaf_warning ("Unsupported enc version %d.\n", enc_version);
3604         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
3605                      "Unsupported encryption version");
3606         return -1;
3607     }
3608 
3609     if (enc_version >= 2) {
3610         if (!magic || strlen(magic) != 64) {
3611             seaf_warning ("Bad magic.\n");
3612             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
3613                          "Bad magic");
3614             return -1;
3615         }
3616         if (!random_key || strlen(random_key) != 96) {
3617             seaf_warning ("Bad random key.\n");
3618             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
3619                          "Bad random key");
3620             return -1;
3621         }
3622     }
3623     if (enc_version >= 3) {
3624         if (!salt || strlen(salt) != 64) {
3625             seaf_warning ("Bad salt.\n");
3626             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
3627                          "Bad salt");
3628             return -1;
3629         }
3630     }
3631 
3632     repo = seaf_repo_new (repo_id, repo_name, repo_desc);
3633 
3634     repo->no_local_history = TRUE;
3635     if (enc_version >= 2) {
3636         repo->encrypted = TRUE;
3637         repo->enc_version = enc_version;
3638         memcpy (repo->magic, magic, 64);
3639         memcpy (repo->random_key, random_key, 96);
3640     }
3641     if (enc_version >= 3)
3642         memcpy (repo->salt, salt, 64);
3643 
3644     repo->version = CURRENT_REPO_VERSION;
3645     memcpy (repo->store_id, repo_id, 36);
3646 
3647     commit = seaf_commit_new (NULL, repo->id,
3648                               EMPTY_SHA1, /* root id */
3649                               user, /* creator */
3650                               EMPTY_SHA1, /* creator id */
3651                               "Created library",  /* description */
3652                               0);         /* ctime */
3653 
3654     seaf_repo_to_commit (repo, commit);
3655     if (seaf_commit_manager_add_commit (seaf->commit_mgr, commit) < 0) {
3656         seaf_warning ("Failed to add commit.\n");
3657         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
3658                      "Failed to add commit");
3659         goto out;
3660     }
3661 
3662     master = seaf_branch_new ("master", repo->id, commit->commit_id);
3663     if (seaf_branch_manager_add_branch (seaf->branch_mgr, master) < 0) {
3664         seaf_warning ("Failed to add branch.\n");
3665         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
3666                      "Failed to add branch");
3667         goto out;
3668     }
3669 
3670     if (seaf_repo_set_head (repo, master) < 0) {
3671         seaf_warning ("Failed to set repo head.\n");
3672         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
3673                      "Failed to set repo head.");
3674         goto out;
3675     }
3676 
3677     if (seaf_repo_manager_add_repo (mgr, repo) < 0) {
3678         seaf_warning ("Failed to add repo.\n");
3679         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
3680                      "Failed to add repo.");
3681         goto out;
3682     }
3683 
3684     seaf_repo_manager_update_repo_info (mgr, repo->id, repo->head->commit_id);
3685 
3686     ret = 0;
3687 out:
3688     if (repo)
3689         seaf_repo_unref (repo);
3690     if (commit)
3691         seaf_commit_unref (commit);
3692     if (master)
3693         seaf_branch_unref (master);
3694 
3695     return ret;
3696 }
3697 
3698 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)3699 seaf_repo_manager_create_new_repo (SeafRepoManager *mgr,
3700                                    const char *repo_name,
3701                                    const char *repo_desc,
3702                                    const char *owner_email,
3703                                    const char *passwd,
3704                                    int enc_version,
3705                                    GError **error)
3706 {
3707     char *repo_id = NULL;
3708     char salt[65], magic[65], random_key[97];
3709 
3710     repo_id = gen_uuid ();
3711 
3712     if (passwd && passwd[0] != 0) {
3713         if (seafile_generate_repo_salt (salt) < 0) {
3714             goto bad;
3715         }
3716         seafile_generate_magic (enc_version, repo_id, passwd, salt, magic);
3717         if (seafile_generate_random_key (passwd, enc_version, salt, random_key) < 0) {
3718             goto bad;
3719         }
3720     }
3721 
3722     int rc;
3723     if (passwd)
3724         rc = create_repo_common (mgr, repo_id, repo_name, repo_desc, owner_email,
3725                                  magic, random_key, salt, enc_version, error);
3726     else
3727         rc = create_repo_common (mgr, repo_id, repo_name, repo_desc, owner_email,
3728                                  NULL, NULL, NULL, -1, error);
3729     if (rc < 0)
3730         goto bad;
3731 
3732     if (seaf_repo_manager_set_repo_owner (mgr, repo_id, owner_email) < 0) {
3733         seaf_warning ("Failed to set repo owner.\n");
3734         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
3735                      "Failed to set repo owner.");
3736         goto bad;
3737     }
3738 
3739     return repo_id;
3740 
3741 bad:
3742     if (repo_id)
3743         g_free (repo_id);
3744     return NULL;
3745 }
3746 
3747 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)3748 seaf_repo_manager_create_enc_repo (SeafRepoManager *mgr,
3749                                    const char *repo_id,
3750                                    const char *repo_name,
3751                                    const char *repo_desc,
3752                                    const char *owner_email,
3753                                    const char *magic,
3754                                    const char *random_key,
3755                                    const char *salt,
3756                                    int enc_version,
3757                                    GError **error)
3758 {
3759     if (!repo_id || !is_uuid_valid (repo_id)) {
3760         seaf_warning ("Invalid repo_id.\n");
3761         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
3762                      "Invalid repo id");
3763         return NULL;
3764     }
3765 
3766     if (seaf_repo_manager_repo_exists (mgr, repo_id)) {
3767         seaf_warning ("Repo %s exists, refuse to create.\n", repo_id);
3768         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
3769                      "Repo already exists");
3770         return NULL;
3771     }
3772 
3773     if (create_repo_common (mgr, repo_id, repo_name, repo_desc, owner_email,
3774                             magic, random_key, salt, enc_version, error) < 0)
3775         return NULL;
3776 
3777     if (seaf_repo_manager_set_repo_owner (mgr, repo_id, owner_email) < 0) {
3778         seaf_warning ("Failed to set repo owner.\n");
3779         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
3780                      "Failed to set repo owner.");
3781         return NULL;
3782     }
3783 
3784     return g_strdup (repo_id);
3785 }
3786 
reap_token(void * data)3787 static int reap_token (void *data)
3788 {
3789     SeafRepoManager *mgr = data;
3790     GHashTableIter iter;
3791     gpointer key, value;
3792     DecryptedToken *t;
3793 
3794     pthread_rwlock_wrlock (&mgr->priv->lock);
3795 
3796     gint64 now = (gint64)time(NULL);
3797 
3798     g_hash_table_iter_init (&iter, mgr->priv->decrypted_tokens);
3799     while (g_hash_table_iter_next (&iter, &key, &value)) {
3800         t = value;
3801         if (now >= t->reap_time)
3802             g_hash_table_iter_remove (&iter);
3803     }
3804 
3805     pthread_rwlock_unlock (&mgr->priv->lock);
3806 
3807     return TRUE;
3808 }
3809 
decrypted_token_free(DecryptedToken * token)3810 static void decrypted_token_free (DecryptedToken *token)
3811 {
3812     if (!token)
3813         return;
3814     g_free (token->token);
3815     g_free (token);
3816 }
3817 
3818 void
seaf_repo_manager_add_decrypted_token(SeafRepoManager * mgr,const char * encrypted_token,const char * session_key,const char * decrypted_token)3819 seaf_repo_manager_add_decrypted_token (SeafRepoManager *mgr,
3820                                        const char *encrypted_token,
3821                                        const char *session_key,
3822                                        const char *decrypted_token)
3823 {
3824     char key[256];
3825     DecryptedToken *token;
3826 
3827     snprintf (key, sizeof(key), "%s%s", encrypted_token, session_key);
3828     key[255] = 0;
3829 
3830     pthread_rwlock_wrlock (&mgr->priv->lock);
3831 
3832     token = g_new0 (DecryptedToken, 1);
3833     token->token = g_strdup(decrypted_token);
3834     token->reap_time = (gint64)time(NULL) + DECRYPTED_TOKEN_TTL;
3835 
3836     g_hash_table_insert (mgr->priv->decrypted_tokens,
3837                          g_strdup(key),
3838                          token);
3839 
3840     pthread_rwlock_unlock (&mgr->priv->lock);
3841 }
3842 
3843 char *
seaf_repo_manager_get_decrypted_token(SeafRepoManager * mgr,const char * encrypted_token,const char * session_key)3844 seaf_repo_manager_get_decrypted_token (SeafRepoManager *mgr,
3845                                        const char *encrypted_token,
3846                                        const char *session_key)
3847 {
3848     char key[256];
3849     DecryptedToken *token;
3850 
3851     snprintf (key, sizeof(key), "%s%s", encrypted_token, session_key);
3852     key[255] = 0;
3853 
3854     pthread_rwlock_rdlock (&mgr->priv->lock);
3855     token = g_hash_table_lookup (mgr->priv->decrypted_tokens, key);
3856     pthread_rwlock_unlock (&mgr->priv->lock);
3857 
3858     if (token)
3859         return g_strdup(token->token);
3860     return NULL;
3861 }
3862 
3863 static gboolean
get_shared_users(SeafDBRow * row,void * data)3864 get_shared_users (SeafDBRow *row, void *data)
3865 {
3866     GList **shared_users = data;
3867     const char *user = seaf_db_row_get_column_text (row, 0);
3868     const char *perm = seaf_db_row_get_column_text (row, 1);
3869     const char *repo_id = seaf_db_row_get_column_text (row, 2);
3870 
3871     SeafileSharedUser *uobj = g_object_new (SEAFILE_TYPE_SHARED_USER,
3872                                             "repo_id", repo_id,
3873                                             "user", user,
3874                                             "perm", perm,
3875                                             NULL);
3876     *shared_users = g_list_prepend (*shared_users, uobj);
3877 
3878     return TRUE;
3879 }
3880 
3881 GList *
seaf_repo_manager_get_shared_users_for_subdir(SeafRepoManager * mgr,const char * repo_id,const char * path,const char * from_user,GError ** error)3882 seaf_repo_manager_get_shared_users_for_subdir (SeafRepoManager *mgr,
3883                                                const char *repo_id,
3884                                                const char *path,
3885                                                const char *from_user,
3886                                                GError **error)
3887 {
3888     GList *shared_users = NULL;
3889     int ret = seaf_db_statement_foreach_row (mgr->seaf->db,
3890                                              "SELECT to_email, permission, v.repo_id "
3891                                              "FROM SharedRepo s, VirtualRepo v "
3892                                              "WHERE s.repo_id = v.repo_id AND v.origin_repo = ? "
3893                                              "AND v.path = ? AND s.from_email = ?",
3894                                              get_shared_users, &shared_users, 3, "string", repo_id,
3895                                              "string", path, "string", from_user);
3896     if (ret < 0) {
3897         seaf_warning ("Failed to get shared users for %.8s(%s).\n", repo_id, path);
3898         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
3899                      "Failed to get shared users for subdir from db");
3900         while (shared_users) {
3901             g_object_unref (shared_users->data);
3902             shared_users = g_list_delete_link (shared_users, shared_users);
3903         }
3904         return NULL;
3905     }
3906 
3907     return shared_users;
3908 }
3909 
3910 static gboolean
get_shared_groups(SeafDBRow * row,void * data)3911 get_shared_groups (SeafDBRow *row, void *data)
3912 {
3913     GList **shared_groups = data;
3914     int group = seaf_db_row_get_column_int (row, 0);
3915     const char *perm = seaf_db_row_get_column_text (row, 1);
3916     const char *repo_id = seaf_db_row_get_column_text (row, 2);
3917 
3918     SeafileSharedGroup *gobj = g_object_new (SEAFILE_TYPE_SHARED_GROUP,
3919                                              "repo_id", repo_id,
3920                                              "group_id", group,
3921                                              "perm", perm,
3922                                              NULL);
3923 
3924     *shared_groups = g_list_prepend (*shared_groups, gobj);
3925 
3926     return TRUE;
3927 }
3928 
3929 GList *
seaf_repo_manager_get_shared_groups_for_subdir(SeafRepoManager * mgr,const char * repo_id,const char * path,const char * from_user,GError ** error)3930 seaf_repo_manager_get_shared_groups_for_subdir (SeafRepoManager *mgr,
3931                                                 const char *repo_id,
3932                                                 const char *path,
3933                                                 const char *from_user,
3934                                                 GError **error)
3935 {
3936     GList *shared_groups = NULL;
3937     int ret = seaf_db_statement_foreach_row (mgr->seaf->db,
3938                                              "SELECT group_id, permission, v.repo_id "
3939                                              "FROM RepoGroup r, VirtualRepo v "
3940                                              "WHERE r.repo_id = v.repo_id AND v.origin_repo = ? "
3941                                              "AND v.path = ? AND r.user_name = ?",
3942                                              get_shared_groups, &shared_groups, 3, "string", repo_id,
3943                                              "string", path, "string", from_user);
3944     if (ret < 0) {
3945         seaf_warning ("Failed to get shared groups for %.8s(%s).\n", repo_id, path);
3946         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
3947                      "Failed to get shared groups fro subdir from db");
3948         while (shared_groups) {
3949             g_object_unref (shared_groups->data);
3950             shared_groups = g_list_delete_link (shared_groups, shared_groups);
3951         }
3952         return NULL;
3953     }
3954 
3955     return shared_groups;
3956 }
3957 int
seaf_repo_manager_edit_repo(const char * repo_id,const char * name,const char * description,const char * user,GError ** error)3958 seaf_repo_manager_edit_repo (const char *repo_id,
3959                              const char *name,
3960                              const char *description,
3961                              const char *user,
3962                              GError **error)
3963 {
3964     SeafRepo *repo = NULL;
3965     SeafCommit *commit = NULL, *parent = NULL;
3966     int ret = 0;
3967 
3968     if (!name && !description) {
3969         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
3970                      "At least one argument should be non-null");
3971         return -1;
3972     }
3973 
3974     if (!is_uuid_valid (repo_id)) {
3975         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid repo id");
3976         return -1;
3977     }
3978 
3979 retry:
3980     repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
3981     if (!repo) {
3982         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "No such library");
3983         return -1;
3984     }
3985     if (!name)
3986         name = repo->name;
3987     if (!description)
3988         description = repo->desc;
3989 
3990     /*
3991      * We only change repo_name or repo_desc, so just copy the head commit
3992      * and change these two fields.
3993      */
3994     parent = seaf_commit_manager_get_commit (seaf->commit_mgr,
3995                                              repo->id, repo->version,
3996                                              repo->head->commit_id);
3997     if (!parent) {
3998         seaf_warning ("Failed to get commit %s:%s.\n",
3999                       repo->id, repo->head->commit_id);
4000         ret = -1;
4001         goto out;
4002     }
4003     if (!user) {
4004         user = parent->creator_name;
4005     }
4006 
4007     commit = seaf_commit_new (NULL,
4008                               repo->id,
4009                               parent->root_id,
4010                               user,
4011                               EMPTY_SHA1,
4012                               "Changed library name or description",
4013                               0);
4014     commit->parent_id = g_strdup(parent->commit_id);
4015     seaf_repo_to_commit (repo, commit);
4016 
4017     g_free (commit->repo_name);
4018     commit->repo_name = g_strdup(name);
4019     g_free (commit->repo_desc);
4020     commit->repo_desc = g_strdup(description);
4021 
4022     if (seaf_commit_manager_add_commit (seaf->commit_mgr, commit) < 0) {
4023         ret = -1;
4024         goto out;
4025     }
4026 
4027     seaf_branch_set_commit (repo->head, commit->commit_id);
4028     if (seaf_branch_manager_test_and_update_branch (seaf->branch_mgr,
4029                                                     repo->head,
4030                                                     parent->commit_id) < 0) {
4031         seaf_repo_unref (repo);
4032         seaf_commit_unref (commit);
4033         seaf_commit_unref (parent);
4034         repo = NULL;
4035         commit = NULL;
4036         parent = NULL;
4037         goto retry;
4038     }
4039 
4040     seaf_repo_manager_update_repo_info (seaf->repo_mgr, repo_id, repo->head->commit_id);
4041 
4042 out:
4043     seaf_commit_unref (commit);
4044     seaf_commit_unref (parent);
4045     seaf_repo_unref (repo);
4046 
4047     return ret;
4048 }
4049 
4050 gboolean
get_total_file_number_cb(SeafDBRow * row,void * vdata)4051 get_total_file_number_cb (SeafDBRow *row, void *vdata)
4052 {
4053     gint64 *data = (gint64 *)vdata;
4054     gint64 count = seaf_db_row_get_column_int64 (row, 0);
4055     *data = count;
4056 
4057     return FALSE;
4058 }
4059 
4060 gint64
seaf_get_total_file_number(GError ** error)4061 seaf_get_total_file_number (GError **error)
4062 {
4063     gint64 count = 0;
4064     int ret = seaf_db_statement_foreach_row (seaf->db,
4065                                              "SELECT SUM(file_count) FROM RepoFileCount f "
4066                                              "LEFT JOIN VirtualRepo v "
4067                                              "ON f.repo_id=v.repo_id,"
4068                                              "Repo r "
4069                                              "WHERE v.repo_id IS NULL AND "
4070                                              "f.repo_id=r.repo_id",
4071                                              get_total_file_number_cb,
4072                                              &count, 0);
4073     if (ret < 0) {
4074         seaf_warning ("Failed to get total file number.\n");
4075         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
4076                      "Failed to get total file number from db.");
4077         return -1;
4078     }
4079 
4080     return count;
4081 }
4082 
4083 gboolean
get_total_storage_cb(SeafDBRow * row,void * vdata)4084 get_total_storage_cb(SeafDBRow *row, void *vdata)
4085 {
4086     gint64 *data = (gint64 *)vdata;
4087     gint64 size = seaf_db_row_get_column_int64 (row, 0);
4088     *data = size;
4089 
4090     return FALSE;
4091 }
4092 
4093 gint64
seaf_get_total_storage(GError ** error)4094 seaf_get_total_storage (GError **error)
4095 {
4096     gint64 size = 0;
4097     int ret;
4098     if (seaf_db_type(seaf->db) == SEAF_DB_TYPE_PGSQL) {
4099         ret = seaf_db_statement_foreach_row (seaf->db,
4100                                              "SELECT SUM(\"size\") FROM RepoSize s "
4101                                              "LEFT JOIN VirtualRepo v "
4102                                              "ON s.repo_id=v.repo_id "
4103                                              "WHERE v.repo_id IS NULL",
4104                                              get_total_storage_cb,
4105                                              &size, 0);
4106     } else {
4107         ret = seaf_db_statement_foreach_row (seaf->db,
4108                                              "SELECT SUM(size) FROM RepoSize s "
4109                                              "LEFT JOIN VirtualRepo v "
4110                                              "ON s.repo_id=v.repo_id "
4111                                              "WHERE v.repo_id IS NULL",
4112                                              get_total_storage_cb,
4113                                              &size, 0);
4114     }
4115     if (ret < 0) {
4116         seaf_warning ("Failed to get total storage occupation.\n");
4117         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
4118                      "Failed to get total storage occupation from db.");
4119         return -1;
4120     }
4121 
4122     return size;
4123 }
4124 
4125 int
seaf_repo_manager_add_upload_tmp_file(SeafRepoManager * mgr,const char * repo_id,const char * file_path,const char * tmp_file,GError ** error)4126 seaf_repo_manager_add_upload_tmp_file (SeafRepoManager *mgr,
4127                                        const char *repo_id,
4128                                        const char *file_path,
4129                                        const char *tmp_file,
4130                                        GError **error)
4131 {
4132     char *file_path_with_slash = NULL;
4133 
4134     if (file_path[0] == '/') {
4135         file_path_with_slash = g_strdup(file_path);
4136     } else {
4137         file_path_with_slash = g_strconcat("/", file_path, NULL);
4138     }
4139 
4140     int ret = seaf_db_statement_query (mgr->seaf->db,
4141                                        "INSERT INTO WebUploadTempFiles "
4142                                        "(repo_id, file_path, tmp_file_path) "
4143                                        "VALUES (?, ?, ?)", 3, "string", repo_id,
4144                                        "string", file_path_with_slash, "string", tmp_file);
4145 
4146     if (ret < 0) {
4147         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
4148                      "Failed to add upload tmp file record to db.");
4149     }
4150 
4151     g_free (file_path_with_slash);
4152     return ret;
4153 }
4154 
4155 int
seaf_repo_manager_del_upload_tmp_file(SeafRepoManager * mgr,const char * repo_id,const char * file_path,GError ** error)4156 seaf_repo_manager_del_upload_tmp_file (SeafRepoManager *mgr,
4157                                        const char *repo_id,
4158                                        const char *file_path,
4159                                        GError **error)
4160 {
4161     char *file_path_with_slash = NULL, *file_path_no_slash = NULL;
4162 
4163     /* Due to a bug in early versions of 7.0, some file_path may be stored in the db without
4164      * a leading slash. To be compatible with those records, we need to check the path
4165      * with and without leading slash.
4166      */
4167     if (file_path[0] == '/') {
4168         file_path_with_slash = g_strdup(file_path);
4169         file_path_no_slash = g_strdup(file_path+1);
4170     } else {
4171         file_path_with_slash = g_strconcat("/", file_path, NULL);
4172         file_path_no_slash = g_strdup(file_path);
4173     }
4174 
4175     int ret = seaf_db_statement_query (mgr->seaf->db,
4176                                        "DELETE FROM WebUploadTempFiles WHERE "
4177                                        "repo_id = ? AND file_path IN (?, ?)",
4178                                        3, "string", repo_id,
4179                                        "string", file_path_with_slash,
4180                                        "string", file_path_no_slash);
4181     if (ret < 0) {
4182         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
4183                      "Failed to delete upload tmp file record from db.");
4184     }
4185 
4186     g_free (file_path_with_slash);
4187     g_free (file_path_no_slash);
4188     return ret;
4189 }
4190 
4191 static gboolean
get_tmp_file_path(SeafDBRow * row,void * data)4192 get_tmp_file_path (SeafDBRow *row, void *data)
4193 {
4194     char **path = data;
4195 
4196     *path = g_strdup (seaf_db_row_get_column_text (row, 0));
4197 
4198     return FALSE;
4199 }
4200 
4201 char *
seaf_repo_manager_get_upload_tmp_file(SeafRepoManager * mgr,const char * repo_id,const char * file_path,GError ** error)4202 seaf_repo_manager_get_upload_tmp_file (SeafRepoManager *mgr,
4203                                        const char *repo_id,
4204                                        const char *file_path,
4205                                        GError **error)
4206 {
4207     char *tmp_file_path = NULL;
4208     char *file_path_with_slash = NULL, *file_path_no_slash = NULL;
4209 
4210     /* Due to a bug in early versions of 7.0, some file_path may be stored in the db without
4211      * a leading slash. To be compatible with those records, we need to check the path
4212      * with and without leading slash.
4213      * The correct file_path in db should be with a leading slash.
4214      */
4215     if (file_path[0] == '/') {
4216         file_path_with_slash = g_strdup(file_path);
4217         file_path_no_slash = g_strdup(file_path+1);
4218     } else {
4219         file_path_with_slash = g_strconcat("/", file_path, NULL);
4220         file_path_no_slash = g_strdup(file_path);
4221     }
4222 
4223     int ret = seaf_db_statement_foreach_row (mgr->seaf->db,
4224                                              "SELECT tmp_file_path FROM WebUploadTempFiles "
4225                                              "WHERE repo_id = ? AND file_path = ?",
4226                                              get_tmp_file_path, &tmp_file_path,
4227                                              2, "string", repo_id,
4228                                              "string", file_path_with_slash);
4229     if (ret < 0) {
4230         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
4231                      "Failed to get upload temp file path from db.");
4232         goto out;
4233     }
4234 
4235     if (!tmp_file_path) {
4236         /* Try file_path without slash. */
4237         int ret = seaf_db_statement_foreach_row (mgr->seaf->db,
4238                                                  "SELECT tmp_file_path FROM WebUploadTempFiles "
4239                                                  "WHERE repo_id = ? AND file_path = ?",
4240                                                  get_tmp_file_path, &tmp_file_path,
4241                                                  2, "string", repo_id,
4242                                                  "string", file_path_no_slash);
4243         if (ret < 0) {
4244             g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
4245                          "Failed to get upload temp file path from db.");
4246             goto out;
4247         }
4248     }
4249 
4250 out:
4251     g_free (file_path_with_slash);
4252     g_free (file_path_no_slash);
4253     return tmp_file_path;
4254 }
4255 
4256 gint64
seaf_repo_manager_get_upload_tmp_file_offset(SeafRepoManager * mgr,const char * repo_id,const char * file_path,GError ** error)4257 seaf_repo_manager_get_upload_tmp_file_offset (SeafRepoManager *mgr,
4258                                               const char *repo_id,
4259                                               const char *file_path,
4260                                               GError **error)
4261 {
4262     char *tmp_file_path = NULL;
4263     SeafStat file_stat;
4264 
4265     tmp_file_path = seaf_repo_manager_get_upload_tmp_file (mgr, repo_id,
4266                                                            file_path, error);
4267     if (*error) {
4268         return -1;
4269     }
4270 
4271     if (!tmp_file_path)
4272         return 0;
4273 
4274     if (seaf_stat (tmp_file_path, &file_stat) < 0) {
4275         if (errno == ENOENT) {
4276             seaf_message ("Temp file %s doesn't exist, remove reocrd from db.\n",
4277                           tmp_file_path);
4278             if (seaf_repo_manager_del_upload_tmp_file (mgr, repo_id,
4279                                                        file_path, error) < 0) {
4280                 g_free (tmp_file_path);
4281                 return -1;
4282             }
4283             return 0;
4284         }
4285         seaf_warning ("Failed to stat temp file %s: %s.\n",
4286                       tmp_file_path, strerror(errno));
4287         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
4288                      "Failed to stat temp file.");
4289         g_free (tmp_file_path);
4290         return -1;
4291     }
4292 
4293     g_free (tmp_file_path);
4294 
4295     return file_stat.st_size;
4296 }
4297 
4298 void
seaf_repo_manager_update_repo_info(SeafRepoManager * mgr,const char * repo_id,const char * head_commit_id)4299 seaf_repo_manager_update_repo_info (SeafRepoManager *mgr,
4300                                     const char *repo_id, const char *head_commit_id)
4301 {
4302     SeafCommit *head;
4303 
4304     head = seaf_commit_manager_get_commit (seaf->commit_mgr,
4305                                            repo_id, 1, head_commit_id);
4306     if (!head) {
4307         seaf_warning ("Failed to get commit %s:%s.\n", repo_id, head_commit_id);
4308         return;
4309     }
4310 
4311     set_repo_commit_to_db (repo_id, head->repo_name, head->ctime, head->version,
4312                            (head->encrypted ? 1 : 0), head->creator_name);
4313 
4314     seaf_commit_unref (head);
4315 }
4316 
4317 char *
seaf_get_trash_repo_owner(const char * repo_id)4318 seaf_get_trash_repo_owner (const char *repo_id)
4319 {
4320     char *sql = "SELECT owner_id from RepoTrash WHERE repo_id = ?";
4321     return seaf_db_statement_get_string(seaf->db, sql, 1, "string", repo_id);
4322 }
4323 
4324 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)4325 seaf_get_group_shared_repo_by_path (SeafRepoManager *mgr,
4326                                     const char *repo_id,
4327                                     const char *path,
4328                                     int group_id,
4329                                     gboolean is_org,
4330                                     GError **error)
4331 {
4332     char *sql;
4333     char *real_repo_id = NULL;
4334     GList *repo = NULL;
4335     GObject *ret = NULL;
4336 
4337     /* If path is NULL, 'repo_id' represents for the repo we want,
4338      * otherwise, 'repo_id' represents for the origin repo,
4339      * find virtual repo by path first.
4340      */
4341     if (path != NULL) {
4342         real_repo_id = seaf_repo_manager_get_virtual_repo_id (mgr, repo_id, path, NULL);
4343         if (!real_repo_id) {
4344             seaf_warning ("Failed to get virtual repo_id by path %s, origin_repo: %s\n", path, repo_id);
4345             return NULL;
4346         }
4347     }
4348     if (!real_repo_id)
4349         real_repo_id = g_strdup (repo_id);
4350 
4351     if (!is_org)
4352         sql = "SELECT RepoGroup.repo_id, v.repo_id, "
4353               "group_id, user_name, permission, commit_id, s.size, "
4354               "v.origin_repo, v.path, i.name, "
4355               "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
4356               "(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo) "
4357               "FROM RepoGroup LEFT JOIN VirtualRepo v ON "
4358               "RepoGroup.repo_id = v.repo_id "
4359               "LEFT JOIN RepoInfo i ON RepoGroup.repo_id = i.repo_id "
4360               "LEFT JOIN RepoSize s ON RepoGroup.repo_id = s.repo_id, "
4361               "Branch WHERE group_id = ? AND "
4362               "RepoGroup.repo_id = Branch.repo_id AND "
4363               "RepoGroup.repo_id = ? AND "
4364               "Branch.name = 'master'";
4365     else
4366         sql = "SELECT OrgGroupRepo.repo_id, v.repo_id, "
4367               "group_id, owner, permission, commit_id, s.size, "
4368               "v.origin_repo, v.path, i.name, "
4369               "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
4370               "(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo) "
4371               "FROM OrgGroupRepo LEFT JOIN VirtualRepo v ON "
4372               "OrgGroupRepo.repo_id = v.repo_id "
4373               "LEFT JOIN RepoInfo i ON OrgRepoGroup.repo_id = i.repo_id "
4374               "LEFT JOIN RepoSize s ON OrgGroupRepo.repo_id = s.repo_id, "
4375               "Branch WHERE group_id = ? AND "
4376               "OrgGroupRepo.repo_id = Branch.repo_id AND "
4377               "OrgGroupRepo.repo_id = ? AND "
4378               "Branch.name = 'master'";
4379 
4380     /* The list 'repo' should have only one repo,
4381      * use existing api get_group_repos_cb() to get it.
4382      */
4383     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_group_repos_cb,
4384                                        &repo, 2, "int", group_id,
4385                                        "string", real_repo_id) < 0) {
4386         g_free (real_repo_id);
4387         g_list_free (repo);
4388         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
4389                      "Failed to get repo by group_id from db.");
4390         return NULL;
4391     }
4392     g_free (real_repo_id);
4393 
4394     if (repo) {
4395         seaf_fill_repo_obj_from_commit (&repo);
4396         if (repo)
4397             ret = (GObject *)(repo->data);
4398         g_list_free (repo);
4399     }
4400 
4401     return ret;
4402 }
4403 
4404 GList *
seaf_get_group_repos_by_user(SeafRepoManager * mgr,const char * user,int org_id,GError ** error)4405 seaf_get_group_repos_by_user (SeafRepoManager *mgr,
4406                               const char *user,
4407                               int org_id,
4408                               GError **error)
4409 {
4410     CcnetGroup *group;
4411     GList *groups = NULL, *p, *q;
4412     GList *repos = NULL;
4413     SeafileRepo *repo = NULL;
4414     GString *sql = NULL;
4415     int group_id = 0;
4416 
4417     /* Get the groups this user belongs to. */
4418     groups = ccnet_group_manager_get_groups_by_user (seaf->group_mgr, user,
4419                                                      1, NULL);
4420     if (!groups) {
4421         goto out;
4422     }
4423 
4424     sql = g_string_new ("");
4425     g_string_printf (sql, "SELECT g.repo_id, v.repo_id, "
4426                           "group_id, %s, permission, commit_id, s.size, "
4427                           "v.origin_repo, v.path, i.name, "
4428                           "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
4429                           "(SELECT name FROM RepoInfo WHERE repo_id=v.origin_repo)"
4430                           "FROM %s g LEFT JOIN VirtualRepo v ON "
4431                           "g.repo_id = v.repo_id "
4432                           "LEFT JOIN RepoInfo i ON g.repo_id = i.repo_id "
4433                           "LEFT JOIN RepoSize s ON g.repo_id = s.repo_id, "
4434                           "Branch b WHERE g.repo_id = b.repo_id AND "
4435                           "b.name = 'master' AND group_id IN (",
4436                           org_id < 0 ? "user_name" : "owner",
4437                           org_id < 0 ? "RepoGroup" : "OrgGroupRepo");
4438     for (p = groups; p != NULL; p = p->next) {
4439         group = p->data;
4440         g_object_get (group, "id", &group_id, NULL);
4441 
4442         g_string_append_printf (sql, "%d", group_id);
4443         if (p->next)
4444             g_string_append_printf (sql, ",");
4445     }
4446     g_string_append_printf (sql, " ) ORDER BY group_id");
4447 
4448     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql->str, get_group_repos_cb,
4449                                        &repos, 0) < 0) {
4450         for (p = repos; p; p = p->next) {
4451             g_object_unref (p->data);
4452         }
4453         g_list_free (repos);
4454         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
4455                      "Failed to get user group repos from db.");
4456         seaf_warning ("Failed to get user[%s] group repos from db.\n", user);
4457         goto out;
4458     }
4459 
4460     int repo_group_id = 0;
4461     char *group_name = NULL;
4462     q = repos;
4463 
4464     /* Add group_name to repo. Both groups and repos are listed by group_id in descending order */
4465     for (p = groups; p; p = p->next) {
4466         group = p->data;
4467         g_object_get (group, "id", &group_id, NULL);
4468         g_object_get (group, "group_name", &group_name, NULL);
4469 
4470         for (; q; q = q->next) {
4471             repo = q->data;
4472             g_object_get (repo, "group_id", &repo_group_id, NULL);
4473             if (repo_group_id == group_id)
4474                 g_object_set (repo, "group_name", group_name, NULL);
4475             else
4476                 break;
4477         }
4478         g_free (group_name);
4479         if (q == NULL)
4480             break;
4481     }
4482 
4483     seaf_fill_repo_obj_from_commit (&repos);
4484 
4485 out:
4486     if (sql)
4487         g_string_free (sql, TRUE);
4488 
4489     for (p = groups; p != NULL; p = p->next)
4490         g_object_unref ((GObject *)p->data);
4491     g_list_free (groups);
4492 
4493     return g_list_reverse (repos);
4494 }
4495 
4496 typedef struct RepoPath {
4497     char *repo_id;
4498     char *path;
4499     int group_id;
4500 } RepoPath;
4501 
4502 
4503 gboolean
convert_repo_path_cb(SeafDBRow * row,void * data)4504 convert_repo_path_cb (SeafDBRow *row, void *data)
4505 {
4506     GList **repo_paths = data;
4507 
4508     const char *repo_id = seaf_db_row_get_column_text (row, 0);
4509     const char *path = seaf_db_row_get_column_text (row, 1);
4510     int group_id = seaf_db_row_get_column_int (row, 2);
4511 
4512     RepoPath *rp = g_new0(RepoPath, 1);
4513     rp->repo_id = g_strdup(repo_id);
4514     rp->path = g_strdup(path);
4515     rp->group_id = group_id;
4516     *repo_paths = g_list_append (*repo_paths, rp);
4517 
4518     return TRUE;
4519 }
4520 
4521 static void
free_repo_path(gpointer data)4522 free_repo_path (gpointer data)
4523 {
4524     if (!data)
4525         return;
4526 
4527     RepoPath *rp = data;
4528     g_free (rp->repo_id);
4529     g_free (rp->path);
4530     g_free (rp);
4531 }
4532 
4533 static char *
filter_path(GList * repo_paths,const char * path)4534 filter_path (GList *repo_paths, const char *path)
4535 {
4536     GList *ptr = NULL;
4537     int len;
4538     const char *relative_path;
4539     char *ret = NULL;
4540     RepoPath *rp = NULL, res;
4541     res.repo_id = NULL;
4542     res.path = NULL;
4543     res.group_id = 0;
4544 
4545     /* Find nearest item which contains @path, */
4546     for (ptr = repo_paths; ptr; ptr = ptr->next) {
4547         rp = ptr->data;
4548         len = strlen(rp->path);
4549         if (strncmp(rp->path, path, len) == 0 && (path[len] == '/' || path[len] == '\0')) {
4550 
4551             if (g_strcmp0(rp->path, res.path) > 0) {
4552                 res.path = rp->path;
4553                 res.repo_id = rp->repo_id;
4554                 res.group_id = rp->group_id;
4555             }
4556         }
4557     }
4558     if (res.repo_id && res.path) {
4559         relative_path = path + strlen(res.path);
4560         if (relative_path[0] == '\0')
4561             relative_path = "/";
4562 
4563         json_t *json = json_object ();
4564         json_object_set_string_member(json, "repo_id", res.repo_id);
4565         json_object_set_string_member(json, "path", relative_path);
4566         if (res.group_id > 0)
4567             json_object_set_int_member(json, "group_id", res.group_id);
4568         ret = json_dumps (json, 0);
4569         json_decref (json);
4570     }
4571 
4572     return ret;
4573 }
4574 
4575 /* Convert origin repo and path to virtual repo and relative path */
4576 char *
seaf_repo_manager_convert_repo_path(SeafRepoManager * mgr,const char * repo_id,const char * path,const char * user,gboolean is_org,GError ** error)4577 seaf_repo_manager_convert_repo_path (SeafRepoManager *mgr,
4578                                      const char *repo_id,
4579                                      const char *path,
4580                                      const char *user,
4581                                      gboolean is_org,
4582                                      GError **error)
4583 {
4584     char *ret = NULL;
4585     int rc;
4586     int group_id;
4587     GString *sql;
4588     CcnetGroup *group;
4589     GList *groups = NULL, *p1;
4590     GList *repo_paths = NULL;
4591     SeafVirtRepo *vinfo = NULL;
4592     const char *r_repo_id = repo_id;
4593     char *r_path = NULL;
4594 
4595     vinfo = seaf_repo_manager_get_virtual_repo_info (mgr, repo_id);
4596     if (vinfo) {
4597         r_repo_id = vinfo->origin_repo_id;
4598         r_path = g_strconcat (vinfo->path, path, NULL);
4599     } else {
4600         r_path = g_strdup(path);
4601     }
4602 
4603     sql = g_string_new ("");
4604     g_string_printf (sql, "SELECT v.repo_id, path, 0 FROM VirtualRepo v, %s s WHERE "
4605                      "v.origin_repo=? AND v.repo_id=s.repo_id AND s.to_email=?",
4606                      is_org ? "OrgSharedRepo" : "SharedRepo");
4607     rc = seaf_db_statement_foreach_row (seaf->db,
4608                                         sql->str, convert_repo_path_cb,
4609                                         &repo_paths, 2,
4610                                         "string", r_repo_id, "string", user);
4611     if (rc < 0) {
4612         seaf_warning("Failed to convert repo path [%s:%s] to virtual repo path, db_error.\n",
4613                      repo_id, path);
4614         goto out;
4615     }
4616     ret = filter_path(repo_paths, r_path);
4617     g_list_free_full(repo_paths, free_repo_path);
4618     repo_paths = NULL;
4619     if (ret)
4620         goto out;
4621 
4622     /* Get the groups this user belongs to. */
4623 
4624     groups = ccnet_group_manager_get_groups_by_user (seaf->group_mgr, user,
4625                                                      1, NULL);
4626     if (!groups) {
4627         goto out;
4628     }
4629 
4630     g_string_printf (sql, "SELECT v.repo_id, path, r.group_id FROM VirtualRepo v, %s r WHERE "
4631                      "v.origin_repo=? AND v.repo_id=r.repo_id AND r.group_id IN(",
4632                      is_org ? "OrgGroupRepo" : "RepoGroup");
4633     for (p1 = groups; p1 != NULL; p1 = p1->next) {
4634         group = p1->data;
4635         g_object_get (group, "id", &group_id, NULL);
4636 
4637         g_string_append_printf (sql, "%d", group_id);
4638         if (p1->next)
4639             g_string_append_printf (sql, ",");
4640     }
4641     g_string_append_printf (sql, ")");
4642 
4643     rc = seaf_db_statement_foreach_row (seaf->db,
4644                                         sql->str, convert_repo_path_cb,
4645                                         &repo_paths, 1,
4646                                         "string", r_repo_id);
4647     if (rc < 0) {
4648         seaf_warning("Failed to convert repo path [%s:%s] to virtual repo path, db error.\n",
4649                      repo_id, path);
4650         g_string_free (sql, TRUE);
4651         goto out;
4652     }
4653     ret = filter_path(repo_paths, r_path);
4654     g_list_free_full(repo_paths, free_repo_path);
4655 
4656 out:
4657     g_free (r_path);
4658     if (vinfo)
4659         seaf_virtual_repo_info_free (vinfo);
4660     g_string_free (sql, TRUE);
4661     for (p1 = groups; p1 != NULL; p1 = p1->next)
4662         g_object_unref ((GObject *)p1->data);
4663     g_list_free (groups);
4664 
4665     return ret;
4666 }
4667 
4668 int
seaf_repo_manager_set_repo_status(SeafRepoManager * mgr,const char * repo_id,RepoStatus status)4669 seaf_repo_manager_set_repo_status(SeafRepoManager *mgr,
4670                                   const char *repo_id, RepoStatus status)
4671 {
4672     int ret = 0;
4673 
4674     if (seaf_db_statement_query (mgr->seaf->db,
4675                                  "UPDATE RepoInfo SET status=? "
4676                                  "WHERE repo_id=? OR repo_id IN "
4677                                  "(SELECT repo_id FROM VirtualRepo WHERE origin_repo=?)",
4678                                  3, "int", status,
4679                                  "string", repo_id, "string", repo_id) < 0)
4680         ret = -1;
4681 
4682     return ret;
4683 }
4684 
4685 int
seaf_repo_manager_get_repo_status(SeafRepoManager * mgr,const char * repo_id)4686 seaf_repo_manager_get_repo_status(SeafRepoManager *mgr,
4687                                   const char *repo_id)
4688 {
4689     char *sql = "SELECT status FROM RepoInfo WHERE repo_id=?";
4690 
4691     return seaf_db_statement_get_int (mgr->seaf->db, sql,
4692                                       1, "string", repo_id);
4693 }
4694