1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 #include "common.h"
4 #include "utils.h"
5 
6 #include "log.h"
7 
8 #include "seafile-session.h"
9 #include "share-mgr.h"
10 
11 #include "seaf-db.h"
12 #include "log.h"
13 #include "seafile-error.h"
14 
15 SeafShareManager *
seaf_share_manager_new(SeafileSession * seaf)16 seaf_share_manager_new (SeafileSession *seaf)
17 {
18     SeafShareManager *mgr = g_new0 (SeafShareManager, 1);
19 
20     mgr->seaf = seaf;
21 
22     return mgr;
23 }
24 
25 int
seaf_share_manager_start(SeafShareManager * mgr)26 seaf_share_manager_start (SeafShareManager *mgr)
27 {
28     if (!mgr->seaf->create_tables && seaf_db_type (mgr->seaf->db) != SEAF_DB_TYPE_PGSQL)
29         return 0;
30 
31     SeafDB *db = mgr->seaf->db;
32     const char *sql;
33 
34     int db_type = seaf_db_type (db);
35     if (db_type == SEAF_DB_TYPE_MYSQL) {
36         sql = "CREATE TABLE IF NOT EXISTS SharedRepo "
37             "(id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,"
38             "repo_id CHAR(37) , from_email VARCHAR(255), to_email VARCHAR(255), "
39             "permission CHAR(15), INDEX (repo_id), "
40             "INDEX(from_email), INDEX(to_email)) ENGINE=INNODB";
41 
42         if (seaf_db_query (db, sql) < 0)
43             return -1;
44     } else if (db_type == SEAF_DB_TYPE_SQLITE) {
45         sql = "CREATE TABLE IF NOT EXISTS SharedRepo "
46             "(repo_id CHAR(37) , from_email VARCHAR(255), to_email VARCHAR(255), "
47             "permission CHAR(15))";
48         if (seaf_db_query (db, sql) < 0)
49             return -1;
50         sql = "CREATE INDEX IF NOT EXISTS RepoIdIndex on SharedRepo (repo_id)";
51         if (seaf_db_query (db, sql) < 0)
52             return -1;
53         sql = "CREATE INDEX IF NOT EXISTS FromEmailIndex on SharedRepo (from_email)";
54         if (seaf_db_query (db, sql) < 0)
55             return -1;
56         sql = "CREATE INDEX IF NOT EXISTS ToEmailIndex on SharedRepo (to_email)";
57         if (seaf_db_query (db, sql) < 0)
58             return -1;
59     }
60     /* else if (db_type == SEAF_DB_TYPE_PGSQL) { */
61     /*     sql = "CREATE TABLE IF NOT EXISTS SharedRepo " */
62     /*         "(repo_id CHAR(36) , from_email VARCHAR(255), to_email VARCHAR(255), " */
63     /*         "permission VARCHAR(15))"; */
64     /*     if (seaf_db_query (db, sql) < 0) */
65     /*         return -1; */
66 
67     /*     if (!pgsql_index_exists (db, "sharedrepo_repoid_idx")) { */
68     /*         sql = "CREATE INDEX sharedrepo_repoid_idx ON SharedRepo (repo_id)"; */
69     /*         if (seaf_db_query (db, sql) < 0) */
70     /*             return -1; */
71     /*     } */
72     /*     if (!pgsql_index_exists (db, "sharedrepo_from_email_idx")) { */
73     /*         sql = "CREATE INDEX sharedrepo_from_email_idx ON SharedRepo (from_email)"; */
74     /*         if (seaf_db_query (db, sql) < 0) */
75     /*             return -1; */
76     /*     } */
77     /*     if (!pgsql_index_exists (db, "sharedrepo_to_email_idx")) { */
78     /*         sql = "CREATE INDEX sharedrepo_to_email_idx ON SharedRepo (to_email)"; */
79     /*         if (seaf_db_query (db, sql) < 0) */
80     /*             return -1; */
81     /*     } */
82     /* } */
83 
84     return 0;
85 }
86 
87 int
seaf_share_manager_add_share(SeafShareManager * mgr,const char * repo_id,const char * from_email,const char * to_email,const char * permission)88 seaf_share_manager_add_share (SeafShareManager *mgr, const char *repo_id,
89                               const char *from_email, const char *to_email,
90                               const char *permission)
91 {
92     gboolean db_err = FALSE;
93     int ret = 0;
94 
95     char *from_email_l = g_ascii_strdown (from_email, -1);
96     char *to_email_l = g_ascii_strdown (to_email, -1);
97 
98     if (seaf_db_statement_exists (mgr->seaf->db,
99                                   "SELECT repo_id from SharedRepo "
100                                   "WHERE repo_id=? AND "
101                                   "from_email=? AND to_email=?",
102                                   &db_err, 3, "string", repo_id,
103                                   "string", from_email_l, "string", to_email_l))
104         goto out;
105 
106     if (seaf_db_statement_query (mgr->seaf->db,
107                                  "INSERT INTO SharedRepo (repo_id, from_email, "
108                                  "to_email, permission) VALUES (?, ?, ?, ?)",
109                                  4, "string", repo_id, "string", from_email_l,
110                                  "string", to_email_l, "string", permission) < 0) {
111         ret = -1;
112         goto out;
113     }
114 
115 out:
116     g_free (from_email_l);
117     g_free (to_email_l);
118     return ret;
119 }
120 
121 int
seaf_share_manager_set_subdir_perm_by_path(SeafShareManager * mgr,const char * repo_id,const char * from_email,const char * to_email,const char * permission,const char * path)122 seaf_share_manager_set_subdir_perm_by_path (SeafShareManager *mgr, const char *repo_id,
123                                            const char *from_email, const char *to_email,
124                                            const char *permission, const char *path)
125 {
126     char *sql;
127     int ret;
128 
129     char *from_email_l = g_ascii_strdown (from_email, -1);
130     char *to_email_l = g_ascii_strdown (to_email, -1);
131     sql = "UPDATE SharedRepo SET permission=? WHERE repo_id IN "
132           "(SELECT repo_id FROM VirtualRepo WHERE origin_repo=? AND path=?) "
133           "AND from_email=? AND to_email=?";
134 
135     ret = seaf_db_statement_query (mgr->seaf->db, sql,
136                                    5, "string", permission,
137                                    "string", repo_id,
138                                    "string", path,
139                                    "string", from_email_l,
140                                    "string", to_email_l);
141     g_free (from_email_l);
142     g_free (to_email_l);
143     return ret;
144 }
145 
146 int
seaf_share_manager_set_permission(SeafShareManager * mgr,const char * repo_id,const char * from_email,const char * to_email,const char * permission)147 seaf_share_manager_set_permission (SeafShareManager *mgr, const char *repo_id,
148                                    const char *from_email, const char *to_email,
149                                    const char *permission)
150 {
151     char *sql;
152     int ret;
153 
154     char *from_email_l = g_ascii_strdown (from_email, -1);
155     char *to_email_l = g_ascii_strdown (to_email, -1);
156     sql = "UPDATE SharedRepo SET permission=? WHERE "
157         "repo_id=? AND from_email=? AND to_email=?";
158 
159     ret = seaf_db_statement_query (mgr->seaf->db, sql,
160                                    4, "string", permission, "string", repo_id,
161                                    "string", from_email_l, "string", to_email_l);
162 
163     g_free (from_email_l);
164     g_free (to_email_l);
165     return ret;
166 }
167 
168 static gboolean
collect_repos(SeafDBRow * row,void * data)169 collect_repos (SeafDBRow *row, void *data)
170 {
171     GList **p_repos = data;
172     const char *repo_id;
173     const char *vrepo_id;
174     const char *email;
175     const char *permission;
176     const char *commit_id;
177     gint64 size;
178     SeafileRepo *repo;
179 
180     repo_id = seaf_db_row_get_column_text (row, 0);
181     vrepo_id = seaf_db_row_get_column_text (row, 1);
182     email = seaf_db_row_get_column_text (row, 2);
183     permission = seaf_db_row_get_column_text (row, 3);
184     commit_id = seaf_db_row_get_column_text (row, 4);
185     size = seaf_db_row_get_column_int64 (row, 5);
186     const char *repo_name = seaf_db_row_get_column_text (row, 8);
187     gint64 update_time = seaf_db_row_get_column_int64 (row, 9);
188     int version = seaf_db_row_get_column_int (row, 10);
189     gboolean is_encrypted = seaf_db_row_get_column_int (row, 11) ? TRUE : FALSE;
190     const char *last_modifier = seaf_db_row_get_column_text (row, 12);
191     int status = seaf_db_row_get_column_int (row, 13);
192     const char *origin_repo_name = seaf_db_row_get_column_text (row, 14);
193 
194     char *email_l = g_ascii_strdown (email, -1);
195 
196     repo = g_object_new (SEAFILE_TYPE_REPO,
197                          "share_type", "personal",
198                          "repo_id", repo_id,
199                          "id", repo_id,
200                          "head_cmmt_id", commit_id,
201                          "user", email_l,
202                          "permission", permission,
203                          "is_virtual", (vrepo_id != NULL),
204                          "size", size,
205                          "status", status,
206                          NULL);
207     g_free (email_l);
208 
209     if (repo) {
210         if (vrepo_id) {
211             const char *origin_repo_id = seaf_db_row_get_column_text (row, 6);
212             const char *origin_path = seaf_db_row_get_column_text (row, 7);
213             g_object_set (repo, "store_id", origin_repo_id,
214                           "origin_repo_id", origin_repo_id,
215                           "origin_repo_name", origin_repo_name,
216                           "origin_path", origin_path, NULL);
217         } else {
218             g_object_set (repo, "store_id", repo_id, NULL);
219         }
220         if (repo_name) {
221             g_object_set (repo, "name", repo_name,
222                           "repo_name", repo_name,
223                           "last_modify", update_time,
224                           "last_modified", update_time,
225                           "version", version,
226                           "encrypted", is_encrypted,
227                           "last_modifier", last_modifier, NULL);
228         }
229         *p_repos = g_list_prepend (*p_repos, repo);
230     }
231 
232     return TRUE;
233 }
234 
235 static void
seaf_fill_repo_commit_if_not_in_db(GList ** repos)236 seaf_fill_repo_commit_if_not_in_db (GList **repos)
237 {
238     char *repo_name = NULL;
239     char *last_modifier = NULL;
240     char *repo_id = NULL;
241     char *commit_id = NULL;
242     SeafileRepo *repo = NULL;
243     GList *p = NULL;
244 
245     for (p = *repos; p;) {
246         repo = p->data;
247         g_object_get (repo, "name", &repo_name, NULL);
248         g_object_get (repo, "last_modifier", &last_modifier, NULL);
249         if (!repo_name || !last_modifier) {
250             g_object_get (repo, "repo_id", &repo_id,
251                           "head_cmmt_id", &commit_id, NULL);
252             SeafCommit *commit = seaf_commit_manager_get_commit_compatible (seaf->commit_mgr,
253                                                                             repo_id, commit_id);
254             if (!commit) {
255                 seaf_warning ("Commit %s:%s is missing\n", repo_id, commit_id);
256                 GList *next = p->next;
257                 g_object_unref (repo);
258                 *repos = g_list_delete_link (*repos, p);
259                 p = next;
260                 if (repo_name)
261                     g_free (repo_name);
262                 if (last_modifier)
263                     g_free (last_modifier);
264                 continue;
265             } else {
266                 g_object_set (repo, "name", commit->repo_name,
267                                     "repo_name", commit->repo_name,
268                                     "last_modify", commit->ctime,
269                                     "last_modified", commit->ctime,
270                                     "version", commit->version,
271                                     "encrypted", commit->encrypted,
272                                     "last_modifier", commit->creator_name,
273                                     NULL);
274 
275                 /* Set to database */
276                 set_repo_commit_to_db (repo_id, commit->repo_name, commit->ctime, commit->version,
277                                        commit->encrypted, commit->creator_name);
278 
279                 seaf_commit_unref (commit);
280             }
281             g_free (repo_id);
282             g_free (commit_id);
283         }
284         if (repo_name)
285             g_free (repo_name);
286         if (last_modifier)
287             g_free (last_modifier);
288 
289         p = p->next;
290     }
291 }
292 
293 GList*
seaf_share_manager_list_share_repos(SeafShareManager * mgr,const char * email,const char * type,int start,int limit,gboolean * db_err)294 seaf_share_manager_list_share_repos (SeafShareManager *mgr, const char *email,
295                                      const char *type, int start, int limit,
296                                      gboolean *db_err)
297 {
298     GList *ret = NULL, *p;
299     char *sql;
300 
301     if (start == -1 && limit == -1) {
302         if (g_strcmp0 (type, "from_email") == 0) {
303             sql = "SELECT sh.repo_id, v.repo_id, "
304                 "to_email, permission, commit_id, s.size, "
305                 "v.origin_repo, v.path, i.name, "
306                 "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
307                 "(SELECT name from RepoInfo WHERE repo_id=v.origin_repo) FROM "
308                 "SharedRepo sh LEFT JOIN VirtualRepo v ON "
309                 "sh.repo_id=v.repo_id "
310                 "LEFT JOIN RepoSize s ON sh.repo_id = s.repo_id "
311                 "LEFT JOIN RepoInfo i ON sh.repo_id = i.repo_id, Branch b "
312                 "WHERE from_email=? AND "
313                 "sh.repo_id = b.repo_id AND "
314                 "b.name = 'master' "
315                 "ORDER BY i.update_time DESC, sh.repo_id";
316         } else if (g_strcmp0 (type, "to_email") == 0) {
317             sql = "SELECT sh.repo_id, v.repo_id, "
318                 "from_email, permission, commit_id, s.size, "
319                 "v.origin_repo, v.path, i.name, "
320                 "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
321                 "(SELECT name from RepoInfo WHERE repo_id=v.origin_repo) FROM "
322                 "SharedRepo sh LEFT JOIN VirtualRepo v ON "
323                 "sh.repo_id=v.repo_id "
324                 "LEFT JOIN RepoSize s ON sh.repo_id = s.repo_id "
325                 "LEFT JOIN RepoInfo i ON sh.repo_id = i.repo_id, Branch b "
326                 "WHERE to_email=? AND "
327                 "sh.repo_id = b.repo_id AND "
328                 "b.name = 'master' "
329                 "ORDER BY i.update_time DESC, sh.repo_id";
330         } else {
331             /* should never reach here */
332             seaf_warning ("[share mgr] Wrong column type");
333             return NULL;
334         }
335 
336         if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
337                                            collect_repos, &ret,
338                                            1, "string", email) < 0) {
339             seaf_warning ("[share mgr] DB error when get shared repo id and email "
340                        "for %s.\n", email);
341             for (p = ret; p; p = p->next)
342                 g_object_unref (p->data);
343             g_list_free (ret);
344             if (db_err)
345                 *db_err = TRUE;
346             return NULL;
347         }
348     }
349     else {
350         if (g_strcmp0 (type, "from_email") == 0) {
351             sql = "SELECT sh.repo_id, v.repo_id, "
352                 "to_email, permission, commit_id, s.size, "
353                 "v.origin_repo, v.path, i.name, "
354                 "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
355                 "(SELECT name from RepoInfo WHERE repo_id=v.origin_repo) FROM "
356                 "SharedRepo sh LEFT JOIN VirtualRepo v ON "
357                 "sh.repo_id=v.repo_id "
358                 "LEFT JOIN RepoSize s ON sh.repo_id = s.repo_id "
359                 "LEFT JOIN RepoInfo i ON sh.repo_id = i.repo_id, Branch b "
360                 "WHERE from_email=? "
361                 "AND sh.repo_id = b.repo_id "
362                 "AND b.name = 'master' "
363                 "ORDER BY i.update_time DESC, sh.repo_id "
364                 "LIMIT ? OFFSET ?";
365         } else if (g_strcmp0 (type, "to_email") == 0) {
366             sql = "SELECT sh.repo_id, v.repo_id, "
367                 "from_email, permission, commit_id, s.size, "
368                 "v.origin_repo, v.path, i.name, "
369                 "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
370                 "(SELECT name from RepoInfo WHERE repo_id=v.origin_repo) FROM "
371                 "SharedRepo sh LEFT JOIN VirtualRepo v ON "
372                 "sh.repo_id=v.repo_id "
373                 "LEFT JOIN RepoSize s ON sh.repo_id = s.repo_id "
374                 "LEFT JOIN RepoInfo i ON sh.repo_id = i.repo_id, Branch b "
375                 "WHERE to_email=? "
376                 "AND sh.repo_id = b.repo_id "
377                 "AND b.name = 'master' "
378                 "ORDER BY i.update_time DESC, sh.repo_id "
379                 "LIMIT ? OFFSET ?";
380         } else {
381             /* should never reach here */
382             seaf_warning ("[share mgr] Wrong column type");
383             return NULL;
384         }
385 
386         if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
387                                            collect_repos, &ret,
388                                            3, "string", email,
389                                            "int", limit, "int", start) < 0) {
390             seaf_warning ("[share mgr] DB error when get shared repo id and email "
391                        "for %s.\n", email);
392             for (p = ret; p; p = p->next)
393                 g_object_unref (p->data);
394             g_list_free (ret);
395             if (db_err)
396                 *db_err = TRUE;
397             return NULL;
398         }
399     }
400 
401     seaf_fill_repo_commit_if_not_in_db (&ret);
402 
403     return g_list_reverse (ret);
404 }
405 
406 static gboolean
collect_shared_to(SeafDBRow * row,void * data)407 collect_shared_to (SeafDBRow *row, void *data)
408 {
409     GList **plist = data;
410     const char *to_email;
411 
412     to_email = seaf_db_row_get_column_text (row, 0);
413     *plist = g_list_prepend (*plist, g_ascii_strdown(to_email, -1));
414 
415     return TRUE;
416 }
417 
418 GList *
seaf_share_manager_list_shared_to(SeafShareManager * mgr,const char * owner,const char * repo_id)419 seaf_share_manager_list_shared_to (SeafShareManager *mgr,
420                                    const char *owner,
421                                    const char *repo_id)
422 {
423     char *sql;
424     GList *ret = NULL;
425 
426     sql = "SELECT to_email FROM SharedRepo WHERE "
427         "from_email=? AND repo_id=?";
428     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
429                                        collect_shared_to, &ret,
430                                        2, "string", owner, "string", repo_id) < 0) {
431         seaf_warning ("[share mgr] DB error when list shared to.\n");
432         string_list_free (ret);
433         return NULL;
434     }
435 
436     return ret;
437 }
438 
439 static gboolean
collect_repo_shared_to(SeafDBRow * row,void * data)440 collect_repo_shared_to (SeafDBRow *row, void *data)
441 {
442     GList **shared_to = data;
443     const char *to_email = seaf_db_row_get_column_text (row, 0);
444     char *email_down = g_ascii_strdown(to_email, -1);
445     const char *perm = seaf_db_row_get_column_text (row, 1);
446     const char *repo_id = seaf_db_row_get_column_text (row, 2);
447 
448     SeafileSharedUser *uobj = g_object_new (SEAFILE_TYPE_SHARED_USER,
449                                             "repo_id", repo_id,
450                                             "user", email_down,
451                                             "perm", perm,
452                                             NULL);
453     *shared_to = g_list_prepend (*shared_to, uobj);
454     g_free (email_down);
455 
456     return TRUE;
457 }
458 
459 GList *
seaf_share_manager_list_repo_shared_to(SeafShareManager * mgr,const char * from_email,const char * repo_id,GError ** error)460 seaf_share_manager_list_repo_shared_to (SeafShareManager *mgr,
461                                         const char *from_email,
462                                         const char *repo_id,
463                                         GError **error)
464 {
465     GList *shared_to = NULL;
466     char *sql = "SELECT to_email, permission, repo_id FROM SharedRepo WHERE "
467                 "from_email=? AND repo_id=?";
468 
469     int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
470                                              collect_repo_shared_to, &shared_to,
471                                              2, "string", from_email, "string", repo_id);
472     if (ret < 0) {
473         seaf_warning ("Failed to list repo %s shared to from db.\n", repo_id);
474         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
475                      "Failed to list repo shared to from db");
476         while (shared_to) {
477             g_object_unref (shared_to->data);
478             shared_to = g_list_delete_link (shared_to, shared_to);
479         }
480         return NULL;
481     }
482 
483     return shared_to;
484 }
485 
486 static gboolean
collect_repo_shared_group(SeafDBRow * row,void * data)487 collect_repo_shared_group (SeafDBRow *row, void *data)
488 {
489     GList **shared_group = data;
490     int group_id = seaf_db_row_get_column_int (row, 0);
491     const char *perm = seaf_db_row_get_column_text (row, 1);
492     const char *repo_id = seaf_db_row_get_column_text (row, 2);
493 
494     SeafileSharedGroup *gobj = g_object_new (SEAFILE_TYPE_SHARED_GROUP,
495                                              "repo_id", repo_id,
496                                              "group_id", group_id,
497                                              "perm", perm,
498                                              NULL);
499     *shared_group = g_list_prepend (*shared_group, gobj);
500 
501     return TRUE;
502 }
503 
504 GList *
seaf_share_manager_list_repo_shared_group(SeafShareManager * mgr,const char * from_email,const char * repo_id,GError ** error)505 seaf_share_manager_list_repo_shared_group (SeafShareManager *mgr,
506                                            const char *from_email,
507                                            const char *repo_id,
508                                            GError **error)
509 {
510     GList *shared_group = NULL;
511     char *sql = "SELECT group_id, permission, repo_id FROM RepoGroup WHERE "
512                 "user_name=? AND repo_id=?";
513 
514     int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
515                                              collect_repo_shared_group, &shared_group,
516                                              2, "string", from_email, "string", repo_id);
517     if (ret < 0) {
518         seaf_warning ("Failed to list repo %s shared group from db.\n", repo_id);
519         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
520                      "Failed to list repo shared group from db");
521         while (shared_group) {
522             g_object_unref (shared_group->data);
523             shared_group = g_list_delete_link (shared_group, shared_group);
524         }
525         return NULL;
526     }
527 
528     return shared_group;
529 }
530 
531 static gboolean
get_shared_dirs_to_user(SeafDBRow * row,void * data)532 get_shared_dirs_to_user (SeafDBRow *row, void *data)
533 {
534     GHashTable *dirs = data;
535 
536     const char *path = seaf_db_row_get_column_text (row, 0);
537     const char *perm = seaf_db_row_get_column_text (row, 1);
538     g_hash_table_replace (dirs, g_strdup (path), g_strdup (perm));
539 
540     return TRUE;
541 }
542 
543 static gboolean
get_shared_dirs_to_group(SeafDBRow * row,void * data)544 get_shared_dirs_to_group (SeafDBRow *row, void *data)
545 {
546     GHashTable *dirs = data;
547 
548     const char *path = seaf_db_row_get_column_text (row, 0);
549     const char *perm = seaf_db_row_get_column_text (row, 1);
550 
551     char *prev_perm = g_hash_table_lookup (dirs, path);
552     if (g_strcmp0 (perm, prev_perm) != 0 &&
553         (prev_perm == NULL || g_strcmp0 (prev_perm, "r") == 0)) {
554         g_hash_table_replace (dirs, g_strdup (path), g_strdup (perm));
555     }
556 
557     return TRUE;
558 }
559 
560 // Conver group id list to comma separated str
561 // [1, 2, 3] -> 1,2,3
562 static GString *
convert_group_list_to_str(GList * groups)563 convert_group_list_to_str (GList *groups)
564 {
565     GList *iter = groups;
566     CcnetGroup *group;
567     int group_id;
568     GString *group_ids = g_string_new ("");
569 
570     for (; iter; iter = iter->next) {
571         group = iter->data;
572         g_object_get (group, "id", &group_id, NULL);
573         g_string_append_printf (group_ids, "%d,", group_id);
574     }
575     group_ids = g_string_erase (group_ids, group_ids->len - 1, 1);
576 
577     return group_ids;
578 }
579 
580 GHashTable *
seaf_share_manager_get_shared_dirs_to_user(SeafShareManager * mgr,const char * orig_repo_id,const char * to_email)581 seaf_share_manager_get_shared_dirs_to_user (SeafShareManager *mgr,
582                                             const char *orig_repo_id,
583                                             const char *to_email)
584 {
585     GHashTable *dirs;
586     char *sql;
587 
588     dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
589     sql = "SELECT v.path, s.permission FROM SharedRepo s, VirtualRepo v WHERE "
590           "s.repo_id = v.repo_id AND s.to_email = ? AND v.origin_repo = ?";
591 
592     int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_shared_dirs_to_user,
593                                              dirs, 2, "string", to_email,
594                                              "string", orig_repo_id);
595     if (ret < 0) {
596         seaf_warning ("Failed to get all shared folder perms "
597                       "in parent repo %.8s for user %s.\n", orig_repo_id, to_email);
598         g_hash_table_destroy (dirs);
599         return NULL;
600     }
601 
602     return dirs;
603 }
604 
605 GHashTable *
seaf_share_manager_get_shared_dirs_to_group(SeafShareManager * mgr,const char * orig_repo_id,GList * groups)606 seaf_share_manager_get_shared_dirs_to_group (SeafShareManager *mgr,
607                                              const char *orig_repo_id,
608                                              GList *groups)
609 {
610     GHashTable *dirs;
611     GString *group_ids;
612     char *sql;
613 
614     dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
615     group_ids = convert_group_list_to_str (groups);
616     sql = g_strdup_printf ("SELECT v.path, s.permission "
617                            "FROM RepoGroup s, VirtualRepo v WHERE "
618                            "s.repo_id = v.repo_id AND v.origin_repo = ? "
619                            "AND s.group_id in (%s)", group_ids->str);
620 
621     int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_shared_dirs_to_group,
622                                              dirs, 1, "string", orig_repo_id);
623     g_free (sql);
624     g_string_free (group_ids, TRUE);
625 
626     if (ret < 0) {
627         seaf_warning ("Failed to get all shared folder perm from parent repo %.8s "
628                       "to all user groups.\n", orig_repo_id);
629         g_hash_table_destroy (dirs);
630         return NULL;
631     }
632 
633     return dirs;
634 }
635 
636 int
seaf_share_manager_remove_share(SeafShareManager * mgr,const char * repo_id,const char * from_email,const char * to_email)637 seaf_share_manager_remove_share (SeafShareManager *mgr, const char *repo_id,
638                                  const char *from_email, const char *to_email)
639 {
640     if (seaf_db_statement_query (mgr->seaf->db,
641                        "DELETE FROM SharedRepo WHERE repo_id = ? AND from_email ="
642                        " ? AND to_email = ?",
643                        3, "string", repo_id, "string", from_email,
644                        "string", to_email) < 0)
645         return -1;
646 
647     return 0;
648 }
649 
650 int
seaf_share_manager_unshare_subdir(SeafShareManager * mgr,const char * orig_repo_id,const char * path,const char * from_email,const char * to_email)651 seaf_share_manager_unshare_subdir (SeafShareManager* mgr,
652                                    const char *orig_repo_id,
653                                    const char *path,
654                                    const char *from_email,
655                                    const char *to_email)
656 {
657     if (seaf_db_statement_query (mgr->seaf->db,
658                                  "DELETE FROM SharedRepo WHERE "
659                                  "from_email = ? AND to_email = ? "
660                                  "AND repo_id IN "
661                                  "(SELECT repo_id FROM VirtualRepo WHERE "
662                                  "origin_repo = ? AND path = ?)",
663                                  4, "string", from_email,
664                                  "string", to_email,
665                                  "string", orig_repo_id,
666                                  "string", path) < 0)
667         return -1;
668 
669     return 0;
670 }
671 
672 int
seaf_share_manager_remove_repo(SeafShareManager * mgr,const char * repo_id)673 seaf_share_manager_remove_repo (SeafShareManager *mgr, const char *repo_id)
674 {
675     if (seaf_db_statement_query (mgr->seaf->db,
676                        "DELETE FROM SharedRepo WHERE repo_id = ?",
677                        1, "string", repo_id) < 0)
678         return -1;
679 
680     return 0;
681 }
682 
683 char *
seaf_share_manager_check_permission(SeafShareManager * mgr,const char * repo_id,const char * email)684 seaf_share_manager_check_permission (SeafShareManager *mgr,
685                                      const char *repo_id,
686                                      const char *email)
687 {
688     char *sql;
689 
690     sql = "SELECT permission FROM SharedRepo WHERE repo_id=? AND to_email=?";
691     return seaf_db_statement_get_string (mgr->seaf->db, sql,
692                                          2, "string", repo_id, "string", email);
693 }
694 
695 static gboolean
get_shared_sub_dirs(SeafDBRow * row,void * data)696 get_shared_sub_dirs (SeafDBRow *row, void *data)
697 {
698     GHashTable *sub_dirs = data;
699     int dummy;
700 
701     const char *sub_dir = seaf_db_row_get_column_text (row, 0);
702     g_hash_table_replace (sub_dirs, g_strdup(sub_dir), &dummy);
703 
704     return TRUE;
705 }
706 
707 GHashTable *
seaf_share_manager_get_shared_sub_dirs(SeafShareManager * mgr,const char * repo_id,const char * path)708 seaf_share_manager_get_shared_sub_dirs (SeafShareManager *mgr,
709                                         const char *repo_id,
710                                         const char *path)
711 {
712     GHashTable *sub_dirs = g_hash_table_new_full (g_str_hash, g_str_equal,
713                                                   g_free, NULL);
714     char *pattern;
715     if (strcmp (path, "/") == 0) {
716         pattern = g_strdup_printf("%s%%", path);
717     } else {
718         pattern = g_strdup_printf ("%s/%%", path);
719     }
720     int ret = seaf_db_statement_foreach_row (mgr->seaf->db,
721                                              "SELECT v.path FROM VirtualRepo v, SharedRepo s "
722                                              "WHERE v.repo_id = s.repo_id and "
723                                              "v.origin_repo = ? AND v.path LIKE ?",
724                                              get_shared_sub_dirs, sub_dirs,
725                                              2, "string", repo_id, "string", pattern);
726 
727     if (ret < 0) {
728         g_free (pattern);
729         seaf_warning ("Failed to get shared sub dirs from db.\n");
730         g_hash_table_destroy (sub_dirs);
731         return NULL;
732     }
733 
734     ret = seaf_db_statement_foreach_row (mgr->seaf->db,
735                                          "SELECT v.path FROM VirtualRepo v, RepoGroup r "
736                                          "WHERE v.repo_id = r.repo_id and "
737                                          "v.origin_repo = ? AND v.path LIKE ?",
738                                          get_shared_sub_dirs, sub_dirs,
739                                          2, "string", repo_id, "string", pattern);
740     g_free (pattern);
741 
742     if (ret < 0) {
743         seaf_warning ("Failed to get shared sub dirs from db.\n");
744         g_hash_table_destroy (sub_dirs);
745         return NULL;
746     }
747 
748     return sub_dirs;
749 }
750 
751 int
seaf_share_manager_is_repo_shared(SeafShareManager * mgr,const char * repo_id)752 seaf_share_manager_is_repo_shared (SeafShareManager *mgr,
753                                    const char *repo_id)
754 {
755     gboolean ret;
756     gboolean db_err = FALSE;
757 
758     ret = seaf_db_statement_exists (mgr->seaf->db,
759                                     "SELECT repo_id FROM SharedRepo WHERE "
760                                     "repo_id = ?", &db_err,
761                                     1, "string", repo_id);
762     if (db_err) {
763         seaf_warning ("DB error when check repo exist in SharedRepo.\n");
764         return -1;
765     }
766 
767     if (!ret) {
768         ret = seaf_db_statement_exists (mgr->seaf->db,
769                                         "SELECT repo_id FROM RepoGroup WHERE "
770                                         "repo_id = ?", &db_err,
771                                         1, "string", repo_id);
772         if (db_err) {
773             seaf_warning ("DB error when check repo exist in RepoGroup.\n");
774             return -1;
775         }
776     }
777 
778     return ret;
779 }
780 
781 GObject *
seaf_get_shared_repo_by_path(SeafRepoManager * mgr,const char * repo_id,const char * path,const char * shared_to,int is_org,GError ** error)782 seaf_get_shared_repo_by_path (SeafRepoManager *mgr,
783                               const char *repo_id,
784                               const char *path,
785                               const char *shared_to,
786                               int is_org,
787                               GError **error)
788 {
789     char *sql;
790     char *real_repo_id = NULL;
791     GList *repo = NULL;
792     GObject *ret = NULL;
793 
794     /* If path is NULL, 'repo_id' represents for the repo we want,
795      * otherwise, 'repo_id' represents for the origin repo,
796      * find virtual repo by path first.
797      */
798     if (path != NULL) {
799         real_repo_id = seaf_repo_manager_get_virtual_repo_id (mgr, repo_id, path, NULL);
800         if (!real_repo_id) {
801             seaf_warning ("Failed to get virtual repo_id by path %s, origin_repo: %s\n", path, repo_id);
802             return NULL;
803         }
804     }
805     if (!real_repo_id)
806         real_repo_id = g_strdup (repo_id);
807 
808     if (!is_org)
809         sql = "SELECT sh.repo_id, v.repo_id, "
810               "from_email, permission, commit_id, s.size, "
811               "v.origin_repo, v.path, i.name, "
812               "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
813               "(SELECT name from RepoInfo WHERE repo_id=v.origin_repo) FROM "
814               "SharedRepo sh LEFT JOIN VirtualRepo v ON "
815               "sh.repo_id=v.repo_id "
816               "LEFT JOIN RepoSize s ON sh.repo_id = s.repo_id "
817               "LEFT JOIN RepoInfo i ON sh.repo_id = i.repo_id, Branch b "
818               "WHERE to_email=? AND "
819               "sh.repo_id = b.repo_id AND sh.repo_id=? AND "
820               "b.name = 'master' ";
821     else
822         sql = "SELECT sh.repo_id, v.repo_id, "
823               "from_email, permission, commit_id, s.size, "
824               "v.origin_repo, v.path, i.name, "
825               "i.update_time, i.version, i.is_encrypted, i.last_modifier, i.status, "
826               "(SELECT name from RepoInfo WHERE repo_id=v.origin_repo) FROM "
827               "OrgSharedRepo sh LEFT JOIN VirtualRepo v ON "
828               "sh.repo_id=v.repo_id "
829               "LEFT JOIN RepoSize s ON sh.repo_id = s.repo_id "
830               "LEFT JOIN RepoInfo i ON sh.repo_id = i.repo_id, Branch b "
831               "WHERE to_email=? AND "
832               "sh.repo_id = b.repo_id AND sh.repo_id=? AND "
833               "b.name = 'master' ";
834 
835     /* The list 'repo' should have only one repo,
836      * use existing api collect_repos() to get it.
837      */
838     if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
839                                        collect_repos, &repo,
840                                        2, "string", shared_to, "string", real_repo_id) < 0) {
841             g_free (real_repo_id);
842             g_list_free (repo);
843             seaf_warning ("[share mgr] DB error when get shared repo "
844                           "for %s, path:%s\n", shared_to, path);
845             return NULL;
846     }
847     g_free (real_repo_id);
848     if (repo) {
849         ret = (GObject *)(repo->data);
850         g_list_free (repo);
851     }
852 
853     return ret;
854 }
855 
856 int
seaf_share_manager_unshare_group_subdir(SeafShareManager * mgr,const char * repo_id,const char * path,const char * owner,int group_id)857 seaf_share_manager_unshare_group_subdir (SeafShareManager* mgr,
858                                          const char *repo_id,
859                                          const char *path,
860                                          const char *owner,
861                                          int group_id)
862 {
863     if (seaf_db_statement_query (mgr->seaf->db,
864                                  "DELETE FROM RepoGroup WHERE "
865                                  "user_name = ? AND group_id = ? "
866                                  "AND repo_id IN "
867                                  "(SELECT repo_id FROM VirtualRepo WHERE "
868                                  "origin_repo = ? AND path = ?)",
869                                  4, "string", owner,
870                                  "int", group_id,
871                                  "string", repo_id,
872                                  "string", path) < 0)
873         return -1;
874 
875     return 0;
876 }
877 
878 gboolean
seaf_share_manager_repo_has_been_shared(SeafShareManager * mgr,const char * repo_id,gboolean including_groups)879 seaf_share_manager_repo_has_been_shared (SeafShareManager* mgr,
880                                          const char *repo_id,
881                                          gboolean including_groups)
882 {
883     gboolean exists;
884     gboolean db_err = FALSE;
885     char *sql;
886 
887     sql = "SELECT 1 FROM SharedRepo WHERE repo_id=?";
888     exists = seaf_db_statement_exists (mgr->seaf->db, sql, &db_err,
889                                        1, "string", repo_id);
890     if (db_err) {
891         seaf_warning ("DB error when check repo exist in SharedRepo and RepoGroup.\n");
892         return FALSE;
893     }
894 
895     if (!exists && including_groups) {
896         sql = "SELECT 1 FROM RepoGroup WHERE repo_id=?";
897         exists = seaf_db_statement_exists (mgr->seaf->db, sql, &db_err,
898                                            1, "string", repo_id);
899     }
900 
901     return exists;
902 }
903 
904 gboolean
get_shared_users_cb(SeafDBRow * row,void * data)905 get_shared_users_cb (SeafDBRow *row, void *data)
906 {
907     GList **users = data;
908     const char *repo_id = seaf_db_row_get_column_text (row, 0);
909     const char *user = seaf_db_row_get_column_text (row, 1);
910     const char *perm = seaf_db_row_get_column_text (row, 2);
911     SeafileSharedUser *uobj = g_object_new (SEAFILE_TYPE_SHARED_USER,
912                                             "repo_id", repo_id,
913                                             "user", user,
914                                             "perm", perm,
915                                             NULL);
916     *users = g_list_append (*users, uobj);
917 
918     return TRUE;
919 }
920 
921 GList *
seaf_share_manager_org_get_shared_users_by_repo(SeafShareManager * mgr,int org_id,const char * repo_id)922 seaf_share_manager_org_get_shared_users_by_repo (SeafShareManager* mgr,
923                                                  int org_id,
924                                                  const char *repo_id)
925 {
926     GList *users = NULL;
927     char *sql = "SELECT repo_id, to_email, permission FROM OrgSharedRepo WHERE org_id=? AND "
928                 "repo_id=?";
929 
930     int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
931                                              get_shared_users_cb, &users,
932                                              2, "int", org_id, "string", repo_id);
933     if (ret < 0) {
934         seaf_warning("Failed to get users by repo_id[%s], org_id[%d]\n",
935                      repo_id, org_id);
936         return NULL;
937     }
938 
939     return users;
940 }
941 
942 
943 GList *
seaf_share_manager_get_shared_users_by_repo(SeafShareManager * mgr,const char * repo_id)944 seaf_share_manager_get_shared_users_by_repo(SeafShareManager* mgr,
945                                             const char *repo_id)
946 {
947     GList *users = NULL;
948     char *sql = "SELECT repo_id, to_email, permission FROM SharedRepo WHERE "
949                 "repo_id=?";
950 
951     int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql,
952                                              get_shared_users_cb, &users,
953                                              1, "string", repo_id);
954     if (ret < 0) {
955         seaf_warning("Failed to get users by repo_id[%s]\n", repo_id);
956         return NULL;
957     }
958 
959     return users;
960 }
961