mirror of
https://github.com/haiwen/seafile-server.git
synced 2025-09-13 13:51:53 +00:00
Add byte-range upload.
This commit is contained in:
529
server/upload-file.c
Normal file → Executable file
529
server/upload-file.c
Normal file → Executable file
@@ -78,12 +78,19 @@ typedef struct RecvFSM {
|
||||
char *token_type; /* For sending statistic type */
|
||||
|
||||
gboolean need_idx_progress;
|
||||
|
||||
gint64 rstart;
|
||||
gint64 rend;
|
||||
gint64 fsize;
|
||||
} RecvFSM;
|
||||
|
||||
#define MAX_CONTENT_LINE 10240
|
||||
|
||||
static GHashTable *upload_progress;
|
||||
static pthread_mutex_t pg_lock;
|
||||
static int
|
||||
append_block_data_to_tmp_file (RecvFSM *fsm, const char *parent_dir,
|
||||
const char *file_name);
|
||||
|
||||
/* IE8 will set filename to the full path of the uploaded file.
|
||||
* So we need to strip out the basename from it.
|
||||
@@ -167,6 +174,24 @@ send_redirect_reply (evhtp_request_t *req)
|
||||
evhtp_send_reply(req, EVHTP_RES_SEEOTHER);
|
||||
}
|
||||
|
||||
static void
|
||||
send_success_reply_ie8_compatible (evhtp_request_t *req, evhtp_res code)
|
||||
{
|
||||
set_content_length_header (req);
|
||||
|
||||
const char *accept = evhtp_kv_find (req->headers_in, "Accept");
|
||||
if (accept && strstr (accept, "application/json") != NULL) {
|
||||
evhtp_headers_add_header (
|
||||
req->headers_out,
|
||||
evhtp_header_new("Content-Type", "application/json; charset=utf-8", 1, 1));
|
||||
} else {
|
||||
evhtp_headers_add_header (
|
||||
req->headers_out,
|
||||
evhtp_header_new("Content-Type", "text/plain", 1, 1));
|
||||
}
|
||||
evhtp_send_reply (req, code);
|
||||
}
|
||||
|
||||
static void
|
||||
redirect_to_upload_error (evhtp_request_t *req,
|
||||
const char *repo_id,
|
||||
@@ -370,37 +395,27 @@ file_list_to_json (GList *files)
|
||||
}
|
||||
|
||||
static int
|
||||
create_relative_path (RecvFSM *fsm, char *parent_dir, char **abs_path)
|
||||
create_relative_path (RecvFSM *fsm, char *parent_dir, char *relative_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;
|
||||
}
|
||||
if (!relative_path)
|
||||
return 0;
|
||||
|
||||
*abs_path = g_new0 (char, SEAF_PATH_MAX);
|
||||
if (!*abs_path) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
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);
|
||||
}
|
||||
strcat (*abs_path, parent_dir);
|
||||
strcat (*abs_path, relative_path);
|
||||
}
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -518,6 +533,7 @@ upload_api_cb(evhtp_request_t *req, void *arg)
|
||||
{
|
||||
RecvFSM *fsm = arg;
|
||||
char *parent_dir, *replace_str;
|
||||
char *relative_path = NULL, *new_parent_dir = NULL;
|
||||
GError *error = NULL;
|
||||
int error_code = ERROR_INTERNAL;
|
||||
char *filenames_json, *tmp_files_json;
|
||||
@@ -552,7 +568,7 @@ upload_api_cb(evhtp_request_t *req, void *arg)
|
||||
if (!fsm || fsm->state == RECV_ERROR)
|
||||
return;
|
||||
|
||||
if (!fsm->files) {
|
||||
if (!fsm->filenames) {
|
||||
seaf_debug ("[upload] No file uploaded.\n");
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "No file.\n");
|
||||
return;
|
||||
@@ -573,14 +589,65 @@ upload_api_cb(evhtp_request_t *req, void *arg)
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "Invalid URL.\n");
|
||||
return;
|
||||
}
|
||||
relative_path = g_hash_table_lookup (fsm->form_kvs, "relative_path");
|
||||
if (relative_path != NULL) {
|
||||
if (relative_path[0] == '/' || relative_path[0] == '\\') {
|
||||
seaf_warning ("Invalid relative path %s.\n", relative_path);
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "Invalid relative path.");
|
||||
return;
|
||||
}
|
||||
char *tmp_p = get_canonical_path(parent_dir);
|
||||
char *tmp_r = get_canonical_path(relative_path);
|
||||
new_parent_dir = g_build_path("/", tmp_p, tmp_r, NULL);
|
||||
g_free(tmp_p);
|
||||
g_free(tmp_r);
|
||||
} else {
|
||||
new_parent_dir = get_canonical_path(parent_dir);
|
||||
}
|
||||
|
||||
if (fsm->rstart >= 0) {
|
||||
if (fsm->filenames->next) {
|
||||
seaf_debug ("[upload] Breakpoint transfer only support one file in one request.\n");
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "More files in one request.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (parent_dir[0] != '/') {
|
||||
seaf_debug ("[upload] Invalid parent dir, should start with /.\n");
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "Invalid parent dir.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (append_block_data_to_tmp_file (fsm, new_parent_dir,
|
||||
(char *)fsm->filenames->data) < 0) {
|
||||
error_code = ERROR_INTERNAL;
|
||||
goto error;
|
||||
}
|
||||
if (fsm->rend != fsm->fsize - 1) {
|
||||
const char *success_str = "{\"success\": true}";
|
||||
evbuffer_add (req->buffer_out, success_str, strlen(success_str));
|
||||
send_success_reply_ie8_compatible (req, EVHTP_RES_OK);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fsm->files) {
|
||||
seaf_debug ("[upload] No file uploaded.\n");
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "No file.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!check_parent_dir (req, fsm->repo_id, parent_dir))
|
||||
return;
|
||||
goto out;
|
||||
|
||||
if (!check_tmp_file_list (fsm->files, &error_code))
|
||||
goto error;
|
||||
|
||||
gint64 content_len = get_content_length(req);
|
||||
gint64 content_len;
|
||||
if (fsm->fsize > 0)
|
||||
content_len = fsm->fsize;
|
||||
else
|
||||
content_len = get_content_length (req);
|
||||
if (seaf_quota_manager_check_quota_with_delta (seaf->quota_mgr,
|
||||
fsm->repo_id,
|
||||
content_len) != 0) {
|
||||
@@ -588,22 +655,18 @@ upload_api_cb(evhtp_request_t *req, void *arg)
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = create_relative_path (fsm, parent_dir, relative_path);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
|
||||
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;
|
||||
char *task_id = NULL;
|
||||
rc = seaf_repo_manager_post_multi_files (seaf->repo_mgr,
|
||||
fsm->repo_id,
|
||||
parent_dir,
|
||||
new_parent_dir,
|
||||
filenames_json,
|
||||
tmp_files_json,
|
||||
fsm->user,
|
||||
@@ -611,8 +674,6 @@ upload_api_cb(evhtp_request_t *req, void *arg)
|
||||
&ret_json,
|
||||
fsm->need_idx_progress ? &task_id : NULL,
|
||||
&error);
|
||||
if (abs_path)
|
||||
g_free (abs_path);
|
||||
g_free (filenames_json);
|
||||
g_free (tmp_files_json);
|
||||
if (rc < 0) {
|
||||
@@ -648,9 +709,27 @@ upload_api_cb(evhtp_request_t *req, void *arg)
|
||||
oper = "link-file-upload";
|
||||
send_statistic_msg(fsm->repo_id, fsm->user, oper, (guint64)content_len);
|
||||
|
||||
if (fsm->rstart >= 0 && fsm->rend == fsm->fsize - 1) {
|
||||
// File upload success, try to remove tmp file from WebUploadTmpFile table
|
||||
char *abs_path;
|
||||
|
||||
if (new_parent_dir[strlen(new_parent_dir) - 1] == '/') {
|
||||
abs_path = g_strconcat (new_parent_dir, (char *)fsm->filenames->data, NULL);
|
||||
} else {
|
||||
abs_path = g_strconcat (new_parent_dir, "/", (char *)fsm->filenames->data, NULL);
|
||||
}
|
||||
|
||||
seaf_repo_manager_del_upload_tmp_file (seaf->repo_mgr, fsm->repo_id, abs_path, NULL);
|
||||
g_free (abs_path);
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(new_parent_dir);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
g_free(new_parent_dir);
|
||||
switch (error_code) {
|
||||
case ERROR_FILENAME:
|
||||
send_error_reply (req, SEAF_HTTP_RES_BADFILENAME, "Invalid filename.\n");
|
||||
@@ -1000,6 +1079,118 @@ error:
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
static int
|
||||
copy_block_to_tmp_file (int blk_fd, int tmp_fd)
|
||||
{
|
||||
if (lseek(blk_fd, 0, SEEK_SET) < 0) {
|
||||
seaf_warning ("Failed to rewind block temp file position to start: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[8192];
|
||||
int buf_len = sizeof(buf);
|
||||
ssize_t len;
|
||||
|
||||
while (TRUE) {
|
||||
len = readn (blk_fd, buf, buf_len);
|
||||
if (len < 0) {
|
||||
seaf_warning ("Failed to read content from block temp file: %s.\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (writen (tmp_fd, buf, len) != len) {
|
||||
seaf_warning ("Failed to write content to temp file: %s.\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
append_block_data_to_tmp_file (RecvFSM *fsm, const char *parent_dir,
|
||||
const char *file_name)
|
||||
{
|
||||
char *abs_path;
|
||||
char *temp_file = NULL;
|
||||
GError *error = NULL;
|
||||
int tmp_fd = -1;
|
||||
int ret = 0;
|
||||
HttpServerStruct *htp_server = seaf->http_server;
|
||||
int cluster_shared_temp_file_mode = htp_server->cluster_shared_temp_file_mode;
|
||||
|
||||
if (parent_dir[strlen(parent_dir) - 1] == '/') {
|
||||
abs_path = g_strconcat (parent_dir, file_name, NULL);
|
||||
} else {
|
||||
abs_path = g_strconcat (parent_dir, "/", file_name, NULL);
|
||||
}
|
||||
|
||||
temp_file = seaf_repo_manager_get_upload_tmp_file (seaf->repo_mgr,
|
||||
fsm->repo_id,
|
||||
abs_path, &error);
|
||||
if (error) {
|
||||
seaf_warning ("%s\n", error->message);
|
||||
g_clear_error (&error);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!temp_file) {
|
||||
temp_file = g_strdup_printf ("%s/cluster-shared/%sXXXXXX",
|
||||
seaf->http_server->http_temp_dir,
|
||||
file_name);
|
||||
tmp_fd = g_mkstemp_full (temp_file, O_RDWR, cluster_shared_temp_file_mode);
|
||||
if (tmp_fd >= 0) {
|
||||
if (seaf_repo_manager_add_upload_tmp_file (seaf->repo_mgr,
|
||||
fsm->repo_id,
|
||||
abs_path, temp_file,
|
||||
&error) < 0) {
|
||||
seaf_warning ("%s\n", error->message);
|
||||
g_clear_error (&error);
|
||||
close (tmp_fd);
|
||||
g_unlink (temp_file);
|
||||
tmp_fd = -1;
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tmp_fd = g_open (temp_file, O_WRONLY | O_APPEND);
|
||||
}
|
||||
|
||||
if (tmp_fd < 0) {
|
||||
seaf_warning ("Failed to open upload temp file: %s.\n", strerror(errno));
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_block_to_tmp_file (fsm->fd, tmp_fd) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fsm->rend == fsm->fsize - 1) {
|
||||
// For the last block, record tmp_files for upload to seafile and remove
|
||||
fsm->tmp_files = g_list_prepend (fsm->tmp_files, g_strdup(temp_file)); // for cleaning up
|
||||
fsm->files = g_list_prepend (fsm->files, g_strdup(temp_file)); // for virus checking, indexing...
|
||||
}
|
||||
|
||||
out:
|
||||
g_free (abs_path);
|
||||
if (tmp_fd >= 0) {
|
||||
close (tmp_fd);
|
||||
}
|
||||
g_free (temp_file);
|
||||
close (fsm->fd);
|
||||
g_unlink (fsm->tmp_file);
|
||||
g_free (fsm->tmp_file);
|
||||
fsm->tmp_file = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
Handle AJAX file upload.
|
||||
@return an array of json data, e.g. [{"name": "foo.txt"}]
|
||||
@@ -1008,7 +1199,7 @@ static void
|
||||
upload_ajax_cb(evhtp_request_t *req, void *arg)
|
||||
{
|
||||
RecvFSM *fsm = arg;
|
||||
char *parent_dir;
|
||||
char *parent_dir = NULL, *relative_path = NULL, *new_parent_dir = NULL;
|
||||
GError *error = NULL;
|
||||
int error_code = ERROR_INTERNAL;
|
||||
char *filenames_json, *tmp_files_json;
|
||||
@@ -1016,7 +1207,7 @@ upload_ajax_cb(evhtp_request_t *req, void *arg)
|
||||
|
||||
evhtp_headers_add_header (req->headers_out,
|
||||
evhtp_header_new("Access-Control-Allow-Headers",
|
||||
"x-requested-with, content-type, accept, origin, authorization", 1, 1));
|
||||
"x-requested-with, content-type, content-range, content-disposition, accept, origin, authorization", 1, 1));
|
||||
evhtp_headers_add_header (req->headers_out,
|
||||
evhtp_header_new("Access-Control-Allow-Methods",
|
||||
"GET, POST, PUT, PATCH, DELETE, OPTIONS", 1, 1));
|
||||
@@ -1042,12 +1233,6 @@ upload_ajax_cb(evhtp_request_t *req, void *arg)
|
||||
if (!fsm || fsm->state == RECV_ERROR)
|
||||
return;
|
||||
|
||||
if (!fsm->files) {
|
||||
seaf_debug ("[upload] No file uploaded.\n");
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "No file.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
parent_dir = g_hash_table_lookup (fsm->form_kvs, "parent_dir");
|
||||
if (!parent_dir) {
|
||||
seaf_debug ("[upload] No parent dir given.\n");
|
||||
@@ -1055,13 +1240,72 @@ upload_ajax_cb(evhtp_request_t *req, void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!check_parent_dir (req, fsm->repo_id, parent_dir))
|
||||
if (!fsm->filenames) {
|
||||
seaf_debug ("[upload] No file uploaded.\n");
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "No file.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
relative_path = g_hash_table_lookup (fsm->form_kvs, "relative_path");
|
||||
if (relative_path != NULL) {
|
||||
if (relative_path[0] == '/' || relative_path[0] == '\\') {
|
||||
seaf_warning ("Invalid relative path %s.\n", relative_path);
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "Invalid relative path.");
|
||||
return;
|
||||
}
|
||||
char *tmp_p = get_canonical_path(parent_dir);
|
||||
char *tmp_r = get_canonical_path(relative_path);
|
||||
new_parent_dir = g_build_path("/", tmp_p, tmp_r, NULL);
|
||||
g_free(tmp_p);
|
||||
g_free(tmp_r);
|
||||
} else {
|
||||
new_parent_dir = get_canonical_path(parent_dir);
|
||||
}
|
||||
|
||||
if (fsm->rstart >= 0) {
|
||||
if (fsm->filenames->next) {
|
||||
seaf_debug ("[upload] Breakpoint transfer only support one file in one request.\n");
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "More files in one request.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (parent_dir[0] != '/') {
|
||||
seaf_debug ("[upload] Invalid parent dir, should start with /.\n");
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "Invalid parent dir.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (append_block_data_to_tmp_file (fsm, new_parent_dir,
|
||||
(char *)fsm->filenames->data) < 0) {
|
||||
error_code = ERROR_INTERNAL;
|
||||
goto error;
|
||||
}
|
||||
if (fsm->rend != fsm->fsize - 1) {
|
||||
const char *success_str = "{\"success\": true}";
|
||||
evbuffer_add (req->buffer_out, success_str, strlen(success_str));
|
||||
send_success_reply_ie8_compatible (req, EVHTP_RES_OK);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fsm->files) {
|
||||
seaf_debug ("[upload] No file uploaded.\n");
|
||||
send_error_reply (req, EVHTP_RES_BADREQ, "No file.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!check_parent_dir (req, fsm->repo_id, parent_dir))
|
||||
goto out;
|
||||
|
||||
if (!check_tmp_file_list (fsm->files, &error_code))
|
||||
goto error;
|
||||
|
||||
gint64 content_len = get_content_length (req);
|
||||
gint64 content_len;
|
||||
if (fsm->fsize > 0)
|
||||
content_len = fsm->fsize;
|
||||
else
|
||||
content_len = get_content_length (req);
|
||||
|
||||
if (seaf_quota_manager_check_quota_with_delta (seaf->quota_mgr,
|
||||
fsm->repo_id,
|
||||
content_len) != 0) {
|
||||
@@ -1069,13 +1313,9 @@ upload_ajax_cb(evhtp_request_t *req, void *arg)
|
||||
goto error;
|
||||
}
|
||||
|
||||
char *abs_path = NULL;
|
||||
rc = create_relative_path (fsm, parent_dir, &abs_path);
|
||||
if (rc < 0) {
|
||||
rc = create_relative_path (fsm, parent_dir, relative_path);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
} else if (abs_path) {
|
||||
parent_dir = abs_path;
|
||||
}
|
||||
|
||||
filenames_json = file_list_to_json (fsm->filenames);
|
||||
tmp_files_json = file_list_to_json (fsm->files);
|
||||
@@ -1084,7 +1324,7 @@ upload_ajax_cb(evhtp_request_t *req, void *arg)
|
||||
char *task_id = NULL;
|
||||
rc = seaf_repo_manager_post_multi_files (seaf->repo_mgr,
|
||||
fsm->repo_id,
|
||||
parent_dir,
|
||||
new_parent_dir,
|
||||
filenames_json,
|
||||
tmp_files_json,
|
||||
fsm->user,
|
||||
@@ -1092,8 +1332,6 @@ upload_ajax_cb(evhtp_request_t *req, void *arg)
|
||||
&ret_json,
|
||||
fsm->need_idx_progress ? &task_id : NULL,
|
||||
&error);
|
||||
if (abs_path)
|
||||
g_free (abs_path);
|
||||
g_free (filenames_json);
|
||||
g_free (tmp_files_json);
|
||||
if (rc < 0) {
|
||||
@@ -1114,29 +1352,34 @@ upload_ajax_cb(evhtp_request_t *req, void *arg)
|
||||
}
|
||||
g_free (ret_json);
|
||||
|
||||
// send_success_reply (req);
|
||||
set_content_length_header (req);
|
||||
if (fsm->rstart >= 0 && fsm->rend == fsm->fsize - 1) {
|
||||
// File upload success, try to remove tmp file from WebUploadTmpFile table
|
||||
char *abs_path;
|
||||
|
||||
const char *accept = evhtp_kv_find (req->headers_in, "Accept");
|
||||
if (accept && strstr (accept, "application/json") != NULL) {
|
||||
evhtp_headers_add_header (
|
||||
req->headers_out,
|
||||
evhtp_header_new("Content-Type", "application/json; charset=utf-8", 1, 1));
|
||||
} else {
|
||||
evhtp_headers_add_header (
|
||||
req->headers_out,
|
||||
evhtp_header_new("Content-Type", "text/plain", 1, 1));
|
||||
if (new_parent_dir[strlen(new_parent_dir) - 1] == '/') {
|
||||
abs_path = g_strconcat (new_parent_dir, (char *)fsm->filenames->data, NULL);
|
||||
} else {
|
||||
abs_path = g_strconcat (new_parent_dir, "/", (char *)fsm->filenames->data, NULL);
|
||||
}
|
||||
|
||||
seaf_repo_manager_del_upload_tmp_file (seaf->repo_mgr, fsm->repo_id, abs_path, NULL);
|
||||
g_free (abs_path);
|
||||
}
|
||||
evhtp_send_reply (req, EVHTP_RES_OK);
|
||||
|
||||
send_success_reply_ie8_compatible (req, EVHTP_RES_OK);
|
||||
|
||||
char *oper = "web-file-upload";
|
||||
if (g_strcmp0(fsm->token_type, "upload-link") == 0)
|
||||
oper = "link-file-upload";
|
||||
send_statistic_msg(fsm->repo_id, fsm->user, oper, (guint64)content_len);
|
||||
|
||||
out:
|
||||
g_free (new_parent_dir);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
g_free(new_parent_dir);
|
||||
switch (error_code) {
|
||||
case ERROR_FILENAME:
|
||||
send_error_reply (req, SEAF_HTTP_RES_BADFILENAME, "Invalid filename.");
|
||||
@@ -1783,6 +2026,10 @@ upload_finish_cb (evhtp_request_t *req, void *arg)
|
||||
g_free (fsm->file_name);
|
||||
if (fsm->tmp_file) {
|
||||
close (fsm->fd);
|
||||
// For resumable upload, in case tmp file not be deleted
|
||||
if (fsm->rstart >= 0) {
|
||||
g_unlink (fsm->tmp_file);
|
||||
}
|
||||
}
|
||||
g_free (fsm->tmp_file);
|
||||
|
||||
@@ -1827,8 +2074,41 @@ get_mime_header_param_value (const char *param)
|
||||
return value;
|
||||
}
|
||||
|
||||
static char *
|
||||
parse_file_name_from_header (evhtp_request_t *req)
|
||||
{
|
||||
const char *dispose = NULL;
|
||||
char **p;
|
||||
char **params;
|
||||
char *dec_file_name = NULL;
|
||||
|
||||
dispose = evhtp_kv_find (req->headers_in, "Content-Disposition");
|
||||
if (!dispose)
|
||||
return NULL;
|
||||
|
||||
params = g_strsplit (dispose, ";", 2);
|
||||
for (p = params; *p != NULL; ++p)
|
||||
*p = g_strstrip (*p);
|
||||
|
||||
if (g_strv_length (params) != 2 ||
|
||||
strcasecmp (params[0], "attachment") != 0 ||
|
||||
strncasecmp (params[1], "filename", strlen("filename")) != 0) {
|
||||
seaf_warning ("[upload] Invalid Content-Disposition header.\n");
|
||||
g_strfreev (params);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *file_name = get_mime_header_param_value (params[1]);
|
||||
if (file_name)
|
||||
dec_file_name = g_uri_unescape_string (file_name, NULL);
|
||||
g_free (file_name);
|
||||
g_strfreev (params);
|
||||
|
||||
return dec_file_name;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_mime_header (char *header, RecvFSM *fsm)
|
||||
parse_mime_header (evhtp_request_t *req, char *header, RecvFSM *fsm)
|
||||
{
|
||||
char *colon;
|
||||
char **params, **p;
|
||||
@@ -1872,7 +2152,11 @@ parse_mime_header (char *header, RecvFSM *fsm)
|
||||
char *file_name;
|
||||
for (p = params; *p != NULL; ++p) {
|
||||
if (strncasecmp (*p, "filename", strlen("filename")) == 0) {
|
||||
file_name = get_mime_header_param_value (*p);
|
||||
if (fsm->rstart >= 0) {
|
||||
file_name = parse_file_name_from_header (req);
|
||||
} else {
|
||||
file_name = get_mime_header_param_value (*p);
|
||||
}
|
||||
if (file_name) {
|
||||
fsm->file_name = normalize_utf8_path (file_name);
|
||||
if (!fsm->file_name)
|
||||
@@ -1906,13 +2190,16 @@ open_temp_file (RecvFSM *fsm)
|
||||
|
||||
fsm->fd = g_mkstemp (temp_file->str);
|
||||
if (fsm->fd < 0) {
|
||||
seaf_warning("[upload] Failed to open temp file: %s.\n", strerror(errno));
|
||||
g_string_free (temp_file, TRUE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fsm->tmp_file = g_string_free (temp_file, FALSE);
|
||||
/* For clean up later. */
|
||||
fsm->tmp_files = g_list_prepend (fsm->tmp_files, g_strdup(fsm->tmp_file));
|
||||
if (fsm->rstart < 0) {
|
||||
fsm->tmp_files = g_list_prepend (fsm->tmp_files, g_strdup(fsm->tmp_file));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1954,16 +2241,25 @@ recv_form_field (RecvFSM *fsm, gboolean *no_line)
|
||||
static void
|
||||
add_uploaded_file (RecvFSM *fsm)
|
||||
{
|
||||
fsm->filenames = g_list_prepend (fsm->filenames,
|
||||
get_basename(fsm->file_name));
|
||||
fsm->files = g_list_prepend (fsm->files, g_strdup(fsm->tmp_file));
|
||||
if (fsm->rstart < 0) {
|
||||
// Non breakpoint transfer, same as original
|
||||
fsm->filenames = g_list_prepend (fsm->filenames,
|
||||
get_basename(fsm->file_name));
|
||||
fsm->files = g_list_prepend (fsm->files, g_strdup(fsm->tmp_file));
|
||||
|
||||
g_free (fsm->file_name);
|
||||
g_free (fsm->tmp_file);
|
||||
close (fsm->fd);
|
||||
fsm->file_name = NULL;
|
||||
fsm->tmp_file = NULL;
|
||||
fsm->recved_crlf = FALSE;
|
||||
g_free (fsm->file_name);
|
||||
g_free (fsm->tmp_file);
|
||||
close (fsm->fd);
|
||||
fsm->file_name = NULL;
|
||||
fsm->tmp_file = NULL;
|
||||
fsm->recved_crlf = FALSE;
|
||||
} else {
|
||||
fsm->filenames = g_list_prepend (fsm->filenames,
|
||||
get_basename(fsm->file_name));
|
||||
g_free (fsm->file_name);
|
||||
fsm->file_name = NULL;
|
||||
fsm->recved_crlf = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static evhtp_res
|
||||
@@ -2111,7 +2407,7 @@ upload_read_cb (evhtp_request_t *req, evbuf_t *buf, void *arg)
|
||||
}
|
||||
seaf_debug ("[upload] Start to recv %s.\n", fsm->input_name);
|
||||
fsm->state = RECV_CONTENT;
|
||||
} else if (parse_mime_header (line, fsm) < 0) {
|
||||
} else if (parse_mime_header (req, line, fsm) < 0) {
|
||||
free (line);
|
||||
res = EVHTP_RES_BADREQ;
|
||||
goto out;
|
||||
@@ -2252,6 +2548,56 @@ check_access_token (const char *token,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_range_val (evhtp_headers_t *hdr, gint64 *rstart,
|
||||
gint64 *rend, gint64 *rfsize)
|
||||
{
|
||||
const char *tmp = evhtp_kv_find (hdr, "Content-Range");
|
||||
if (!tmp)
|
||||
return TRUE;
|
||||
|
||||
char *next = NULL;
|
||||
gint64 start;
|
||||
gint64 end;
|
||||
gint64 fsize;
|
||||
|
||||
if (strstr (tmp, "bytes") != tmp) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tmp += strlen("bytes");
|
||||
while (tmp && *tmp == ' ') {
|
||||
tmp++;
|
||||
}
|
||||
|
||||
start = strtoll (tmp, &next, 10);
|
||||
if ((start == 0 && next == tmp) || *next != '-') {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tmp = next + 1;
|
||||
end = strtoll (tmp, &next, 10);
|
||||
if ((end == 0 && next == tmp) || *next != '/') {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tmp = next + 1;
|
||||
fsize = strtoll (tmp, &next, 10);
|
||||
if ((fsize == 0 && next == tmp) || *next != '\0') {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (start > end || end >= fsize) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*rstart = start;
|
||||
*rend = end;
|
||||
*rfsize = fsize;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
get_progress_info (evhtp_request_t *req,
|
||||
evhtp_headers_t *hdr,
|
||||
@@ -2332,11 +2678,23 @@ upload_headers_cb (evhtp_request_t *req, evhtp_headers_t *hdr, void *arg)
|
||||
pthread_mutex_unlock (&pg_lock);
|
||||
}
|
||||
|
||||
gint64 rstart = -1;
|
||||
gint64 rend = -1;
|
||||
gint64 fsize = -1;
|
||||
if (!parse_range_val (hdr, &rstart, &rend, &fsize)) {
|
||||
seaf_warning ("Invalid Seafile-Content-Range value.\n");
|
||||
err_msg = "Invalid Seafile-Content-Range";
|
||||
goto err;
|
||||
}
|
||||
|
||||
fsm = g_new0 (RecvFSM, 1);
|
||||
fsm->boundary = boundary;
|
||||
fsm->repo_id = repo_id;
|
||||
fsm->user = user;
|
||||
fsm->token_type = token_type;
|
||||
fsm->rstart = rstart;
|
||||
fsm->rend = rend;
|
||||
fsm->fsize = fsize;
|
||||
fsm->line = evbuffer_new ();
|
||||
fsm->form_kvs = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, g_free);
|
||||
@@ -2464,6 +2822,15 @@ upload_file_init (evhtp_t *htp, const char *http_temp_dir)
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *cluster_shared_dir = g_strdup_printf ("%s/cluster-shared", http_temp_dir);
|
||||
if (g_mkdir_with_parents (cluster_shared_dir, 0777) < 0) {
|
||||
seaf_warning ("Failed to create cluster shared dir %s.\n",
|
||||
cluster_shared_dir);
|
||||
g_free (cluster_shared_dir);
|
||||
return -1;
|
||||
}
|
||||
g_free (cluster_shared_dir);
|
||||
|
||||
cb = evhtp_set_regex_cb (htp, "^/upload/.*", upload_cb, NULL);
|
||||
/* upload_headers_cb() will be called after evhtp parsed all http headers. */
|
||||
evhtp_set_hook(&cb->hooks, evhtp_hook_on_headers, upload_headers_cb, NULL);
|
||||
|
Reference in New Issue
Block a user