diff --git a/server/repo-mgr.h b/server/repo-mgr.h index 0319c19..90b7aa9 100644 --- a/server/repo-mgr.h +++ b/server/repo-mgr.h @@ -365,6 +365,14 @@ seaf_repo_manager_post_dir (SeafRepoManager *mgr, const char *user, GError **error); +int +seaf_repo_manager_mkdir_with_parents (SeafRepoManager *mgr, + const char *repo_id, + const char *parent_dir, + const char *new_dir_path, + const char *user, + GError **error); + /** * Update an existing file in a repo * @params: same as seaf_repo_manager_post_file diff --git a/server/repo-op.c b/server/repo-op.c index bac4672..9237433 100644 --- a/server/repo-op.c +++ b/server/repo-op.c @@ -5464,3 +5464,164 @@ seaf_repo_diff (SeafRepo *repo, const char *old, const char *new, int fold_dir_r return diff_entries; } + +int +seaf_repo_manager_mkdir_with_parents (SeafRepoManager *mgr, + const char *repo_id, + const char *parent_dir, + const char *new_dir_path, + const char *user, + GError **error) +{ + SeafRepo *repo = NULL; + SeafCommit *head_commit = NULL; + char **sub_folders = NULL; + int nfolder; + char buf[SEAF_PATH_MAX]; + char *root_id = NULL; + SeafDirent *new_dent = NULL; + char *parent_dir_can = NULL; + char *relative_dir_can = NULL; + char *abs_path = NULL; + int total_path_len; + int sub_folder_len; + GList *uncre_dir_list = NULL; + GList *iter_list = NULL; + char *uncre_dir; + int ret = 0; + + if (new_dir_path[0] == '/' || new_dir_path[0] == '\\') { + seaf_warning ("[mkdir with parent] Invalid relative path %s.\n", new_dir_path); + g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, + "Invalid relative path"); + return -1; + } + + GET_REPO_OR_FAIL(repo, repo_id); + GET_COMMIT_OR_FAIL(head_commit, repo->id, repo->version, repo->head->commit_id); + + relative_dir_can = get_canonical_path (new_dir_path); + sub_folders = g_strsplit (relative_dir_can, "/", 0); + nfolder = g_strv_length (sub_folders); + + int i = 0; + for (; i < nfolder; ++i) { + if (strcmp (sub_folders[i], "") == 0) + continue; + + if (should_ignore_file (sub_folders[i], NULL)) { + seaf_warning ("[post dir] Invalid dir name %s.\n", sub_folders[i]); + g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, + "Invalid dir name"); + ret = -1; + goto out; + } + } + + if (strcmp (parent_dir, "/") == 0 || + strcmp (parent_dir, "\\") == 0) { + parent_dir_can = g_strdup ("/"); + abs_path = g_strdup_printf ("%s%s", parent_dir_can, relative_dir_can); + } else { + parent_dir_can = get_canonical_path (parent_dir); + abs_path = g_strdup_printf ("%s/%s", parent_dir_can, relative_dir_can); + } + if (!abs_path) { + seaf_warning ("[mkdir with parent] Out of memory.\n"); + g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL, + "Out of memory"); + ret = -1; + goto out; + } + total_path_len = strlen (abs_path); + + // from the last, to check the folder exist + i = nfolder - 1; + for (; i >= 0; --i) { + if (strcmp (sub_folders[i], "") == 0) + continue; + + sub_folder_len = strlen (sub_folders[i]) + 1; + total_path_len -= sub_folder_len; + memset (abs_path + total_path_len, '\0', sub_folder_len); + + if (check_file_exists (repo->store_id, repo->version, + head_commit->root_id, abs_path, sub_folders[i], NULL)) { + // folder exist, skip loop to create unexist subfolder + strcat (abs_path, "/"); + strcat (abs_path, sub_folders[i]); + break; + } else { + // folder not exist, cache it to create later + uncre_dir_list = g_list_prepend (uncre_dir_list, sub_folders[i]); + } + } + + if (uncre_dir_list) { + // exist parent folder has been found, based on it to create unexist subfolder + char new_root_id[41]; + memcpy (new_root_id, head_commit->root_id, 40); + new_root_id[40] = '\0'; + + for (iter_list = uncre_dir_list; iter_list; iter_list = iter_list->next) { + uncre_dir = iter_list->data; + new_dent = seaf_dirent_new (dir_version_from_repo_version(repo->version), + EMPTY_SHA1, S_IFDIR, uncre_dir, + (gint64)time(NULL), NULL, -1); + + root_id = do_post_file (repo, + new_root_id, abs_path, new_dent); + if (!root_id) { + seaf_warning ("[put dir] Failed to put dir.\n"); + g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, + "Failed to put dir"); + ret = -1; + seaf_dirent_free (new_dent); + goto out; + } + + // the last folder has been created + if (!iter_list->next) { + seaf_dirent_free (new_dent); + break; + } + + strcat (abs_path, "/"); + strcat (abs_path, uncre_dir); + memcpy (new_root_id, root_id, 40); + + seaf_dirent_free (new_dent); + g_free (root_id); + } + + /* Commit. */ + snprintf(buf, SEAF_PATH_MAX, "Added directory \"%s\"", relative_dir_can); + if (gen_new_commit (repo_id, head_commit, root_id, + user, buf, NULL, error) < 0) { + ret = -1; + g_free (root_id); + goto out; + } + + seaf_repo_manager_merge_virtual_repo (mgr, repo_id, NULL); + g_free (root_id); + } + +out: + if (repo) + seaf_repo_unref (repo); + if (head_commit) + seaf_commit_unref(head_commit); + if (sub_folders) + g_strfreev (sub_folders); + if (uncre_dir_list) + g_list_free (uncre_dir_list); + if (relative_dir_can) + g_free (relative_dir_can); + if (parent_dir_can) + g_free (parent_dir_can); + if (abs_path) + g_free (abs_path); + + return ret; +} diff --git a/server/upload-file.c b/server/upload-file.c index 9c75c80..7f17c99 100644 --- a/server/upload-file.c +++ b/server/upload-file.c @@ -909,6 +909,41 @@ error: } } +static int +create_relative_path (RecvFSM *fsm, char *parent_dir, char **abs_path) +{ + int rc = 0; + GError *error = NULL; + + char *relative_path = g_hash_table_lookup (fsm->form_kvs, "relative_path"); + if (relative_path != NULL && strcmp(relative_path, "") != 0) { + rc = seaf_repo_manager_mkdir_with_parents (seaf->repo_mgr, + fsm->repo_id, + parent_dir, + relative_path, + fsm->user, + &error); + if (rc < 0) { + if (error) { + seaf_warning ("[upload folder] %s.", error->message); + g_clear_error (&error); + } + goto out; + } + + *abs_path = g_new0 (char, SEAF_PATH_MAX); + if (!*abs_path) { + rc = -1; + goto out; + } + strcat (*abs_path, parent_dir); + strcat (*abs_path, relative_path); + } + +out: + return rc; +} + /* Handle AJAX file upload. @return an array of json data, e.g. [{"name": "foo.txt"}] @@ -921,6 +956,7 @@ upload_ajax_cb(evhtp_request_t *req, void *arg) GError *error = NULL; int error_code = ERROR_INTERNAL; char *filenames_json, *tmp_files_json; + int rc; evhtp_headers_add_header (req->headers_out, evhtp_header_new("Access-Control-Allow-Headers", @@ -980,16 +1016,24 @@ upload_ajax_cb(evhtp_request_t *req, void *arg) filenames_json = file_list_to_json (fsm->filenames); tmp_files_json = file_list_to_json (fsm->files); + char *abs_path = NULL; + rc = create_relative_path (fsm, parent_dir, &abs_path); + if (rc < 0) { + goto error; + } else if (abs_path) { + parent_dir = abs_path; + } + char *ret_json = NULL; - int rc = seaf_repo_manager_post_multi_files (seaf->repo_mgr, - fsm->repo_id, - parent_dir, - filenames_json, - tmp_files_json, - fsm->user, - 0, - &ret_json, - &error); + rc = seaf_repo_manager_post_multi_files (seaf->repo_mgr, + fsm->repo_id, + parent_dir, + filenames_json, + tmp_files_json, + fsm->user, + 0, + &ret_json, + &error); g_free (filenames_json); g_free (tmp_files_json); if (rc < 0) {