1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 #include "common.h"
4 #include <glib/gstdio.h>
5 #include <ctype.h>
6 
7 #include <sys/stat.h>
8 #include <dirent.h>
9 #include "utils.h"
10 
11 #include "seafile-session.h"
12 #include "fs-mgr.h"
13 #include "repo-mgr.h"
14 #include "seafile-error.h"
15 #include "seafile-rpc.h"
16 #include "common/mq-mgr.h"
17 #include "seafile-config.h"
18 #include "seafile-object.h"
19 #include "seafile-error-impl.h"
20 #define DEBUG_FLAG SEAFILE_DEBUG_OTHER
21 #include "log.h"
22 
23 #include "../daemon/vc-utils.h"
24 
25 
26 /* -------- Utilities -------- */
27 static GObject*
convert_repo(SeafRepo * r)28 convert_repo (SeafRepo *r)
29 {
30     SeafileRepo *repo = NULL;
31 
32     if (r->head == NULL)
33         return NULL;
34 
35     if (r->worktree_invalid && !seafile_session_config_get_allow_invalid_worktree(seaf))
36         return NULL;
37 
38     repo = seafile_repo_new ();
39     if (!repo)
40         return NULL;
41 
42     g_object_set (repo, "id", r->id, "name", r->name,
43                   "desc", r->desc, "encrypted", r->encrypted,
44                   "magic", r->magic, "enc_version", r->enc_version,
45                   "head_cmmt_id", r->head ? r->head->commit_id : NULL,
46                   "root", r->root_id,
47                   "version", r->version, "last_modify", (int)r->last_modify,
48                   NULL);
49     g_object_set (repo,
50                   "repo_id", r->id, "repo_name", r->name,
51                   "repo_desc", r->desc, "last_modified", (int)r->last_modify,
52                   NULL);
53 
54     g_object_set (repo, "worktree", r->worktree,
55                   "relay-id", r->relay_id,
56                   "worktree-invalid", r->worktree_invalid,
57                   "last-sync-time", r->last_sync_time,
58                   "auto-sync", r->auto_sync,
59                   NULL);
60 
61     return (GObject *)repo;
62 }
63 
64 static void
free_repo_obj(gpointer repo)65 free_repo_obj (gpointer repo)
66 {
67     if (!repo)
68         return;
69     g_object_unref ((GObject *)repo);
70 }
71 
72 static GList *
convert_repo_list(GList * inner_repos)73 convert_repo_list (GList *inner_repos)
74 {
75     GList *ret = NULL, *ptr;
76     GObject *repo = NULL;
77 
78     for (ptr = inner_repos; ptr; ptr=ptr->next) {
79         SeafRepo *r = ptr->data;
80         repo = convert_repo (r);
81         if (!repo) {
82             g_list_free_full (ret, free_repo_obj);
83             return NULL;
84         }
85 
86         ret = g_list_prepend (ret, repo);
87     }
88 
89     return g_list_reverse (ret);
90 }
91 
92 /*
93  * RPC functions only available for clients.
94  */
95 
96 #include "sync-mgr.h"
97 
98 int
seafile_set_config(const char * key,const char * value,GError ** error)99 seafile_set_config (const char *key, const char *value, GError **error)
100 {
101     return seafile_session_config_set_string(seaf, key, value);
102 }
103 
104 char *
seafile_get_config(const char * key,GError ** error)105 seafile_get_config (const char *key, GError **error)
106 {
107     return seafile_session_config_get_string(seaf, key);
108 }
109 
110 int
seafile_set_config_int(const char * key,int value,GError ** error)111 seafile_set_config_int (const char *key, int value, GError **error)
112 {
113     return seafile_session_config_set_int(seaf, key, value);
114 }
115 
116 int
seafile_get_config_int(const char * key,GError ** error)117 seafile_get_config_int (const char *key, GError **error)
118 {
119     gboolean exists = TRUE;
120 
121     int ret = seafile_session_config_get_int(seaf, key, &exists);
122 
123     if (!exists) {
124         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Config not exists");
125         return -1;
126     }
127 
128     return ret;
129 }
130 
131 int
seafile_set_upload_rate_limit(int limit,GError ** error)132 seafile_set_upload_rate_limit (int limit, GError **error)
133 {
134     if (limit < 0)
135         limit = 0;
136 
137     seaf->sync_mgr->upload_limit = limit;
138 
139     return seafile_session_config_set_int (seaf, KEY_UPLOAD_LIMIT, limit);
140 }
141 
142 int
seafile_set_download_rate_limit(int limit,GError ** error)143 seafile_set_download_rate_limit (int limit, GError **error)
144 {
145     if (limit < 0)
146         limit = 0;
147 
148     seaf->sync_mgr->download_limit = limit;
149 
150     return seafile_session_config_set_int (seaf, KEY_DOWNLOAD_LIMIT, limit);
151 }
152 
153 char *
seafile_gen_default_worktree(const char * worktree_parent,const char * repo_name,GError ** error)154 seafile_gen_default_worktree (const char *worktree_parent,
155                               const char *repo_name,
156                               GError **error)
157 {
158     if (!worktree_parent || !repo_name) {
159         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Empty args");
160         return NULL;
161     }
162 
163     return seaf_clone_manager_gen_default_worktree (seaf->clone_mgr,
164                                                     worktree_parent,
165                                                     repo_name);
166 }
167 
168 int
seafile_check_path_for_clone(const char * path,GError ** error)169 seafile_check_path_for_clone (const char *path, GError **error)
170 {
171     if (!seaf_clone_manager_check_worktree_path(seaf->clone_mgr, path, error)) {
172         return -1;
173     }
174 
175     return 0;
176 }
177 
178 char *
seafile_clone(const char * repo_id,int repo_version,const char * repo_name,const char * worktree,const char * token,const char * passwd,const char * magic,const char * email,const char * random_key,int enc_version,const char * more_info,GError ** error)179 seafile_clone (const char *repo_id,
180                int repo_version,
181                const char *repo_name,
182                const char *worktree,
183                const char *token,
184                const char *passwd,
185                const char *magic,
186                const char *email,
187                const char *random_key,
188                int enc_version,
189                const char *more_info,
190                GError **error)
191 {
192     if (!repo_id || strlen(repo_id) != 36) {
193         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid repo id");
194         return NULL;
195     }
196 
197     if (!worktree) {
198         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
199                      "Worktre must be specified");
200         return NULL;
201     }
202 
203     if (!token || !email ) {
204         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
205                      "Argument can't be NULL");
206         return NULL;
207     }
208 
209     return seaf_clone_manager_add_task (seaf->clone_mgr,
210                                         repo_id, repo_version,
211                                         repo_name, token,
212                                         passwd, magic,
213                                         enc_version,
214                                         random_key,
215                                         worktree,
216                                         email, more_info,
217                                         error);
218 }
219 
220 char *
seafile_download(const char * repo_id,int repo_version,const char * repo_name,const char * wt_parent,const char * token,const char * passwd,const char * magic,const char * email,const char * random_key,int enc_version,const char * more_info,GError ** error)221 seafile_download (const char *repo_id,
222                   int repo_version,
223                   const char *repo_name,
224                   const char *wt_parent,
225                   const char *token,
226                   const char *passwd,
227                   const char *magic,
228                   const char *email,
229                   const char *random_key,
230                   int enc_version,
231                   const char *more_info,
232                   GError **error)
233 {
234     if (!repo_id || strlen(repo_id) != 36) {
235         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid repo id");
236         return NULL;
237     }
238 
239     if (!wt_parent) {
240         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
241                      "Worktre must be specified");
242         return NULL;
243     }
244 
245     if (!token || !email ) {
246         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
247                      "Argument can't be NULL");
248         return NULL;
249     }
250 
251     return seaf_clone_manager_add_download_task (seaf->clone_mgr,
252                                                  repo_id, repo_version,
253                                                  repo_name, token,
254                                                  passwd, magic,
255                                                  enc_version, random_key,
256                                                  wt_parent,
257                                                  email, more_info,
258                                                  error);
259 }
260 
261 int
seafile_cancel_clone_task(const char * repo_id,GError ** error)262 seafile_cancel_clone_task (const char *repo_id, GError **error)
263 {
264     return seaf_clone_manager_cancel_task (seaf->clone_mgr, repo_id);
265 }
266 
267 GList *
seafile_get_clone_tasks(GError ** error)268 seafile_get_clone_tasks (GError **error)
269 {
270     GList *tasks, *ptr;
271     GList *ret = NULL;
272     CloneTask *task;
273     SeafileCloneTask *t;
274 
275     tasks = seaf_clone_manager_get_tasks (seaf->clone_mgr);
276     for (ptr = tasks; ptr != NULL; ptr = ptr->next) {
277         task = ptr->data;
278         t = g_object_new (SEAFILE_TYPE_CLONE_TASK,
279                           "state", clone_task_state_to_str(task->state),
280                           "error", task->error,
281                           "repo_id", task->repo_id,
282                           "repo_name", task->repo_name,
283                           "worktree", task->worktree,
284                           NULL);
285         ret = g_list_prepend (ret, t);
286     }
287 
288     g_list_free (tasks);
289     return ret;
290 }
291 
292 int
seafile_sync(const char * repo_id,const char * peer_id,GError ** error)293 seafile_sync (const char *repo_id, const char *peer_id, GError **error)
294 {
295     if (!repo_id) {
296         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Repo ID should not be null");
297         return -1;
298     }
299 
300     return seaf_sync_manager_add_sync_task (seaf->sync_mgr, repo_id, error);
301 }
302 
303 static SeafileTask *
convert_http_task(HttpTxTask * task)304 convert_http_task (HttpTxTask *task)
305 {
306     SeafileTask *t = seafile_task_new();
307 
308     g_object_set (t,
309                   "repo_id", task->repo_id,
310                   "state", http_task_state_to_str(task->state),
311                   "rt_state", http_task_rt_state_to_str(task->runtime_state),
312                   NULL);
313 
314     if (task->type == HTTP_TASK_TYPE_DOWNLOAD) {
315         g_object_set (t, "ttype", "download", NULL);
316         if (task->runtime_state == HTTP_TASK_RT_STATE_BLOCK) {
317             g_object_set (t, "block_total", task->total_download,
318                           "block_done", task->done_download,
319                           NULL);
320             g_object_set (t, "rate", http_tx_task_get_rate(task), NULL);
321         } else if (task->runtime_state == HTTP_TASK_RT_STATE_FS) {
322             g_object_set (t, "fs_objects_total", task->n_fs_objs,
323                           "fs_objects_done", task->done_fs_objs,
324                           NULL);
325         }
326     } else {
327         g_object_set (t, "ttype", "upload", NULL);
328         if (task->runtime_state == HTTP_TASK_RT_STATE_BLOCK) {
329             SyncInfo *info = seaf_sync_manager_get_sync_info (seaf->sync_mgr, task->repo_id);
330             if (info && info->multipart_upload) {
331                 g_object_set (t, "block_total", info->total_bytes,
332                               "block_done", info->uploaded_bytes,
333                               NULL);
334             } else {
335                 g_object_set (t, "block_total", (gint64)task->n_blocks,
336                               "block_done", (gint64)task->done_blocks,
337                               NULL);
338             }
339             g_object_set (t, "rate", http_tx_task_get_rate(task), NULL);
340         }
341     }
342 
343     return t;
344 }
345 
346 GObject *
seafile_find_transfer_task(const char * repo_id,GError * error)347 seafile_find_transfer_task (const char *repo_id, GError *error)
348 {
349     HttpTxTask *http_task;
350 
351     http_task = http_tx_manager_find_task (seaf->http_tx_mgr, repo_id);
352     if (http_task)
353         return (GObject *)convert_http_task (http_task);
354 
355     return NULL;
356 }
357 
358 int
seafile_get_upload_rate(GError ** error)359 seafile_get_upload_rate(GError **error)
360 {
361     return seaf->sync_mgr->last_sent_bytes;
362 }
363 
364 int
seafile_get_download_rate(GError ** error)365 seafile_get_download_rate(GError **error)
366 {
367     return seaf->sync_mgr->last_recv_bytes;
368 }
369 
370 GObject *
seafile_get_repo_sync_task(const char * repo_id,GError ** error)371 seafile_get_repo_sync_task (const char *repo_id, GError **error)
372 {
373     SeafRepo *repo;
374     repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
375 
376     if (!repo) {
377         return NULL;
378     }
379 
380     SyncInfo *info = seaf_sync_manager_get_sync_info (seaf->sync_mgr, repo_id);
381     if (!info || !info->current_task)
382         return NULL;
383 
384     SyncTask *task = info->current_task;
385     const char *sync_state;
386     char allzeros[41] = {0};
387 
388     if (!info->in_sync && memcmp(allzeros, info->head_commit, 41) == 0) {
389         sync_state = "waiting for sync";
390     } else {
391         sync_state = sync_state_to_str(task->state);
392     }
393 
394     SeafileSyncTask *s_task;
395     s_task = g_object_new (SEAFILE_TYPE_SYNC_TASK,
396                            "force_upload", task->is_manual_sync,
397                            "state", sync_state,
398                            "error", task->error,
399                            "repo_id", info->repo_id,
400                            NULL);
401 
402     return (GObject *)s_task;
403 }
404 
405 int
seafile_set_repo_property(const char * repo_id,const char * key,const char * value,GError ** error)406 seafile_set_repo_property (const char *repo_id,
407                            const char *key,
408                            const char *value,
409                            GError **error)
410 {
411     int ret;
412 
413     if (repo_id == NULL || key == NULL || value == NULL) {
414         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Arguments should not be empty");
415         return -1;
416     }
417 
418     SeafRepo *repo;
419     repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
420     if (!repo) {
421         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_REPO, "Can't find Repo %s", repo_id);
422         return -1;
423     }
424 
425     ret = seaf_repo_manager_set_repo_property (seaf->repo_mgr,
426                                                repo->id, key, value);
427     if (ret < 0) {
428         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL,
429                      "Failed to set key for repo %s", repo_id);
430         return -1;
431     }
432 
433     return 0;
434 }
435 
436 gchar *
seafile_get_repo_property(const char * repo_id,const char * key,GError ** error)437 seafile_get_repo_property (const char *repo_id,
438                            const char *key,
439                            GError **error)
440 {
441     char *value = NULL;
442 
443     if (!repo_id || !key) {
444         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Arguments should not be empty");
445         return NULL;
446     }
447 
448     SeafRepo *repo;
449     repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
450     if (!repo) {
451         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_REPO, "Can't find Repo %s", repo_id);
452         return NULL;
453     }
454 
455     value = seaf_repo_manager_get_repo_property (seaf->repo_mgr, repo->id, key);
456     return value;
457 }
458 
459 int
seafile_update_repos_server_host(const char * old_server_url,const char * new_server_url,GError ** error)460 seafile_update_repos_server_host (const char *old_server_url,
461                                   const char *new_server_url,
462                                   GError **error)
463 {
464     if (!old_server_url || !new_server_url) {
465         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
466         return -1;
467     }
468 
469     return seaf_repo_manager_update_repos_server_host(
470         seaf->repo_mgr, old_server_url, new_server_url);
471 }
472 
473 int
seafile_calc_dir_size(const char * path,GError ** error)474 seafile_calc_dir_size (const char *path, GError **error)
475 {
476     if (!path) {
477         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
478         return -1;
479     }
480 
481     gint64 size_64 = ccnet_calc_directory_size(path, error);
482     if (size_64 < 0) {
483         seaf_warning ("failed to calculate dir size for %s\n", path);
484         return -1;
485     }
486 
487     /* get the size in MB */
488     int size = (int) (size_64 >> 20);
489     return size;
490 }
491 
492 int
seafile_disable_auto_sync(GError ** error)493 seafile_disable_auto_sync (GError **error)
494 {
495     return seaf_sync_manager_disable_auto_sync (seaf->sync_mgr);
496 }
497 
498 int
seafile_enable_auto_sync(GError ** error)499 seafile_enable_auto_sync (GError **error)
500 {
501     return seaf_sync_manager_enable_auto_sync (seaf->sync_mgr);
502 }
503 
seafile_is_auto_sync_enabled(GError ** error)504 int seafile_is_auto_sync_enabled (GError **error)
505 {
506     return seaf_sync_manager_is_auto_sync_enabled (seaf->sync_mgr);
507 }
508 
509 char *
seafile_get_path_sync_status(const char * repo_id,const char * path,int is_dir,GError ** error)510 seafile_get_path_sync_status (const char *repo_id,
511                               const char *path,
512                               int is_dir,
513                               GError **error)
514 {
515     char *canon_path = NULL;
516     int len;
517     char *status;
518 
519     if (!repo_id || !path) {
520         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
521         return NULL;
522     }
523 
524     /* Empty path means to get status of the worktree folder. */
525     if (strcmp (path, "") != 0) {
526         if (*path == '/')
527             ++path;
528         canon_path = g_strdup(path);
529         len = strlen(canon_path);
530         if (canon_path[len-1] == '/')
531             canon_path[len-1] = 0;
532     } else {
533         canon_path = g_strdup(path);
534     }
535 
536     status = seaf_sync_manager_get_path_sync_status (seaf->sync_mgr,
537                                                      repo_id,
538                                                      canon_path,
539                                                      is_dir);
540     g_free (canon_path);
541     return status;
542 }
543 
544 int
seafile_mark_file_locked(const char * repo_id,const char * path,GError ** error)545 seafile_mark_file_locked (const char *repo_id, const char *path, GError **error)
546 {
547     char *canon_path = NULL;
548     int len;
549     int ret;
550 
551     if (!repo_id || !path) {
552         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
553         return -1;
554     }
555 
556     if (*path == '/')
557         ++path;
558 
559     if (path[0] == 0) {
560         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid path");
561         return -1;
562     }
563 
564     canon_path = g_strdup(path);
565     len = strlen(canon_path);
566     if (canon_path[len-1] == '/')
567         canon_path[len-1] = 0;
568 
569     ret = seaf_filelock_manager_mark_file_locked (seaf->filelock_mgr,
570                                                   repo_id, path, FALSE);
571 
572     g_free (canon_path);
573     return ret;
574 }
575 
576 int
seafile_mark_file_unlocked(const char * repo_id,const char * path,GError ** error)577 seafile_mark_file_unlocked (const char *repo_id, const char *path, GError **error)
578 {
579     char *canon_path = NULL;
580     int len;
581     int ret;
582 
583     if (!repo_id || !path) {
584         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
585         return -1;
586     }
587 
588     if (*path == '/')
589         ++path;
590 
591     if (path[0] == 0) {
592         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid path");
593         return -1;
594     }
595 
596     canon_path = g_strdup(path);
597     len = strlen(canon_path);
598     if (canon_path[len-1] == '/')
599         canon_path[len-1] = 0;
600 
601     ret = seaf_filelock_manager_mark_file_unlocked (seaf->filelock_mgr,
602                                                     repo_id, path);
603 
604     g_free (canon_path);
605     return ret;
606 }
607 
608 json_t *
seafile_get_sync_notification(GError ** error)609 seafile_get_sync_notification (GError **error)
610 {
611     return seaf_mq_manager_pop_message (seaf->mq_mgr);
612 }
613 
614 char *
seafile_get_server_property(const char * server_url,const char * key,GError ** error)615 seafile_get_server_property (const char *server_url, const char *key, GError **error)
616 {
617     if (!server_url || !key) {
618         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
619                      "Argument should not be null");
620         return NULL;
621     }
622 
623     return seaf_repo_manager_get_server_property (seaf->repo_mgr,
624                                                   server_url,
625                                                   key);
626 }
627 
628 int
seafile_set_server_property(const char * server_url,const char * key,const char * value,GError ** error)629 seafile_set_server_property (const char *server_url,
630                              const char *key,
631                              const char *value,
632                              GError **error)
633 {
634     if (!server_url || !key || !value) {
635         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
636                      "Argument should not be null");
637         return -1;
638     }
639 
640     return seaf_repo_manager_set_server_property (seaf->repo_mgr,
641                                                   server_url,
642                                                   key, value);
643 }
644 
645 GList *
seafile_get_file_sync_errors(int offset,int limit,GError ** error)646 seafile_get_file_sync_errors (int offset, int limit, GError **error)
647 {
648     return seaf_repo_manager_get_file_sync_errors (seaf->repo_mgr, offset, limit);
649 }
650 
651 int
seafile_del_file_sync_error_by_id(int id,GError ** error)652 seafile_del_file_sync_error_by_id (int id, GError **error)
653 {
654     return seaf_repo_manager_del_file_sync_error_by_id (seaf->repo_mgr, id);
655 }
656 
657 GList*
seafile_get_repo_list(int start,int limit,GError ** error)658 seafile_get_repo_list (int start, int limit, GError **error)
659 {
660     GList *repos = seaf_repo_manager_get_repo_list(seaf->repo_mgr, start, limit);
661     GList *ret = NULL;
662 
663     ret = convert_repo_list (repos);
664 
665     g_list_free (repos);
666 
667     return ret;
668 }
669 
670 GObject*
seafile_get_repo(const char * repo_id,GError ** error)671 seafile_get_repo (const char *repo_id, GError **error)
672 {
673     SeafRepo *r;
674 
675     if (!repo_id) {
676         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
677         return NULL;
678     }
679     if (!is_uuid_valid (repo_id)) {
680         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid repo id");
681         return NULL;
682     }
683 
684     r = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
685     /* Don't return repo that's not checked out. */
686     if (r == NULL)
687         return NULL;
688 
689     GObject *repo = convert_repo (r);
690 
691     return repo;
692 }
693 
694 static
do_unsync_repo(SeafRepo * repo)695 int do_unsync_repo(SeafRepo *repo)
696 {
697     if (!seaf->started) {
698         seaf_message ("System not started, skip removing repo.\n");
699         return -1;
700     }
701 
702     if (repo->auto_sync && (repo->sync_interval == 0))
703         seaf_wt_monitor_unwatch_repo (seaf->wt_monitor, repo->id);
704 
705     seaf_sync_manager_cancel_sync_task (seaf->sync_mgr, repo->id);
706 
707     SyncInfo *info = seaf_sync_manager_get_sync_info (seaf->sync_mgr, repo->id);
708 
709     /* If we are syncing the repo,
710      * we just mark the repo as deleted and let sync-mgr actually delete it.
711      * Otherwise we are safe to delete the repo.
712      */
713     char *worktree = g_strdup (repo->worktree);
714     if (info != NULL && info->in_sync) {
715         seaf_repo_manager_mark_repo_deleted (seaf->repo_mgr, repo);
716     } else {
717         seaf_repo_manager_del_repo (seaf->repo_mgr, repo);
718     }
719 
720     g_free (worktree);
721 
722     return 0;
723 }
724 
725 static void
cancel_clone_tasks_by_account(const char * account_server_url,const char * account_email)726 cancel_clone_tasks_by_account (const char *account_server_url, const char *account_email)
727 {
728     GList *ptr, *tasks;
729     CloneTask *task;
730 
731     tasks = seaf_clone_manager_get_tasks (seaf->clone_mgr);
732     for (ptr = tasks; ptr != NULL; ptr = ptr->next) {
733         task = ptr->data;
734 
735         if (g_strcmp0(account_server_url, task->server_url) == 0
736             && g_strcmp0(account_email, task->email) == 0) {
737             seaf_clone_manager_cancel_task (seaf->clone_mgr, task->repo_id);
738         }
739     }
740 
741     g_list_free (tasks);
742 }
743 
744 int
seafile_unsync_repos_by_account(const char * server_url,const char * email,GError ** error)745 seafile_unsync_repos_by_account (const char *server_url, const char *email, GError **error)
746 {
747     if (!server_url || !email) {
748         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
749         return -1;
750     }
751     char *canon_server_url = canonical_server_url (server_url);
752 
753     GList *ptr, *repos = seaf_repo_manager_get_repo_list(seaf->repo_mgr, -1, -1);
754     if (!repos) {
755         return 0;
756     }
757 
758     for (ptr = repos; ptr; ptr = ptr->next) {
759         SeafRepo *repo = (SeafRepo*)ptr->data;
760         if (g_strcmp0(repo->server_url, canon_server_url) == 0 && g_strcmp0(repo->email, email) == 0) {
761             if (do_unsync_repo(repo) < 0) {
762                 return -1;
763             }
764         }
765     }
766 
767     g_list_free (repos);
768     g_free (canon_server_url);
769 
770     cancel_clone_tasks_by_account (server_url, email);
771 
772     return 0;
773 }
774 
775 int
seafile_remove_repo_tokens_by_account(const char * server_url,const char * email,GError ** error)776 seafile_remove_repo_tokens_by_account (const char *server_url, const char *email, GError **error)
777 {
778     if (!server_url || !email) {
779         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
780         return -1;
781     }
782     char *canon_server_url = canonical_server_url (server_url);
783 
784     GList *ptr, *repos = seaf_repo_manager_get_repo_list(seaf->repo_mgr, -1, -1);
785     if (!repos) {
786         return 0;
787     }
788 
789     for (ptr = repos; ptr; ptr = ptr->next) {
790         SeafRepo *repo = (SeafRepo*)ptr->data;
791         if (g_strcmp0(repo->server_url, canon_server_url) == 0 && g_strcmp0(repo->email, email) == 0) {
792             if (seaf_repo_manager_remove_repo_token(seaf->repo_mgr, repo) < 0) {
793                 return -1;
794             }
795         }
796     }
797 
798     g_list_free (repos);
799     g_free (canon_server_url);
800 
801     cancel_clone_tasks_by_account (server_url, email);
802 
803     return 0;
804 }
805 
806 int
seafile_set_repo_token(const char * repo_id,const char * token,GError ** error)807 seafile_set_repo_token (const char *repo_id,
808                         const char *token,
809                         GError **error)
810 {
811     int ret;
812 
813     if (repo_id == NULL || token == NULL) {
814         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Arguments should not be empty");
815         return -1;
816     }
817 
818     SeafRepo *repo;
819     repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
820     if (!repo) {
821         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_REPO, "Can't find Repo %s", repo_id);
822         return -1;
823     }
824 
825     ret = seaf_repo_manager_set_repo_token (seaf->repo_mgr,
826                                             repo, token);
827     if (ret < 0) {
828         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL,
829                      "Failed to set token for repo %s", repo_id);
830         return -1;
831     }
832 
833     return 0;
834 }
835 
836 int
seafile_destroy_repo(const char * repo_id,GError ** error)837 seafile_destroy_repo (const char *repo_id, GError **error)
838 {
839     if (!repo_id) {
840         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
841         return -1;
842     }
843     if (!is_uuid_valid (repo_id)) {
844         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid repo id");
845         return -1;
846     }
847 
848     SeafRepo *repo;
849 
850     repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
851     if (!repo) {
852         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "No such repository");
853         return -1;
854     }
855 
856     return do_unsync_repo(repo);
857 }
858 
859 
860 GObject *
seafile_generate_magic_and_random_key(int enc_version,const char * repo_id,const char * passwd,GError ** error)861 seafile_generate_magic_and_random_key(int enc_version,
862                                       const char* repo_id,
863                                       const char *passwd,
864                                       GError **error)
865 {
866     if (!repo_id || !passwd) {
867         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
868         return NULL;
869     }
870 
871     gchar salt[65] = {0};
872     gchar magic[65] = {0};
873     gchar random_key[97] = {0};
874 
875     if (enc_version >= 3 && seafile_generate_repo_salt (salt) < 0) {
876         return NULL;
877     }
878 
879     seafile_generate_magic (enc_version, repo_id, passwd, salt, magic);
880     if (seafile_generate_random_key (passwd, enc_version, salt, random_key) < 0) {
881         return NULL;
882     }
883 
884     SeafileEncryptionInfo *sinfo;
885     sinfo = g_object_new (SEAFILE_TYPE_ENCRYPTION_INFO,
886                           "repo_id", repo_id,
887                           "passwd", passwd,
888                           "enc_version", enc_version,
889                           "magic", magic,
890                           "random_key", random_key,
891                           NULL);
892 
893     if (enc_version >= 3)
894         g_object_set (sinfo, "salt", salt, NULL);
895 
896     return (GObject *)sinfo;
897 
898 }
899 
900 #include "diff-simple.h"
901 
902 inline static const char*
get_diff_status_str(char status)903 get_diff_status_str(char status)
904 {
905     if (status == DIFF_STATUS_ADDED)
906         return "add";
907     if (status == DIFF_STATUS_DELETED)
908         return "del";
909     if (status == DIFF_STATUS_MODIFIED)
910         return "mod";
911     if (status == DIFF_STATUS_RENAMED)
912         return "mov";
913     if (status == DIFF_STATUS_DIR_ADDED)
914         return "newdir";
915     if (status == DIFF_STATUS_DIR_DELETED)
916         return "deldir";
917     return NULL;
918 }
919 
920 GList *
seafile_diff(const char * repo_id,const char * arg1,const char * arg2,int fold_dir_diff,GError ** error)921 seafile_diff (const char *repo_id, const char *arg1, const char *arg2, int fold_dir_diff, GError **error)
922 {
923     SeafRepo *repo;
924     char *err_msgs = NULL;
925     GList *diff_entries, *p;
926     GList *ret = NULL;
927 
928     if (!repo_id || !arg1 || !arg2) {
929         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
930         return NULL;
931     }
932 
933     if (!is_uuid_valid (repo_id)) {
934         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid repo id");
935         return NULL;
936     }
937 
938     if ((arg1[0] != 0 && !is_object_id_valid (arg1)) || !is_object_id_valid(arg2)) {
939         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Invalid commit id");
940         return NULL;
941     }
942 
943     repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
944     if (!repo) {
945         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "No such repository");
946         return NULL;
947     }
948 
949     diff_entries = seaf_repo_diff (repo, arg1, arg2, fold_dir_diff, &err_msgs);
950     if (err_msgs) {
951         g_set_error (error, SEAFILE_DOMAIN, -1, "%s", err_msgs);
952         g_free (err_msgs);
953         return NULL;
954     }
955 
956     for (p = diff_entries; p != NULL; p = p->next) {
957         DiffEntry *de = p->data;
958         SeafileDiffEntry *entry = g_object_new (
959             SEAFILE_TYPE_DIFF_ENTRY,
960             "status", get_diff_status_str(de->status),
961             "name", de->name,
962             "new_name", de->new_name,
963             NULL);
964         ret = g_list_prepend (ret, entry);
965     }
966 
967     for (p = diff_entries; p != NULL; p = p->next) {
968         DiffEntry *de = p->data;
969         diff_entry_free (de);
970     }
971     g_list_free (diff_entries);
972 
973     return g_list_reverse (ret);
974 }
975 
976 int
seafile_shutdown(GError ** error)977 seafile_shutdown (GError **error)
978 {
979     seaf_warning ("Got an exit command. Now exiting\n");
980     exit(0);
981     return 0;
982 }
983 
984 char*
seafile_sync_error_id_to_str(int error_id,GError ** error)985 seafile_sync_error_id_to_str (int error_id, GError **error)
986 {
987     return g_strdup(sync_error_id_to_str (error_id));
988 }
989